Skip to main content

ADR-020: Minimal Initial Deployment of the Guardian Committee

StatusProposed
Date2026-04-22

Context

ADR-010 defines the full threshold compliance system: Regulator, Compliance Officer, and Guardian Committee roles mediated by an untrusted Coordination Service, with automated GC-side processing of signed follow-up requests. The decryption protocol spec Part 2 defines full t-of-n threshold decryption with Feldman-VSS distributed key generation. Both are the long-term target but too much to ship for initial launch.

ADR-010 explicitly notes that "the internal workings of the Guardian Committee... are material for a separate ADR." This ADR is that separate ADR - but narrower still: it also defers everything else in ADR-010 (CO role, Coordination Service, Dashboard, follow-up automation) and specifies only the full set of functionality required for initial deployment: key generation, on-chain registration of the combined public key, decryption of a single ciphertext handed to the guardians, and the operational process that ties these steps together.

The compliance public key is referred to as TRC_PK (Threshold Recursive Compliance Public Key). The "Recursive" in the name is forward-looking: the system at this stage is plain threshold compliance, and the recursive aspect arrives in a later iteration. The on-chain storage slot is trcPK and the setter is setTrcPK.

Proposal

Scope

This ADR covers:

  • Guardian key generation.
  • On-chain registration of the combined public key.
  • Decryption of a single ciphertext passed to the guardians out-of-band.
  • Communication topology - which actors exist, what channels they use, and the order of messages during each ceremony.

Deferred to later iterations, to be introduced via key rotation (see Migration):

  • Feldman-VSS DKG and t-of-n threshold (t < n). This deployment is fixed 3-of-3.
  • Coordination Service, Compliance Dashboard, Compliance Officer role.
  • Automated GC-side processing of signed follow-up requests.
  • zk-SNARK proof chains for UTXO-layer follow-ups.
  • Hardened key storage beyond an encrypted file on each guardian's machine.

Guardian set

Three fixed guardians. Membership does not change during this deployment; adding or removing guardians, as well as changing the threshold (e.g. to 2-of-3), is done by rotating to the next scheme, not incrementally.

Sharing model

Additive sharing over the Grumpkin scalar field:

trc_sk = x_1 + x_2 + x_3
TRC_PK = X_1 + X_2 + X_3 where X_i = x_i * G

No polynomial, no Feldman commitments, no Lagrange interpolation. Decryption combines partial shares by plain addition: D = D_1 + D_2 + D_3.

Communication topology

Actors

  • Orchestrator - coordinates communication for the key-generation ceremony. Opens the key-gen chat, picks the ceremony_id, posts it, and relays messages between the Owner and the chat. Holds no key material, has no on-chain authority, and does not pick m / r or construct (R, C). A single person at launch.
  • Owner - the on-chain role authorized to call setTrcPK. Not an EOA (typically a multisig or governance contract). For the test-decrypt the Owner generates random m and r, computes (R, C), hands (R, C) to the Orchestrator (keeps m private), and after receiving the ceremony transcript verifies the result locally before calling setTrcPK(TRC_PK). Identity, signer set, and approval mechanism for the Owner are defined elsewhere and are outside this ADR.
  • Technical expert - sources transactions to be decrypted from on-chain data. Uses chain-explorer tooling that the guardians are not expected to operate. Holds no key material. Delivers each transaction to the guardians for decryption and collects the result.
  • Three guardians - each holds a share x_i on their own machine.
  • External requester - whoever needs a ciphertext decrypted. Vetting of this party is outside this ADR.

Channels

Two Signal group chats:

  • Key-gen chat - Orchestrator + 3 guardians. Used for the key-generation ceremony only, including the Owner-driven test-decrypt that the Orchestrator relays in. Goes dormant after setTrcPK is called. Neither the Owner nor the technical expert is a member.
  • Decryption chat - 3 guardians only. Used for per-transaction decryptions. Neither the Orchestrator, the Owner, nor the technical expert is a member, so partial decryption shares D_i and DLEQ proofs for real transactions are only ever seen by the guardians themselves.

The technical expert interacts with each guardian individually (e.g. via Signal DMs) to deliver each (R, C) and collect the recovered amount. Cross-checks that guarantee all three guardians received the same (R, C), and that all three recovered the same amount, happen inside the decryption chat (see the per-decryption ceremony).

