feat: let mission 17 sign a real event and show what one is - #94
Merged
Conversation
Mission 17 listed the five fields of a Nostr event and asked the learner which kind number is a text note. They now write something, the browser signs it with their key, and the app shows the actual event: kind, content, pubkey, id, sig, with a nudge to run it again with one character changed and watch the id and sig move completely. Nothing is published. Mission 26 is where broadcasting happens, and the outcome copy is careful not to imply otherwise. The lesson gained a paragraph on the two computed fields, because "the id is a hash of the contents and the sig is that id signed by your key" is the idea that makes the rest of Nostr make sense. The server checks both halves of that claim. `Event::verify` recomputes the canonical hash, confirms the id really is that hash, and checks the BIP340 signature against the embedded pubkey; then we compare that pubkey against the npub registered in mission 14, so the event has to be theirs. Verification is offline by construction: the signing path was split out of `broadcast_signed_event` so this mission never reaches a relay. `needsNsec` no longer piggybacks on `needsPublishConfirm`. Mission 17 needs the key but must not warn that something is public and permanent when nobody will ever see it. Verified against a running backend by trying to break it. A tampered content field, an event signed by a different key, a swapped id, a zeroed signature and plain garbage are all rejected; only the learner's own signed event is accepted. A new browser test (15 assertions) drives the real flow, asserts all five fields reach the learner, re-derives the id to show it is content-derived, and confirms no relay call is made and the nsec is never sent. Note on the e2e harness: seeding mission 14 now registers a real deterministic npub instead of a placeholder, because mission 17's verifier requires the signing key to match the registered identity. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
Closes #85 in part.
Mission 17 listed the five fields of a Nostr event and then asked which kind number is a text note. Now the learner writes something, the browser signs it with their key, and the app shows the actual event:
…with a nudge to run it again with one character changed and watch the id and sig move completely.
Nothing is published. Mission 26 is where broadcasting happens, and the outcome copy is careful not to imply otherwise. The lesson gained a short paragraph on the two computed fields, because "the id is a hash of the contents, and the sig is that id signed by your key" is the idea that makes the rest of Nostr make sense.
The server checks both halves of that claim
Event::verifyrecomputes the canonical hash, confirms theidreally is that hash, and checks the BIP340 signature against the embedded pubkey. Then we compare that pubkey to the npub registered in mission 14, so the event has to be theirs. Verification is offline by construction: the signing check was split out ofbroadcast_signed_event, so this mission can never reach a relay.Verified by trying to break it
Against a running backend, one fresh learner per case:
Plus a new browser test (15 assertions) that drives the real flow, asserts all five fields reach the learner, re-derives the id to show it is content-derived, and confirms no relay call is made and the nsec is never sent.
Full suite green: sign-event 15, badge-share 19, passphrase-fork 10, secret-reveal 7/6, publish-confirm 5, mission-prose 3, solo-rank 5. Plus 30 unit, 30 integration, tsc and build clean.
Notes
needsNsecno longer piggybacks onneedsPublishConfirm: mission 17 needs the key but must not warn that something is "public and permanent" when nobody will ever see it.🤖 Generated with Claude Code