Parameter Disclosure
Every Agent Receipt commits to its tool-call parameters via parameters_hash — a SHA-256 digest that proves what happened without retaining the raw payload. That is the privacy-preserving baseline and the default for every emitter.
Parameter disclosure is the opt-in mechanism that lets an operator also make the raw parameters recoverable — but only by the holder of a specific private key, and only on demand. The parameters are encrypted inside the signed receipt body using HPKE (Hybrid Public Key Encryption). The daemon, the SDKs, every storage adapter, and every downstream sink all see opaque ciphertext. Plaintext never hits the wire.
Privacy by default
Section titled “Privacy by default”Out of the box, with no disclosure configuration, the daemon stores only parameters_hash:
"action": { "type": "system.command.execute", "risk_level": "high", "parameters_hash": "sha256:9c84a8c9d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1"}The hash is authoritative for integrity — it binds the signed receipt to the exact parameters object. parameters_disclosure is always additive; enabling disclosure never replaces the hash.
The disclosure envelope
Section titled “The disclosure envelope”When disclosure is enabled and the daemon is given a forensic X25519 public key, it encrypts each qualifying parameters object and attaches the result as parameters_disclosure:
The v1 envelope uses HPKE base mode (RFC 9180) with ciphersuite hpke-x25519-hkdf-sha256-aes-256-gcm — KEM = DHKEM(X25519, HKDF-SHA256), KDF = HKDF-SHA256, AEAD = AES-256-GCM. Both info and the AEAD additional-data field are the empty string. There is no nonce field; HPKE single-shot derives it internally from the KEM output.
A concrete envelope as it appears in a receipt:
"action": { "type": "system.command.execute", "risk_level": "high", "parameters_hash": "sha256:9c84a8c9d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1", "parameters_disclosure": { "v": "1", "alg": "hpke-x25519-hkdf-sha256-aes-256-gcm", "recipients": [ { "kid": "sha256:3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4", "enc": "N_2jVnvb1ijohmjDyNfpfR0SU7bU6m1EwVD3QfG_RDE" } ], "ct": "YGn3i4NpiZxHjeZVggTP8lTxb0ZVdLl-2HjW31qsvo28PjQ_Lt_UQgAMidEXjzwhJPHM7OM" }}All binary fields (enc, ct) are unpadded base64url (no =, +, or /). The kid value is the key fingerprint: sha256: followed by the lowercase hex SHA-256 of the raw 32-byte X25519 public key. This lets a forensic tool derive the fingerprint from a held private key and locate matching receipts without any key registry.
The parameters_hash is always present, whether or not disclosure is enabled. The envelope is additive metadata; removing it has no effect on chain verification.
For the full field reference, see the Agent Receipt Schema — parameters_disclosure envelope.
Two keys, two purposes
Section titled “Two keys, two purposes”The signing key (Ed25519) and the forensic key (X25519) are different keys with different purposes and different holders:
| Signing key | Forensic key | |
|---|---|---|
| Algorithm | Ed25519 (signing) | X25519 (HPKE) |
| Held by | Daemon (private key never leaves) | Public key: daemon config. Private key: forensic responder. |
| Purpose | Proves the receipt is authentic and unmodified | Allows parameters to be recovered on demand |
| Can read disclosures? | No | Private-key holder only |
| Can forge receipts? | No (public key only in config) | No |
The daemon holds only the forensic public key and genuinely cannot read its own past disclosures — the same trust model as the agent not owning its own audit trail (per ADR-0010). Signing-key holders cannot read disclosures; forensic private-key holders cannot forge receipts.
Disclosure modes
Section titled “Disclosure modes”The parameter_disclosure setting controls which actions get an encrypted envelope attached. It is configured on the daemon, which owns disclosure policy under ADR-0010 (daemon process separation). The daemon offers four modes; only the spelling of the allowlist differs by config surface (see the note below):
| Mode | Behavior |
|---|---|
off (false) | Hash only — no envelope (default) |
all (true) | Envelope for every action |
high ("high") | Envelope for high and critical risk actions only |
| allowlist | Envelope for the listed action types only |
The "high" mode uses the taxonomy’s risk classification. The allowlist is a set of action-type strings; see the Action Taxonomy for the full list.
Daemon configuration
Section titled “Daemon configuration”Disclosure is controlled by operator config — never by agent-supplied input. The daemon reads the forensic public key and the disclosure granularity from its config file, environment, or flags.
Generate a forensic key pair
Section titled “Generate a forensic key pair”The daemon ships a one-shot generator. It writes the raw 32-byte private key (mode 0600) and public key (mode 0644), and prints the fingerprint so you can confirm the key the daemon encrypts to matches the private key you keep for recovery:
agent-receipts-daemon --init-forensic-key \ --forensic-key ~/.local/share/agent-receipts/forensic.keygenerated forensic private key: ~/.local/share/agent-receipts/forensic.key (keep this offline; the daemon never reads it)forensic public key: ~/.local/share/agent-receipts/forensic.key.pubfingerprint (kid): sha256:c1102522e88e2511…Keep the private key offline — the daemon never reads it. Only the public key goes into daemon config. Like signing-key generation, the command refuses to overwrite an existing key file.
Config file
Section titled “Config file”In TOML, parameter_disclosure accepts a boolean, a keyword string, or an array of action types:
# Disclosure mode. Accepts: false | true | "high"# | an action-type array ["system.command.execute", "filesystem.file.delete"]# | a comma-separated string "system.command.execute,filesystem.file.delete"parameter_disclosure = "high"
# Path to the forensic X25519 public key file (the raw 32-byte key)forensic_public_key = "/home/me/.local/share/agent-receipts/forensic.key.pub"Environment variables / flags
Section titled “Environment variables / flags”The flag and environment variable have no array type, so the allowlist form is a comma-separated string:
# keyword modeAGENTRECEIPTS_PARAMETER_DISCLOSURE=high \AGENTRECEIPTS_FORENSIC_PUBLIC_KEY=/path/to/forensic.key.pub \ agent-receipts-daemon
# allowlist mode (comma-separated)agent-receipts-daemon \ --forensic-public-key /path/to/forensic.key.pub \ --parameter-disclosure system.command.execute,filesystem.file.deleteThe daemon will not start when the disclosure policy is enabled (anything other than off/false) and no forensic_public_key is configured — an envelope requires a recipient.
When both are present, the daemon logs the forensic key fingerprint at startup so you can confirm it matches the private key you intend to use for recovery:
Parameter disclosure ACTIVE: policy=high, forensic key sha256:3b4c5d6e… — matching parameters will be HPKE-encrypted to that key (recoverable only with the private key)See Daemon Setup for the full configuration reference.
Encrypt-failure behaviour
Section titled “Encrypt-failure behaviour”If HPKE encryption fails for a given event — for example, due to a malformed key or a transient error — the daemon falls back to hash-only for that receipt and logs a warning. The receipt is still stored and the audit chain stays intact and gap-free. A persistent failure at startup (unreadable key file, wrong key format) is a fatal error; the daemon will not start.
Forensic recovery
Section titled “Forensic recovery”When an operator holds the forensic private key, they can decrypt the parameters_disclosure envelope for any matching receipt.
A solo operator generates a forensic keypair, configures the daemon with the public key, and later loads their private key into the dashboard or a CLI tool. The workflow:
- Generate a forensic keypair (X25519) with
agent-receipts-daemon --init-forensic-key(see Generate a forensic key pair above). - Configure the daemon with the public key path and the desired disclosure mode.
- During an incident or audit, load the private key into the dashboard.
- The dashboard derives the key fingerprint (
sha256:<hex of raw public key>), queries receipts bykid, and decrypts each matchingparameters_disclosureinline. - The operator sees the original parameters in the receipt detail view.
The dashboard feature is the intended forensic-recovery UX for the envelope. CLI-based recovery for automated forensic pipelines is also planned (see ADR-0012 Phase B).
Recover from code today
Section titled “Recover from code today”You don’t need the dashboard (or the planned forensic CLI) to read an envelope. All three SDKs ship a decrypt helper that works today — Python decrypt_disclosure, Go DecryptDisclosure, and TypeScript decryptDisclosure. The file --init-forensic-key wrote is the raw 32-byte X25519 private key (no PEM, no decoding); pass it together with the envelope from agent-receipts show <seq> --chain-id <id> --json — found at credentialSubject.action.parameters_disclosure — to your SDK’s helper. The plaintext lives only in your process; nothing is written back to the store.
GDPR and data handling
Section titled “GDPR and data handling”Enabling disclosure on payloads that contain personal data makes the operator a data controller for that data under GDPR and equivalent regulations. The encrypted ciphertext is still personal data if the corresponding private key is held within the jurisdiction.
The right-to-erasure story is crypto-shredding: destroy the forensic private key and every parameters_disclosure envelope becomes permanently unreadable, while the receipt chain — and its hash-based tamper-evidence — stays fully intact. No rows are deleted; no chain integrity is broken.
Practical checklist before enabling:
- Inventory which action types produce sensitive parameters in your deployment.
- Confirm the forensic private key is stored securely, with appropriate access controls and backup.
- Document a key-rotation and crypto-shredding procedure before enabling disclosure in production.
- If your jurisdiction has data-residency requirements, confirm the database (and any SIEM fan-out) is located appropriately.
Key rotation and key accumulation
Section titled “Key rotation and key accumulation”Public keys can rotate freely — configure the daemon with a new forensic public key and new receipts use it. Private keys accumulate: because receipts are immutable, you cannot re-encrypt historical disclosures when the forensic private key rotates. Retain all historical forensic private keys alongside the receipt database or you will permanently lose the ability to recover older disclosures.
Threat model
Section titled “Threat model”| Threat | Mitigated? | How |
|---|---|---|
| Agent reads its own parameters from the receipt store | Yes | Daemon holds only the public key; private key lives with the forensic responder |
| Storage adapter or SIEM sees plaintext parameters | Yes | Only ciphertext is stored or forwarded; adapters need no key-management |
| Attacker modifies the ciphertext in the database | Yes | Ed25519 signature over the full receipt (including parameters_disclosure) detects any tampering |
| Forensic responder forges a receipt | No (by design) | Signing and forensic keys are separate; the forensic private key has no signing capability |
| Disclosure gap caused by an encryption failure | Mitigated | Encrypt-failure falls back to hash-only; the receipt is stored and the chain stays valid |
| PII leakage via plaintext window | Mitigated (daemon mode) | Daemon minimises the plaintext window to a single process that doesn’t hold the signing key; SDK-direct mode keeps it in the agent process |