Skip to content

feat(mobile): per-device pairing tokens - #27

Merged
Eyalm321 merged 1 commit into
mainfrom
feat/mobile-per-device-tokens
Jul 19, 2026
Merged

feat(mobile): per-device pairing tokens#27
Eyalm321 merged 1 commit into
mainfrom
feat/mobile-per-device-tokens

Conversation

@Eyalm321

Copy link
Copy Markdown
Owner

Why

For a public "pair your own host" mobile app, hyperpanes pair was handing every phone the master token. This wires pairing to mint a per-device credential instead, so the master token never leaves the host and each device can be listed and revoked independently.

The existing scoped-token path (POST /tokens) is the wrong tool for this: a scope is a whitelist of the panes that exist now, so a scoped token would go blind the moment a new pane opens — and scoped tokens are in-memory only (die on restart). A phone is a full remote head that must survive restarts, so device tokens are a new class: unscoped (full authority) but named, revocable, TTL-able, and persisted.

What

  • hyperpanes pair [--device <label>] [--ttl <30d|12h|90m|<ms>>] — POSTs the new master-only /devices endpoint, mints a device token, embeds that in the QR. TTL omitted = never expires.
  • hyperpanes devices — list paired clients by label (tokens never echoed).
  • hyperpanes revoke <label> — drop one without disturbing the others.
  • Persisted to device-tokens.json (0600, beside control.json), reloaded on start → pairing survives a host restart, same as the master token on remote binds.
  • POST/GET/DELETE /devices are all master-only — a sandboxed agent's scoped token can't mint or enumerate device creds.

The Flutter app needs no change (a device token is a bearer token like any other).

Files

  • Core: control/tokens.rs (device methods + resolve/TTL/clear), persistence/device_tokens.rs (new), persistence/paths.rs, control/server.rs (load on start), control/routes.rs (endpoints).
  • App CLI: control_cli.rs (new — connect/base-url/ttl helpers), pair.rs (mint via API), devices.rs (new), main.rs (dispatch + usage).
  • Docs: docs/mobile-client-plan.md security model, mobile README.md.

Tests

  • 7 TokenStore device tests (mint/resolve/TTL-prune/revoke-by-label/list/clear), 3 persistence round-trip tests, control_cli base-url + TTL-parse tests, and a /devices HTTP round-trip (mint → full-authority on /state → list-omits-token → 403 for scoped token → revoke → 401).
  • Full suites green: core 593 + app 147; clippy + fmt clean (core), no new app-crate warnings.

🤖 Generated with Claude Code

…manage

`hyperpanes pair` no longer hands the phone the master token. It POSTs the new
master-only `/devices` endpoint to mint a distinct, labelled, full-authority
(unscoped) token and embeds THAT in the QR, so the master credential never leaves
the host.

A scope is a whitelist of the panes that exist now, so it can't serve a full remote
head that must reach panes opened later — hence device tokens are unscoped, but:
  * individually revocable   — `hyperpanes revoke <label>` / DELETE /devices?label=
  * listable                 — `hyperpanes devices` / GET /devices (never echoes tokens)
  * optionally TTL'd         — `hyperpanes pair --ttl 30d`
  * persisted                — device-tokens.json (0600, beside control.json),
                               reloaded on start, so pairing survives a host restart
                               (the guarantee the master token gets on remote binds).

All three /devices verbs are master-only, so a sandboxed agent's scoped token can
never mint or enumerate device credentials.

Core: TokenStore device methods + resolve/TTL/clear; persistence/device_tokens.rs;
server loads the table on start; routes for POST/GET/DELETE /devices.
App CLI: control_cli.rs (connect/base-url/ttl helpers), pair.rs mints via the API,
new devices.rs (`devices`/`revoke`), main.rs dispatch + usage.

Tests: 7 TokenStore device tests, 3 persistence tests, control_cli/base-url/ttl
tests, and a /devices HTTP round-trip (mint → full-authority → list → 403 for scoped
→ revoke → 401). Docs: mobile-client-plan.md security model + mobile README.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Eyalm321
Eyalm321 merged commit 3d43776 into main Jul 19, 2026
27 checks passed
@Eyalm321
Eyalm321 deleted the feat/mobile-per-device-tokens branch July 19, 2026 18:05
Eyalm321 added a commit that referenced this pull request Jul 20, 2026
…r pattern

Since v0.0.24:
  * feat(mobile): per-device pairing tokens — pair mints, devices/revoke manage (#27)
  * docs(goals): advisor + plan-big/execute-small patterns in agent personas (#28)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant