@zanii/broker 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -58,3 +58,7 @@ read the broker's memory. And receipt-or-nothing governs calls made *through* th
58
58
  broker: it removes the agent's need to hold credentials; it cannot stop an operator
59
59
  who hands the agent raw keys anyway. Adopt the broker AND delete the keys from the
60
60
  agent's environment — the second half is yours.
61
+
62
+ ## Changelog
63
+
64
+ - **0.1.2** — Composable controls: `composeGates`, `spendGate` (call budgets, size caps), `dualControlGate` (M distinct approvers), `loadCredentials` from your secret store, `createExchangeBroker` (short-lived exchanged tokens), loud time-boxed `createBreakGlass`, `createKillSwitch` (hold, release, revoke everywhere), `credentialUsageReport`, and `brokerHandler` so any HTTP client can use the broker as a sidecar.
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Controls that compose with `createBroker`: short-lived exchanged tokens, spending and
3
+ * rate limits, dual control, secrets loaded from your vault, loud break-glass access,
4
+ * holds, revocation, usage reports, and an HTTP handler so any client can use the broker
5
+ * without code changes. Every control is a gate or a wrapper: the broker's own
6
+ * receipt-before-call invariant is unchanged.
7
+ */
8
+ import { type Broker, type BrokerOptions, type BrokerRequest, type CredentialSpec } from './index.js';
9
+ export type Gate = (req: BrokerRequest) => boolean | Promise<boolean>;
10
+ /** All gates must pass, in order. A throwing gate refuses. */
11
+ export declare function composeGates(...gates: Array<Gate | undefined>): Gate;
12
+ /** Per-credential call budgets in a rolling window, and a request-size cap. */
13
+ export declare function spendGate(limits: Record<string, {
14
+ maxCalls?: number;
15
+ windowMs?: number;
16
+ maxBodyBytes?: number;
17
+ }>, now?: () => number): Gate;
18
+ /** Sensitive calls need M distinct approvers (people or agents) before the credential is used. */
19
+ export declare function dualControlGate(opts: {
20
+ needsApproval: (req: BrokerRequest) => boolean;
21
+ approve: (req: BrokerRequest) => Promise<string[]>;
22
+ minApprovers: number;
23
+ }): Gate;
24
+ /**
25
+ * Load secrets from your secret store at start-up (Vault, AWS Secrets Manager, Azure Key
26
+ * Vault, GCP Secret Manager, 1Password): you supply `resolve`, the specs never hold a
27
+ * secret in config files.
28
+ */
29
+ export declare function loadCredentials(specs: Record<string, Omit<CredentialSpec, 'secret'> & {
30
+ secretRef: string;
31
+ }>, resolve: (ref: string) => Promise<string>): Promise<Record<string, CredentialSpec>>;
32
+ /**
33
+ * A broker whose credential is a short-lived token from a token-exchange endpoint (OAuth
34
+ * token exchange and the like), cached until shortly before it expires. The long-lived
35
+ * secret never leaves your exchange.
36
+ */
37
+ export declare function createExchangeBroker(opts: Omit<BrokerOptions, 'credentials'> & {
38
+ credential: string;
39
+ spec: Omit<CredentialSpec, 'secret'>;
40
+ exchange: (credential: string) => Promise<{
41
+ token: string;
42
+ expiresAt: number;
43
+ }>;
44
+ now?: () => number;
45
+ }): Broker;
46
+ /**
47
+ * Break-glass: emergency use of a held credential, time-boxed, recorded and announced.
48
+ * Wire `gate` into the broker so the credential works only while an emergency is open.
49
+ */
50
+ export declare function createBreakGlass(opts: {
51
+ record: BrokerOptions['record'];
52
+ alert: (e: {
53
+ credential: string;
54
+ by: string;
55
+ reason: string;
56
+ until: string;
57
+ }) => void;
58
+ now?: () => number;
59
+ }): {
60
+ open(credential: string, by: string, reason: string, ttlMs: number): Promise<void>;
61
+ close(credential: string): void;
62
+ gate: Gate;
63
+ };
64
+ /** Hold and revoke credentials at runtime (e.g. on a sentinel alert). Revocation is recorded. */
65
+ export declare function createKillSwitch(record: BrokerOptions['record']): {
66
+ hold(credential: string, reason: string): void;
67
+ release(credential: string): void;
68
+ revokeEverywhere(credentials: string[], reason: string): Promise<void>;
69
+ reason: (credential: string) => string | null;
70
+ gate: Gate;
71
+ };
72
+ /** Per credential and method: how many calls went through, from recorded broker payloads. */
73
+ export declare function credentialUsageReport(recorded: Array<{
74
+ target: string;
75
+ }>): Record<string, Record<string, number>>;
76
+ /**
77
+ * A fetch-style handler so any HTTP client can go through the broker: send the request to
78
+ * the handler with `x-zanii-credential` and `x-zanii-url` headers. Run it as a sidecar in a
79
+ * separate process from the agent; that separation is what makes the isolation real.
80
+ */
81
+ export declare function brokerHandler(broker: Broker): (request: Request) => Promise<Response>;
82
+ //# sourceMappingURL=controls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controls.d.ts","sourceRoot":"","sources":["../src/controls.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Controls that compose with `createBroker`: short-lived exchanged tokens, spending and
3
+ * rate limits, dual control, secrets loaded from your vault, loud break-glass access,
4
+ * holds, revocation, usage reports, and an HTTP handler so any client can use the broker
5
+ * without code changes. Every control is a gate or a wrapper: the broker's own
6
+ * receipt-before-call invariant is unchanged.
7
+ */
8
+ import { createBroker } from './index.js';
9
+ /** All gates must pass, in order. A throwing gate refuses. */
10
+ export function composeGates(...gates) {
11
+ return async (req) => {
12
+ for (const g of gates) {
13
+ if (!g)
14
+ continue;
15
+ try {
16
+ if (!(await g(req)))
17
+ return false;
18
+ }
19
+ catch {
20
+ return false;
21
+ }
22
+ }
23
+ return true;
24
+ };
25
+ }
26
+ /** Per-credential call budgets in a rolling window, and a request-size cap. */
27
+ export function spendGate(limits, now = Date.now) {
28
+ const calls = new Map();
29
+ return (req) => {
30
+ const l = limits[req.credential];
31
+ if (!l)
32
+ return true;
33
+ if (l.maxBodyBytes !== undefined && new TextEncoder().encode(req.body ?? '').length > l.maxBodyBytes)
34
+ return false;
35
+ if (l.maxCalls !== undefined) {
36
+ const w = l.windowMs ?? 60_000;
37
+ const t = now();
38
+ const recent = (calls.get(req.credential) ?? []).filter((x) => t - x < w);
39
+ if (recent.length >= l.maxCalls)
40
+ return false;
41
+ recent.push(t);
42
+ calls.set(req.credential, recent);
43
+ }
44
+ return true;
45
+ };
46
+ }
47
+ /** Sensitive calls need M distinct approvers (people or agents) before the credential is used. */
48
+ export function dualControlGate(opts) {
49
+ return async (req) => {
50
+ if (!opts.needsApproval(req))
51
+ return true;
52
+ const approvers = new Set(await opts.approve(req));
53
+ return approvers.size >= opts.minApprovers;
54
+ };
55
+ }
56
+ /**
57
+ * Load secrets from your secret store at start-up (Vault, AWS Secrets Manager, Azure Key
58
+ * Vault, GCP Secret Manager, 1Password): you supply `resolve`, the specs never hold a
59
+ * secret in config files.
60
+ */
61
+ export async function loadCredentials(specs, resolve) {
62
+ const out = {};
63
+ for (const [name, { secretRef, ...rest }] of Object.entries(specs)) {
64
+ const secret = await resolve(secretRef);
65
+ if (!secret)
66
+ throw new Error(`broker: secret ${secretRef} for '${name}' resolved empty`);
67
+ out[name] = { ...rest, secret };
68
+ }
69
+ return out;
70
+ }
71
+ /**
72
+ * A broker whose credential is a short-lived token from a token-exchange endpoint (OAuth
73
+ * token exchange and the like), cached until shortly before it expires. The long-lived
74
+ * secret never leaves your exchange.
75
+ */
76
+ export function createExchangeBroker(opts) {
77
+ const now = opts.now ?? Date.now;
78
+ let cached = null;
79
+ return {
80
+ list: () => [opts.credential],
81
+ async execute(req) {
82
+ if (!cached || cached.expiresAt - 30_000 <= now())
83
+ cached = await opts.exchange(opts.credential);
84
+ return createBroker({ ...opts, credentials: { [opts.credential]: { ...opts.spec, secret: cached.token } } }).execute(req);
85
+ },
86
+ };
87
+ }
88
+ /**
89
+ * Break-glass: emergency use of a held credential, time-boxed, recorded and announced.
90
+ * Wire `gate` into the broker so the credential works only while an emergency is open.
91
+ */
92
+ export function createBreakGlass(opts) {
93
+ const now = opts.now ?? Date.now;
94
+ const open = new Map();
95
+ return {
96
+ async open(credential, by, reason, ttlMs) {
97
+ if (!reason.trim())
98
+ throw new Error('broker: break-glass needs a reason');
99
+ const until = now() + ttlMs;
100
+ await opts.record({ target: `broker.${credential}.break-glass`, payload: { by, reason, until: new Date(until).toISOString() } });
101
+ open.set(credential, until);
102
+ opts.alert({ credential, by, reason, until: new Date(until).toISOString() });
103
+ },
104
+ close(credential) {
105
+ open.delete(credential);
106
+ },
107
+ gate: ((req) => (open.get(req.credential) ?? 0) > now()),
108
+ };
109
+ }
110
+ /** Hold and revoke credentials at runtime (e.g. on a sentinel alert). Revocation is recorded. */
111
+ export function createKillSwitch(record) {
112
+ const held = new Map();
113
+ return {
114
+ hold(credential, reason) {
115
+ held.set(credential, reason);
116
+ },
117
+ release(credential) {
118
+ held.delete(credential);
119
+ },
120
+ async revokeEverywhere(credentials, reason) {
121
+ for (const c of credentials)
122
+ held.set(c, `revoked: ${reason}`);
123
+ await record({ target: 'broker.revoke-all', payload: { credentials, reason } });
124
+ },
125
+ reason: (credential) => held.get(credential) ?? null,
126
+ gate: ((req) => !held.has(req.credential)),
127
+ };
128
+ }
129
+ /** Per credential and method: how many calls went through, from recorded broker payloads. */
130
+ export function credentialUsageReport(recorded) {
131
+ const out = {};
132
+ for (const r of recorded) {
133
+ const m = r.target.match(/^broker\.(.+)\.([a-z-]+)$/);
134
+ if (!m)
135
+ continue;
136
+ const c = (out[m[1]] ??= {});
137
+ c[m[2]] = (c[m[2]] ?? 0) + 1;
138
+ }
139
+ return out;
140
+ }
141
+ /**
142
+ * A fetch-style handler so any HTTP client can go through the broker: send the request to
143
+ * the handler with `x-zanii-credential` and `x-zanii-url` headers. Run it as a sidecar in a
144
+ * separate process from the agent; that separation is what makes the isolation real.
145
+ */
146
+ export function brokerHandler(broker) {
147
+ return async (request) => {
148
+ const credential = request.headers.get('x-zanii-credential');
149
+ const url = request.headers.get('x-zanii-url');
150
+ if (!credential || !url)
151
+ return new Response('x-zanii-credential and x-zanii-url are required', { status: 400 });
152
+ const headers = {};
153
+ request.headers.forEach((v, k) => {
154
+ if (!k.startsWith('x-zanii-') && k !== 'host' && k !== 'content-length')
155
+ headers[k] = v;
156
+ });
157
+ try {
158
+ const body = ['GET', 'HEAD'].includes(request.method) ? undefined : await request.text();
159
+ const res = await broker.execute({ credential, method: request.method, url, headers, ...(body !== undefined ? { body } : {}) });
160
+ return new Response(res.body, { status: res.status, headers: res.headers });
161
+ }
162
+ catch (err) {
163
+ return new Response(`refused: ${err.message}`, { status: 403 });
164
+ }
165
+ };
166
+ }
167
+ //# sourceMappingURL=controls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controls.js","sourceRoot":"","sources":["../src/controls.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -56,4 +56,5 @@ export interface Broker {
56
56
  list(): string[];
