headgate-crypto for
Rust and go/headgatecrypto for Go. Encryption happens before enqueue, and decryption happens
inside the registered handler. Core and the storage drivers never receive encryption keys.
Install
Encrypt before enqueue
The key provider exposes one active write key and any historical read keys. The built-in static keyring is useful for configuration-backed keys; production applications can implement the provider interface over a KMS or secret manager.What is protected
AES-256-GCM encrypts onlyEnvelope.payload, using a fresh 96-bit nonce for every
encryption. The authenticated data binds the ciphertext to the job ID, task kind, and
schema version. Moving ciphertext to another job or changing its decoder identity fails
authentication and moves the job to undecodable instead of retrying forever.
The following values remain visible because admission and operations need them:
- job ID, kind, schema version, queue, partition, rate class, tags, and headers;
- priority, schedule, retry, lease, and retention metadata;
- results, progress, mid-run output, logs, and attempt errors.
Fingerprints and equality
Headgate computes the fingerprint from plaintext before adding the random nonce. That preserves uniqueness and poison-pill quarantine across identical encrypted payloads. It also reveals that two payloads are equal even though their ciphertext differs.Rotate keys without losing jobs
- Add the new key while retaining all old keys.
- Change the provider’s active key ID so new jobs use the new key.
- Keep historical keys until no queued, scheduled, retryable, quarantined, or retained job can reference them.
- Remove an old key only after that retention window has closed.
Inspect encrypted jobs in the console
The job detail view recognizes the encrypted envelope and labels it as AES-256-GCM. It shows the wire-format version, key ID, and copyable Base64 ciphertext. By default it does not offer decryption. An embedding application may opt into read-only reveal by installing aPayloadRevealer in the Rust or Go control API configuration.
That callback is the authorization and decryption boundary. It receives the established
request identity/context and a complete job containing ciphertext, and returns plaintext
bytes—not keys. A typical implementation checks an application role, reconstructs the
minimal envelope identity (id, kind, schema_version, and payload), and calls
decrypt_envelope / DecryptEnvelope with its server-side key provider. Map a missing or
invalid historical key to the public “cannot be revealed” error; do not return KMS or key
details to the browser.
Once configured, /meta advertises payload_reveal and the encrypted-payload panel shows
an explicit Reveal plaintext button. GET /jobs/{id}/payload/reveal is read-only and
works when the console itself is in read-only mode. It fetches and decrypts for that
response only: it never edits, re-encrypts, retries, or changes the stored job. Responses
carry Cache-Control: no-store, and the console keeps plaintext outside its shared query
cache and clears it on hide, close, navigation, or an aborted request. Neither API logs
the plaintext; application callbacks must preserve that rule in their own telemetry.
Routing metadata, tags, progress, results, output, and errors remain visible because this
layer encrypts only the payload bytes.
Run the encryption example
Verify ciphertext, authenticated decoding, and the shared application boundary locally.