7.6 CISO Access Control Unit & Zero-Plaintext Inboxes
chinwag-acu policy verify --zero-plaintextTechnical specification of crates/chinwag-acu: BLS12-381 pairing-based split-key encryption, CISO fat-client instant revocation over iroh, zero-plaintext-at-rest inboxes with CISO-gated forensic message archiving over S3/R2 Parquet + DuckDB, and zero-re-encryption agent handoffs derived from DASTE (ePrint 2026/1141).
7.6 CISO Access Control Unit & Zero-Plaintext Inboxes
chinwag-acu policy verify --zero-plaintext
Your CISO holds the kill switch. Inboxes store zero plaintext at rest. Agent handoffs never decrypt and re-encrypt. Implemented in
crates/chinwag-acu(#![forbid(unsafe_code)]) overBLS12-381bilinear pairings (e: G1 × G2 → GT), with dynamic ciphertext-only policy evolution derived from the pairing-based DASTE construction (IACR ePrint 2026/1141).
When autonomous coding, review, and QA agents coordinate across a shared Git repository, enterprise CISOs require three hard cryptographic guarantees:
- Customer Key Custody & Instant Revocation: Chinwag Cloud must never hold decryption keys, and the CISO must be able to cut off a compromised laptop or rogue agent in 0 ms, even for encrypted payloads already sitting on the bus or workstation.
- Zero Plaintext At Rest in Inboxes + CISO-Gated Forensic Archive: No agent or human inbox may store decrypted messages at rest on disk, yet the CISO must retain the ability to authorize forensic decryption of historical agent messages during an incident or compliance audit, even after an agent's key has been revoked and wiped.
- Zero-Re-Encryption Multi-Agent Handoffs: When an agent fails at Step 11 and warm-forks (
chinwag fork-run) to a second agent, granting the new agent access to the encrypted state must not require decrypting and re-encrypting the payload.
chinwag-acu (crates/chinwag-acu) delivers all three guarantees in a single BLS12-381 pairing envelope.
1. The Canonical 2-of-3 Policy: ACU AND (Coding Agent OR Archive Unit)
Instead of encrypting to a single recipient or storing decrypted messages in an agent's local inbox, every CI/CD root-cause payload and every sent/received agent or human message is encrypted under a canonical 2-of-3 Linear Secret Sharing Scheme (LSSS) policy anchored on the CISO's Access Control Unit (ACU):
Policy = ACU (CISO Fat Client) AND (Recipient (Coding Agent / Human) OR Archive (Message Archive Unit))
Every agent and human automatically CC's the organization's Message Archive Unit (archive:vault) inside the same ciphertext header (3 LSSS rows over a single encrypted payload C_0):
| Access Path | Participating Seats (2-of-3) | Operational Purpose |
|---|---|---|
| Live Execution Path | ACU + Coding Agent (acu:98939834 + branch:8687872) | The agent wakes, requests a 576-byte partial pairing share D_ACU in GT from the CISO's ACU over the iroh P2P bus, combines D_ACU + D_Agent in RAM as its own local combiner, and never writes plaintext to its inbox on disk. |
| Forensic Archive Path | ACU + Archive Unit (acu:98939834 + archive:vault) | The Archive Unit stores every message encrypted at rest and cannot decrypt anything on its own. If an investigation requires inspecting historical messages, the CISO authorizes release via the ACU, and ACU + Archive Unit co-decrypts the message with a permanent entry in the immutable audit log. |
| Collusion Attempt (Blocked) | Coding Agent + Archive Unit (without ACU) | Mathematically impossible (PolicyNotSatisfied). Because the LSSS rows for Coding Agent ([1, 2]) and Archive Unit ([1, 2]) share the same right-hand OR sub-branch, even an attacker who steals both the developer laptop and the Message Archive storage cannot reconstruct (1, 0) without the CISO's ACU share ([1, 1]). |
2. Zero-Config CI/CD Policy Resolution via Git (chinwag-config.git)
The chinwag CI/CD runner never calls an external policy database to figure out who should receive a failed build payload. Your Git repository (chinwag-config.git) is the policy directory:
- Inspect PR / Commit Provenance: When a CI build runs on a commit or pull request from
branch:8687872, the runner extracts the inner Git commitEd25519signature and the outer envelopeEd25519signature. - Validate Against
.chinwag/allowed_signers: The runner looks upbranch:8687872,acu:98939834, andarchive:vaultin.chinwag/allowed_signers(mounted fromchinwag-config.git). Each record binds the seat'sEd25519verification key alongside itsBLS12-381pairing public key (pk1 = g1^skinG1,pk2 = g2^skinG2), verified for bilinear pairing consistencye(pk1, g2) == e(g1, pk2). - Fail Closed Before Encryption: If either
Ed25519signature or pairing consistency check fails, the runner aborts immediately. - Auto-Synthesize the
2-of-3Ciphertext: When 20 CI shards fail, the runner collapses them into one root-cause payload, encrypts it viaacu_encryptunderacu:98939834 AND (branch:8687872 OR archive:vault), and writes the single encrypted envelope tochinwag-bus.git.
3. CISO Fat Client (chinwag Desktop) Over the iroh P2P Bus
Chinwag Cloud never holds the ACU private key (sk_ACU). Instead, the CISO runs the ACU inside their own chinwag fat client (the native desktop app or an on-prem security node) connected to the fleet over Chinwag's outbound-only iroh QUIC P2P mesh (chinwag-mesh):
┌────────────────────────────────────────────────────────────────────────────┐
│ 1. CI Runner encrypts failed build under: ACU ∧ (Agent_8687872 ∨ Archive) │
└─────────────────────────────────────┬──────────────────────────────────────┘
│ Outbound :443 (chinwag-bus.git)
▼
┌────────────────────────────────────────────────────────────────────────────┐
│ 2. Agent_8687872 wakes locally -> dials CISO Fat Client over iroh P2P mesh │
│ Sends signed AcuShareRequest (Row_ACU only; C_0 & payload stay local!) │
└─────────────────────────────────────┬──────────────────────────────────────┘
│ iroh QUIC Stream (ALPN: /chinwag/acu/1)
▼
┌────────────────────────────────────────────────────────────────────────────┐
│ 3. CISO Fat Client (acu:98939834) │
│ • Verifies Agent_8687872 Ed25519 signature & live revocation status │
│ • Appends hash-chained AuditLogEntry to Immutable CISO Audit Log │
│ • Computes D_ACU = e(C_{ACU,1}, g2) · e(g1, C_{ACU,2})^(-sk_ACU) in GT │
│ • Streams 576-byte signed AcuShareResponse back to Agent_8687872 │
└─────────────────────────────────────┬──────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────────────────┐
│ 4. Agent_8687872 acts as its own Local Combiner in RAM: │
│ • Verifies D_ACU via acu_verify_partial_share │
│ • Computes own half D_Agent = acu_partial_decrypt(Row_Agent, sk_Agent) │
│ • Calls acu_combine(ct, &[D_ACU, D_Agent]) -> unblinds C_0 in RAM! │
└────────────────────────────────────────────────────────────────────────────┘
- Zero Plaintext Exposure to the ACU: The CISO's ACU only ever receives its own LSSS row
(C_{ACU,1}, C_{ACU,2}), neverC_0or the encrypted CI payload. - 0 ms Instant Revocation: The moment the CISO revokes
branch:8687872in the fat client, the ACU refuses to emitD_ACU, rendering every encrypted message or checkpoint held by that agent mathematically undecryptable.
4. Ciphertext-Only Dynamic Policy Evolution (Derived from DASTE)
In standard threshold or attribute-based encryption, changing who can decrypt an existing ciphertext requires somebody holding the secret key to decrypt the payload and re-encrypt it. In an ephemeral CI/CD and multi-agent pipeline, the CI runner that originally encrypted the failure is already gone, and the relay is untrusted.
chinwag-acu adapts the dynamic policy evolution mechanics of the pairing-based DASTE scheme (Decentralized Ad-Hoc Access-Structured Threshold Encryption with Dynamic Policy Evolution, Pradhan & Dutta, ICICS 2026, IACR ePrint 2026/1141) to BLS12-381. Because the LSSS secret shares lambda_j = <M_j, v> sit in the exact prime-order exponent of G1 alongside public basis commitments V_m = g1^(v_m), chinwag-acu updates access policies directly on the ciphertext header without ever decrypting C_0:
| Policy Evolution Operation | chinwag_acu Rust Function | What It Does on the Ciphertext Header |
|---|---|---|
1. Zero-Decryption Agent Handoff (insert_key / insert_sibling_key) | insert_sibling_key(&mut ct, "branch:8687872", &pk_agent2, rng) | When Agent 1 hands off or warm-forks at Step 11 (chinwag fork-run) to Agent 2 (branch:77441122), computes g1^(lambda_new) = Sum_m V_m * M_{new,m} in G1 and appends a new row for Agent 2. Policy becomes ACU AND (Agent 1 OR Agent 2 OR Archive) with zero decryption or re-encryption. |
2. In-Place Threshold Escalation (escalate_threshold) | escalate_threshold(&mut ct, &rows, &dir, delta_t, rng) | When a task touches a high-security file scope (crates/chinwag-crypto/**), extends basis_commitments with delta_t fresh column commitments g1^(u_m) and shifts affected rows by Vandermonde powers x_j^m * u_m, escalating a 2-of-2 gate to 3-of-3 (ACU + Agent + Human Architect) in place. |
3. Subtree Revocation + Ciphertext Rerandomization (revoke_and_rerandomize) | revoke_and_rerandomize(&mut ct, &["branch:8687872"], &dir, rng) | Prunes all LSSS rows for branch:8687872, samples a fresh blinding vector delta_v = (delta_s, ...), rotates C_0 += e(g1 * delta_s, g2), updates V_m and surviving rows (ACU and Archive), and increments policy_epoch. Any partial share D_old cached by the revoked agent at epoch 1 is now algebraically useless, while ACU + Archive can still decrypt at epoch 2 for forensic investigations. |
5. What Stores the Encrypted Message Archive? (DuckDB Over Cloudflare R2 S3-Compatible Parquet)
You never have to stand up or manage MongoDB, Postgres, or a 24/7 database cluster for the Message Archive Unit (archive:vault). Chinwag uses append-only Parquet files on Cloudflare R2 (via R2's S3-compatible API with $0 egress fees) queried by DuckDB embedded inside the CISO's chinwag fat client:
| Component | Technology | How It Works |
|---|---|---|
| 1. Encrypted Archive Storage | Cloudflare R2 Parquet Files (S3 API <account_id>.r2.cloudflarestorage.com + WORM Object Lock) | Every archived message is written as a Parquet row with cleartext routing columns (timestamp_utc, sender_seat, recipient_seat, task_id, room, policy_epoch, ed25519_sig) and an encrypted payload column (acu_ciphertext, encrypted under ACU AND (Agent OR Archive)). |
| 2. Serverless Investigation Query Engine | Embedded DuckDB (httpfs, threads=2, memory_limit='1GB') | Runs off the main GUI thread via tokio::task::spawn_blocking (Arc<Mutex<Connection>>). Queries read_parquet('s3://.../archive.parquet', hive_partitioning = true) over R2 HTTPS Range requests and populates an in-RAM TEMP TABLE active_channel_cache (50 messages per active room) so flipping between channels takes 0 ms and only scrolling past cached history fetches older chunks from R2. |
| 3. In-RAM Forensic Decryption | acu_combine (sk_ACU + sk_Archive) | Only the matching acu_ciphertext BLOB rows returned by DuckDB are verified via ed25519_sig, combined with the CISO's ACU share, and decrypted in RAM inside the fat client, logging GrantedArchiveInvestigation to the immutable audit log. |
6. Rust API Reference (crates/chinwag-acu)
use chinwag_acu::{
acu_combine, acu_encrypt, acu_partial_decrypt, acu_verify_partial_share,
insert_sibling_key, revoke_and_rerandomize, AcuCiphertext, AcuError,
AccessTree, EncryptedCiPayload, LsssMatrix, SeatSecretKey,
};
// 1. Canonical 2-of-3 Policy: ACU AND (Coding Agent OR Message Archive)
let tree = EncryptedCiPayload::build_acu_recipient_or_archive_policy(
"acu:98939834",
"branch:8687872",
"archive:vault",
);
let lsss = LsssMatrix::from_tree(&tree)?;
// 2. Core Pairing Operations (BLS12-381)
let ct: AcuCiphertext = acu_encrypt(&session_key_gt, &lsss, &directory, &mut rng)?;
let share_acu = acu_partial_decrypt(&ct.rows[0], &sk_acu)?;
assert!(acu_verify_partial_share(&ct.rows[0], &pk_acu, &share_acu)?);
let share_agent = acu_partial_decrypt(&ct.rows[1], &sk_agent1)?;
let recovered_gt = acu_combine(&ct, &[share_acu, share_agent])?;
// 3. Zero-Decryption Handoff to Agent 2 (branch:77441122)
insert_sibling_key(&mut ct, "branch:8687872", &pk_agent2, &mut rng)?;
// 4. Revoke Agent 1 & Rerandomize Ciphertext (Archive + ACU remains decryptable!)
revoke_and_rerandomize(&mut ct, &["branch:8687872"], &directory, &mut rng)?;
Next Section: Zero-Tailscale E2EE Mesh & Open Extension SDK
- Previous:
A.509Sovereign Agent PKIX Passports (draft-spector-a509-agent-pkix-00) - Next: Zero-Tailscale
iroh+ Cloudflare E2EE Mesh &chinwag extSDK - Ready for CISO-grade cryptographic agent control? Reserve Your Organization Credit Pack →
A.509 Agent Passports
Cryptographic identity, capability scoping, and mandatory conversation instance addressing (<SEAT>#<INSTANCE>) for autonomous AI coding agents.
chinwag-mesh & 3-Repo Time Machine
How chinwag-mesh connects developer workstations and CI runners via closed-room iroh QUIC P2P, K1+K2 Post-Quantum Hybrid E2EE, the 3-Repo Chinwag Cryptographic Time Machine, and enterprise proxy-resilient .cwcard.png visual receipt cards.