DUNIN7 · LOOMWORKS · RECORD
record.dunin7.com
Status Current
Path protocols/ova/OVA_PROTOCOL_SPEC_V3_1.md

OVA PROTOCOL SPECIFICATION

Version: V3.1 (Toccata-Aligned — v0.6 amendment applied; F-1 remediation) Revision note: V3.1 (2026-06-22): F-1 remediation — REQ-039 off-record counter; type-uniformity guardrail added. Status: Induction-ready — v0.6 amendment (v0.3) applied; submitted for Forge re-induction. Preceded by: OVA_PROTOCOL_SPEC_V1.md → OVA_PROTOCOL_SPEC_V2.md This document supersedes: OVA_PROTOCOL_SPEC_V2.md (Kaspa-Grounded Draft)


Resumption Schedule

This specification was parked at V2 pending the external milestones below. Work resumed June 2026 against the Toccata hard fork (KIP-16/17/20/21); this document is the resulting V3. The table is retained for traceability.

| Milestone | Target Date | Unblocks | |---|---|---| | Kaspa Toccata Hard Fork | ~June 30, 2026 | KIP-16 ZK verification opcode activation | | Post-fork stabilisation window | ~June 30 – July 2026 | Real ZK opcode verification benchmarks on mainnet | | TN12 status confirmation | Mid-May 2026 | Confirms whether Q4 2025 testnet target shipped | | ScopeGas parameter publication | Post-Toccata | REQ-060 ScopeGas companion requirement | | TN12 latency benchmarks | Mid-May 2026 | REQ-048, REQ-059, REQ-061 actual p95 values |

On resumption (completed June 2026): All ⚠ AMENDMENT FLAG markers were refreshed against the then-current Kaspa Research data (Toccata / TN12) and this document was incremented to V3 for Forge induction. The flags remain open pending TN12/mainnet benchmark values that replace the placeholder figures.


Amendment Flags Index

The following requirements carry amendment flags and must be resolved before Forge induction. All other requirements are stable and induction-ready.

| Flag ID | REQs Affected | Blocking Condition | Resolution Action | |---|---|---|---| | [ZK-OPCODE] | REQ-048, REQ-050, REQ-059, REQ-061, Tech Stack | KIP-16 opcode spec finalized (Groth16 confirmed mainnet; RISC Zero STARK live on TN12, mainnet undecided) | Confirm proof system compatibility; replace placeholder latency values with measured TN12 figures | | [SCOPEGAS] | REQ-060, Tech Stack | Kaspa ScopeGas parameters published | Add companion ScopeGas consumption ceiling to REQ-060 AC | | [SCALE] | REQ-060 | TN12 state storage benchmarks available | Validate or amend 1,000,000 egg population threshold |

Amendment flag count: 3 flags, 6 requirements affected. Induction-ready requirement count: 70 of 76 (REQ-001 to REQ-047, REQ-049, REQ-051 to REQ-058, REQ-062 to REQ-076).



Scope

The Ova Protocol is a decentralized access control protocol implemented as a standalone based ZK application on the Kaspa blockDAG, using the Toccata primitives: KIP-16 ZK verification opcodes, KIP-17 covenant opcodes, KIP-20 covenant identifiers (subject to REQ-069), and KIP-21 partitioned sequencing. Ova executes its access-control logic off-chain, inherits transaction ordering from Kaspa Layer 1 consensus, and posts compact zero-knowledge proofs that Layer 1 verifies. Synchronously composable verifiable programs are recorded as a possible future migration target and are not a dependency of this specification. It provides authorization through three cryptographically indistinguishable on-chain objects (eggs): real eggs that carry live authorization state, chaff eggs that are functionally inert decoys, and nest eggs that are managed reusable decoys drawn from a shared pool. All three egg types are serialized identically so that no observer — including one with full on-chain read access — can distinguish egg type without the correct key material.

Access control is enforced through a path-branching mechanism: each authorization hop fans out to N on-chain addresses, exactly one of which is the real next-hop target. The remaining N−1 addresses contain decoy eggs that perform a lightweight decrypt-and-discard operation, producing no observable signal that distinguishes them from real authorizations.

Zero-knowledge proofs allow requesters to prove they satisfy access control list conditions without revealing which condition is satisfied or any auxiliary identity information. All authorization events are logged to the FORAY Protocol audit trail without embedding topology-revealing metadata.

Under KIP-21 partitioned sequencing, the existence and volume of Ova activity within the Ova partition is observable on-chain. Indistinguishability properties apply within the partition — to egg type, authorized content, and requester identity — and never to membership of the partition itself. The traffic-analysis adversary is defined accordingly (REQ-071).

This specification covers the complete Ova application implementation, including egg schema, lifecycle operations, path branching, nest egg pool management, ZK proof circuits, and FORAY integration.


Exclusions


Tech Stack


Glossary

ACL (Access Control List): The set of conditions that a requester must satisfy to obtain authorization from a given egg. Conditions include role membership, attribute value equality, and time-range constraints.

Authorization Chain: A directed sequence of eggs from an entry-point egg to a terminal egg, traversed by a requester to obtain authorization for a protected resource.

Canary: A nest egg configured as a tripwire. Any interrogation of a canary egg that does not originate from the owning authorization chain produces an alert event.

Chaff Egg: An egg containing no live authorization state. Its serialized representation, key structure, and interrogation response are indistinguishable from a real egg to an observer without the correct key material.

Decrypt-and-Discard: The operation performed by an egg at a decoy address when it receives an interrogation it is not the intended target of. The egg decrypts its state header, determines it is not the intended target, and produces a rejection response indistinguishable in timing and byte length from a real rejection.

Egg: The atomic unit of the Ova Protocol. An on-chain object of the Ova based ZK application holding encrypted state, a version field, a creation timestamp, and an optional expiry timestamp. The three egg types — real, chaff, and nest — are serialized identically.

Egg ID: A 32-byte identifier derived deterministically from the egg seed and creation nonce. The Egg ID is the public on-chain handle for the egg.

