Console: NIP-46 bunker fix · Studio: finish Phase 2 receipts/policy · Admin: Phase-3 storage draft#53
Merged
Conversation
The NIP-46 sign-in's security-relevant logic (timeout bounding, signer-error → static-message mapping, auth_url sanitization, the key-material refusal gate) moves to a plain module the node test runner can import — the Svelte island cannot be. 15 tests pin the contracts, including: TimeoutError on a bunker that never answers, late-rejection swallowing, http(s)-only approval URLs (javascript:/data:/blob: dropped), and that bunkerReason never echoes attacker-influenceable signer text. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVPyHAtQ1cdK76RpFasafP
…l failure The two silent-failure defects behind 'bunker sign-in just spins': - connect() and the remote signEvent() had NO timeout — an unanswered on-phone approval or a dead relay hung the UI forever. Both awaits are now bounded (60s each, generous for a human approval tap but never infinite). - the bare catch discarded the actual error. It now goes to console.error as the operator's diagnostic (error object only — never the pasted input, never key material), and the UI message is mapped via bunkerReason() from static strings: timeout → approve-and-retry + the single-use-secret rule; bunker rejection → fresh bunker:// string hint; verify → allowlist refusal. signer.close() is now fire-and-forget so cleanup can never hang the UI either. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVPyHAtQ1cdK76RpFasafP
Web bunkers (nsec.app-style) reply to connect with an auth_url the user must open and approve; without an onauth handler nostr-tools only warns and the connect promise never settles — the third silent-hang path. Amber and other push-approval signers never send one, so this changes nothing for them. The URL is signer-provided input and is treated as such: sanitized via safeApprovalUrl (http/https only, length-capped — javascript:/data:/blob: dropped), opened with noopener,noreferrer, and rendered as a clickable fallback link for the common case where the popup is blocked (the open runs outside the click gesture). The link lives only while the attempt is in flight and is cleared once the signer closes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVPyHAtQ1cdK76RpFasafP
The 'receipts travel' half of the connective-spine Phase 2 stopped at the screen edge: the license-at-commit receipt rendered in the blueprint UI and persisted into session.stack, but the files the builder actually keeps — README.md and AGENT_PROMPT.txt — omitted it. A new pure, tested helper (receiptLine) renders the same facts into both: linked in the README, plain in the agent prompt. Entries with no pinned commit get an honest 'verification pending' note, never a fabricated claim. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVPyHAtQ1cdK76RpFasafP
policyClean/policyMatches were computed since the Phase-2 merge but had zero consumers — the 'live guarantee that the handoff is policy-clean' produced no observable effect. Now: - blueprint step: a ✓ receipt line when the re-check is clean; a conflict alert listing name → org when it is not (silent when /policy.json didn't load — no claim either way, the build-time CI engine stays the authority). - handoff step: an excluded-vendor match PAUSES the handoff — no zip, no GitHub push, no Goose launch — until the stack is clean. Unreachable in practice (the catalog is enforced at build); this is the belt-and-suspenders the receipts promise. Localized en/es/ar. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVPyHAtQ1cdK76RpFasafP
The adversarial review caught the gate leaking: the 'Copy the instructions
for your AI agent' button and the constitution/spec/package.json copy viewers
sat OUTSIDE the {#if policyMatches.length} gate, so with an excluded-vendor
match present the UI said 'handoff paused' while one click still copied an
agent prompt asserting the flagged tool was policy-clean and a package.json
pinning it. A clipboard is as much a handoff channel as a zip — the gate now
wraps all of them.
Also: the ✓ 'policy re-checked' badge is only claimable over a NON-EMPTY
stack — a failed catalog load or an emptied blueprint no longer renders a
vacuous clean claim.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVPyHAtQ1cdK76RpFasafP
…rovisioned The ADMIN_DB schema (staged catalog/content edits, moderation cases + NIP-32 labels, insert-only action audit) lands as code FIRST so it is reviewed before any resource exists: no wrangler.jsonc binding, no database created, no code path reads it. The file header carries the exact three operator steps that bring it live after review. Shape encodes the project's values: public identifiers only (never session tokens or key material), minimal references instead of content copies, insert-only audit (an editable audit trail is theater), staged drafts that expire rather than accumulate, labels stored 1:1 as NIP-32/NIP-69 events so portability stays a switchable mirror, and CSAM evidence explicitly excluded (Phase 6's locked R2 bucket, never this database). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVPyHAtQ1cdK76RpFasafP
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
wecanjustbuildthings | 0235195 | Jul 11 2026, 06:31 AM |
This was referenced Jul 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Three sectioned workstreams, one branch (7 commits, one logical change each):
1. NIP-46 bunker sign-in fix (
/console/) — closes the silent-failure class behind the failed Amber/test-bunker logins:0cf7ddf— purebunker-login.tshelpers, 15 new tests: timeout bounding, http(s)-onlyauth_urlsanitizer, static-strings-only error mapping (signer text is matched, never echoed), key-refusal gate now test-pinned.caf60e3— both bunker round-trips bounded at 60s; the bare catch un-swallowed: real cause → browser console (error object only — never the pasted string, never key material), UI explains via mapped static messages incl. the single-usebunker://secret rule.972cafb— web-bunkerauth_urlapproval handled: sanitized, openednoopener,noreferrer, clickable fallback for popup blockers. No behavior change for push-approval signers (Amber).2. Connective-spine Phase 2, finished (
/build/) — the receipts/policy plumbing that was computed but never wired:9867745— license-at-commit receipts now travel into the downloadedREADME.md(linked) andAGENT_PROMPT.txt(plain) via a pure, testedreceiptLinehelper; unpinned entries say "verification pending", never a fabricated claim.a6a8533— the in-browser policy re-check is surfaced (✓ badge / conflict alert, en/es/ar) and gates the handoff step.439456b— adversarial-review fix: the gate now also covers the copy-prompt button and starter-file viewers (a clipboard is as much a handoff channel as a zip), and the ✓ badge requires a non-empty stack.3. Admin Phase-3 storage draft (review-first, nothing provisioned):
0235195—migrations-admin/0001_admin_storage.sqlas a reviewable spec: staged catalog/content edits (expiring), moderation cases + NIP-32/NIP-69-shaped labels, insert-only action audit. No binding added, no database created, no code path reads it; the file header carries the exact operator steps that bring it live after review.Type of change
Required checks
npm run checkpasses (schema validation)npm run enforcepasses (three-layer exclusion policy + recipe contract)npm testpasses — 335/335 (28 net-new tests)npm run buildsucceeds (fullverify:allgreen, twice — before and after the review fix)Security posture (unchanged or strengthened)
bunker://-secret refusal preserved and now pinned by tests; nothing pasted is ever logged or rendered back.auth_urltreated as signer-provided untrusted input: http(s) only, length-capped,noopener,noreferrer./console/*wss:delta untouched; Path A intact.439456b; 6 candidate findings refuted.(Correction on record: an earlier revision of this description cited the Phase-3 draft commit as
d59a4a9— an unverified hash. The correct commit is0235195.)🤖 Generated with Claude Code
https://claude.ai/code/session_01LVPyHAtQ1cdK76RpFasafP