The Owner interacts only with the Orchestrator, on a separate authenticated channel that is out of scope for this ADR.

Message flow, key generation

  1. Orchestrator opens the key-gen chat, picks a ceremony_id, and posts it. The ceremony_id is any value that is unique per ceremony (e.g. a fresh random 32-byte value); it is mixed into each guardian's commitment hash in step 2 of the key generation ceremony so that commitments from one ceremony cannot be replayed in another.
  2. Guardians run steps 1-5 of the key-generation ceremony (commit, reveal, verify, combine) entirely in the key-gen chat. Each guardian computes TRC_PK locally.
  3. Each guardian posts TRC_PK to the chat. The Orchestrator confirms all three values match and forwards TRC_PK, plus the transcript so far (commitments h_i and reveals X_i), to the Owner.
  4. Owner generates random m and r, computes (R, C) = (r * G, m * G + r * TRC_PK), and sends (R, C) to the Orchestrator. The Owner keeps m private; this prevents anyone in the chat from short-circuiting the test by claiming the right answer without actually decrypting.
  5. Orchestrator posts (R, C) to the key-gen chat.
  6. Guardians run the per-decryption ceremony on (R, C): each posts (D_i, pi_i) to the chat.
  7. Orchestrator forwards the chat transcript - the three (D_i, pi_i) pairs, plus the (R, C) it posted - to the Owner.
  8. Owner verifies locally: each Hash(X_j, ceremony_id, j) == h_j; each DLEQ proof pi_j is valid against G, X_j, R, D_j; TRC_PK == X_1 + X_2 + X_3; D = D_1 + D_2 + D_3; C - D == m * G against the Owner's secret m.
  9. If every check passes, the Owner submits TRC_PK through its approval process (multisig threshold, governance vote, etc.) and calls setTrcPK(TRC_PK) on-chain.

The Orchestrator sees D_i and DLEQ proofs only for the test ciphertext - which the Owner generated and verifies independently - so there is no leakage of real-transaction shares. The Owner sees the same plus the ceremony's commitments and reveals; this is exactly what the Owner needs to convince itself the ceremony was correct.

Key generation ceremony

Steps 1-5 happen in the key-gen chat (Orchestrator + 3 guardians). Step 6 is driven by the Owner via the Orchestrator's relay. Each guardian i in 3:

  1. Sample locally. Pick random scalar x_i from the Grumpkin scalar field; compute X_i = x_i * G.
  2. Commit. Post h_i = Hash(X_i, ceremony_id, i) to the key-gen chat. A guardian must not reveal X_i until all three h_j have been received.
  3. Reveal. Once all three commitments are in, each guardian posts X_i.
  4. Verify commitments. Each guardian checks Hash(X_j, ceremony_id, j) == h_j for the other two guardians.
  5. Combine. Each guardian computes TRC_PK = X_1 + X_2 + X_3 locally and checks TRC_PK is not the identity point.
  6. Test-decrypt. Driven by the Owner per the communication topology: the Owner generates m and r, computes (R, C), and the Orchestrator relays (R, C) to the chat. Guardians run the per-decryption ceremony below on (R, C). The Owner verifies the result against its private m once the Orchestrator forwards the transcript.

The ceremony only proceeds to on-chain registration if every check on the Owner's side passes.

Why commit-then-open + test-decrypt is sufficient. The minimal scheme omits the explicit proof-of-knowledge of x_i used in Feldman-VSS. Two properties fill that gap:

  • The commitment phase blocks rogue-key attacks: a guardian who posts h_i before seeing the others' shares cannot adaptively choose X_i to bias TRC_PK.
  • Test-decrypt requires each guardian to produce a valid D_i = x_i * R with a DLEQ proof against their published X_i. A guardian who picked X_i by subtracting rather than by scalar-multiplying G cannot produce a valid D_i and the ceremony fails.

The test message and randomness are chosen by the Owner rather than by anyone in the chat so that the entity verifying the result has independent knowledge of the expected plaintext - no party that sees the ciphertext also controls the success criterion.

On-chain registration