Expiry: An optional timestamp embedded in an egg after which the egg rejects all interrogation requests and transitions to the retired state.

Fan-out Factor (N): The number of on-chain addresses receiving an authorization hop at each branching point. One address is the real next-hop; N−1 addresses receive decoy eggs.

FORAY Protocol: The tamper-evident audit infrastructure deployed at v4.1 that receives transaction records from Ova. FORAY is consumed as an external dependency; its internal implementation is outside this specification's scope.

Hop: A single step in an authorization chain. A hop consists of the current egg emitting N outbound addresses (one real, N−1 decoy) for the next step.

IND-CPA: Indistinguishability under Chosen Plaintext Attack. The security property proven for egg state-at-rest in Formal Verification Part I.

Interrogation: The operation by which a requester reads the encrypted state of an egg and submits a ZK proof to obtain authorization. Interrogation is the primary access-control operation.

Key Derivation Path: The deterministic derivation sequence from the egg master seed to the per-egg symmetric key, using HKDF-SHA256. The derivation path is identical for all three egg types.

Nest (The Nest): The managed pool of nest eggs available for allocation to authorization chains. The Nest maintains population counts and triggers replenishment when the active count falls below a configured threshold.

Nest Egg: An egg drawn from The Nest, allocated for reuse across multiple authorization chains. When retired, a nest egg returns to The Nest for reassignment.

Nullifier: A 32-byte value derived from the triple (egg ID, requester public key, block height). A nullifier is stored on-chain after a successful authorization to prevent replay attacks.

Provisioning: The operation that assigns an egg to an authorization chain, setting its ACL conditions and linking it to its upstream and downstream eggs in the chain.

Real Egg: An egg carrying live authorization state. Its ACL conditions, state payload, and chain linkage are encrypted under the egg key.

Revocation: The operation that invalidates an egg's authorization state by publishing a nullification commitment to the Kaspa blockDAG. After revocation, interrogation of the egg returns a rejection response indistinguishable from a normal rejection.

State Commitment: A Pedersen commitment to the current encrypted state root of an egg, anchored to the Kaspa blockDAG at each state update.

Based ZK application: A standalone application that executes its logic off-chain and posts compact zero-knowledge proofs that Kaspa Layer 1 verifies via KIP-16 opcodes, inheriting transaction ordering from L1 consensus. Ova is implemented as a based ZK application. (Note: vProgs — synchronously composable verifiable programs — are a future Kaspa stage and a possible migration target for Ova, not a dependency of this specification.)

Covenant Lineage: The KIP-20 covenant identifier relationship linking successive on-chain objects produced by the same egg across its state updates. Ova requires this lineage to be non-linkable across an egg's states (REQ-069).

Ova Partition: The KIP-21 sequencing partition in which Ova activity is recorded. The existence and volume of activity within the partition are observable on-chain; egg type, authorized content, and requester identity within the partition are not (REQ-071).

Proven State Transition: A state transition whose correctness is established within the zero-knowledge proof submitted to Kaspa Layer 1. Decoy rejection and routine nest maintenance are performed outside the proven state transition unless a cited formal verification result requires their inclusion (REQ-072).

Variant-Tagged Serialization: Any serialization construct — enum, tagged union, or discriminated variant — that encodes a value's type as an explicit tag at a serialization boundary. Ova prohibits representing egg type through any variant-tagged serialization construct, preserving byte-length identity across egg types (REQ-070).


Phase 1: Egg Foundation (Cycles 1-2)

Cycle 1: Egg Schema and Type System (1.6 build days)

| ID | Description | Days | Deps | Acceptance Criteria | |---|---|---|---|---| | REQ-001 | The egg record schema defines an egg record containing exactly the following fields: egg_id (32 bytes), version (2 bytes), created_at (8 bytes), expires_at (8 bytes, zero-filled if no expiry), state_ciphertext (variable, minimum 64 bytes), state_nonce (12 bytes), state_tag (16 bytes), commitment (32 bytes). | 0.2 | None | A schema instance constructed with all required fields serializes without error. A schema instance with any required field absent raises a schema validation error identifying the missing field by name. | | REQ-002 | The egg schema does not contain a plaintext type field. The egg type — real, chaff, or nest — is encoded in the first 2 bytes of the state_ciphertext plaintext and is not accessible without the egg key. | 0.2 | REQ-001 | Inspection of the serialized egg record at any field offset reveals no byte sequence that encodes the string "real", "chaff", or "nest" in UTF-8, ASCII, or any fixed-length encoding. | | REQ-003 | Real eggs, chaff eggs, and nest eggs all serialize to the same byte length for identical state_ciphertext lengths. | 0.2 | REQ-001, REQ-002 | A test constructing one real egg, one chaff egg, and one nest egg each with a 64-byte state_ciphertext payload produces three serialized records of equal byte length. | | REQ-004 | The egg_id is derived deterministically from the concatenation of a 32-byte egg seed and a 12-byte creation nonce using SHA-256. | 0.1 | REQ-001 | Given identical seed and nonce inputs, the egg_id derivation function returns the same 32-byte output on two independent invocations. Given distinct nonce inputs with the same seed, the function returns distinct outputs. | | REQ-005 | The egg version field encodes the Ova Protocol specification version under which the egg was created, using the format (major × 256 + minor) as a 2-byte big-endian unsigned integer. | 0.1 | REQ-001 | An egg created under protocol version 0.5 stores the value 0x0005 in the version field. The version field is readable without decryption. | | REQ-006 | The created_at field stores the Kaspa blockDAG block height at which the egg creation transaction was accepted, encoded as a 8-byte big-endian unsigned integer. At post-Crescendo block rate (10 BPS, 100ms per block), block height increments at 10 units per second. | 0.1 | REQ-001 | An egg creation transaction accepted at block height H stores the value H in the created_at field, readable without decryption. | | REQ-007 | The expires_at field stores the Kaspa blockDAG block height after which the egg rejects all interrogation requests. A zero value in expires_at indicates no expiry. | 0.1 | REQ-001 | An egg with expires_at = H rejects an interrogation submitted at block height H+1 with a rejection response. An egg with expires_at = 0 does not reject interrogation on the basis of block height alone. | | REQ-008 | The state_nonce field is generated by a CSPRNG seeded from Kaspa blockDAG entropy at the time of the state write operation. The same nonce value must not appear twice for the same egg_id across the egg's lifetime. | 0.2 | REQ-001 | A sequence of 10,000 successive state write operations on the same egg produces 10,000 distinct nonce values. A nonce collision within this sequence causes a test failure. | | REQ-009 | The commitment field stores a Pedersen commitment to the current state_ciphertext value, computed at the time of each state write. | 0.2 | REQ-001 | Given a known state_ciphertext input, the commitment field value equals the Pedersen commitment of that ciphertext computed with the protocol's defined commitment parameters. The commitment field updates on every state write. | | REQ-010 | The egg schema version is forward-compatible: an Ova application compiled against schema version V accepts a serialized egg record produced under schema version V+1 without raising a parsing error, treating unknown fields as ignored extensions. | 0.2 | REQ-001, REQ-005 | A serialized egg record produced under schema version 0.6 is parsed by an Ova application compiled against schema version 0.5 without error. The parsed record contains all version 0.5 fields with their correct values. |

