Security

How klanex handles
agent credentials

KMS-sealed at ingest, decrypted only in worker memory for the duration of one call, never stored in plaintext, and redacted in every read and log. The design — and why it fails closed.

klanex engineering · August 2026 · ~6 min read

An AI agent that does anything useful needs a live production credential. A Stripe secret key to issue a refund. A Slack token to post a message. A bearer token to write a row in someone else's system. The moment your agent stops being a chatbot and starts acting, it needs the keys to the things it acts on.

Which raises the uncomfortable question every agent platform has to answer, and that a run of 2025–2026 incidents forced into the open: where does that credential live, and who — or what — can read it back? When third-party keys leak out of an agent tool, the honest post-mortem is rarely "the model misbehaved." It's that the credential was reachable at all — sitting in a store, a log, or an environment that something else could get to.

So klanex starts from a single principle: you cannot leak what you never stored. A credential should be unreadable at rest and reconstructable only at the exact moment it's used, in the one component that has to make the call — and nowhere else. Three properties fall out of that:

The lifecycle of one credential

Sealed before anything is written; opened only in the private worker, only in memory, only for the call.

1

Seal at ingest

The public API marshals the target header map and seals it with Cloud KMS before the execution is persisted. The plaintext is then dropped from the request object.

2

Store ciphertext only

Firestore records the ciphertext and the header names for audit — never the values. The queue message carries only the execution ID, not the secret.

3

Open in worker memory

The private worker decrypts just before the call, in memory only. The plaintext is never written back — not even after the doc is re-read to claim it.

1. Sealed before the first write

In the execution model, the field that carries credentials is deliberately marked firestore:"-" — it is structurally impossible to persist it as-is. On the ingest path, before a single byte is written, klanex JSON-marshals the header map and hands it to a sealer. In production that's Cloud KMS symmetric encryption; locally it's AES-256-GCM with a random nonce and a key that comes from the environment, never disk. What lands in Firestore is the ciphertext (encrypted_headers) plus a sorted list of the header names (header_names) for the audit trail. Then the plaintext map is set to nil.

The same is true when the URL itself is the secret — a Slack incoming webhook, a presigned S3 URL. Set seal_url and klanex seals the URL and persists only a redacted scheme://host/[sealed] form. The path that carried the token never touches the database.

2. The source of truth holds only ciphertext

This is what actually sits in Firestore for an execution — and what the queue hands to the worker:

3. Opened only in the worker, only in memory

The worker is a private Cloud Run service. It has no public surface and no auth code of its own — it's invoked only by the Pub/Sub push subscription over OIDC, with Cloud Run IAM as the gate. When a message arrives, the worker loads the execution, then decrypts the headers and any sealed URL into memory only, immediately before the call. Those plaintext fields are never persisted back.

There's a subtlety that shows the intent. Right after decrypting, the worker atomically claims the execution — which re-reads the document from Firestore, and that fresh copy of course has no plaintext. So klanex deliberately re-attaches the in-memory-only fields onto the claimed doc, makes the call, and lets them fall out of scope. At no point is a decrypted credential written anywhere durable.

It fails closed, not open

The most dangerous failure mode for a system like this is a silent downgrade — a misconfigured deploy that quietly starts storing plaintext because the key wasn't wired up. klanex refuses to do that. With no encryption configured, the binary will not boot unless you have explicitly opted into plaintext for local development:

A decrypt failure never burns a retry

Reliability and security meet in one nice detail. If a decrypt fails — a rotated key, a botched IAM change — that's a configuration problem, not a problem with the target API. So the worker nacks the message for redelivery without consuming one of the execution's retry attempts. A key-management mistake can't quietly exhaust an execution's budget and fail it; fix the config and the next delivery just works. The same holds for a managed OAuth token that's been revoked: klanex fails the execution terminally with an llm_hint telling a human to re-authorize the connection — it never loops, and it never leaks.

Redacted everywhere a human or model can look

Sealing at rest only helps if the plaintext doesn't leak out some other door. So the read and log paths are redacted by construction:

What this is — and honestly isn't

klanex is not your organization's identity provider or a general secrets manager, and it doesn't pretend to be. It seals the credentials used for the calls it makes on your behalf. And yes — for the duration of a single call, the worker holds the plaintext in memory, because you cannot make an authenticated HTTP request without it. That's the irreducible core. What klanex does is shrink the blast radius around it to the smallest possible shape: a private service with no auth surface, decrypting exactly what it's about to use, persisting none of it, logging none of it, and returning none of it. A compromised read path or a leaked log yields nothing. The store, breached, yields ciphertext.

That's the whole idea. You can't leak what you never stored — so don't store it.

Give your agent the keys without handing them out.

Start free — 1,000 executions a month, the full reliability engine, no credit card to explore.