Mobile device tracking (which phone is connecting, and should it)
Your phone reaches Omniscio with a pairing token, and anything holding that token used to be able to connect with no idea which device it was. Now a new device is named and surfaced, you can block one without re-pairing everything, and a device has to be approved before it can do anything at all.
What it is
What it is (plain language)
When you turn on Mobile Access, Omniscio gives you a QR code / link secured by a pairing token so your phone can reach your desktop. That token is a bearer credential — until now, anything holding it could connect, with no awareness of which device. Mobile device tracking adds a recognition layer: Omniscio remembers each device that uses your token, trusts your first device silently, and raises an inbox alert the moment a new, unrecognized device connects — so if your token ever leaks, you find out and can act.
Where to find it
Two places: a "New device on your mobile access" inbox alert when an unseen device connects, and the device list behind Mobile Access in Settings, where you approve, block or review devices.
How it behaves
What you see
- A "New device on your mobile access" inbox alert when a device Omniscio hasn't seen before connects with your token. It names what the device looks like (e.g. "A new iPhone just connected…") and — on the desktop — shows the device inline in the inbox with one-click Approve, Trust and Block (and Unblock if it's already blocked), so you act right there without opening Settings; a small Manage devices link jumps straight to that device's row in Settings. It is inbox-only (it does not buzz your phone). On mobile web it shows a "manage on your desktop" note instead (the device controls are desktop-only).
- A Devices card in Settings → Remote Access listing every device that has used the
token: its type/name, richer device detail (browser + operating system, e.g. "iOS 17 ·
Safari"), when it was first and last seen, a network-origin label ("Local network" /
"Tailscale" / "This computer" / "Public internet"), and — when enabled — a coarse location
(city/region/country). Each row has:
- Trust — dismiss the "new" flag on a device you recognize (it won't alert again).
- Rename — give a device a friendly name.
- Block — refuse that one device without kicking everyone off (see below).
- A "Show where devices connect from" toggle (Settings → Remote Access, default ON). It controls the coarse-location lookup: with it on, each phone quietly asks an Omniscio cloud endpoint to turn its own public IP into a rough city/region (the same MaxMind geo engine that powers the "new sign-in location" security emails). Turn it OFF to stop the lookup entirely — the phone then makes NO cloud geo call and the card shows only device detail + network origin.
Where a device is connecting from (location)
The desktop's mobile server only ever sees a private address for a connecting phone (a
home-Wi-Fi 192.168.x, a Tailscale 100.x, or 127.0.0.1 behind the Tailscale proxy, private or public), so it
can't geolocate the phone itself. Instead the phone reports its own location: it fetches its
coarse city/region/country from the resolveMyGeo cloud endpoint (which reads the phone's real
public IP) and sends the result to the desktop over the already-authenticated WebSocket. This
works in every connection mode (a phone always has its own internet), and it's the useful
security signal — a home phone reads as your city; a remote leaked-link phone reads as somewhere
else. Only the coarse location is stored on the device row; the raw IP is never persisted.
How it recognizes a device
Omniscio assigns each device a hidden server-generated id stored in an HttpOnly
cookie (amc_device_id), issued the first time a device authenticates over the web
layer. It is not something the phone makes up (so a thief's fresh browser has no cookie
and shows up as new), and it survives your phone hopping between Wi-Fi and cellular (an
IP-based guess would false-alarm constantly). A device that can't carry a cookie falls
back to a coarser fingerprint of its browser + token.
The first device ever seen on a token is trusted silently — it's your existing phone, not "new." Only a second, unfamiliar device raises the alarm. Re-pairing your own device after you regenerate the token does not false-alarm, because the device keeps its stable id.
Blocking vs. regenerating
- Block adds one device to a denylist the server checks at connect time, so that device is refused (on both the normal and the freeze-proof off-loop connection paths) while your other devices keep working — no re-pairing required.
- Regenerate token (in Mobile Access) remains the full kill-switch: it invalidates the token so every device must re-pair. It's the real remedy for a confirmed leak.
- Forget paired devices now also clears the device registry AND the in-memory approval map.
Device approval — the second factor (mobile 2FA)
The tracking above is DETECTION. Device approval is PREVENTION, and it is ON by default: a phone that connects with the pairing token is inert until you APPROVE that device from your signed-in desktop. Every mobile request — reads, actions, AND the live feed, over BOTH the live socket and plain web (HTTP) requests — is refused for an unapproved device, so a leaked pairing token alone can do nothing.
- You approve from the desktop only. The "Approve your device" inbox alert shows the device inline with one-click Approve / Block (desktop), and the Devices card has the same Approve / Revoke approval controls; approving is a desktop action (the approve channel is blocked from the phone bridge), so a phone can never approve itself. That desktop step — taken while you are signed into your account — IS the second factor.
- Your existing phones keep working. Devices already recognized are grandfathered in, so you are not locked out on upgrade; a brand-new device needs one approval.
- Only you can grant a device — an AI agent can only ask. An agent working for you can't approve, trust or unblock a device itself (every agent on the computer can reach Omniscio's control server, so it can't stand in for you). When one asks to approve or trust a device, the device's own inbox card appears — one card per device, however often it asks — and nothing changes until you click Approve or Trust in it. An agent can't unblock a device at all: you do that in the device list. Taking access away (blocking, revoking an approval) still works from an agent, since that only makes things safer.
- The phone tells you what's happening. An unapproved phone shows a "waiting for approval — approve this device from your desktop" banner instead of a silently broken screen; it clears the moment you approve the device.
- And the desktop never just goes quiet. If nobody is signed in on this computer, approving isn't possible yet — so instead of showing nothing, the same inbox row appears as "Sign in to use mobile access", telling you to sign in here and then approve the device. The card swaps the Approve button for a "sign in on this computer to approve" note (Block still works — blocking never needed an account). Before this, a signed-out desktop meant mobile was completely dead with an empty inbox and no explanation anywhere.
- Two toggles (Settings → Remote Access, both default on, both CLI-blocked): Require device approval (the master switch) and Keep existing devices approved (turn off to make grandfathered devices re-approve — a testing lever).
- Cookie-only + fail-closed. Only a device with a real random
amc_device_idcookie is approvable (the coarse UA+token fingerprint is guessable, so it is never authorized); an unresolved device or a signed-out owner is denied. The per-request check is a pure in-memory lookup — no crypto, no network, no per-tap slowdown.
Honest limits
- With Device approval ON (the default), a leaked token alone is useless — the thief's device shows up unapproved and can do nothing (no actions, no live feed, and no reading the bootstrap snapshot or any file/media route over HTTP either) until you approve it from your desktop. The limits below describe the tracking layer on its own (approval off).
- Tracking alone is detection, not prevention. A determined thief who clears their cookie reappears as a new device — which simply re-alerts you.
- It defends against the common case (the token alone leaking), not theft of your entire browser session (token and the device cookie together).
- Trust-on-first-use assumes the token isn't already leaked when tracking begins; the auditable Devices list is how you'd catch an unexpected trusted device.
- The shown location is phone-supplied, so a sophisticated malicious client could FORGE it to look local. It is a decision-AID only — the fail-closed device-approval gate never trusts it, so a forged location cannot approve a device (approval is a desktop-only action). The "Show where devices connect from" toggle disables the lookup + hides location entirely.
For agents
For agents / the code
- Identity + cookie:
src/main/services/web/web-access-http-auth-cookie.ts(amc_device_id,resolveWsDeviceIdentity). Issued inrefreshAuthCookie(web-access-http-routes.ts) + POST /login. - Detection + alert + block denylist:
src/main/services/web/web-access-device-store.ts; DB layersrc/main/db/queries-web-access-devices.ts(tableweb_access_devices,approved_uid+grandfatheredcolumns). - Coarse geolocation (phone-reported): the phone fetches its own city/region/country from the
resolveMyGeocloud fn (firebase/functions/src/global-auth/user-geo/resolve-my-geo.ts, reusingresolveGeo/getGeoReader;/t/resolve-my-geohosting rewrite; CSPconnect-srcallowshttps://agentmc-shares.web.app). Renderer reports it viasrc/renderer/src/lib/report-device-geo.ts→ thedevice-geoWS frame (web-access-ws-schema.ts) →handleDeviceGeoFrame(web-access-ws-frames.ts, in-main path only for v1; the off-loop worker ignores it → network-label fallback) →recordDeviceGeo→setWebAccessDeviceGeo(geo_country/geo_region/geo_city/ geo_captured_atcolumns — coarse only, never the raw IP). Gated bymobileDeviceLocationEnabled(default on, CLI-flippable; carried in WEB_BOOTSTRAP so the phone reads it at first paint). - Enforcement at connect (block denylist):
web-access-ws.ts(in-main) +web-access-host-entry.ts(off-loop worker, via a mirrored denylist overhost-bridge-protocol.ts). - Device-approval gate (mobile 2FA):
src/main/services/web/web-access-device-approval.ts— fail-closed, cookie-only, owner-scoped, in-memory. Enforced on EVERY request: in-main invoke (web-access-ws-frames.ts) + push feed (web-access-push-forwarder.ts), off-loop invoke (web-access-host.ts) + feed (web-access-host-entry.ts, via a mirroredauthorizedDeviceIdsset), AND the HTTP layer (web-access-http-routes.tsviahttpRequestDeviceApprovalBlocked— skips the authed-route dispatcher for a blocked device so/bootstrap+ every file/media route is refused, while the static app shell still serves so the phone can boot).requireMobileDeviceApprovalis FORCED ON — its value can no longer disable the gate (the Settings toggle is shown locked-on);grandfatherExistingMobileDevices(default on) still lets already-approved / pre-existing devices through so existing phones keep working. Both are inSETTINGS_PATCH_BLOCKED; the ONLY off-switch for device approval is the env kill switchAMC_DISABLE_WEB_ACCESS_DEVICE_APPROVAL(test harness / QA fleet). The sibling account second factor (requireMobileAccountLogin) is NOT also forced on — it is the OPPOSITE: device approval SUPERSEDES it, soaccountVerifyEnabled()returns false whenever device approval is enabled, which is every production install. It is inert here and survives only as the fallback when device approval is env-kill-switched (test/QA). Device approval is the mobile second factor. - Pending-state UX: the phone shows a "waiting for approval" banner
(
src/renderer/src/components/ui/DeviceApprovalBanner.tsx, mounted inAppBanners.tsx) while inert, and adeviceApprovalRequiredbootstrap failure is suppressed from the "could not connect" toast (useAppBootstrap.ts) — the banner is the messaging. The owner reviews via the inbox card's inline Approve / Block (src/renderer/src/features/alerts/AlertMobileDeviceCard.tsx, wired inAlertInboxViewer.tsxviause-alert-actions.ts, desktop-only); thealert-actions/mobile-web.tssettings action is kept as the directive-guard's declared action and backs the card's "Manage devices" link, which deep-links to the device's own Settings row (web-access-device-row-<id>, routed to the Devices tab byWebAccessSettings-tabs.ts). - Settings UI:
src/renderer/src/features/settings/sections/web-access/WebAccessDevicesCard.tsx. - The five management channels (
WEB_ACCESS_DEVICES_LIST/_DEVICE_TRUST/_DEVICE_RENAME/_DEVICE_REVOKE/_DEVICE_APPROVE) are Zod-validated, have headless CLI routes under/web-access/devices*, and are blocked from the phone bridge — device management + approval are desktop-only. - Full invariants:
.claude/memory/contracts/web-access-device-tracking-contract.md(tracking).claude/memory/contracts/web-access-device-approval-contract.md(the 2FA gate).
Related
- mobile-remote-access.md — the phone access these devices are connecting to.
- mobile-remote-access.md — getting a phone connected in the first place.
- inbox-alerts.md — the alert surface the new-device notice arrives on.
Last verified 2026-09-28