Cycle 1 total: 1.6 build days | 10 requirements (REQ-001 – REQ-010)


Cycle 2: Egg Lifecycle Operations (2.4 build days)

| ID | Description | Days | Deps | Acceptance Criteria | |---|---|---|---|---| | REQ-011 | The egg creation operation accepts a seed (32 bytes), a creation nonce (12 bytes), an egg type (real, chaff, or nest), an optional expiry block height, and an optional ACL payload, and produces a valid egg record committed to the Kaspa blockDAG. | 0.3 | REQ-001, REQ-004 | An egg creation call with valid inputs produces a committed egg record retrievable by egg_id within 2 Kaspa blockDAG confirmation blocks (≈ 200ms at 10 BPS post-Crescendo). An egg creation call with a duplicate egg_id returns an error identifying the conflict. | | REQ-012 | The egg provisioning operation accepts an egg_id and an ACL payload (serialized conditions), encrypts the ACL payload under the egg key using AES-256-GCM, writes the ciphertext to the state_ciphertext field, and updates the commitment field. | 0.3 | REQ-011, REQ-015 | A provisioned egg's state_ciphertext decrypts to the original ACL payload under the correct egg key. Decryption under an incorrect key produces an AES-GCM authentication tag failure. | | REQ-013 | The egg interrogation operation accepts an egg_id, a ZK authorization proof, and a nullifier, and returns an authorization result (granted or rejected). The operation does not expose the egg's ACL conditions or type in its return value. | 0.3 | REQ-012, REQ-043 | An interrogation call with a valid ZK proof and a non-spent nullifier returns a granted result. An interrogation call with an invalid ZK proof returns a rejected result. An interrogation call with a spent nullifier returns a rejected result. The return value in all three cases contains no field encoding the egg type or ACL condition details. | | REQ-014 | The egg revocation operation accepts an egg_id and a revocation proof (ZK proof of ownership), publishes a nullification commitment to the Kaspa blockDAG, and transitions the egg to the revoked state. | 0.3 | REQ-011, REQ-043 | After a successful revocation call, all subsequent interrogation calls on the same egg_id return a rejected result. The rejected result from a revoked egg is byte-length-equal to the rejected result from a non-revoked egg with a valid-but-failing proof. | | REQ-015 | Every state write operation on an egg — including creation, provisioning, and revocation — encrypts the state payload using AES-256-GCM with a freshly derived per-operation key. Re-use of a (key, nonce) pair for two distinct state writes is prohibited. | 0.3 | REQ-001, REQ-008 | A test capturing the AES-256-GCM key and nonce for 1000 successive state writes on the same egg finds no repeated (key, nonce) pair. | | REQ-016 | The egg key is re-derived on every state write using HKDF-SHA256 with the egg master seed, the current state write counter, and the operation type as input key material. The key derivation path is identical for real eggs, chaff eggs, and nest eggs. | 0.3 | REQ-015 | Given the same master seed and write counter, the key derivation function produces the same output for a real egg and a chaff egg. Given the same master seed and distinct write counters, the function produces distinct outputs. | | REQ-017 | The HKDF-SHA256 info string for egg key derivation uses the domain separation prefix ova-egg-key-v1: followed by the write counter encoded as a decimal string. No other info string format is accepted. | 0.1 | REQ-016 | A key derivation call using the info string ova-egg-key-v1:42 and a second call using ova-egg-key-v1:043 produce distinct output key material. A call using the info string egg-key:42 returns an error. | | REQ-018 | The state write counter is initialized to 1 at egg creation and incremented by 1 on each subsequent state write. The counter is stored in the egg record as a 4-byte big-endian unsigned integer and is readable without decryption. | 0.1 | REQ-016 | An egg undergoes three state writes. The state write counter fields read 1, 2, and 3 respectively in the order of the writes. | | REQ-019 | The egg state commitment is anchored to the Kaspa blockDAG by including the state commitment in the transaction payload of the state write transaction. The anchoring block height is stored in the egg record as a 8-byte big-endian unsigned integer field named last_anchored_at. | 0.2 | REQ-009 | After a state write transaction accepted at block height H, the egg record's last_anchored_at field equals H, readable without decryption. | | REQ-020 | The egg interrogation operation checks that the current on-chain state commitment matches the commitment included in the submitted ZK proof before evaluating the proof. A mismatch between the on-chain commitment and the proof's committed value causes the interrogation to return a rejected result without evaluating the proof's ACL conditions. | 0.2 | REQ-013, REQ-019 | An interrogation call containing a ZK proof committed to a stale state commitment (one write behind the current on-chain state) returns a rejected result. The rejection does not leak whether the commitment mismatch or an ACL condition failure was the cause. |

Cycle 2 total: 2.4 build days | 10 requirements (REQ-011 – REQ-020)


