# Paired protocol examples — draft 0.3 These examples **replace the old one-sided HTTP enrolment stubs**. Nothing requires an endpoint, web server, central service or published BringPass SDK. Commands name logical exchanges. Actual NFC commands on a legacy reader remain its existing ones. ## Two explicit implementation levels | Languages | Included | Not included | |---|---|---| | Python, JavaScript | Executable token and provider references, Ed25519, canonical signed bytes, all four operations, signed receipts, replay/idempotency guards, legacy payload example, provider-local lost-device revocation | Real login/session or recovery proofing, hardware, durable storage, production threat-model review | | TypeScript, Java, Kotlin, C#, Go, Swift, PHP, Ruby, Rust, C++, C | Both peer handlers, all four operation branches, token-side verification/approval points, receipt processing, absent-token recovery hook and a legacy presentation boundary | Crypto and transaction adapters; these are paired scaffolds, not drop-in verifiers | There is no cURL example: cURL would introduce the very HTTP transport assumption this revision removes. Python and JavaScript examples implement the diagnostic message encoding in `docs/protocol.md`; the other languages expose ports which must preserve exactly those semantics or implement an explicitly separate binding. ## Run the two reference pairs ```sh # From the website project root: python3 -m pip install -r examples/python/requirements.txt python3 examples/python/demo.py node examples/javascript/demo.mjs python3 tests/check_protocol.py node tests/check_protocol.mjs ``` The separate `python/legacy.py` and `javascript/legacy.mjs` modules implement `PROFILE.IMPORT`, `SELECT`, `PRESENT`, `SUSPEND` and `REMOVE` locally. They pass an unchanged approved value to a renderer/driver boundary; they are not NFC drivers. For USE, an empty trusted account field is allowed: a pinned-provider challenge may come first, and the enrolled key identifies the relationship. Registration and management always need a separately authorised account context. The test commands above are included in the full site archive's `tests/` directory, not in the smaller examples-only download. No external account is contacted. Test sessions, approvals and provider pins are **explicit local fixtures**, not actual authentication. Test seeds in `shared/` are public test data, not suitable private keys. Examples use fresh keys by default. ## Peer choreography For REGISTER, USE, REVOKE and RETRACT: 1. The provider adapter verifies the existing account/session or desk authority where needed and issues a signed, one-use CHALLENGE bound to the action/target. 2. The token verifies the pinned provider and the complete request. Its trusted local policy decides user presence/verification/approval, then signs a PROOF. 3. The provider verifies the proof, the actual session/interaction, stored key (except new-key REGISTER), expiry and current authorisation under a transaction. 4. It applies the operation and durably consumes the challenge with the RESULT. 5. The token verifies the provider-signed receipt against the exact proof and scope. Retry the identical request/context only. A cached receipt has `fresh=false`; do not repeat a physical action. Use a durable outbox/idempotency mechanism for actuators. The in-memory reference is single-threaded; it deliberately is not such an outbox. ## Required port contract (the eleven paired scaffolds) **challenge/start**: accept a *local trusted* context, not a client claim. Bind an approved account, operation, expected relationship, exact target, expiry, nonce, provider and chosen suite. Issue a unique transaction and provider signature. For USE, token possession and the provider's current entitlement may suffice; management always requires distinct account authority/verification policy. **verifyProviderAndApprove / signProof**: validate structure and bounds; verify the provider pin; prohibit scope/suite substitution; identify the actual action/target in a trusted local UI/policy. Do not trust a reader-provided `approved=true` flag. For REGISTER, include only the intended new public key and prove its private key. For USE/REVOKE/RETRACT, use a currently enrolled key. Sign all bound fields. **beginVerified**: lock the relevant transaction and relationship. Validate signed bytes against the outstanding challenge and actual transport/session context, exact operation/target/provider/relationship, freshness, permitted key and authority. A cached retry is accepted only for identical proof and trusted context. The resulting `Transaction` is not a deserializable claim from a reader. **registerNewKey**: verify new-key proof plus account management authority; reject reused or revoked IDs and retracted relationships; enforce account binding. **requireCurrentEntitlement**: recheck active relationship, active key and current membership/access rules. A verified signature is not an access grant. **revokeTarget**: disable the precise target key at that provider, retain other keys and the relationship. A lost device does not need to sign. This branch with a proof uses another active enrolled credential plus management authority. **retractRelationship**: explicitly confirm and withdraw the whole BringPass relationship enrolment; disable its keys, leave the underlying local account and unrelated credentials alone. Do not label this GDPR erasure or contract cancellation. **signResult / commitOnce / close**: sign a result bound to the proof and state revision. Atomically persist state, transaction consumption and the receipt. Closing an uncommitted transaction must roll back. Error paths fail closed and never emit a success receipt. Context-specific coarse errors must not enable account enumeration. **verifyResultAndApply**: verify the pinned provider and exact request digest, operation, relationship, credential, target, outcome, revision and effective scope. Never mark remote enrolment active, revoked or retracted merely after sending. A timeout is pending/unknown. Local receipt state must not roll backwards. **recoverRevoke/lost**: a provider-local call from separately verified recovery or account management. Never expose it as an unauthenticated message accepted from a claimant. The management channel receives the signed result; do not demand a lost key. **legacyValue/presentLegacy**: return only the exact approved representation to a renderer or existing driver. Driver, surface, format, keys and radio support must already fit. This helper does not emulate NFC, change immutable UIDs, render a barcode, or add enrolment/revocation capabilities to an unaware provider. ## Constraints to carry into every implementation Transactions, limits, audit minimisation, replay tombstones, receipt durability, rate limits, user verification, key generation/storage, provider-pin rotation and recovery policy need reviewed implementations. No adapter may silently reduce native authentication to a static identifier. An application AID is not in itself an authenticated provider identity. Offline readers need an additional profile; reference receipts explicitly cover only the authoritative online store. Source files are intentionally dependency-light. Compiler/syntax coverage is reported separately in `tests/language-results.json`; untested runtimes are not claimed as validated. Abstract scaffolds do not run until ports are supplied.