57
57
  }
58
58
  export declare function createBroker(opts: BrokerOptions): Broker;
59
+ export * from './controls.js';
59
60
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAoCA,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEjF,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;IACtC,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,4FAA4F;IAC5F,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC;CAChE;AAED,MAAM,WAAW,aAAa;IAC5B,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC5C;;;;OAIG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3F,iGAAiG;IACjG,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,uFAAuF;IACvF,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAgCD,MAAM,WAAW,MAAM;IACrB,OAAO,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACrD,kEAAkE;IAClE,IAAI,IAAI,MAAM,EAAE,CAAC;CAClB;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAqExD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAoCA,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEjF,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;IACtC,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,4FAA4F;IAC5F,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC;CAChE;AAED,MAAM,WAAW,aAAa;IAC5B,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC5C;;;;OAIG;IACH,MAAM,EAAE,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3F,iGAAiG;IACjG,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,uFAAuF;IACvF,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAgCD,MAAM,WAAW,MAAM;IACrB,OAAO,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACrD,kEAAkE;IAClE,IAAI,IAAI,MAAM,EAAE,CAAC;CAClB;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAqExD;AACD,cAAc,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -133,4 +133,5 @@ export function createBroker(opts) {
133
133
  },
134
134
  };
135
135
  }
136
+ export * from './controls.js';
136
137
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zanii/broker",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "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), credentials bound to URL origins so they cannot be exfiltrated to attacker-chosen hosts, secrets never in hash preimages or logged responses.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://ledger.zanii.agency",
@@ -31,7 +31,7 @@
31
31
  "vitest": "^3.0.9"