Phase 2: Path Branching and Chaff Infrastructure (Cycles 3-4)

Cycle 3: Path Branching Engine (2.0 build days)

| ID | Description | Days | Deps | Acceptance Criteria | |---|---|---|---|---| | REQ-021 | The path branching engine accepts a fan-out factor N (integer, minimum 2, maximum 32) as a deployment configuration parameter. The fan-out factor applies uniformly to all hops in all authorization chains managed by a given deployment. | 0.1 | None | A deployment configured with N=5 produces exactly 5 outbound addresses at each hop. A configuration with N=1 is rejected with an error identifying the minimum of 2. A configuration with N=33 is rejected with an error identifying the maximum of 32. | | REQ-022 | At each hop, the path branching engine selects exactly one real next-hop address and generates N−1 decoy addresses. The real address is selected deterministically from the authorization chain seed and the current hop index using HKDF-SHA256 with the info string ova-hop-real-v1:{hop_index}. | 0.2 | REQ-021, REQ-017 | Given an authorization chain seed S and hop index 3, the real address selection function returns the same address on two independent invocations. Given the same seed and hop index 4, the function returns a different address from hop index 3. | | REQ-023 | Each decoy address is generated by the path branching engine using HKDF-SHA256 with the info string ova-hop-decoy-v1:{hop_index}:{decoy_index} where decoy_index runs from 0 to N−2. Decoy addresses are distinct from the real address and from each other at each hop. | 0.2 | REQ-021, REQ-022 | For N=5 at hop index 2, the engine generates 4 decoy addresses. All 5 output addresses (1 real + 4 decoy) are distinct. The real address for hop 2 does not appear in the decoy set for hop 2. | | REQ-024 | The byte length of a decoy address equals the byte length of a real address. The two are statistically indistinguishable: a sample of 10,000 real addresses and 10,000 decoy addresses from the same authorization chain family passes a chi-squared uniformity test at the 0.05 significance level. | 0.3 | REQ-022, REQ-023 | A test drawing 10,000 real addresses and 10,000 decoy addresses from distinct chains with the same fan-out factor N applies a chi-squared test to the distribution of address byte values. The test does not reject the null hypothesis of uniform distribution at p < 0.05 for either sample. | | REQ-025 | The decrypt-and-discard operation is performed by an egg at a decoy address when it receives an interrogation. The operation decrypts the state header, confirms the interrogation is not addressed to this egg, and returns a rejection response. | 0.2 | REQ-013, REQ-016 | An interrogation sent to a decoy egg address returns a rejection response. The response byte length equals the byte length of a rejection response from a real egg with a valid-but-failing ZK proof submitted to the same Ova application version. | | REQ-026 | The decrypt-and-discard rejection response is produced within a time window that does not distinguish it from a real rejection. The p95 latency of a decrypt-and-discard rejection is within 50ms of the p95 latency of a real-egg rejection under the same Kaspa blockDAG load. | 0.3 | REQ-025 | A load test submitting 1000 interrogations to decoy eggs and 1000 interrogations to real eggs with invalid proofs, under identical blockDAG load conditions, produces p95 latency values within 50ms of each other. | | REQ-027 | The path branching engine does not include the real next-hop address in the current hop's on-chain transaction payload in plaintext. The real next-hop address is derivable only by a party holding the authorization chain seed. | 0.2 | REQ-022 | Inspection of the on-chain transaction payload for any hop reveals no byte sequence that matches the real next-hop address in plaintext. | | REQ-028 | The N outbound addresses at each hop are published to the chain in pseudorandom order, not in a fixed order that places the real address at a predictable position. The ordering permutation is derived from HKDF-SHA256 with info string ova-hop-order-v1:{hop_index}. | 0.2 | REQ-022, REQ-023 | A test examining the position of the real address across 1000 hops of the same authorization chain finds the real address at each position 0 through N−1 with a frequency within ±5 percentage points of 1/N. | | REQ-029 | The path branching engine enforces a minimum hop depth of 1 and a maximum hop depth of 64 per authorization chain. A chain construction call specifying a depth outside this range returns an error identifying the violated bound. | 0.1 | REQ-021 | A chain construction call with depth 0 returns an error. A chain construction call with depth 65 returns an error. A chain construction call with depth 1 succeeds. A chain construction call with depth 64 succeeds. | | REQ-030 | The authorization chain seed is a 32-byte value generated by a CSPRNG at chain creation time and stored encrypted under the chain owner's public key. The seed is not stored in plaintext on-chain at any point in the chain lifecycle. | 0.2 | REQ-022 | Inspection of all on-chain records associated with a given authorization chain finds no 32-byte sequence that matches the chain seed in plaintext. |

Cycle 3 total: 2.0 build days | 10 requirements (REQ-021 – REQ-030)


Cycle 4: Nest Egg Pool (1.8 build days)

