01
A consent token moves value out — so it is never an endpoint
You should be able to: Explain why minting consent is a pure function called from one place, not an API a caller hits.
A consent token authorizes moving a holder’s value out. That makes it the single most dangerous thing the rail can produce, so it must be the output of a person approving one specific draft — never a token a caller can request. A `POST /consent-token` that minted on demand would be exactly the auto-approval the consent layer exists to refuse, one layer up.
So the mint is a pure function called from exactly one place: the interaction flow, after an authenticated human has approved a specific draft. The obligation to call it only downstream of a real approval lives with that flow, not with the minter — the minter has no way to be reached except through the approval.
The lesson generalises: when a capability is dangerous, you do not gate the endpoint, you refuse to expose one. A consent primitive with a callable surface is a consent primitive already defeated.
Source: flashyID — the rail consent flow (docs/rail-consent.md)
02
The flow: draft, approve, token, execute
You should be able to: Walk the four steps from a proposed redemption to a settled one.
The relying party drafts a redemption — holder and amount — which is pure and writes nothing; it gets back a draft with an id. It redirects the human to flashyID with that draft id. flashyID authenticates the human, shows them the draft, and on approval mints the consent token, which the human carries back to the relying party. The relying party then calls execute with the draft and the token, and only now does value move.
The shape matters: the draft is cheap and reversible, the token is the point of no return, and a human stands between them by construction. Nothing settles until the token exists, and the token cannot exist until a person approved the exact draft.
This is the estate’s consent doctrine made mechanical for value: draft freely, approve deliberately, execute only with proof of the approval.
Source: flashyID — draft to execute
03
The security crux: render the rail’s own draft
You should be able to: Explain why flashyID fetches and renders the draft from the rail, not from the relying party.
The pivotal detail: when flashyID shows the human what they are approving, it fetches the draft from the rail itself and renders the rail’s own numbers — never text the relying party supplied. A malicious or buggy relying party therefore cannot show the human one thing and obtain consent for another.
This is the classic consent-UI attack closed by construction. If the party requesting consent also controls what the approver sees, the approval is meaningless; so the approver’s screen is drawn from the authority that will act on the token, not from the party that wants it.
The principle travels far beyond this rail: any time you ask a human to approve something, render what they are approving from the system that will execute it, never from the party asking. Otherwise you have collected a signature on a blank cheque.
Source: flashyID — flashyID renders the draft it fetches from the rail
04
What the token binds
You should be able to: Name what the consent token commits to and why each binding is necessary.
The consent token binds the draft id, the holder id, and the action, and is signed with flashyID’s own OIDC key — so its public half is already published for the rail to verify without anything new being distributed. Before minting, flashyID asserts the authenticated subject is the draft’s holder: you cannot approve a redemption from someone else’s account.
Each binding closes a substitution attack. Binding the draft id stops the token being replayed against a different draft; binding the holder stops one person approving another’s redemption; binding the action stops a consent for one operation being reused for another. A token that bound less would be a token reusable in ways the human never approved.
So the token is not a generic "yes" — it is a yes to this holder, this action, this draft, provable offline by anyone with flashyID’s public key. Consent that is not bound this tightly is consent that leaks.
Source: flashyID — the consent token binds draftId, holderId and action
05
Grants: the same rule for standing authority
You should be able to: Extend the consent discipline from a single action to a delegated grant.
A consent token approves one action. A grant is standing authority — permission for a spender to act for a holder over time — and it follows the same discipline: a grant is created only downstream of a verified chain, never on request, and its mapping is root=holder, leaf=spender, so authority flows from the party who owns it to the party who exercises it and never the other way.
The estate’s deepest identity rule governs here: delegation is attenuation, never inheritance. A grant may only ever carry a subset of what the granter holds; a child can never gain authority its parent lacks. The grant primitive, like the consent primitive, is not a callable endpoint — it is the output of verifying a chain, not a favour a caller asks for.
So single-action consent and standing grants are two points on one line: both are proof that the party who owns the authority approved its use, bound so tightly that the proof cannot be reused beyond what was approved. That is what makes a value rail safe to build on.
Source: flashyID — delegation is attenuation (docs/rail-grant.md)