Release: develop -> main - #131
Merged
Merged
Conversation
* docs: propose bootstrap seed plurality for v1.3
Records a design proposal for the NEXT protocol version (v1.3): replace the
single pinned bootstrap_pubkey trust root with a plurality of independent seed
endpoints (initial in-code list, extensible by pull request), resolved by clients
as discovery candidates via union + trust-on-first-use, never auto-delegated
custody. Targets the discovery/liveness/censorship-resistance plane; correctness
stays enforced by client-side validation against Bitcoin.
Explicitly NOT part of the frozen v1 spec (final for v1, spec 1.7.8): v1 keeps the
single bootstrap_pubkey unchanged. Added as a standalone page + sidebar entry; no
normative v1 text touched. Local Docusaurus build passes.
* docs: address review on the v1.3 seed proposal
- remove marketing language per the style guide; reframe the incentives
section neutrally as 'Operator incentives and trade-offs'
- drop the absolute 'can only widen reach' claim; state the D-17
deliberate-delegation residual (effect equivalent to theft on one send)
explicitly, bounded by the never-auto-entrust client rule
- fix terminology: an operator is entrusted with the operational bundle
{ivk, ovk, op, nk, op_secret}, not 'custody' and not only viewing keys
- correct the D-05 attribution: only activation_height is a registered
D-05 residual; the bootstrap_pubkey's non-binding is an analogous,
unregistered gap
- 'every validating node' re-derives, not 'every client' (thin wallet
delegates to its own node, Requirement 4)
- soften the single-operator launch: endpoint redundancy at launch, the
single point of control dissolves only as independent seeds join
- name the correct register (Paper-Conformance Remediation) for the later
v1.3 change; hyperlink spec citations per repo convention
Local Docusaurus build passes (all anchors valid under onBrokenAnchors=throw).
* docs: second review pass on the v1.3 seed proposal
- qualify the single-point-of-control claim: at launch both endpoints share
one operator (endpoint redundancy, not operator independence); it is reduced
only as independently operated seeds join
- fix the node/wallet wording: 'any node' rebuilds from Bitcoin; a thin wallet
relies on its own node (Requirement 4), and a selected foreign node is a
deliberate non-trustless correctness trade-off (threat model) — drop the
'validating node' archetype and the 'client-side validation' bare term
- 'verifier data', not 'on-chain data', for the network-tag/circuit_digest binding
- name account freeze alongside output redirection in the D-17 consequence
- flag the acceptance criterion as necessary-not-sufficient: GET /v1/info is a
self-declared response a non-conforming server can mirror; v1.3 to define a
stronger behavioural check
- correct the 'no central element' attribution to the spec overview (not §1)
- drop the rhetorical 'stated honestly'/'stated plainly' phrasing
Local Docusaurus build passes (all anchors valid, onBrokenAnchors=throw).
* docs: third review pass on the v1.3 seed proposal
- separate account freeze from the 'same as theft' characterisation: per
spec 6.6, that phrase binds to output redirection/burn only; freeze is a
distinct D-17 effect (fixed at both occurrences)
- link the contact-discovery reference to its normative source (spec 4.3),
not only the deviation register
Local Docusaurus build passes.
* docs: use the canonical artefact name network-params.json consistently
The acceptance criterion referred to 'network-params' while the rest of the
page and the spec use 'network-params.json'. Local Docusaurus build passes.
…ce) (#129) * spec: make data permanence a hard requirement — a node never deletes received data Adds Requirement 12 (Data Permanence): a node never deletes, drops, expires, prunes, or garbage-collects any artefact it has received or stored. Every CoinProof bundle, delivery event, SelfDeliveryRecordV1 and stored blob is kept completely and indefinitely, across the kernel, the API, and any relay or blob store the node runs. Redundant copies held elsewhere only add durability; they are never a licence to drop a local copy. An acknowledgement confirms the data survived elsewhere but never permits deletion of one's own copy. The single erase operation — access revocation of the operational bundle — stops a node's future use of the bundle without deleting the account's stored records. §4.8 is rewritten as the absolute, unconditional never-delete invariant (the previous text still permitted a superseded-and-receipt-backed prune and a drop-after-k), and a traceability row is added. Sweeping the dependent references (the §4.6 replication/ReplicaReceipt drop machinery, the Blossom DELETE endpoint, retention expiry, and revoke-erase) follows. * spec: remove every deletion path — no §4.6 drop machinery, no Blossom DELETE, no erase Carries Data Permanence through the whole document. §4.6 is rewritten from "replication factor k" to plain "Data availability": availability follows solely from permanent retention by the sender and every holder; optional redundant copies add durability but are no quorum, authorise no local discard, and carry no ReplicaReceipt / k / trust-list counting machinery — all of which is removed. The §4.2 store-and-forward relays retain indefinitely (no authorised deletion, no retention-policy expiry), the sender keeps its own copy after an ACK instead of dropping it, and the §4.8 superseded-prune and drop-after-k allowances are gone. The Blossom DELETE endpoint, its authorization variant, and the retention_hold refusal are removed — the blob store is append-only. Fail-closed revocation now makes a node cease all use of the operational bundle rather than erase it, since erasing stored data is exactly what Data Permanence forbids; a rogue node could never be compelled to erase anyway. All dependent anchors and cross-references are moved to the new §4.6 / §4.8 targets, leaving no dangling links. * spec: finish data-permanence coherence — twelve requirements, no k in diagrams, contact records retained, revoke ceases use * spec: extend data permanence to derived state — reorg archives (never truncates), idempotency retained indefinitely, glossary entry * docs: fix cross-references to the renamed §4.6 anchor Renaming §4.6 from 'Data availability & replication factor k' to 'Data availability' changed its heading anchor. Five pages still linked to the old '#46-data-availability--replication-factor-k' fragment, which broke the Docusaurus broken-link check. Point them at the current '#46-data-availability' anchor. Verified: local production build passes with zero broken links. * spec: define custody, durability, and seed-only recovery model (wallet/node/network) Add §4.10 responsibility tiers: the wallet is the sole custodian of the seed and nothing else (simultaneous loss of app and seed backup is irrecoverable by design); the node never handles keys and is data-retentive by default; the network provides recovery. Make seed-only recovery complete via the §4.3 recovery-discoverable overlap — every delivery event reaches at least one network seed_relay and every value-bearing blob at least one network blob_store — so a wallet that lost its database and its own relay still rebuilds from the seed alone. A seed_relay holds only the event locator, so the blob duty is separate and equally mandatory. Add the operator durability duty: a real-time backup of the node's PostgreSQL and blob stores, a hosting concern deliberately outside the node software. Add open, self-verifying token provenance (GET /v1/token/<asset_id>/provenance, kernel GetTokenProvenance) so a token survives the loss of its issuer, resolvable by asset_id without a name registry. Make encrypted Nostr/blob redundancy a normative second layer with a self-hostable relay and blob store. Update Requirements 12/13 and the availability risks; drop the obsolete fixed-replica-count references. * spec: fix two-plane fetch precision, honest upload admission-control, and GetTokenProvenance error contract Follow-up to the availability model. §5/§6: a bundle blob is fetched from a blob store, not the relay mesh (the relay carries the delivery event; the blob lives in the content-addressed store) — corrects the shareable-link, data-sources, and read.account fetch descriptions. §7.4: the recovery-overlap upload admission control bounds but cannot cryptographically prevent Sybil-rotated spam (op keys are permissionless), so a manifest blob_store carries the same open-acceptance surface as an open seed_relay — stated as an accepted operational residual instead of over-claiming. §7.5/§7.8: GET /v1/token/<asset_id>/provenance emits 400 malformed_request for a malformed asset_id, and GetTokenProvenance is now listed in the per-procedure kernel error table. * spec: attribute blob fetch to the Blossom store, not the relay (§4.7, §6.4) The §4.7 blob-fetch metadata note and the §6.4 interface-family table conflated the blob fetch with the Nostr relay; a CoinProof blob is served by the co-located Blossom store, while the relay carries only the delivery event. Reconciles the relay.* family description with its own table row. * spec: complete blob-fetch attribution in the §6.1 component list (twin of §6.4/§7.4) * docs: sync companion artefacts to Requirement 13 and the open provenance read Lens-A conformity pass. Add Requirement 13 (seed-only recovery availability) to the requirements list and to both the traceability and the §6.7 security-properties tables; model the open token-provenance read as a sixth interface family (read.provenance) and correct the §6.1 'reads only against a capability' statement to exempt it. Add the Requirement-13 seed-only-recovery Definition-of-Done control (loss of node database and own relay, restore via manifest seed_relay/blob_store) and update stale 'replicated blobs'/'replica failures' gate wording. Add availability, open-provenance, blob_store-Sybil, and operator-backup risk verdicts. Add glossary entries for seed_relay, blob_store, Class A/B, recovery-discoverable overlap, and GetTokenProvenance. Fix stale 'durability receipt' diagram label and the protocol.md 'unchanged' phrasing. * docs: close second-round conformity findings (provenance feature, gate count, recovery gates, glossary) Assign read.provenance to the wallet feature (404 feature_disabled otherwise); extend the Gate-C requirement-row check to 1-13; keep Bitcoin/regtest-chain access explicit in the Requirement-13 recovery controls (§4.5 steps 2/5 still need it); complete the Class A glossary entry with the anonymous recovery-scan path; drop the last stale 'replicated'/'eleven' phrasings in requirements.md, CONTRIBUTING.md, and the release-status matrix. * docs: make read.provenance not feature-gated and fix stale requirement counts Correct the read.provenance surface: it is not gated by a features flag — any API instance that has captured a token's asset_terms MUST serve them to any requester (§4.6), so it is never 404 feature_disabled and works on an explorer-only host that has wallet off. Fix the stale 'twelve'/'eleven' requirement counts in the glossary see-also and architecture doc to thirteen, and drop the abandoned 'replication factor' example from the requirements intro. * docs: state the bootstrap-reachability recovery assumption; sweep remaining replica/provenance ripples Correct the seed-only recovery claim: the Bootstrap Manifest is not seed-derivable — its bootstrap_pubkey is a pinned network parameter but its relay/store URLs are learned from a reachable node's GET /v1/info, so recovery honestly needs the seed AND one reachable bootstrap entry, stated in §4.5, §4.10, and Requirement 13. Note the open read.provenance as the explicit exception to the general features-gating statements (§6.1, §7.5). Drop the last fixed-replica references (comparisons.md k-replicated, the output_refs 'replicas' wording, the orphaned 'replica holder' in keys-and-identities). Add the open Class-B path to the remaining 'name/terms travel only inside bundles' statements (§2.3.1, §6.5, glossary). * docs: bound the recovery guarantee honestly (bootstrap reachability + manifest rotation continuity) State every operational assumption of seed-only recovery in one place: the wallet needs the seed AND a reachable node base URL to fetch and verify the current signed Bootstrap Manifest (the manifest is not seed-derived; only bootstrap_pubkey is pinned). Add a §4.3 manifest rotation-continuity rule — a network MUST NOT delist a seed_relay/blob_store that still holds recoverable artefacts unless that corpus is first carried to a still-listed holder — so no artefact is orphaned on a delisted-but-live holder, and soften the absolute 'finds every artefact' claims to reference it. Fix the leftover §4.3 'seed-derivable manifest' twin, drop the undefined 'pinned bootstrap discovery' term, propagate the bootstrap precondition into the availability risk and the Requirement-13 acceptance test, add the open Class-B path to the last asset_terms glossary entry, and fix a stray German phrase in a vector row. * docs: qualify recovery summaries with the §4.10 conditions; fix §7.7 fetchable-from-any-node overclaim §7.7 said value-bearing objects are fetchable 'from any node'; corrected to the network's seed-discoverable holders (recovery needs a reachable node for the manifest and ≥1 live holder, §4.3/§4.10). Tighten the §4.5 'still holds' phrasing to 'a live, reachable holder still serves'. Point the abbreviated recovery summaries (Requirement-13 traceability rows, §4.6, §6.7, the glossary overlap entry, and the risks 'seed alone' phrasing) at the §4.10 operational conditions so no summary reads as an unqualified guarantee. * docs: qualify remaining from-any-node/holder overclaims (recovery per-plane + token provenance) Exhaustive grep-driven pass over both overclaim classes. Recovery: add the missing 'per plane' qualifier to the §7.7 statement, and give Requirement 6 and the risks verdict row the same §4.10 operational conditions the canonical guarantee carries. Token provenance: asset_terms is optional in a bundle and the endpoint 404s when a node holds no terms, so 'resolvable from any holder' overclaimed; qualify every instance (§1.5, §4.5, §6.5, Requirement 13, the IssuanceTerms glossary entry) to 'any holder that has retained the terms', noting that display-terms availability depends on >=1 such holder existing. * docs: bound token-provenance reads to holders that retained the terms Qualify the remaining unconditional provenance claims so the spec no longer implies every holder can read or serve an asset's terms. The cap_total bound and the immutability of an asset's token standard stay unconditional (protocol guarantees via Pk0 first-occurrence and the asset_id commitment); only reading and verifying the terms is now conditioned on holding the asset_terms, which a holder without them obtains from any holder that has retained them via the open Class-B lookup, and which the endpoint answers 404 for when none does. Affected: §2.3.1 name resolution, §4.6 Class-B definition and token-survival rule, §6.5 standard readability, both token-standard-2 cap statements, the auditability paragraph, and the asset_terms and Class A/B glossary entries. * docs: condition token display on retained terms; state the manifest-rotation recovery condition Two families of remaining overclaims, swept exhaustively: Token provenance: a token stays transferable unconditionally (from its own bundle and the chain), but is only displayable/resolvable from a holder that has retained the asset_terms — the provenance endpoint returns 404 otherwise. Qualify the §4.6 survival clause and residual, the GetTokenProvenance glossary entry, and the two risks.md statements accordingly. Recovery: the honest availability guarantee already depended on manifest rotation preserving recovery-discoverability (§4.3, stated in §4.5 and the risks mitigation), but the parallel summaries listed only a reachable bootstrap node and one live holder per plane. Add the rotation-continuity condition to §4.10, the Requirement-13 rows, §7.7, Requirement 6, and the two risks summaries so every recovery statement carries the same four conditions. * docs: node provides the durable store, the operator provides the backup; mark terms as opaque-optional Two statements predating this branch contradicted the availability model it documents. The §6.1 node responsibility list said the data store 'provides the operator's own backup', but §4.8 makes backup the operator's out-of-repo duty and the node ships no backup subsystem — reworded so the node keeps the durable store and the operator layers the backup on top. The §6.5 display-metadata lead-in said a holder 'obtains' an asset's name/decimals through the asset_terms transport unconditionally; a holder without the terms carries the asset opaquely, so it now reads 'obtains them, if at all'.
) * spec: fill the <REGEN> test vectors from the reference implementation The specification pins its Poseidon-derived values as <REGEN> placeholders so that no one hand-authors them: a wrong vector would lead two implementations to agree on something invalid, and because every such value exists nowhere else, nothing would contradict it. This fills them from the reference implementation, where each value is computed by the code that defines it. Filled: the V.4 table (23 values), the V.2 and V.3 code blocks, V.5 transition signatures for all three networks, the V.6 aggregate scalar, and V.11's log vectors -- eight mth@n and nav_root@n sizes, twelve inclusion paths, five consistency proofs. Placeholders drop from 54 to 25. Two values were added that the document referenced but never stated. V.5 said H(ProofData@0) came 'from V.4' while V.4 said 'derived from the six above', so the bytes appeared nowhere despite V.7 step 3 instructing implementers to substitute them. And R' was absent although the S2C opening R = R' + t·G cannot be checked without it -- that opening is what this fixture exists to pin. R' is placed beneath the V.5 block rather than inside it, because it is not part of the 96-byte SpendRecord and a field line would have implied a 128-byte object. The circuit digests come from a circuit corrected during this work: its size representation admitted a second 64-bit witness for the same Goldilocks element, which let a prover bind a root for one size and have the verifier read another. Sizes are now two range-checked u32 limbs. Fixing it before these digests are pinned matters -- per §1.7.8 the same change afterwards defines a new protocol version. Still open by design: block_anchor.block_hash and .height ('pinned per deployment, not by this spec') and the two harness-selected V.10 coin slots. The remaining 21 occurrences are prose about the discipline itself. s_agg reproduces the value V.8 already pins, which makes it the one vector here with an independent reference to check against. * spec: close the line-break tags in the multi-value cells The page compiles as MDX, where every tag must be closed, and the document already used <br/> throughout. The rows carrying several values per cell introduced <br> instead, which fails the build with end-tag-mismatch. No value changes. * spec: pin the V.12 name-consent framing vector The last unfilled table cell in the test vectors. The preimage and its digest now come from the reference implementation, which gained the §4.3 framing (`name_consent_preimage` / `name_message`) together with the mutation tests this section requires. `name_sig` stays <REGEN> by construction: the section already mandates verifying it under pk0 rather than comparing bytes, because BIP-340 nonce derivation includes auxiliary randomness. That is now stated as a rule rather than left looking like a gap. Also states the ordering the framing depends on: lowercase first, then validate against the §4.3 identifier grammar, and treat a non-conforming name as an error rather than sanitizing it. Without enforcement the ASCII-only assumption this section relies on does not hold — a name carrying a non-ASCII letter survives ASCII lowercasing unchanged while a consumer applying Unicode lowercasing produces different bytes, i.e. two preimages for one name. * ci: pause hosted CI on pull requests while the v1 work is verified locally The v1 work is developed and checked on the build host; hosted CI is out of the loop until it is finished and is switched back on as the last step before the branch is offered for review. Only the trigger changes. The pull_request event is commented out verbatim so re-enabling is a deletion rather than a rewrite, and the build job is untouched. push on develop stays active — that branch is not touched by the feature work. workflow_dispatch is added because this workflow previously had no way to be started by hand at all. * spec: fill the last two vector cells and correct the stale <REGEN> prose V.10's coin_bytes and coin_plain were the only byte-pinnable cells still carrying a description instead of a value. The reference implementation produced them; they are cross-checked against values already pinned here: bytes 0..31 equal V.4's coin.identifier@0, 32..63 equal the V.2 address, 64..79 are 0x3B9ACA00 as a 16-byte big-endian amount, and 80..111 equal asset_id. 224 hex characters in total, and coin_plain carries 150 base64 characters with no padding and no '+' or '/', so the encoding is genuinely base64url. With those filled, a dozen places in the text still said these values were yet to be produced. That was true when written and is not any more, and this is the normative document -- a reader was being told the vectors were open while they sat in the table below. The prose now states that the bytes were produced by the reference implementation, which keeps the part that matters: they are generated, never hand-authored. One place deliberately keeps <REGEN>. The V.11 boundary suite's fixture subtree roots are generated by the harness for every k = 0..63 and are not spec cells at all -- they never appear here, so the marker is still correct there. The same care applies to the V.5/V.6 signature values and the deployment-specific block_anchor, which remain open by construction rather than by omission. The V.4 heading loses its "<REGEN> table" suffix, so the anchor moves. All four internal links to it are updated in the same commit; a dead anchor in the normative spec would be worse than a slightly stale title. No other byte value moved anywhere in this file: exactly one new hex string appears in the diff, and none disappears. * docs: fill the F-08 release-status matrix, and mark what is not implemented The matrix carried six rows of `commit/link` placeholders. F-08's disposition says it is filled at the vectors-pin PR, and the rule under the table says a target property must never be labelled implemented without a code commit and passing evidence — so every cell is now either a verified commit with its merge status and its test, or an explicit statement that the property is not implemented. The merge status is the part that matters. The entire v1 core lives only on the open draft node#231; it is not on `develop`. A row citing a branch commit without saying so would read as "implemented", which is exactly what the rule forbids. Each of the nine cited hashes was checked for existence and for whether it is an ancestor of `develop` — only the Plonky2 pin is. Two rows are deliberately not green: - `recovery` — not implemented. There is no Blossom, ZBE, `k = 3` replica or encrypted bearer-data recovery path on either branch. The local engine / `op_secret` snapshot restore that does exist is a different property and is named as such rather than counted. - `reorg finality` — partial, with the four sub-claims checked one by one (host RFC-6962 log, in-circuit gadget, hard six-confirmation bound, ≥6 fail-stop) and the intentionally absent paper `DistinctElement` no-op noted. The `proof backend` row splits: the crates.io pin is on `develop`, the per-network circuit digests are not, and the `build-report.md` the Evidence column points at does not exist in the node repository at all. A footnote says so, so nobody reads the link as published benchmarks. Three stale passages in the conformance analysis claimed the `<REGEN>` values were still open. They are pinned, so the passages now name what actually remains — V.5/V.6 signatures (verification-bound by construction), the deployment-specific `block_anchor`, and the harness-generated V.11 boundary fixtures — and note that the implementation which produced the pinned values is itself unmerged. The retired `C_batch` reference in the benchmark action is corrected to `C_balance`. * docs: the build report exists now — update the status matrix and its footnote Footnote 3 said the Evidence column pointed at a node artefact that did not yet exist. It does: `docs/build-report.md`, on the open review branch and not on `develop`, carrying measured values rather than estimates — gate counts and degree bits for both circuits, six real circuit builds whose digests all match the pinned file, one real end-to-end proof, and the first complete run of the circuit test suite. The `proof backend` row still keeps three things apart, because collapsing them is exactly what the rule under the table forbids: the crates.io pin is on `develop`, the per-network digests and the report are not, and the report itself carries no proof-size or verification-time distributions — it is single observations, so it is not benchmark evidence in the full sense. It says so itself, and the row says so here. The circuit suite passing 166 tests is real evidence for the rows that assert circuit properties, so it is cited — together with the fact that no CI job runs it. The gates cover `-p node -p shared` only, and "tested once by hand" must not read as "is tested". `recovery` stays **Not implemented**, re-checked rather than carried over: no Blossom client, no ZBE, no `k = 3` replication, no encrypted bearer-data restore on either branch. The neighbouring things that exist are different properties — `blossom_url` is a chain-identity field, the operational bundle and `GetCoinProof` are bootstrap and session surfaces, `recover_inscription` recovers a stuck Bitcoin reveal, and the `op_secret` snapshot is local rather than replicated. * spec: carry the challenge expiry in the redeem body §7.8 already assigns the capability gate to the API layer: it performs the §5.1 challenge–response, computes `chan_bind` from the host it authoritatively serves, and invokes the kernel's pull procedures only for a caller it has already verified. The redeem bodies, however, were written for a monolith — `challenge: { nonce }`, with the note that "the node looks up subject/action/expiry it stored at issuance". A two-process deployment cannot do that. The challenge store lives with the kernel, and `chal = H(domain ‖ nonce ‖ chan_bind ‖ subject ‖ expiry ‖ request_hash)` has to be reconstructed **before** the nonce is consumed — otherwise a mistyped signature would consume the nonce and become a denial of service against the rightful owner. So the redeem body carries the `expiry` the node returned at issuance. It is the smallest change that closes this: no new procedure, no extra round trip, and the API layer stays stateless as §6.1 and §7.5 require. The value is not a secret — the caller received it in the challenge response — and it is not trusted input either: it goes into `chal` and therefore into the signature, so a forged or altered value produces a different `chal` and fails verification. An implementer that does hold a store — including the monolith — must still require the body value to equal the stored one and reject a mismatch under the same code a `chal` mismatch gets, rather than silently preferring its own copy. One reading of the field across both deployment shapes; two would be the drift this change exists to prevent. Five endpoints carry it: `POST /v1/pull` as a top-level field beside `nonce`, matching that endpoint's existing shape, and `POST /v1/attest/balance`, `POST /v1/grants`, `POST /v1/bootstrap/entrust` and `POST /v1/bootstrap/revoke` inside their `challenge` object. Nothing else is relaxed: the nonce stays single-use, the domain stays action-bound, `chan_bind` stays server-side, and a `GrantProof` is still rejected where only owner authentication is accepted. * spec: say when the v1 freeze takes effect, per class The freeze paragraph folded two things into one sentence that have very different consequences: what carries digests and lineages, and what merely lets processes speak to each other. It also gave them a single effectiveness point, the vectors pin. That reading forces an expensive answer to a cheap question. The tag prefixes `"zkCoins/v1/…"` enter every Poseidon digest, so a version bump means regenerating the whole vector series and both circuit digests — even for an addition that touches no gate, no public-input layout and no binding. Meanwhile, between the vectors pin and the public testnet there is no foreign node and no lineage carrying value: the project is green field and rollback before step 10 is free, so a wire freeze in that window protects nothing while costing everything. The two classes now carry their own effectiveness points. The digest- and lineage-bearing class is frozen from step 3 exactly as before, and the paragraph says so explicitly so the separation cannot be read as a general relaxation. The §7 wire formats freeze at step 7, where nodes that are not jointly updated first run and where identical version names under different rules become a real conflict. In between, a wire addition that touches neither a circuit element nor a pinned vector nor a digest is not a new version — and must come with a specification PR stating why it is required. The runbook names step 7 as that point, so the two documents cannot drift apart on it. * spec: give §7.5 the delivery credential §4.3 already assumed §4.3 states that a bare address resolves to nothing without an `Invoice` or a verified kind-0 object, and the node must encrypt a recipient's `CoinProof` to that recipient's `ivpk`. §7.5 never defined a way to carry either across the kernel boundary. The gap makes the specification's own acceptance test unreachable: A-to-Z step 3 sends from Alice to Bob, which fails at an unavailable recipient key, and with it runbook steps 6 through 10. `OutputTemplate` gains an optional `delivery`, a closed tagged union of an `Invoice` or a full kind-0 event. The two carry different authorisations and therefore different check-lists: an invoice is signed twice, by the address holder and by the online op key, while a profile carries a Nostr event signature and no invoice `sig` at all. One shared check-list would have been unsatisfiable for the profile form. An invoice must additionally match its own `OutputTemplate` byte for byte on recipient, asset and amount; a profile matches only the recipient, because it addresses an account rather than authorising a payment. Credentials bind by position, `output_templates[i]` to its own `delivery` — two outputs to the same recipient for the same amount are otherwise indistinguishable. `delivery` is required on every output that is not a self-output, for sends and equally for mints with outputs to third parties, where token standard 2 makes that the normal case. Self-output is defined narrowly and structurally: recipient, subject and the persisted account owner all decode equal, and the operational bundle is held under exactly that subject. A store hit or "the node happens to know this key" does not qualify — on a node holding bundles for many accounts that would let any other customer pass as self. Two limits are written down rather than claimed away. Profile freshness is relay-relative: a relay withholding a newer profile is an availability bound no signature closes, stated in the same terms §4.3 already uses for name-consent currency. And the retention rule — keep `ivpk`, `op_pubkey`, `relays`, discard `pk0`, `nk_commit`, `memo` and the signatures, never log them — is an obligation on implementations, not something the wire format enforces; `pk0` links a recipient to its genesis nullifier on Bitcoin, which is why the obligation exists. Admissible under the §1.7.8 wire-format window: no circuit element, no pinned vector and no digest changes. * ci: switch the hosted build back on The v1 vector work is done and verified locally, so the paused workflow comes back as a deletion: the PAUSED note goes and the two `pull_request:` lines return verbatim. `push` on develop and `workflow_dispatch` stay as they were. * spec: close the delivery check-list gaps a review found Two holes in the delivery credential check-lists, both real. The profile check-list verified the event signature but never bound it to the event's contents: it took the caller-supplied `event.id` on trust. A valid signature over an old `id` would then pass while the profile fields were swapped underneath it. The check-list now recomputes the NIP-01 id from the canonical fields and requires byte-equality before any signature check — the same first step the §7.3 receive path already takes. The invoice check-list required the three §4.3 checks and byte-exact output equality but not a deliverable relay set: a correctly signed invoice with `relays = []` passed, and the job began with no delivery target, pushing a deterministic input error into the async delivery path. It now requires the §1.5/§4.3 structural invariants — fixed-width fields and at least one valid relay URL — like the profile check-list does. Two accuracy fixes alongside: the residual `<REGEN>` exception list (in the runbook step-3 predicate and the assurance gate) now names V.12 `name_sig` and the harness-generated V.11 fixtures, which are runtime values not byte-pinnable cells, so a machine placeholder check cannot reject step 3 forever; and the paper-conformance note no longer claims the build report is "not in the node repository" — it exists on the open node PR, unmerged to develop. * docs: point V.4 references at the renamed Poseidon-values anchor * docs: F-08 recovery — §4.3 overlap enforcement now landed on node #231
…#130) * spec: name protocol versions, token standards, and document editions consistently Add a Conventions subsection distinguishing three version axes — protocol version (vN, one pinned circuit family), token standard (issuance_version N, an independent counter), and document edition (spec-vX.Y) — and pin the parallel-operation rule: a node validates and serves every enabled protocol version and token standard side by side, each independently toggleable by configuration, with no in-protocol conversion between them. Disambiguate prose that used a bare v1/v2 to mean a token standard. * docs: complete the token-standard naming over content merged since this branch Extend the version/standard/edition naming pass to the token-standard v1/v2 references that landed on develop after this branch forked (the §7.5 provenance endpoint's schema/derivation labels and a few clause-list residuals), so no `v1`/`v2` shorthand for a token standard remains. Protocol-version prefixes (`/v1/…`), frozen identifiers (`AssetIdV2`, `IssuanceTerms_v2`, the `terms_hash_v1`/`terms_hash_v2` vector keys) and document editions are left unchanged. * docs: rename the last two token-standard v2 prose references Complete the naming pass: the asset_id glossary entry's supply-cap clause and the terms_hash vector row's formula description still used v2 shorthand for token standard 2. The frozen terms_hash_v1/terms_hash_v2 vector keys and their value labels are left unchanged.
TaprootFreak
approved these changes
Aug 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.
Automatic Release PR
Commits: 1 new commit(s)