You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The Open Source path graduates on something real: a merged PR, verified against the GitHub API. The money side of the curriculum has no equivalent. A learner can finish every flight path without ever having installed a Bitcoin wallet of their own.
Proposal: a capstone mission where the learner sets up a real wallet and proves it works.
Scope, decided up front
No real sats. We are not running a faucet, not distributing sats, and not integrating a service that gives them away. A sats source has to survive being drained by the first person who finds the endpoint, needs a rate limit, and needs a story for the day it runs dry. None of that teaches anything, and all of it can go wrong in public.
Custodial is fine, and is the point. A beginner with a working custodial wallet has learned more than one who bounced off seed-phrase ceremony and gave up. The self-custody path is where that graduation happens, and it already exists.
So the mission is: install a real wallet, and prove you can receive with it. Nothing moves.
Proof, without moving money
Two workable shapes, both checkable server-side:
Paste an invoice from your own wallet. Ask for a BOLT-11 invoice for a specific amount. Decode it server-side, confirm it parses, confirm the amount matches what we asked, and confirm it is not one we issued. That proves a real wallet exists and can generate a receive request. Nothing is ever paid.
Paste your Lightning address. Resolve its LNURL-pay endpoint and confirm it exists and is payable. Smaller and friendlier, and it reuses the resolution logic proposed for mission 38 in Lightning: 2 of 12 missions have a hands-on step #83.
Either works. (1) is stronger evidence of a working wallet, (2) is less to type on a phone.
Which wallet
Do not name one wallet. An endorsement in curriculum copy ages badly and puts us in the position of vouching for a custodian. Offer three or four current custodial options and let the learner pick. The comparison is itself the lesson, and it pays off mission 10 (custodial vs self-custodial) with a real decision instead of a quiz answer.
The migration problem, which is the hard part
Adding a mission to an existing flight path silently un-earns that path's badge for everyone who already completed it.
Badge::all_for in backend/src/models/badge.rs derives badges live from the completion ledger:
required is computed from current tree membership, every time it is read. Add a fourteenth mission to a twelve-mission path and every learner sitting at 12 of 12 drops to 12 of 13, and their badge reverts from earned to unearned. Their completions are safe, and any certificate already issued is stored and stays valid, but the badge they earned disappears from the UI.
This is worth getting right rather than discovering after the fact. Options:
Put the capstone in its own place rather than inside an existing path, so no path's required count changes.
Accept the reversion, if the affected cohort is small enough to be worth it. Check the numbers before assuming.
Freeze earned badges by recording the earn once instead of deriving it live. This is a real design change, and the current derive-on-read approach is deliberate (the doc comment notes it means "no drift, no migration when tree membership shifts"), so do not do this casually.
Whoever picks this up: decide this first, before writing mission copy. Related context in #76.
Notes
Mission membership is mirrored: Tree::from_mission in backend/src/models/mission.rs is the source of truth, TREES in frontend/src/lib/types.ts mirrors it. Both must agree, and Mission::LAST moves when you add one.
A Lightning address is a semi-identifying handle and proofs are stored permanently in mission_completions. Prefer the invoice shape if that concerns you, and do not ask for anything more identifying than the proof requires.
The Open Source path graduates on something real: a merged PR, verified against the GitHub API. The money side of the curriculum has no equivalent. A learner can finish every flight path without ever having installed a Bitcoin wallet of their own.
Proposal: a capstone mission where the learner sets up a real wallet and proves it works.
Scope, decided up front
No real sats. We are not running a faucet, not distributing sats, and not integrating a service that gives them away. A sats source has to survive being drained by the first person who finds the endpoint, needs a rate limit, and needs a story for the day it runs dry. None of that teaches anything, and all of it can go wrong in public.
Custodial is fine, and is the point. A beginner with a working custodial wallet has learned more than one who bounced off seed-phrase ceremony and gave up. The self-custody path is where that graduation happens, and it already exists.
So the mission is: install a real wallet, and prove you can receive with it. Nothing moves.
Proof, without moving money
Two workable shapes, both checkable server-side:
Either works. (1) is stronger evidence of a working wallet, (2) is less to type on a phone.
Which wallet
Do not name one wallet. An endorsement in curriculum copy ages badly and puts us in the position of vouching for a custodian. Offer three or four current custodial options and let the learner pick. The comparison is itself the lesson, and it pays off mission 10 (custodial vs self-custodial) with a real decision instead of a quiz answer.
The migration problem, which is the hard part
Adding a mission to an existing flight path silently un-earns that path's badge for everyone who already completed it.
Badge::all_forinbackend/src/models/badge.rsderives badges live from the completion ledger:requiredis computed from current tree membership, every time it is read. Add a fourteenth mission to a twelve-mission path and every learner sitting at 12 of 12 drops to 12 of 13, and their badge reverts from earned to unearned. Their completions are safe, and any certificate already issued is stored and stays valid, but the badge they earned disappears from the UI.This is worth getting right rather than discovering after the fact. Options:
requiredcount changes.Whoever picks this up: decide this first, before writing mission copy. Related context in #76.
Notes
Tree::from_missioninbackend/src/models/mission.rsis the source of truth,TREESinfrontend/src/lib/types.tsmirrors it. Both must agree, andMission::LASTmoves when you add one.mission_completions. Prefer the invoice shape if that concerns you, and do not ask for anything more identifying than the proof requires.DoKind.