| ID | Description | Days | Deps | Acceptance Criteria | |---|---|---|---|---| | REQ-031 | The Nest is a managed on-chain pool of nest eggs. The Nest tracks, for each nest egg, one of three states: available (unallocated), active (allocated to one or more authorization chains), or retired (expiry reached or explicitly decommissioned). | 0.2 | REQ-011 | A query to The Nest's state returns a count of eggs in each of the three states. The sum of available, active, and retired counts equals the total number of eggs ever committed to The Nest. | | REQ-032 | The Nest enforces a minimum available population threshold T, configured at deployment time (minimum value 1000). When the available count falls below T, The Nest emits a replenishment event containing the current available count and the threshold value. | 0.2 | REQ-031 | A test depleting The Nest's available count to T−1 causes a replenishment event to be emitted. The event contains the available count T−1 and the threshold T. No replenishment event is emitted when the available count equals T. | | REQ-033 | Nest egg allocation assigns an available nest egg to a requesting authorization chain and transitions the egg from available to active. A single nest egg may be allocated to multiple authorization chains simultaneously. | 0.2 | REQ-031 | A nest egg allocated to chain A and then allocated to chain B is in the active state and appears in the active set of both chain A and chain B. | | REQ-034 | The serialized byte representation of an active nest egg on the Kaspa blockDAG is byte-length-equal to a real egg with the same state_ciphertext length. A nest egg in the available state and a nest egg in the active state produce serialized records of the same byte length. | 0.2 | REQ-003, REQ-031 | A test comparing the serialized byte lengths of one available nest egg, one active nest egg, and one real egg — all with 64-byte state_ciphertext payloads — finds all three lengths equal. | | REQ-035 | An interrogation of a nest egg that is not addressed to the interrogating chain's current hop returns a rejection response with the same byte length and within the same latency window as a decrypt-and-discard rejection from a chaff egg, as specified in REQ-025 and REQ-026. | 0.2 | REQ-025, REQ-033 | A test submitting 500 interrogations to available nest eggs and 500 interrogations to chaff eggs finds the response byte lengths identical and the p95 latency values within 50ms of each other. | | REQ-036 | A nest egg may be designated as a canary at allocation time by setting a canary flag in the allocation request. A canary nest egg emits an alert event when interrogated by any address not in the allocation chain's authorized interrogator set. | 0.2 | REQ-033 | A canary nest egg interrogated by an address not in its authorized set emits exactly one alert event. The alert event contains the nest egg's egg_id, the block height of the interrogation, and the interrogating address. A non-canary nest egg interrogated by the same address emits no alert event. | | REQ-037 | The canary alert event does not reveal the allocation chain's identity, the number of chains the canary is allocated to, or any field of the authorization chain seed. | 0.2 | REQ-036 | The canary alert event payload contains only: the egg_id (32 bytes), the interrogation block height (8 bytes), and the interrogating address (32 bytes). No other fields are present in the event payload. | | REQ-038 | Nest egg retirement transitions an active nest egg to the retired state and removes it from all allocation chains. A retired nest egg returns a retired-status response to interrogation, distinct in content from a rejection but equal in byte length. | 0.2 | REQ-031, REQ-033 | After retirement, an interrogation of the egg returns a retired-status response. The retired-status response byte length equals the rejection response byte length for the same Ova application version. The retired-status response does not contain the word "retired" in any plaintext field. | | REQ-039 | The Nest tracks the total number of interrogations each nest egg has received across all its allocation chains. This count is maintained in Nest-side bookkeeping only and MUST NOT appear as a field in the serialized egg record. No interrogation count is stored on, or derivable without decryption from, the on-chain egg. | 0.1 | REQ-031, REQ-013 | After three interrogations of a nest egg (from any combination of allocation chains), the Nest's tracked count for that egg reads 3. A byte-level inspection of the serialized egg record before and after those three interrogations shows no field whose value changed as a function of interrogation count, and the record's byte length is unchanged. | | REQ-040 | The Nest exposes a population query operation returning the current available, active, and retired counts as a triple of 4-byte unsigned integers. The query does not reveal individual egg identities. | 0.1 | REQ-031 | A population query returns a three-field response (available, active, retired). The sum of the three fields equals the total number of eggs registered to The Nest. The response contains no egg_id values. |

Cycle 4 total: 1.8 build days | 10 requirements (REQ-031 – REQ-040)

> Type-uniformity guardrail (F-1 class). No attribute that is present for, or differs by, egg type may be stored as a plaintext field in the serialized egg record. This explicitly includes: interrogation count (REQ-039, off-record), nest state (REQ-031, Nest-side), and the canary flag (REQ-036, allocation-request-side). Any field added to the serialized egg record MUST be present and identically structured across real, chaff, and nest eggs (REQ-070). A field that exists on one type but not another, or whose presence or length correlates with type, is an F-1-class leak and is prohibited.


Phase 3: ZK Proof and Audit Integration (Cycles 5-6)

Cycle 5: ZK Circuits and Proof System (2.1 build days)

| ID | Description | Days | Deps | Acceptance Criteria | |---|---|---|---|---| | REQ-041 | The least-privilege ZK circuit accepts as private inputs the requester's credential set and the egg's ACL conditions, and as public inputs the egg's current state commitment and a nullifier. The circuit produces a proof that the credential set satisfies at least one ACL condition without revealing which condition is satisfied. | 0.4 | REQ-009, REQ-018 | A proof generated with a credential set satisfying condition C2 of a three-condition ACL verifies successfully. The verifier's inputs do not include C2 or any information about which of the three conditions was satisfied. | | REQ-042 | The ZK circuit supports three ACL condition types: (1) role membership — the credential set contains a role identifier matching a role in the ACL's role list; (2) attribute value — the credential set contains an attribute key-value pair matching an entry in the ACL's attribute map; (3) time range — the current block height falls within the range [start_height, end_height] inclusive. | 0.3 | REQ-041 | A proof is generated and verified successfully for each of the three condition types in isolation. A proof is generated and verified for a mixed ACL containing one condition of each type. A proof generation attempt with a credential set satisfying none of the ACL conditions fails at the circuit constraint level before producing a proof. | | REQ-043 | The nullifier is computed as SHA-256(egg_id ‖ requester_pubkey ‖ block_height) where block_height is the Kaspa blockDAG block height at the time of the interrogation transaction submission. | 0.2 | REQ-041 | Given known values for egg_id, requester_pubkey, and block_height, the nullifier function returns the expected SHA-256 output. The nullifier for the same egg_id and requester_pubkey at block heights H and H+1 are distinct. | | REQ-044 | A nullifier is stored on-chain in The Nest's nullifier registry upon successful authorization grant. Subsequent interrogation calls submitting the same nullifier return a rejected result without evaluating the ZK proof. | 0.2 | REQ-043, REQ-013 | An interrogation with nullifier N succeeds. A second interrogation with the same nullifier N on the same egg returns a rejected result. The rejection does not reveal that a nullifier collision was the cause of rejection. | | REQ-045 | The ZK proof includes a circuit version identifier as a public input. The L1 proof verifier (KIP-16) rejects proofs produced under a circuit version older than the current deployment's minimum accepted circuit version. | 0.1 | REQ-041 | A proof produced under circuit version 1 is rejected by a verifier with minimum version 2. A proof produced under circuit version 2 is accepted by the same verifier. The rejection response for a version mismatch is byte-length-equal to the rejection response for a constraint failure. | | REQ-046 | Proof generation executes entirely on the requester's local device. No intermediate circuit state, private input, or credential data is transmitted to any on-chain or off-chain service during proof generation. | 0.2 | REQ-041 | A network capture during proof generation shows zero outbound data packets between proof generation start and proof generation complete events. The only network activity is the submission of the completed proof to the Ova application. | | REQ-047 | The ZK proof size is bounded at 32,768 bytes (32 KiB). A proof exceeding this bound is rejected by the L1 proof verifier (KIP-16) with an error identifying the actual proof size. | 0.1 | REQ-041 | The proof generated by the least-privilege circuit for a three-condition ACL under standard inputs is ≤ 32,768 bytes. The verifier rejects a synthetically padded proof of 32,769 bytes with an error containing the value 32769. | | REQ-048 | The L1 proof verifier (KIP-16) for the least-privilege circuit completes proof verification within 200ms at p95 under standard Kaspa blockDAG load. ⚠ AMENDMENT FLAG [ZK-OPCODE]: The 200ms ceiling is a conservative placeholder. Once KIP-16 (Groth16 confirmed mainnet; RISC Zero STARK live on TN12, mainnet undecided) is active post-Toccata hard fork (mainnet activation ~June 30, 2026, rusty-kaspa v2.0.0, DAA score 474,165,565), real on-chain verifier latency data from TN12 (full Toccata feature set live now — benchmarks can be gathered before mainnet activation) must replace this figure. | 0.2 | REQ-041 | A load test submitting 1000 sequential verification calls to the L1 proof verifier (KIP-16) records p95 verification time ≤ 200ms. | | REQ-049 | The ZK circuit does not accept a state commitment as a private input. The state commitment is always a public input, binding the proof to the egg's current on-chain state. | 0.1 | REQ-041, REQ-020 | A proof generated with state commitment C1 fails verification against an egg whose current on-chain commitment is C2 ≠ C1. | | REQ-050 | The ZK proving system uses a universal trusted setup. The setup parameters are published and pinned to a known hash in the Ova application deployment configuration. A verifier initialized with a different setup parameter hash rejects all proofs. ⚠ AMENDMENT FLAG [ZK-OPCODE]: Once the KIP-16 opcode specification (Groth16 confirmed mainnet; RISC Zero STARK live on TN12, mainnet undecided) is finalized (expected post-Toccata hard fork, mainnet activation ~June 30, 2026, rusty-kaspa v2.0.0, DAA score 474,165,565), this requirement must be amended to confirm that the selected proving system and trusted setup are compatible with the opcode's accepted proof formats. If UltraPlonk is incompatible, the setup pinning mechanism remains correct but the proof system designation changes. | 0.3 | REQ-041 | The deployment configuration contains a 32-byte SHA-256 hash of the setup parameters. A verifier initialized from these parameters accepts a valid proof. A verifier initialized from setup parameters producing a different hash rejects the same proof. |

Cycle 5 total: 2.1 build days | 10 requirements (REQ-041 – REQ-050)


Cycle 6: FORAY Audit Integration (1.5 build days)

| ID | Description | Days | Deps | Acceptance Criteria | |---|---|---|---|---| | REQ-051 | The Ova application emits a FORAY transaction on egg creation. The transaction payload contains: the SHA-256 hash of the egg_id (not the egg_id itself), the string literal CREATE, and the block height. | 0.2 | REQ-011 | An egg creation call at block height H produces exactly one FORAY transaction. The transaction payload contains a 32-byte SHA-256 hash, the string CREATE, and the value H. The payload does not contain the raw egg_id or any field of the egg's state. | | REQ-052 | The Ova application emits a FORAY transaction on egg interrogation regardless of whether the interrogation result is granted or rejected. The transaction payload contains: the SHA-256 hash of the egg_id, the string literal INTERROGATE, and the block height. | 0.2 | REQ-013 | An interrogation call at block height H produces exactly one FORAY transaction with the string INTERROGATE and the value H. The transaction does not contain the interrogation result (granted or rejected), the requester address, or the nullifier. | | REQ-053 | The Ova application emits a FORAY transaction on egg revocation. The transaction payload contains: the SHA-256 hash of the egg_id, the string literal REVOKE, and the block height. | 0.1 | REQ-014 | A revocation call at block height H produces exactly one FORAY transaction with the string REVOKE and the value H. No additional FORAY transaction is emitted for the revocation. | | REQ-054 | FORAY transaction payloads emitted by Ova contain no field that identifies the egg type (real, chaff, or nest), the authorization chain the egg belongs to, the fan-out factor N, or the hop index of the egg within the chain. | 0.2 | REQ-051, REQ-052, REQ-053 | Inspection of the FORAY transaction payloads for 100 distinct egg operations finds no field encoding an egg type string, a chain identifier, an N value, or a hop index in any plaintext or fixed-offset position. | | REQ-055 | FORAY transactions for operations on the same egg are emitted with a causal ordering field: an 8-byte big-endian unsigned integer initialized to 1 at creation and incremented by 1 for each subsequent operation on the same egg. | 0.2 | REQ-051 | The FORAY transactions for create, interrogate, and revoke operations on the same egg carry causal ordering values 1, 2, and 3 respectively. Two interrogation transactions on the same egg carry consecutive causal ordering values. | | REQ-056 | If the FORAY endpoint is unreachable at the time of an Ova operation, the Ova application completes the local operation and queues the FORAY transaction in an on-chain retry queue. The retry queue is processed on the next successful FORAY connection. | 0.3 | REQ-051 | A test simulating FORAY unavailability during 10 egg operations finds all 10 operations complete successfully and all 10 FORAY transactions appear in the retry queue. After FORAY availability is restored, all 10 transactions are delivered to FORAY within 5 subsequent block confirmations. | | REQ-057 | The retry queue is bounded at 10,000 entries. When the queue reaches capacity, new FORAY transactions are dropped and an overflow event is emitted containing the current queue size and the total number of dropped transactions since the last successful flush. | 0.1 | REQ-056 | A test filling the retry queue to 10,000 entries and then triggering one additional operation finds the queue at 10,000 entries and an overflow event containing queue size 10,000 and dropped count 1. | | REQ-058 | The SHA-256 hash used in FORAY transaction payloads is computed as SHA-256(egg_id ‖ deployment_id) where deployment_id is a 16-byte identifier unique to the Ova deployment instance. This prevents cross-deployment egg_id correlation. | 0.2 | REQ-051 | Two deployments with different deployment_ids but the same egg_id produce different SHA-256 hashes in their FORAY payloads for the same egg. |

Cycle 6 total: 1.5 build days | 8 requirements (REQ-051 – REQ-058)


Phase 4: Non-Functional Requirements and Hardening (Cycles 7-8)

Cycle 7: Performance, Security, and Availability (2.0 build days)

| ID | Description | Days | Deps | Acceptance Criteria | |---|---|---|---|---| | REQ-059 | Egg interrogation — including ZK proof verification, state commitment check, nullifier check, and FORAY emission — completes within 500ms at p95 under a Kaspa blockDAG transaction throughput of 100 transactions per second. ⚠ AMENDMENT FLAG [ZK-OPCODE]: The 500ms ceiling is a conservative placeholder. At 10 BPS post-Crescendo, Kaspa's block time is 100ms. The KIP-16 opcode (Groth16 confirmed mainnet; RISC Zero STARK live on TN12, mainnet undecided) verification cost is unquantified until the Toccata hard fork activates (mainnet activation ~June 30, 2026, rusty-kaspa v2.0.0, DAA score 474,165,565). This requirement must be amended with a measured p95 figure from TN12 benchmarks (full Toccata feature set live now — benchmarks can be gathered before mainnet activation). | 0.2 | REQ-013, REQ-048 | A load test submitting 1000 interrogation calls at a sustained rate of 100 tps records p95 end-to-end latency ≤ 500ms. | | REQ-060 | The Nest supports a minimum active population of 1,000,000 nest eggs without causing Kaspa blockDAG throughput degradation exceeding 5% compared to a baseline with an empty Nest. ⚠ AMENDMENT FLAG [SCOPEGAS]: The 5% degradation ceiling is expressed against blockDAG transaction throughput (TPS) as a proxy. Once Kaspa ScopeGas parameters are finalized (final pricing implemented, KIP pending — expected post-Toccata hard fork, mainnet activation ~June 30, 2026, rusty-kaspa v2.0.0, DAA score 474,165,565), this requirement must be amended to add a companion ScopeGas consumption ceiling expressed in ScopeGas units per 1,000,000-egg Nest operation. The TPS-based ceiling remains valid as a baseline benchmark. ⚠ AMENDMENT FLAG [SCALE]: The 1,000,000 egg threshold is a reasoned estimate against Kaspa's post-Crescendo sustained throughput of 60–200 TPS. This figure requires validation against actual state storage benchmarks, available now on TN12 (full Toccata feature set live). | 0.3 | REQ-031 | A benchmark with The Nest holding 1,000,000 active eggs records Kaspa blockDAG transaction throughput at no less than 95% of the throughput recorded with an empty Nest under identical load. | | REQ-061 | A path branching operation with fan-out factor N=10 — including real address selection, decoy address generation, ordering permutation, and on-chain transaction submission — completes within 2000ms at p95. ⚠ AMENDMENT FLAG [ZK-OPCODE]: The 2000ms ceiling is a conservative placeholder derived from the 10-hop submission overhead at 100ms block time. The actual cost of submitting N simultaneous on-chain transactions with ZK proofs against the KIP-16 opcode (Groth16 confirmed mainnet; RISC Zero STARK live on TN12, mainnet undecided) is unquantified until the Toccata hard fork activates (mainnet activation ~June 30, 2026, rusty-kaspa v2.0.0, DAA score 474,165,565). This requirement must be amended with a measured p95 figure from TN12 (full Toccata feature set live now — benchmarks can be gathered before mainnet activation). | 0.2 | REQ-021, REQ-028 | A load test submitting 500 path branching operations with N=10 records p95 completion time ≤ 2000ms. | | REQ-062 | All communication between Ova applications and the FORAY endpoint uses TLS 1.3 or higher. Connections using TLS 1.2 or lower are rejected at the Ova application level with an error identifying the negotiated TLS version. | 0.2 | REQ-056 | An Ova application configured to connect to a FORAY endpoint presenting a TLS 1.2 certificate rejects the connection and emits an error containing the string "TLS 1.2". An Ova application connecting to a FORAY endpoint presenting a TLS 1.3 certificate establishes the connection successfully. | | REQ-063 | Egg master seeds, per-operation keys, and chain seeds are never written to any persistent storage medium in plaintext. All seed and key material is held in memory only for the duration of the operation that requires it. | 0.2 | REQ-016, REQ-030 | A forensic capture of all Ova application storage writes during 1000 egg operations finds no byte sequence matching any of the known test seed or key values in plaintext. | | REQ-064 | The test suite validates all three egg types (real, chaff, nest) across all lifecycle operations: creation, provisioning, interrogation, and revocation. A test run with all egg types and all operations produces zero failures. | 0.2 | REQ-020, REQ-038 | Executing the full test suite against a deployed Ova application instance produces a result of 0 failures and ≥ 120 passing test cases. | | REQ-065 | The test suite includes a path reconstruction adversarial test: an adversary with full on-chain read access and 1000 observed interrogation sequences attempts to identify the real next-hop address at each hop by statistical analysis. The adversary's identification accuracy must not exceed 1/N + 0.02 where N is the fan-out factor. | 0.3 | REQ-028, REQ-024 | A path reconstruction test using 1000 observed hop sequences with N=5 finds the adversary identifying the correct real address with frequency ≤ 0.22 (= 1/5 + 0.02). | | REQ-066 | FORAY audit entries for Ova operations are retained on the FORAY endpoint for a minimum of 90 days from the block height at which the operation was recorded. | 0.1 | REQ-051 | A FORAY query for an audit entry created at block height H returns the entry at block height H + (90 × average blocks per day). A query for an entry at block height H − 1 day before the 90-day boundary returns the entry. | | REQ-067 | The egg record schema supports forward-compatible minor version increments: an Ova application at version V.x accepts serialized egg records produced by an Ova application at version V.(x+1) without data loss for all fields defined in version V.x. | 0.2 | REQ-010 | An Ova application at version 0.5 parses a serialized egg produced by an Ova application at version 0.6. All fields present in the 0.5 schema are populated with their correct values. No parsing error is raised for unknown fields introduced in 0.6. | | REQ-068 | The Ova application deployment configuration exposes a liveness check endpoint that returns HTTP 200 with the body {"status":"live","version":"<N.M>"} when the Ova application is operational, and returns no response within 5 seconds when the Ova application is non-operational. | 0.1 | None | A GET request to the liveness endpoint of a running Ova application returns HTTP 200 with a body matching the pattern {"status":"live","version":"<semver>"} within 1 second. A GET request to the endpoint of a stopped Ova application times out after 5 seconds with no response received. |

