Skip to content

Release: develop -> main - #131

Merged
TaprootFreak merged 4 commits into
mainfrom
develop
Aug 11, 2026
Merged

Release: develop -> main#131
TaprootFreak merged 4 commits into
mainfrom
develop

Conversation

@github-actions

@github-actions github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

Automatic Release PR

Commits: 1 new commit(s)

  • Review all changes
  • Verify CI passes
  • Merge when ready for production

* 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.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 8, 2026

Copy link
Copy Markdown

Deploying zkcoins-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 91fe5f7
Status:⚡️  Build in progress...

View logs

…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
TaprootFreak merged commit e032acf into main Aug 11, 2026
8 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant