
Research
/Security News
PolinRider Spreads Through Compromised GitHub Accounts and Packagist
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.
@device-router/probe
Advanced tools
Lightweight (~1 KB gzipped) client-side probe for collecting device capability signals
A ~1 KB (gzipped) client-side probe that collects device capability signals via browser APIs for DeviceRouter.
pnpm add @device-router/probe
The probe runs once per session in the browser. It collects device signals using standard browser APIs, then POSTs them to your server's probe endpoint. A session cookie prevents repeated collection.
Browser Server
│ │
│ collectSignals() │
│ getBattery() │
│ │
│ POST /device-router/probe │
│ ─────────────────────────────────────> │
│ { hardwareConcurrency: 8, │
│ deviceMemory: 8, ... } │
│ │
│ { sessionToken: "abc123" } │
│ <───────────────────────────────────── │
│ │
│ Set cookie: device-router-session=abc123 │
└────────────────────────────────────────────┘
The simplest approach — include the pre-built bundle:
<script src="/device-router-probe.min.js"></script>
The probe auto-executes on load. Serve the file from dist/device-router-probe.min.js.
Middleware packages can auto-inject the probe into HTML responses:
const { middleware, probeEndpoint, injectionMiddleware } = createDeviceRouter({
storage,
injectProbe: true,
});
import { runProbe } from '@device-router/probe';
await runProbe({
endpoint: '/device-router/probe', // default
cookieName: 'device-router-session', // default
cookiePath: '/', // default
});
import { runProbeWithRetry } from '@device-router/probe';
await runProbeWithRetry({
endpoint: '/device-router/probe',
retry: {
maxRetries: 3, // default: 3
baseDelay: 500, // default: 500ms
maxDelay: 5000, // default: 5000ms
},
});
Uses exponential backoff with jitter on network failure. Signals are collected once before the retry loop. Does not affect the IIFE bundle size.
| Signal | API | Browser Support |
|---|---|---|
| CPU cores | hardwareConcurrency | All modern browsers |
| Device memory | deviceMemory | Chromium |
| Connection info | navigator.connection | Chromium |
| User agent | navigator.userAgent | All browsers |
| Viewport dimensions | window.innerWidth/Height | All browsers |
| Pixel ratio | devicePixelRatio | All browsers |
| Prefers reduced motion | matchMedia | All modern browsers |
| Color scheme | matchMedia | All modern browsers |
| GPU renderer | WebGL debug info | Most browsers |
| Battery status | navigator.getBattery() | Chromium |
All signals are optional — the probe gracefully degrades based on what the browser supports. Unavailable APIs are silently skipped.
The IIFE bundle is strictly capped at 1024 bytes gzipped. This is enforced at build time — the build fails if the limit is exceeded.
runProbe(options?) — Run the probe (async, idempotent per session)collectSignals() — Collect all synchronous device signalsProbeSignals — Type for the collected signal objectProbeOptions — Configuration type for runProberunProbeWithRetry(options?) — Run the probe with retry on failureRetryOptions — Retry configuration typeProbeWithRetryOptions — Configuration type for runProbeWithRetryIndividual collectors are also exported for selective use:
collectHardwareConcurrency()collectDeviceMemory()collectConnection()collectUserAgent()collectViewport()collectPixelRatio()collectPrefersReducedMotion()collectPrefersColorScheme()collectGpuRenderer()MIT
FAQs
Lightweight (~1 KB gzipped) client-side probe for collecting device capability signals
We found that @device-router/probe demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.

Security News
GitHub Actions now supports cache-mode, a least-privilege control on the Actions cache aimed at the cache poisoning technique behind recent compromises.

Company News
Allow myself to introduce... myself.