Conversation
…anism to it
Ten inaccuracies the audit found in the remote group, and eight sections whose
mechanism belongs at the module that carries it.
Corrections: the direct handoff deadline is the setup deadline (15 s), not five
seconds; the Relay's challenge issuers are the one unkeyed transient store, so
Guardrails states the exception rather than a universal claim; the setup-token
mint answers `{ token, expiresAt }`, meaning no origin and no URL rather than
"the token alone"; the runtime file's placement outside the state dir is the
installers' convention, not a Relay check; push is switched off by a missing
VAPID subject alone, since the key is always minted; the install notice
deliberately omits push; the scanner has two camera lines, not one; the
Burrows-view install notice is gated on push not being disabled; ImageAddon
loads only where `inlineImages` is on; the pairing prologue puts `burrowId`
ahead of the version the QR leads with; the push upsert response is
VAPID-current; a passkey label's control characters become spaces before the
bidi set is dropped; `DORMOUSE_ORIGIN` must be http(s); the root-build pin named
a test that pins only the assertion's own fixtures. Pocket's phone-side
selfhost-onboarding item is done, so it leaves `## Future`, and only
`getUserMedia` in a Home Screen web app is still unverified on a device.
`paneRef` is marked reserved — it equals `surfaceId` and no Client reads it —
alongside `focused` and `exitCode`, which have no Client reader either.
The QR panel, the Burrow status store, the connect-src build guards, the Pocket
storage probe and the capability harness keep their bolded rules and their
`Source of truth:` pointers; their mechanism now lives in comments at the code,
and the harness gets its own heading, since it is not serving. Two code comments
cited scopes that no longer exist.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…d the direct-path queue bound Three duplications the audit found, each of one fact expressed twice. `MobileTerminalUi` carried `activeSection` / `defaultSection` / `onSectionChange` beside `activeKeyboardMode` / `defaultKeyboardMode` / `onKeyboardModeChange` for one piece of state, resolved through `??` ladders. The `*Section` trio had no production consumer, only Storybook; it is gone, and the stories drive the keyboard-mode props directly. The per-pane mouse override — Select mode plus a pane that reports mouse events means `permanent`, everything else `off` — was written out in Pocket's wall and again in the website playground. `lib` now exports `paneMouseOverride`, both compositions loop over their panes calling it, and `mobile-terminal-ui.md` gains a real `Source of truth:` for a rule it previously stated only in prose. `MAX_DIRECT_OUTBOUND_BYTES` / `_FRAMES` were literal aliases of `MAX_DIRECT_PENDING_BYTES` / `_FRAMES`, feeding the same `DirectFrameQueue` class, and the Burrow-bounds table spent two rows saying so. One pair of constants now bounds both directions, documented where they are declared. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Deploying mouseterm with
|
| Latest commit: |
4a76d97
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://e8286d07.mouseterm.pages.dev |
| Branch Preview URL: | https://specs-remote-audit.mouseterm.pages.dev |
dormouse-bot
left a comment
There was a problem hiding this comment.
Feedback on work in progress, not a merge verdict — mark the PR ready when you want the full review. The corrections I spot-checked against code all hold: DIRECT_HANDOFF_TIMEOUT_MS = DIRECT_SETUP_TIMEOUT_MS, { token, expiresAt }, the two camera lines, the VAPID-subject-only switch, the needs-install push-disabled gate, the prologue field order against pairingInvitationFields, paneRef === surfaceId with no Client reader for focused/exitCode, and the facts dropped from pocket-app.md -> "What Pocket stores" (nonextractable keys, probe-database deletion) are each still owned elsewhere and pinned by pocket-key-storage.test.ts. Two of the condensations say something the code doesn't, both inline.
One pointer outside the diff went stale in the same edit: SELF_HOST.md (in "Storage compatibility failures", the line reading "The diagnostic contract is docs/specs/pocket-app.md -> "Serving the built bundle"") now names the parent section rather than the new ### The capability harness that owns the contract — the same heading the new capabilities.js comment cites. Happy to push that one-line fix if you want it.
Budget note: the two inline rewords take docs/specs/relay.md to ~10,111 words, past the 10,100 this PR sets, so they need node scripts/spec-lint.mjs --ratchet docs/specs/relay.md (10,150) in the same edit — or a compensating cut in that section.
| error boundary**, so neither a Relay swap nor a refused encode reaches the | ||
| app-wide ErrorBoundary. |
There was a problem hiding this comment.
The keying and the boundary have different consequences, and this attributes the boundary's to both. A Relay swap never throws — EnrolledView's key={state.status.burrowId ?? state.status.relayUrl ?? 'enrolled'} carries the comment "must not leave a setup code, or an error, belonging to the machine we just left", so what the keying buys is dropping the stale code, not staying out of the app-wide ErrorBoundary. ScannableCode's boundary is what keeps a failed chunk fetch or a refused encode off it.
| error boundary**, so neither a Relay swap nor a refused encode reaches the | |
| app-wide ErrorBoundary. | |
| error boundary**: a Relay swap drops the stale code, and a refused encode | |
| costs a retry rather than the app-wide ErrorBoundary. |
| exception** — one flat `MAX_PENDING_CHALLENGES` map apiece, whose oldest entry | ||
| any unauthenticated caller can evict at the cost of one ceremony's retry | ||
| (WebAuthn below; rationale). |
There was a problem hiding this comment.
Only signinChallenges is reachable unauthenticated. POST /api/setup/begin runs readSetupGated<SetupBeginRequest>(c, 'peek') and returns its Response before setupChallenges.issue(), so evicting from that map costs a live setup token — which is why the comment above the two constructions singles out sign-in ("POST /api/signin/begin needs no auth and no body"). As written the exception grants both maps the weaker property.
| exception** — one flat `MAX_PENDING_CHALLENGES` map apiece, whose oldest entry | |
| any unauthenticated caller can evict at the cost of one ceremony's retry | |
| (WebAuthn below; rationale). | |
| exception** — one flat `MAX_PENDING_CHALLENGES` map apiece, whose oldest entry | |
| a caller past that route's gate can evict at the cost of one ceremony's retry, | |
| and sign-in has no gate (WebAuthn below; rationale). |
Spec audit, group 4 (remote-security-model / remote-api / relay / pocket-app / mobile-terminal-ui). Stacked on #711 (the spec-audit PRs merge in order #708 → #715); independent of the alert series. One minimal wording change in
security-remote.mdline 239 because the symbols it named are deleted below;pnpm lint:e2eis green.Commit 1 — docs and comments. Corrections:
DIRECT_HANDOFF_TIMEOUT_MSis 15 000 (=DIRECT_SETUP_TIMEOUT_MS), not 5 000; the Guardrails rule now says "every keyed store" and names the two challenge issuers as the accepted exception; a stale Future item the scope owner already calls done;{ token, expiresAt }; the runtime-file location is an installer fact, not a Relay check; the install notice deliberately omits push; two scanner error lines, not one; the live VAPID rule (only a missing subject withholds the sender); seven smaller precision fixes. Condensed: Settings-dialog QR mechanics, the Pocket DB probe ladder, the diagnostics harness (now its own heading), duplicate direct-path rules across the two remote specs.Commit 2 — code.
MobileTerminalUiloses theactiveSection/defaultSection/onSectionChangetrio (Storybook-only).paneMouseOverride(touchMode, reporting)replaces the rule implemented twice inPocketWalland the website's Pocket experience; tests added.MAX_DIRECT_OUTBOUND_*were literal aliases ofMAX_DIRECT_PENDING_*; deleted.Budgets: remote-security-model 4800→4750, relay 10200→10100.
For the owner (deferred, needs a security-model decision first): re-pairing appends an ACL record forever (
BurrowAcl.approvepushes unconditionally), and the Burrow seals one push envelope per active record, so every re-pair adds a permanently failing push recipient.pairing.mdx§9 already documents the visible half. Also deferred: whether to keepDORMOUSE_REQUIRE_USER_VERIFICATION.🤖 Generated with Claude Code