Repository navigation
A person's approved key opens a fragment's pages: POST /api/f/{name}/session - #1032
Conversation
…session A native app has no browser session. Its approved NIP-98 key now asks for the same one-use, one-fragment __signin redemption /auth/fragment mints for a browser, with the same consent: silent on the person's own and shared fragments, asked first on anyone else's, the yes remembered. The registry mints it from a parent session of the key's own (one a key, sessions.device_key, schema step 9). Its token is never kept, so it is no bearer session. Each site session redeemed from it, and each unspent redemption, lives only while the key is still its person's: revoking the key ends them and nothing else. An agent's key is refused (403). The new `native` e2e section covers refusals, one use, wrong fragment, consent, and revocation isolation; restart checks a key's site session outlives a restart. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
futurepaul
left a comment
There was a problem hiding this comment.
Codex review @ 6b68df5: PASS
Checked signed URL/method/body binding, person-only key resolution, confined returnTo, and the existing consent path: no credential reaches a stranger's app before consent, and the internal key parent's random bearer token is discarded rather than exposed. Both live_session and redemption validate the active key/identity binding; parent creation awaits only storage operations under Cloudflare's storage concurrency guarantees, and schema step 9 plus the release/schema checks prevent older registry code from accepting these rows. Local cargo xtask check passed, as did direct probes using the actual migrations and SQL for active/revoked/missing/reassigned keys, missing parents and browser-session isolation; I found no obvious large Wasm saving beyond the shared Mint/mint_for path, did not rerun e2e or measure Wasm, and CI's check is still running.
alexlwn123
left a comment
There was a problem hiding this comment.
Pass within the reviewed scope. No actionable regression found at 6b68df5cf280be4f6df8cf8727dcf83b9cd29a5c against bcc50fcd87addc3bd56a9cc06995d7cb0ba048cd.
Reviewed signed method/URL/body binding, person-only key resolution, consent reuse, return-path confinement, key-parent lifetime, revocation isolation, wipe behavior, and schema/release compatibility. An independent second source review found no concrete defect.
Validation: cargo test --locked -p fragment-core migrate:: (24 passed), cargo test --locked -p fragment-core site:: (11 passed), and git diff --check. Additional disposable SQLite probes using the actual migrations and session queries passed browser-session preservation, revocation isolation, expiry, ownership mismatch, absent-parent, and unique-key-parent checks. Those probes mirror the Rust rejection predicate; they do not execute the Worker handlers.
Coverage limits: the managed Docker daemon responds, but a disposable container's DNS lookup of index.crates.io fails with Connection refused / no reachable nameserver. The standard container-backed native, signin, identities, frames, and restart e2e lanes were not run. No workerd, live Cloudflare/vendor, or browser/WebView execution is claimed; fixtures were synthetic local SQLite data. No UI files changed, so no screenshots were required. CI check passed; the green e2e job is the PR pass-through and its shards were skipped. This is a COMMENT review, not an approval.
The server half of #1026 (split from that draft, which stays as it is): a person's approved key opens a fragment's pages, so a native app with no browser session can open private apps and brains in a web view.
What.
POST /api/f/{name}/session, NIP-98 signed by a person's approved key, takes{returnTo?, consent?}. It answers{consentRequired: false, url}, the fragment's one-use, 60-second__signinredemption, or{consentRequired: true, canonical, email}with no credential./auth/fragment: silent on the person's own and shared fragments, asks first on anyone else's, and remembers the yes.mint_forand the same registryMintcall.Mintnow names atoken(a browser's platform session) or akey, exactly one; a key's mint is top-level only.returnTooff the fragment's origin, or an unknown field, is 400.The key's parent session. The registry mints from a parent session of the key's own: one per key,
sessions.device_key, schema step 9key_sessions.live_sessionandredeemjoin the live key, so revoking the key ends its site sessions and its unspent redemptions, and nothing else of the person's.Rollback (asked in review). During a rollback, an older release wouldn't apply the key check to children minted from a key parent. This can't happen, for two reasons:
xtask/src/rollout.rseligible).SchemaError::Newer→schema_refused,crates/core/src/migrate.rs).So no older code ever reads these rows, and every site-session lookup goes to the registry live, with no cache. Nothing extra is written for it.
Idempotent posts (also asked). The chat channel already treats a repeated post id as the same post: same id and body → the original record with
replayed: true; a different body → 409 (cell/src/channels.rspostandposted,docs/chat-records.md).Tests. A new e2e section,
native, with 18 checks:returnTo, unknown field;restartchecks that a key's site session outlives a restart.Checks
cargo xtask checkpasses on the head merged with masterbcc50fcd8.Wasm: +24,404 bytes against master
bcc50fcd8, the exact sha merged (6,520,856 → 6,545,260; 54,740 left of the budget).Mintcall andmint_for, instead of a second call type and handler, cut that.key_parent, the joins inlive_session, and strings. The remainder is LTO inlining shifts spread across unrelated functions, measured by name with twiggy on the pre-wasm-optbuild.🤖 Generated with Claude Code