After the Owner has independently verified the ceremony transcript, it calls setTrcPK(TRC_PK) on the contract through whatever approval process the Owner uses. This writes into the trcPK storage slot defined in on-chain components. The Orchestrator has no on-chain authority and cannot register TRC_PK even after a successful ceremony - the Owner is the only party that can.

Per-decryption ceremony

Input: a ciphertext (R, C) the technical expert sends to each guardian individually via their out-of-chat channel. Each guardian posts the (R, C) they received to the decryption chat; the ceremony proceeds only once all three postings match. If they disagree, the ceremony aborts and is investigated offline.

The same ceremony is also used during key generation (step 6) for the Owner-supplied test ciphertext, in which case it runs in the key-gen chat instead, with (R, C) posted by the Orchestrator on behalf of the Owner.

Each guardian i runs a local CLI tool:

  1. Loads x_i from the guardian's own encrypted key store.
  2. Computes D_i = x_i * R.
  3. Produces a Chaum-Pedersen DLEQ proof pi_i that log_G(X_i) == log_R(D_i) (see decryption protocol spec).
  4. Posts (D_i, pi_i) to the chat (decryption chat for real decryptions; key-gen chat for the test-decrypt).

Once a guardian has seen all three (D_j, pi_j) messages, they run the combine step locally:

  1. Verify each pi_j against the public values G, X_j, R, D_j. Reject if any proof fails.
  2. Compute D = D_1 + D_2 + D_3 and M = C - D.
  3. Solve the bounded discrete log to recover the plaintext amount (see Discrete Log Solver).

All three guardians perform the combine independently. There is no designated combiner. For a real decryption, each guardian reports the recovered amount back to the technical expert via the same out-of-chat channel the input arrived on; the expert proceeds only if all three reports agree. For the test-decrypt during key generation, the Orchestrator forwards the chat's (D_i, pi_i) posts to the Owner, who performs the combine and verification themselves against their private m.

DLEQ proofs are kept in this minimal version (rather than deferred with the rest of the threshold machinery) because without them a single corrupted D_i produces a silently-wrong plaintext with no way to identify the source. With DLEQ, any such attempt is caught and attributed.

Migration

Migration to the full scheme (target: 2-of-3 threshold with Feldman-VSS DKG and the ADR-010 coordination stack, or any future variant) is not backward-compatible:

  1. The same three guardians run a fresh key-generation ceremony producing TRC_PK'.
  2. The Owner calls setTrcPK(TRC_PK') through its approval process.
  3. Ciphertexts created after the rotation are decrypted with the new scheme.
  4. Ciphertexts created before the rotation remain encrypted under the old TRC_PK and require all three guardians to cooperate indefinitely. The three guardians retain their old x_i values alongside any new key material.

There is no dual-key period and no re-encryption of historical ciphertexts.

Consequences

Easier

  • No DKG implementation (key generation is simple enough to be feasible over a single Signal conversation), no Feldman commitments, no Lagrange interpolation code paths in the minimal CLI.
  • No Coordination Service, Dashboard, or GC-side automation to build and operate for launch.
  • Signal plus a small CLI is sufficient to run the committee end-to-end; no new backend infrastructure.
  • The Orchestrator is a low-trust role: anything it does that affects the protocol can be cross-checked by the Owner from the transcript, so the role can be staffed by anyone reliable for coordination without needing strong key custody guarantees.

Harder / accepted trade-offs

  • Any single guardian unavailable for any reason (lost key, offline, unresponsive) blocks all decryptions.
  • Audit trail is informal: the Signal history plus each guardian's local logs and the Owner's locally-stored transcript. No structured log tied to regulatory inquiries (arrives with ADR-010).
  • Every single tx decryption requires a human-driven ceremony. Does not scale past the small volume expected at launch.
  • On-chain registration requires the Owner's approval process in addition to a successful ceremony; the Owner is an independent gate, which adds latency but prevents the Orchestrator from registering a key unilaterally.
  • Old ciphertexts remain decryptable only via 3-of-3 for the lifetime of the system, even after the scheme is replaced.

Unchanged

  • On-chain contracts, circuits, and the compliance ciphertext format defined in on-chain components.
  • The ElGamal scheme used for compliance ciphertexts and the shape of DLEQ proofs.
  • End-user privacy properties - users do not interact with this subsystem.