@zanii/broker
v0.1.0
Published
The credential broker that closes Zanii's stated custody hole: the agent never holds raw API keys - the broker does, and it injects them ONLY into calls that produced a receipt first. Receipt-or-nothing, fail-closed (ledger unreachable = no call), credent
Readme
@zanii/broker
The credential broker that closes Zanii's stated custody hole. Every package's limits section ends the same way: "an agent still holding raw credentials can act off the rails." This replaces detection with one invariant:
The agent never holds the credential. The broker does — and it injects it ONLY into calls that produced a receipt first.
npm install @zanii/broker @zanii/coreimport { createBroker } from '@zanii/broker';
const broker = createBroker({
credentials: {
openai: { inject: 'bearer', secret: process.env.OPENAI_KEY!, allow: ['https://api.openai.com/v1'] },
maps: { inject: 'query', name: 'key', secret: process.env.MAPS_KEY!, allow: ['https://maps.googleapis.com/'] },
},
// MUST reject unless the receipt is durably accepted — record + flush + check:
record: async (f) => {
await agent.record({ target: f.target, payload: f.payload });
const results = await agent.flush();
if (results.some((r) => !r.ok)) throw new Error('receipt not accepted');
},
gate: (req) => policy.evaluate(req).decision !== 'deny', // optional @zanii/policy hook
});
// the agent asks; it never sees the key:
const res = await broker.execute({
credential: 'openai', method: 'POST',
url: 'https://api.openai.com/v1/chat/completions', body: JSON.stringify(payload),
});Python: from zanii.broker import create_broker — same semantics.
Fail-closed by construction (none of this is configurable)
- Allow-list first — each credential is bound to explicit https origins + path
prefixes. Origins compare exactly (
api.openai.com.evil.comcannot masquerade), path prefixes respect segment boundaries (/v1allows/v1/chat, not/v1abc). A prompt-injected agent cannot exfiltrate the key to an attacker-chosen URL. - Receipt BEFORE call —
recordmust succeed before the upstream is contacted. Ledger unreachable = no call. Over-recording is safe; under-recording is the hole. - The secret never enters the receipt — the payload is hashed from the request as the agent submitted it, before injection.
- Responses are scrubbed —
set-cookie(configurable) stripped before the agent sees them.
The limit, stated up front
Isolation is only real when the broker runs in a separate process/host from the
agent (the @zanii/gateway deployment shape) — in-process, a compromised agent can
read the broker's memory. And receipt-or-nothing governs calls made through the
broker: it removes the agent's need to hold credentials; it cannot stop an operator
who hands the agent raw keys anyway. Adopt the broker AND delete the keys from the
agent's environment — the second half is yours.