32
32
  },
33
33
  "dependencies": {
34
- "@zanii/core": "0.4.0"
34
+ "@zanii/core": "^0.5.4"
35
35
  },
36
36
  "scripts": {
37
37
  "build": "tsc -p tsconfig.build.json",
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Controls that compose with `createBroker`: short-lived exchanged tokens, spending and
3
+ * rate limits, dual control, secrets loaded from your vault, loud break-glass access,
4
+ * holds, revocation, usage reports, and an HTTP handler so any client can use the broker
5
+ * without code changes. Every control is a gate or a wrapper: the broker's own
6
+ * receipt-before-call invariant is unchanged.
7
+ */
8
+ import { createBroker, type Broker, type BrokerOptions, type BrokerRequest, type CredentialSpec } from './index.js';
9
+
10
+ export type Gate = (req: BrokerRequest) => boolean | Promise<boolean>;
11
+
12
+ /** All gates must pass, in order. A throwing gate refuses. */
13
+ export function composeGates(...gates: Array<Gate | undefined>): Gate {
14
+ return async (req) => {
15
+ for (const g of gates) {
16
+ if (!g) continue;
17
+ try {
18
+ if (!(await g(req))) return false;
19
+ } catch {
20
+ return false;
21
+ }
22
+ }
23
+ return true;
24
+ };
25
+ }
26
+
27
+ /** Per-credential call budgets in a rolling window, and a request-size cap. */
28
+ export function spendGate(limits: Record<string, { maxCalls?: number; windowMs?: number; maxBodyBytes?: number }>, now: () => number = Date.now): Gate {
29
+ const calls = new Map<string, number[]>();
30
+ return (req) => {
31
+ const l = limits[req.credential];
32
+ if (!l) return true;
33
+ if (l.maxBodyBytes !== undefined && new TextEncoder().encode(req.body ?? '').length > l.maxBodyBytes) return false;
34
+ if (l.maxCalls !== undefined) {
35
+ const w = l.windowMs ?? 60_000;
36
+ const t = now();
37
+ const recent = (calls.get(req.credential) ?? []).filter((x) => t - x < w);
38
+ if (recent.length >= l.maxCalls) return false;
39
+ recent.push(t);
40
+ calls.set(req.credential, recent);
41
+ }
42
+ return true;
43
+ };
44
+ }
45
+
46
+ /** Sensitive calls need M distinct approvers (people or agents) before the credential is used. */
47
+ export function dualControlGate(opts: { needsApproval: (req: BrokerRequest) => boolean; approve: (req: BrokerRequest) => Promise<string[]>; minApprovers: number }): Gate {
48
+ return async (req) => {
49
+ if (!opts.needsApproval(req)) return true;
50
+ const approvers = new Set(await opts.approve(req));
51
+ return approvers.size >= opts.minApprovers;
52
+ };
53
+ }
54
+
55
+ /**
56
+ * Load secrets from your secret store at start-up (Vault, AWS Secrets Manager, Azure Key
57
+ * Vault, GCP Secret Manager, 1Password): you supply `resolve`, the specs never hold a
58
+ * secret in config files.
59
+ */
60
+ export async function loadCredentials(
61
+ specs: Record<string, Omit<CredentialSpec, 'secret'> & { secretRef: string }>,
62
+ resolve: (ref: string) => Promise<string>,
63
+ ): Promise<Record<string, CredentialSpec>> {
64
+ const out: Record<string, CredentialSpec> = {};
65
+ for (const [name, { secretRef, ...rest }] of Object.entries(specs)) {
66
+ const secret = await resolve(secretRef);
67
+ if (!secret) throw new Error(`broker: secret ${secretRef} for '${name}' resolved empty`);
68
+ out[name] = { ...rest, secret };
69
+ }
70
+ return out;
71
+ }
72
+
73
+ /**
74
+ * A broker whose credential is a short-lived token from a token-exchange endpoint (OAuth
75
+ * token exchange and the like), cached until shortly before it expires. The long-lived
76
+ * secret never leaves your exchange.
77
+ */
78
+ export function createExchangeBroker(opts: Omit<BrokerOptions, 'credentials'> & {
79
+ credential: string;
80
+ spec: Omit<CredentialSpec, 'secret'>;
81
+ exchange: (credential: string) => Promise<{ token: string; expiresAt: number }>;
82
+ now?: () => number;
83
+ }): Broker {
84
+ const now = opts.now ?? Date.now;
85
+ let cached: { token: string; expiresAt: number } | null = null;
86
+ return {
87
+ list: () => [opts.credential],
88
+ async execute(req) {
89
+ if (!cached || cached.expiresAt - 30_000 <= now()) cached = await opts.exchange(opts.credential);
90
+ return createBroker({ ...opts, credentials: { [opts.credential]: { ...opts.spec, secret: cached.token } } }).execute(req);
91
+ },
92
+ };
93
+ }
94
+
95
+ /**
96
+ * Break-glass: emergency use of a held credential, time-boxed, recorded and announced.
97
+ * Wire `gate` into the broker so the credential works only while an emergency is open.
98
+ */
99
+ export function createBreakGlass(opts: { record: BrokerOptions['record']; alert: (e: { credential: string; by: string; reason: string; until: string }) => void; now?: () => number }) {
100
+ const now = opts.now ?? Date.now;
101
+ const open = new Map<string, number>();
102
+ return {
103
+ async open(credential: string, by: string, reason: string, ttlMs: number) {
104
+ if (!reason.trim()) throw new Error('broker: break-glass needs a reason');
105
+ const until = now() + ttlMs;
106
+ await opts.record({ target: `broker.${credential}.break-glass`, payload: { by, reason, until: new Date(until).toISOString() } });
107
+ open.set(credential, until);
108
+ opts.alert({ credential, by, reason, until: new Date(until).toISOString() });
109
+ },
110
+ close(credential: string) {
111
+ open.delete(credential);
112
+ },
113
+ gate: ((req) => (open.get(req.credential) ?? 0) > now()) as Gate,
114
+ };
115
+ }
116
+
117
+ /** Hold and revoke credentials at runtime (e.g. on a sentinel alert). Revocation is recorded. */
118
+ export function createKillSwitch(record: BrokerOptions['record']) {
119
+ const held = new Map<string, string>();
120
+ return {
121
+ hold(credential: string, reason: string) {
122
+ held.set(credential, reason);
123
+ },
124
+ release(credential: string) {
125
+ held.delete(credential);
126
+ },
127
+ async revokeEverywhere(credentials: string[], reason: string) {
128
+ for (const c of credentials) held.set(c, `revoked: ${reason}`);
129
+ await record({ target: 'broker.revoke-all', payload: { credentials, reason } });
130
+ },
131
+ reason: (credential: string) => held.get(credential) ?? null,
132
+ gate: ((req) => !held.has(req.credential)) as Gate,
133
+ };
134
+ }
135
+
136
+ /** Per credential and method: how many calls went through, from recorded broker payloads. */
137
+ export function credentialUsageReport(recorded: Array<{ target: string }>) {
138
+ const out: Record<string, Record<string, number>> = {};
139
+ for (const r of recorded) {
140
+ const m = r.target.match(/^broker\.(.+)\.([a-z-]+)$/);
141
+ if (!m) continue;
142
+ const c = (out[m[1]!] ??= {});
143
+ c[m[2]!] = (c[m[2]!] ?? 0) + 1;
144
+ }
145
+ return out;
146
+ }
147
+
148
+ /**
149
+ * A fetch-style handler so any HTTP client can go through the broker: send the request to
150
+ * the handler with `x-zanii-credential` and `x-zanii-url` headers. Run it as a sidecar in a
151
+ * separate process from the agent; that separation is what makes the isolation real.
152
+ */
153
+ export function brokerHandler(broker: Broker): (request: Request) => Promise<Response> {
154
+ return async (request) => {
155
+ const credential = request.headers.get('x-zanii-credential');
156
+ const url = request.headers.get('x-zanii-url');
157
+ if (!credential || !url) return new Response('x-zanii-credential and x-zanii-url are required', { status: 400 });
158
+ const headers: Record<string, string> = {};
159
+ request.headers.forEach((v, k) => {
160
+ if (!k.startsWith('x-zanii-') && k !== 'host' && k !== 'content-length') headers[k] = v;
161
+ });
162
+ try {
163
+ const body = ['GET', 'HEAD'].includes(request.method) ? undefined : await request.text();
164
+ const res = await broker.execute({ credential, method: request.method, url, headers, ...(body !== undefined ? { body } : {}) });
165
+ return new Response(res.body, { status: res.status, headers: res.headers });
166
+ } catch (err) {
167
+ return new Response(`refused: ${(err as Error).message}`, { status: 403 });
168
+ }
169
+ };
170
+ }
package/src/index.ts CHANGED
@@ -191,3 +191,4 @@ export function createBroker(opts: BrokerOptions): Broker {
191
191
  },
192
192
  };
193
193
  }
194
+ export * from './controls.js';