R-215 — Cross-recipient genesis-pinning gossip protocol
Status: research; proposes a protocol shape, identifies decisions, leaves implementation to a future T-task.
Motivates: THREATS §4.14 (genesis confusion / forking traces) residual risk: "A new joiner with no prior state cannot distinguish the original trace from a fork. They will pin whichever they see first."
Problem statement
Per [SPEC §3.3a], a trace is fixed by the genesis envelope's id. Recipients pin first-seen: the first envelope they observe for a conversation establishes the trace-to-genesis.id binding for the rest of that conversation. This works for established participants.
It fails for:
1. New joiners entering an existing conversation. If an attacker has emitted a fork (a divergent envelope with a different trace pointing at a doctored genesis), the joiner sees both candidates and has no information to decide which is canonical.
2. Returning participants whose pinned state has been lost (cleared cache, fresh device, restored from old backup before the fork).
3. Heralds and conduits asked to attest about a trace they have not previously observed.
Cross-recipient gossip — "here is the genesis I have for trace T" — is the canonical defence flagged by THREATS but not specified by EXT v0.2. R-215's job is to design that defence.
Design constraints
1. Reuse existing infrastructure where possible. EXT §2.5 already defines STH gossip across DNS TXT, P2P, and cross-log channels. A genesis-gossip primitive should not invent parallel transport.
2. Don't leak conversation participants. Gossip must reveal the (trace_id, genesis_id) binding without revealing who is in the conversation. The trace_id and genesis_id are themselves opaque hashes; that's the privacy budget.
3. Don't push; pull. Gossip-on-demand (joiner queries) scales better than continuous push (every recipient broadcasts every genesis they hold).
4. Cryptographic, not consensus. No global agreement on "the canonical trace genesis" — that's reputation/governance. The protocol provides the query primitive; deployment policy decides what to do with conflicts.
5. Address the new-joiner case directly. The joiner with no prior state must be able to query some independent source and get a verifiable answer.
Proposed design — three-layer
Layer 1: CT-log index extension
Every conformant HERALD CT log SHOULD support a /trace_genesis/<trace_id> query endpoint. The endpoint returns:
- All CT log entries
EwhereE.trace == trace_id && E.parent == null(i.e. all entries that claim to be the genesis for this trace). - For each: the SCT, the inclusion proof, and the envelope's full canonical body.
Why this works:
- Genesis envelopes are content-addressed:
trace == id, so two distinct genesis claims for the same trace produce two distinct envelope IDs. Both can be logged; the existence of two entries is the fork signal. - The CT log already has gossip among logs ([EXT §2.5]) and across DNS TXT publication. A query against a single log + cross-check against a peer log is the existing verifier discipline.
- No new wire format. The genesis envelope itself is the gossip payload; the CT log is the deduplicator and indexer.
The query primitive:
GET /trace_genesis/<trace_id>
→ {
"trace_id": "<bytes>",
"candidates": [
{ "envelope_id": "<bytes>",
"canonical_body": "<base64-cbor>",
"sct": { ... },
"inclusion_proof": { ... },
"first_logged": <Unix seconds>
},
...
],
"log_id": "<this-log's-id>",
"queried_at": <Unix seconds>,
"log_signature": "<sig over (trace_id, candidates_digest, queried_at)>"
}The log_signature lets a joiner verify the response came from the claimed log without re-checking every inclusion proof.
Layer 2: Cross-log gossip of trace-genesis claims
When a CT log records a genesis envelope, it SHOULD propagate the (trace_id, envelope_id) tuple to its gossip peers via the existing EXT §2.5 channels:
- DNS TXT publication at
_herald-trace.<trace_id>.<log-domain>— the TXT record containsgenesis=<envelope_id>;sct=<base64>;ts=<Unix>for each genesis the log has logged with that trace. - P2P gossip — peers exchange
(trace_id, envelope_id, sct)triples. - Cross-publication in unrelated logs — a HERALD CT log MAY include trace-genesis tuples from peer logs as opaque payloads, in the same way it cross-publishes STHs.
The DNS TXT path is most useful for high-traffic traces; it lets a joiner with only DNS to query the gossip view without contacting any specific log.
Privacy mitigation: gossip carries only (trace_id, envelope_id, sct) — no participant identifiers, no content hashes beyond envelope_id (which is content-addressed but reveals nothing about who's in the conversation since it's just a hash). The DNS TXT path leaks the existence of a trace at a log domain; that's already revealed by any inclusion proof, so no new privacy loss.
Layer 3: Joiner query discipline
A new joiner observing a non-genesis envelope E with E.trace == T:
1. Query at least two independent CT logs for /trace_genesis/<T>.
2. Union the candidate sets. Verify the inclusion proof for each candidate.
3. Apply policy:
- Single candidate across logs (consistent): pin it. Standard case.
- Multiple candidates: the trace has been forked. The joiner must apply a policy:
- Conservative: refuse to participate in any fork — abort.
- Reputation-system policy: ask a reputation aggregator which fork has more endorsements / weight.
- Operator-trust: pick the fork whose genesis sigil has been verified via Herald endorsement.
- Empty across logs: the genesis was never logged. Per [EXT §2.4], state-transition envelopes MUST be logged; a non-logged genesis is non-conformant. Refuse.
4. Record the chosen genesis as pinned for trace T.
This is the "verifiable-by-query, decided-by-policy" pattern. The protocol gives joiners the information; deployments choose what to do with it.
Why three layers
Layer 1 (CT log query endpoint) is the substrate primitive — without it, the gossip is opaque to joiners. It's a small extension to the existing CT log API.
Layer 2 (cross-log gossip) is where the equivocation defence lives. If only one log knows about a genesis, that log can lie to a joiner. Gossip across multiple logs makes equivocation expensive: the attacker would need to compromise enough logs to silence them all simultaneously, which is exactly the EXT §2.5 split-view defence at the trace level instead of the STH level.
Layer 3 (joiner discipline) is the user-visible behaviour — what verifiers actually do. It mirrors the existing EXT §2.5 "consult at least two independent gossip sources" rule but applies it to genesis pinning rather than STH consistency.
Decisions left to deployment
The protocol says:
- Logs SHOULD support the query endpoint.
- Logs SHOULD propagate trace-genesis tuples through gossip.
- Verifiers SHOULD query at least two logs before pinning.
It does not say:
- Which fork a joiner picks. (Reputation / governance decision.)
- Whether forks are visible at the renderer layer. (Viewer-conformance question; T-021 territory.)
- How many logs a verifier consults. (Policy parameter, default 2.)
This matches HERALD's "legibility, not prevention" doctrine (D-307 thesis): the protocol makes forks observable; deployments decide how to weigh them.
Privacy analysis
What does the gossip leak?
- Existence of trace T. Anyone querying
/trace_genesis/Tlearns that T exists if it returns any candidate. Mitigation: trace_ids are 256-bit hashes; you cannot enumerate them, only query specific ones you've observed elsewhere. - Number of candidates per trace. The fork-detection signal is itself the leak: a query that returns N>1 candidates tells the querier (and any monitor) that this trace has been forked. This is intentional — that's the point of the design.
- Log participation. A genesis logged at log L tells the world that L's logging policy accepted T's genesis. Already revealed by any inclusion proof.
What does it NOT leak?
- Conversation participants. The genesis envelope's
fromfield reveals the originator of the genesis but not subsequent participants. - Conversation content. Per EXT §2.2, only minimum-metadata fields are logged:
id, hash ofparent, hash oftrace, hourly timestamp. Body is not logged. - Who is querying. The query endpoint can be accessed via any anonymising network if needed; the log learns who queries but not why.
The privacy budget is therefore comparable to existing CT inclusion proofs. R-215 does not introduce new leakage beyond what the audit log already exposes.
Threat model
Attacks the design defends against:
- Fork attack (THREATS §4.14): attacker emits a divergent envelope with
trace == Tand a doctored genesis. Defence: joiner queries logs, sees two candidates, applies fork policy. Cost to attacker: must persist the doctored genesis at a log; that log's STH gossip will eventually expose the fork to monitors. - Joiner-targeting attack: attacker shows joiner only the doctored fork. Defence: joiner queries logs independently of the attacker's view. Cost to attacker: must compromise the joiner's log selection, which is harder than compromising the joiner's first-seen view.
- Returning-participant cache loss: same as joiner; query primitive provides recovery.
Attacks the design does NOT defend against:
- All-logs collusion: if every log a joiner consults serves the same fork, the joiner accepts it. This is the cross-log silo collusion in THREATS §4.4 residual risk. Defence is gossip diversity (joiner queries logs in non-aligned jurisdictions); not a protocol property.
- Gag-bound log refusal: a log compelled to refuse
/trace_genesis/<T>queries for specific T cannot be detected without out-of-band signal. Same residual as gag-bound refusal in D-310'slegal_compulsion_undisclosed. - Pre-existing-state-attack: if the joiner has been running for some time and pinned a fork before this protocol was deployed, the protocol does not retroactively unpin. New behaviour applies to new traces.
Comparison with prior art
- CT precertificate-based gossip (RFC 6962-bis): closest analogue. Genesis-gossip is structurally CT split-view detection, applied at the per-trace level.
- DNSCurve / DNSSEC: zone-publication of fingerprints. Not directly applicable but the DNS TXT path borrows the convention.
- Bitcoin block-header gossip: full-mesh broadcast of (height, hash) tuples. Heavier-weight than needed; HERALD's gossip is on-demand pull, not push.
- Matrix/IPFS DHT: distributed hash tables for content lookup. Could be a Layer-2 alternative if/when an IPFS-style DHT becomes a HERALD discovery primitive (currently not in v0.2).
Implementation roadmap
If this design is adopted, the path is:
1. EXT §2.8 — Trace-genesis query endpoint (small extension, ~half a page).
2. EXT §2.5 amendment — Trace-genesis gossip in addition to STH gossip (one paragraph).
3. SPEC §3.3a amendment — New-joiner discipline ("a verifier joining an existing trace MUST query at least two CT logs for /trace_genesis/<trace_id> and apply fork-handling policy"; one paragraph).
4. T-task — reference implementation of the query endpoint as part of T-009 (audit-log reference implementation, future).
5. Test vectors — fork scenarios in T-027 / T-028 (reserved-sigil and conformance test additions).
6. R-task follow-on — Fork-resolution-policy survey (R-218 candidate): catalogue the policies a deployment might apply (reputation-weighted, Herald-quorum-witness, conservative-abort).
Estimated effort:
- EXT/SPEC normative additions: ~1 hour each (design is laid out here).
- Reference implementation: 1–2 days.
- Test vectors: ~1 day.
Open questions for cohort-2
1. Should the query endpoint include the genesis sigil's birth attestation in the response, or just the genesis envelope itself? Including it lets a joiner verify the genesis sigil's identity in one round-trip; excluding it keeps the endpoint single-purpose. Recommendation: exclude — keep endpoints orthogonal — and let the joiner make a separate birth_attestation/<sigil> query.
2. Is the gossip "per-trace" or "per-log-batch"? Per-trace is simpler; per-log-batch is more efficient. Recommendation: per-trace (the message is small, traces are sparse).
3. Should joiners cache fork-positive responses? A joiner who learns trace T is forked may want to remember that across sessions to avoid re-querying. Recommendation: yes, cache; the cache key is (trace_id, fork_status, queried_at).
4. What's the joiner's behaviour if logs disagree about whether a fork exists? (Log A returns 1 candidate; log B returns 2.) This is itself fork evidence — log A is suppressing a candidate, OR log B is fabricating one. Recommendation: treat as fork-positive; alert monitors.
Closes
- THREATS §4.14 residual risk (cross-recipient gossip for genesis pinning) — addressed by Layer 1 + Layer 2 + Layer 3.
- D-307 SR-009 (audit-log centralisation) recommendation — partially addressed (genesis gossip extends the existing STH-gossip logic to per-trace pinning).
Cross-references
- [SPEC §3.3a] trace-parent binding (the local-state pinning rule R-215 extends)
- [EXT §2.5] consistency proofs and gossip (the transport reused)
- [EXT §2.2] minimum-metadata profile (privacy boundary)
- [THREATS §4.14] genesis confusion (the motivating walkthrough)
- [THREATS §4.4] split-view attack (parallel structure at STH level)
- D-307 SR-009 (audit-log centralisation systemic risk)
- D-310 reason codes
envelope_invalid_signature,trace_parent_invalid(refusals a fork-aware verifier might issue)
— R-215 research output, by driver-claude, 2026-05-05.