Skip to content

Devices: tell same-machine rows apart by origin and key fingerprint - #85

Merged
karngyan merged 1 commit into
mainfrom
worktree-devices-origin-71
Aug 17, 2026
Merged

Devices: tell same-machine rows apart by origin and key fingerprint#85
karngyan merged 1 commit into
mainfrom
worktree-devices-origin-71

Conversation

@karngyan

Copy link
Copy Markdown
Contributor

Closes #71.

What

A Devices screen could list four rows all reading Browser on karn.local, with nothing but a paired-at and a last-seen date to tell them apart. This is the trust model working as designed — the device key is scoped to the browser origin — so the fix is to surface the fact that differs, not to change enrolment:

  • The daemon records the Origin header of the enrolment POST on the local registry row (crypto.Device.Origin), carries it on the wire (wire.DeviceInfo.Origin, omitempty), and the Devices row shows it as a place — 127.0.0.1:7719, localhost:7719 — with the scheme dropped.
  • Every row shows a short fingerprint: the first six characters of the device id, which is already a digest of the key. This is what tells apart rows that have no origin, e.g. this machine's browser rows as they appear on a sibling machine.

Decisions on the issue's open points

  • Hint, not identity. A row with no origin renders as ordinary: no placeholder, no warning. That is the state of every QR-paired phone, every fleet-admitted row, and every row from before this change.
  • Local row, not the certificate. The origin is a fact about how this machine was reached; it is never minted into the cert, so nothing about the fleet trust model moves.
  • First-seen, not latest. DeviceStore.SetOrigin writes once and refuses overwrites, so a tab that later loads from the daemon's other spelling does not rewrite where it enrolled. One value is enough because enrolment is the only moment an origin is known.
  • Older rows backfill themselves. The browser enrols on every page load, so a pre-origin row picks its origin up on its next load; nothing is singled out in the meantime.

Worth knowing: the vite dev server's proxy rewrites Origin to the daemon's own (web/vite.config.ts), so the vite tab records 127.0.0.1:7719 — same as the direct tab. Those two remain distinguishable only by fingerprint, which is part of why the fingerprint ships alongside the origin.

Not in this PR

The issue's secondary suggestions — connected-right-now and a truer last-seen. Loopback sockets carry no device identity (the session cookie is the credential, and wire.Hello names no device), so "connected now" cannot be answered for exactly the rows this issue is about without a protocol change, and it needs its own broadcast machinery. Better as a follow-up.

Tests

  • TestSetOriginKeepsTheFirstOrigin (store semantics), TestEnrolRecordsWhereTheBrowserCameFrom (record, wire carry, no-overwrite, backfill).
  • Web: rows distinguishable by origin and fingerprint; a row with no origin renders as ordinary.
  • Full go test ./..., pnpm vitest run (1465 tests, includes the Tailwind prose-scanner build guard), and pnpm run lint all pass.

🤖 Generated with Claude Code

A browser's device key lives in IndexedDB, which the browser scopes to
the origin, so the same browser reaching the same daemon from
127.0.0.1, localhost and the vite dev server enrols as three devices —
and their rows all read "Browser on <host>" with nothing but dates to
tell them apart.

Record the Origin header of the enrolment POST on the local registry
row and show it on the Devices row, beside a short fingerprint of the
device id. First seen wins; older rows are backfilled on their next
load (the browser enrols on every load); the origin stays out of the
certificate, because it is a fact about how this machine was reached
rather than about the device; and a row with no origin — paired by QR,
admitted on a fleet cert — renders as ordinary.

Closes #71

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@karngyan
karngyan merged commit d207e63 into main Aug 17, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Devices rows for the same machine are indistinguishable, and Revoke is the only verb

1 participant