Cycle 7 total: 2.0 build days | 10 requirements (REQ-059 – REQ-068)


Cycle 8: Toccata Alignment and Behavioral Uniformity (1.9 build days)

| ID | Description | Days | Deps | Acceptance Criteria | |---|---|---|---|---| | REQ-069 | Egg state transitions MUST NOT be linkable through KIP-20 covenant lineage. Each egg state update produces an on-chain object whose covenant lineage identifier has no derivable relationship to the lineage identifier of the prior state of that egg. | 0.3 | REQ-016 | Given 1000 sequences of 5 successive state updates to the same egg, an adversary with full on-chain read access and all covenant lineage data identifies which objects belong to the same egg with accuracy not exceeding the random-guess baseline + 0.02. | | REQ-070 | Within a given schema version, real, chaff, and nest eggs MUST share a single byte-length-identical structure layout at every serialization boundary. Egg type MUST NOT be represented by any variant-tagged serialization construct (enum, tagged union, or discriminated variant) in any serialized form. Forward-compatible extension fields (REQ-010) MUST be applied uniformly across all three types so length identity within a schema version is preserved. | 0.2 | REQ-001, REQ-010 | A byte-level inspection of 1000 serialized eggs of one schema version (mixed types, equal counts) finds all records byte-length identical, and a static analysis of the serialization code path finds zero enum or tagged-union types crossing a serialization boundary. | | REQ-071 | The traffic-analysis adversary model MUST be defined as an observer of the Ova partition specifically, with full visibility of partition activity timing and volume, and zero visibility of egg type, authorized content, or requester identity. | 0.2 | None | The adversary model section enumerates the observer's visible inputs (partition activity, timing, volume) and explicitly lists egg type, authorized content, and requester identity as not visible. A reviewer confirms each item is classified. | | REQ-072 | Decoy rejection (decrypt-and-discard) and routine nest maintenance operations MUST be performed outside the proven state transition, UNLESS a cited formal verification result requires inclusion to preserve a stated indistinguishability property. Any inclusion MUST cite the requiring proof by Part number. | 0.3 | REQ-021, REQ-025 | A trace of 1000 decoy rejection operations records zero decoy rejections included in a proven state transition, OR every included decoy rejection is annotated with a verification Part citation. A run with no citations and any included decoy rejection fails. | | REQ-073 | Proof system selection MUST be recorded per deployment as a non-normative implementation decision with written rationale, including trusted-setup status and mainnet-availability status of the chosen system at time of selection. | 0.1 | None | A deployment record exists containing the selected proof system name, its trusted-setup status, and its mainnet-availability status. A deployment record missing any of the three fields fails. | | REQ-074 | The egg interrogation handler MUST execute in time independent of egg type. Interrogation of a real, chaff, or nest egg MUST be indistinguishable by wall-clock timing. | 0.3 | REQ-025 | Over 10,000 interrogations per type under fixed load, a per-type timing classifier achieves accuracy not exceeding 0.5 + 0.02. | | REQ-075 | Decoy rejection MUST NOT produce any on-chain artifact (transaction shape, address-touch pattern, or fee signature) that differs from the on-chain footprint of a real-egg interrogation of equivalent position. | 0.3 | REQ-072, REQ-021 | An adversary with full on-chain read access, given 1000 interrogations of mixed real/decoy targets, classifies decoy vs real by on-chain artifact alone with accuracy not exceeding 0.5 + 0.02. | | REQ-076 | The interrogation status surface MUST present a type-independent set of status and error codes. No observable status or error code MUST correlate with egg type. This requirement covers the status/error-code channel only; response content and response byte length are covered by REQ-025 and REQ-038 respectively. | 0.2 | REQ-025, REQ-038 | Over 10,000 interrogations per type, a classifier given only the observed status/error values achieves per-type accuracy not exceeding 0.5 + 0.02, and the set of distinct status values observed is identical across types. |

Cycle 8 total: 1.9 build days | 8 requirements (REQ-069 – REQ-076)


Kaspa Research Grounding Notes

The following facts from primary Kaspa sources have been incorporated into V2. These replace assumptions used in V1 and are the evidentiary basis for all grounded values in this specification.

Source: Kaspa Developments page (kaspa.org/developments), current as of March 2026

Source: vProgs Yellow Paper v0.0.1, September 11, 2025 (Kaspa Research — msutton, FreshAir08, hashdag)

Source: Crescendo hard fork activation data, May 5, 2025

Implication for Ova NFRs: Kaspa's base layer is not the throughput bottleneck. The binding constraint on Ova's performance is ZK proof generation and verification latency at the application layer — figures that will not be measurable until the L1 ZK opcode is live and vProgs testnet benchmarks are published.