agent-authority 0.1.0

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.
Files changed (85) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/LICENSE +21 -0
  3. package/QUICKSTART.md +91 -0
  4. package/README.md +553 -0
  5. package/dist/a2a.d.ts +73 -0
  6. package/dist/a2a.d.ts.map +1 -0
  7. package/dist/a2a.js +117 -0
  8. package/dist/a2a.js.map +1 -0
  9. package/dist/audit.d.ts +12 -0
  10. package/dist/audit.d.ts.map +1 -0
  11. package/dist/audit.js +52 -0
  12. package/dist/audit.js.map +1 -0
  13. package/dist/behalf.d.ts +173 -0
  14. package/dist/behalf.d.ts.map +1 -0
  15. package/dist/behalf.js +475 -0
  16. package/dist/behalf.js.map +1 -0
  17. package/dist/capability.d.ts +56 -0
  18. package/dist/capability.d.ts.map +1 -0
  19. package/dist/capability.js +176 -0
  20. package/dist/capability.js.map +1 -0
  21. package/dist/cli.d.ts +3 -0
  22. package/dist/cli.d.ts.map +1 -0
  23. package/dist/cli.js +273 -0
  24. package/dist/cli.js.map +1 -0
  25. package/dist/control-plane.d.ts +57 -0
  26. package/dist/control-plane.d.ts.map +1 -0
  27. package/dist/control-plane.js +332 -0
  28. package/dist/control-plane.js.map +1 -0
  29. package/dist/crypto.d.ts +68 -0
  30. package/dist/crypto.d.ts.map +1 -0
  31. package/dist/crypto.js +105 -0
  32. package/dist/crypto.js.map +1 -0
  33. package/dist/errors.d.ts +25 -0
  34. package/dist/errors.d.ts.map +1 -0
  35. package/dist/errors.js +40 -0
  36. package/dist/errors.js.map +1 -0
  37. package/dist/index.d.ts +35 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +34 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/lint.d.ts +17 -0
  42. package/dist/lint.d.ts.map +1 -0
  43. package/dist/lint.js +75 -0
  44. package/dist/lint.js.map +1 -0
  45. package/dist/mandate.d.ts +99 -0
  46. package/dist/mandate.d.ts.map +1 -0
  47. package/dist/mandate.js +141 -0
  48. package/dist/mandate.js.map +1 -0
  49. package/dist/mcp-server.d.ts +26 -0
  50. package/dist/mcp-server.d.ts.map +1 -0
  51. package/dist/mcp-server.js +111 -0
  52. package/dist/mcp-server.js.map +1 -0
  53. package/dist/mcp.d.ts +63 -0
  54. package/dist/mcp.d.ts.map +1 -0
  55. package/dist/mcp.js +123 -0
  56. package/dist/mcp.js.map +1 -0
  57. package/dist/persist.d.ts +51 -0
  58. package/dist/persist.d.ts.map +1 -0
  59. package/dist/persist.js +150 -0
  60. package/dist/persist.js.map +1 -0
  61. package/dist/quickstart.d.ts +63 -0
  62. package/dist/quickstart.d.ts.map +1 -0
  63. package/dist/quickstart.js +171 -0
  64. package/dist/quickstart.js.map +1 -0
  65. package/dist/remote.d.ts +93 -0
  66. package/dist/remote.d.ts.map +1 -0
  67. package/dist/remote.js +120 -0
  68. package/dist/remote.js.map +1 -0
  69. package/dist/seal.d.ts +12 -0
  70. package/dist/seal.d.ts.map +1 -0
  71. package/dist/seal.js +96 -0
  72. package/dist/seal.js.map +1 -0
  73. package/dist/store.d.ts +119 -0
  74. package/dist/store.d.ts.map +1 -0
  75. package/dist/store.js +139 -0
  76. package/dist/store.js.map +1 -0
  77. package/dist/types.d.ts +173 -0
  78. package/dist/types.d.ts.map +1 -0
  79. package/dist/types.js +17 -0
  80. package/dist/types.js.map +1 -0
  81. package/llms.txt +106 -0
  82. package/package.json +107 -0
  83. package/schemas/capability.schema.json +14 -0
  84. package/schemas/mandate.schema.json +68 -0
  85. package/vectors/mandate-vector.json +63 -0
package/dist/a2a.js ADDED
@@ -0,0 +1,117 @@
1
+ import { Behalf } from "./behalf.js";
2
+ import { permits } from "./capability.js";
3
+ import { AuthorizationError } from "./errors.js";
4
+ /**
5
+ * A2A (agent-to-agent) HTTP transport.
6
+ *
7
+ * The in-process `withBehalf` middleware secures tool calls inside one agent;
8
+ * this secures calls *between* agents over the wire. A caller attaches its
9
+ * mandate to an outgoing request (optionally attenuating it first, so the callee
10
+ * receives strictly less authority); the callee verifies the mandate and its
11
+ * whole delegation chain — offline, with only the issuer's public key — and
12
+ * authorizes the action before doing any work. The verifiable chain travels with
13
+ * the request, so a compromised hop still can't widen scope.
14
+ *
15
+ * Zero dependencies: built on `node:http` types and the global `fetch`.
16
+ */
17
+ /** Header carrying the serialized mandate on an A2A request. */
18
+ export const MANDATE_HEADER = "x-behalf-mandate";
19
+ /** Header carrying the caller's proof of possession (anti-truncation / replay). */
20
+ export const PROOF_HEADER = "x-behalf-proof";
21
+ /** Header carrying the caller's declared action (the proof is bound to it). */
22
+ export const ACTION_HEADER = "x-behalf-action";
23
+ /**
24
+ * Build the headers that carry a mandate to a downstream agent: the serialized
25
+ * (optionally attenuated) token, the caller's declared action, and a fresh proof
26
+ * of possession bound to that action and the exact chain. The callee verifies
27
+ * all three, so a truncated, intercepted, or repurposed token is useless.
28
+ */
29
+ export function present(mandate, opts) {
30
+ const outgoing = opts.attenuate ? mandate.attenuate(opts.attenuate) : mandate;
31
+ const proof = outgoing.prove(opts.action, { agentKeys: opts.agentKeys });
32
+ return {
33
+ [MANDATE_HEADER]: outgoing.serialize(),
34
+ [ACTION_HEADER]: opts.action,
35
+ [PROOF_HEADER]: Buffer.from(JSON.stringify(proof), "utf8").toString("base64url"),
36
+ };
37
+ }
38
+ /** `fetch` wrapper that attaches (and optionally attenuates) a mandate. */
39
+ export function behalfFetch(input, mandate, init = {}, presentOpts = { action: "" }) {
40
+ const headers = { ...init.headers, ...present(mandate, presentOpts) };
41
+ return fetch(input, { ...init, headers });
42
+ }
43
+ function headerValue(headers, name) {
44
+ if (typeof headers.get === "function") {
45
+ return headers.get(name) ?? undefined;
46
+ }
47
+ const raw = headers[name];
48
+ return Array.isArray(raw) ? raw[0] : raw;
49
+ }
50
+ /**
51
+ * Verify + authorize an incoming A2A request. Returns the verified Mandate, or
52
+ * throws {@link AuthorizationError} if it is missing, untrusted, or out of scope.
53
+ * Framework-agnostic: pass any header bag (node:http or fetch `Headers`).
54
+ */
55
+ export async function authorizeIncoming(engine, headers, capability) {
56
+ const raw = headerValue(headers, MANDATE_HEADER);
57
+ if (!raw)
58
+ throw new AuthorizationError(capability, "no mandate presented");
59
+ const proofRaw = headerValue(headers, PROOF_HEADER);
60
+ if (!proofRaw)
61
+ throw new AuthorizationError(capability, "no possession proof presented");
62
+ const declaredAction = headerValue(headers, ACTION_HEADER);
63
+ if (!declaredAction)
64
+ throw new AuthorizationError(capability, "no declared action presented");
65
+ let proof;
66
+ try {
67
+ proof = JSON.parse(Buffer.from(proofRaw, "base64url").toString("utf8"));
68
+ }
69
+ catch {
70
+ throw new AuthorizationError(capability, "malformed possession proof");
71
+ }
72
+ // The caller's declared action (which the proof is bound to) must satisfy what
73
+ // this route requires — so it can't prove a benign action and perform another.
74
+ if (!permits(capability, declaredAction)) {
75
+ throw new AuthorizationError(capability, `declared action "${declaredAction}" exceeds route`);
76
+ }
77
+ const mandate = engine.import(raw);
78
+ // Verifies the chain, the proof of possession (bound to declaredAction), scope,
79
+ // expiry, and revocation; audits under the concrete declared action.
80
+ await engine.authorize(mandate.token, declaredAction, proof);
81
+ return mandate;
82
+ }
83
+ /**
84
+ * A connect/express/node:http-style middleware that authorizes each request
85
+ * before it reaches your handler. On success the verified mandate is attached as
86
+ * `req.mandate` and `next()` is called; on failure it writes 403 and returns.
87
+ */
88
+ export function guard(opts) {
89
+ const engine = opts.engine ?? Behalf.default;
90
+ return async (req, res, next) => {
91
+ const capability = opts.capability(req);
92
+ if (capability === undefined) {
93
+ next?.();
94
+ return true;
95
+ }
96
+ try {
97
+ req.mandate = await authorizeIncoming(engine, req.headers, capability);
98
+ next?.();
99
+ return true;
100
+ }
101
+ catch (e) {
102
+ const reason = e instanceof AuthorizationError ? e.reason : String(e);
103
+ if (opts.onDenied) {
104
+ opts.onDenied(req, res, reason);
105
+ }
106
+ else {
107
+ res.statusCode = 403;
108
+ res.setHeader("content-type", "application/json");
109
+ res.end(JSON.stringify({ error: "forbidden", reason }));
110
+ }
111
+ return false;
112
+ }
113
+ };
114
+ }
115
+ /** The symmetric in-process A2A binding (re-exported for continuity). */
116
+ export { withBehalfA2A } from "./mcp.js";
117
+ //# sourceMappingURL=a2a.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a2a.js","sourceRoot":"","sources":["../src/a2a.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAIjD;;;;;;;;;;;;GAYG;AAEH,gEAAgE;AAChE,MAAM,CAAC,MAAM,cAAc,GAAG,kBAAkB,CAAC;AAEjD,mFAAmF;AACnF,MAAM,CAAC,MAAM,YAAY,GAAG,gBAAgB,CAAC;AAE7C,+EAA+E;AAC/E,MAAM,CAAC,MAAM,aAAa,GAAG,iBAAiB,CAAC;AAgB/C;;;;;GAKG;AACH,MAAM,UAAU,OAAO,CAAC,OAAgB,EAAE,IAAoB;IAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAC9E,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;IACzE,OAAO;QACL,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,SAAS,EAAE;QACtC,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC,MAAM;QAC5B,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;KACjF,CAAC;AACJ,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,WAAW,CACzB,KAAmB,EACnB,OAAgB,EAChB,OAAoB,EAAE,EACtB,cAA8B,EAAE,MAAM,EAAE,EAAE,EAAE;IAE5C,MAAM,OAAO,GAAG,EAAE,GAAI,IAAI,CAAC,OAAkC,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,CAAC;IAClG,OAAO,KAAK,CAAC,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;AAC5C,CAAC;AAED,SAAS,WAAW,CAClB,OAAgE,EAChE,IAAY;IAEZ,IAAI,OAAQ,OAAmB,CAAC,GAAG,KAAK,UAAU,EAAE,CAAC;QACnD,OAAQ,OAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC;IACrD,CAAC;IACD,MAAM,GAAG,GAAI,OAAyD,CAAC,IAAI,CAAC,CAAC;IAC7E,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AAC3C,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAAc,EACd,OAAgE,EAChE,UAAkB;IAElB,MAAM,GAAG,GAAG,WAAW,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;IACjD,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,kBAAkB,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC;IAC3E,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACpD,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,kBAAkB,CAAC,UAAU,EAAE,+BAA+B,CAAC,CAAC;IACzF,MAAM,cAAc,GAAG,WAAW,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IAC3D,IAAI,CAAC,cAAc;QAAE,MAAM,IAAI,kBAAkB,CAAC,UAAU,EAAE,8BAA8B,CAAC,CAAC;IAC9F,IAAI,KAAY,CAAC;IACjB,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAU,CAAC;IACnF,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,kBAAkB,CAAC,UAAU,EAAE,4BAA4B,CAAC,CAAC;IACzE,CAAC;IACD,+EAA+E;IAC/E,+EAA+E;IAC/E,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,cAAc,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,kBAAkB,CAAC,UAAU,EAAE,oBAAoB,cAAc,iBAAiB,CAAC,CAAC;IAChG,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACnC,gFAAgF;IAChF,qEAAqE;IACrE,MAAM,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,CAAC,CAAC;IAC7D,OAAO,OAAO,CAAC;AACjB,CAAC;AAgBD;;;;GAIG;AACH,MAAM,UAAU,KAAK,CAAC,IAAkB;IACtC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO,CAAC;IAC7C,OAAO,KAAK,EAAE,GAAmB,EAAE,GAAmB,EAAE,IAAiB,EAAoB,EAAE;QAC7F,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,IAAI,EAAE,EAAE,CAAC;YACT,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,CAAC;YACH,GAAG,CAAC,OAAO,GAAG,MAAM,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;YACvE,IAAI,EAAE,EAAE,CAAC;YACT,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,MAAM,GAAG,CAAC,YAAY,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACtE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAClB,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;YAClC,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;gBACrB,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;gBAClD,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;YAC1D,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,yEAAyE;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { AuditEntry, AuditFields, AuditIntegrity } from "./types.js";
2
+ export declare const GENESIS: string;
3
+ /**
4
+ * Seal a new entry onto the chain after `prev` (or `null` for the first entry).
5
+ * Pure and O(1): the caller supplies the previous entry, so there is no need to
6
+ * re-read the whole log per record. The store that owns the log is the single
7
+ * writer, which keeps the hash chain race-free.
8
+ */
9
+ export declare function seal(prev: AuditEntry | null, fields: AuditFields): AuditEntry;
10
+ /** Replay the hash chain to confirm nothing was tampered with. */
11
+ export declare function verify(entries: AuditEntry[]): AuditIntegrity;
12
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE1E,eAAO,MAAM,OAAO,QAAiB,CAAC;AAiBtC;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,MAAM,EAAE,WAAW,GAAG,UAAU,CAe7E;AAED,kEAAkE;AAClE,wBAAgB,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,cAAc,CAS5D"}
package/dist/audit.js ADDED
@@ -0,0 +1,52 @@
1
+ import { sha256Hex } from "./crypto.js";
2
+ export const GENESIS = "0".repeat(64);
3
+ /** Canonical body (everything that is hashed, excluding the hash itself). */
4
+ function body(e) {
5
+ return JSON.stringify({
6
+ seq: e.seq,
7
+ ts: e.ts,
8
+ mandateId: e.mandateId,
9
+ issuer: e.issuer ?? "",
10
+ chain: e.chain,
11
+ action: e.action,
12
+ decision: e.decision,
13
+ reason: e.reason ?? "",
14
+ prevHash: e.prevHash,
15
+ });
16
+ }
17
+ /**
18
+ * Seal a new entry onto the chain after `prev` (or `null` for the first entry).
19
+ * Pure and O(1): the caller supplies the previous entry, so there is no need to
20
+ * re-read the whole log per record. The store that owns the log is the single
21
+ * writer, which keeps the hash chain race-free.
22
+ */
23
+ export function seal(prev, fields) {
24
+ const seq = prev ? prev.seq + 1 : 0;
25
+ const prevHash = prev ? prev.hash : GENESIS;
26
+ const partial = {
27
+ seq,
28
+ ts: Date.now(),
29
+ mandateId: fields.mandateId,
30
+ issuer: fields.issuer,
31
+ chain: fields.chain,
32
+ action: fields.action,
33
+ decision: fields.decision,
34
+ reason: fields.reason,
35
+ prevHash,
36
+ };
37
+ return { ...partial, hash: sha256Hex(prevHash + body(partial)) };
38
+ }
39
+ /** Replay the hash chain to confirm nothing was tampered with. */
40
+ export function verify(entries) {
41
+ let prevHash = GENESIS;
42
+ for (const e of entries) {
43
+ if (e.prevHash !== prevHash)
44
+ return { ok: false, brokenAt: e.seq };
45
+ const expected = sha256Hex(prevHash + body(e));
46
+ if (expected !== e.hash)
47
+ return { ok: false, brokenAt: e.seq };
48
+ prevHash = e.hash;
49
+ }
50
+ return { ok: true };
51
+ }
52
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,MAAM,CAAC,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAEtC,6EAA6E;AAC7E,SAAS,IAAI,CAAC,CAA2B;IACvC,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,GAAG,EAAE,CAAC,CAAC,GAAG;QACV,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE;QACtB,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE;QACtB,QAAQ,EAAE,CAAC,CAAC,QAAQ;KACrB,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,IAAI,CAAC,IAAuB,EAAE,MAAmB;IAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;IAC5C,MAAM,OAAO,GAA6B;QACxC,GAAG;QACH,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE;QACd,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ;KACT,CAAC;IACF,OAAO,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;AACnE,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,MAAM,CAAC,OAAqB;IAC1C,IAAI,QAAQ,GAAG,OAAO,CAAC;IACvB,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC;QACnE,MAAM,QAAQ,GAAG,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/C,IAAI,QAAQ,KAAK,CAAC,CAAC,IAAI;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC;QAC/D,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC;IACpB,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;AACtB,CAAC"}
@@ -0,0 +1,173 @@
1
+ import { type KeyPair } from "./crypto.js";
2
+ import { type AuditStore, type RevocationStore, type RateStore } from "./store.js";
3
+ import { Mandate, type Engine } from "./mandate.js";
4
+ import { type SealKeyPair } from "./seal.js";
5
+ import { BehalfError } from "./errors.js";
6
+ import type { AttenuateOptions, AuditCheckpoint, AuditEntry, AuditIntegrity, GrantOptions, MandateToken, Proof } from "./types.js";
7
+ import type { KeyObject } from "node:crypto";
8
+ export interface BehalfConfig {
9
+ /** Issuer keypair. Auto-generated if omitted (so the engine can grant). */
10
+ rootKeyPair?: KeyPair;
11
+ /**
12
+ * This engine's agent-identity keypair (SVID-style). When set, in-process
13
+ * `authorize` automatically proves possession of it, satisfying any
14
+ * `agentKey` caveat bound to `agentPublicKey`. Distribute `agentPublicKey`
15
+ * to whoever grants you a mandate so they can bind it.
16
+ */
17
+ agentKey?: KeyPair;
18
+ /** Additional trusted issuer public keys (base64url SPKI) for foreign mandates. */
19
+ trust?: string[];
20
+ revocations?: RevocationStore;
21
+ audit?: AuditStore;
22
+ /** Rate-limit accounting. Use a shared store to enforce one cap across agents. */
23
+ rate?: RateStore;
24
+ /** Max age (ms) of a possession proof accepted at authorize. Default 5 min. */
25
+ proofSkewMs?: number;
26
+ /**
27
+ * Require a verifier-issued single-use nonce (see `challenge()`) on every
28
+ * possession proof — eliminates replay within the freshness window at the
29
+ * cost of a challenge round-trip. Default false.
30
+ */
31
+ requireNonce?: boolean;
32
+ /** Override the clock — handy for tests. */
33
+ now?: () => number;
34
+ }
35
+ /**
36
+ * The Behalf engine: holds the issuer keypair and the revocation / audit stores,
37
+ * and implements the five verbs. Use the default singleton via the static
38
+ * facade (`Behalf.grant`, ...) or construct an isolated instance with
39
+ * `createBehalf()`. A verify-only engine (no grant) is made with
40
+ * `createBehalf({ trust: [issuerPublicKey] })` and a generated throwaway key.
41
+ */
42
+ export declare class Behalf implements Engine {
43
+ private readonly rootKeyPair;
44
+ private readonly agentKey?;
45
+ private readonly trusted;
46
+ private readonly revocations;
47
+ private readonly auditStore;
48
+ private readonly rateStore;
49
+ private readonly proofSkewMs;
50
+ private readonly requireNonce;
51
+ /** Outstanding single-use challenges: nonce → expiry (ms). */
52
+ private readonly nonces;
53
+ private readonly now;
54
+ constructor(config?: BehalfConfig);
55
+ /**
56
+ * Issue a single-use challenge nonce. The holder binds it into its possession
57
+ * proof (`prove(action, { nonce })`); `authorize` consumes it, so that proof
58
+ * can never be replayed — even within the freshness window.
59
+ */
60
+ challenge(): string;
61
+ /** This engine's issuer public key (base64url SPKI). Share it with verifiers. */
62
+ get publicKey(): string;
63
+ /**
64
+ * This engine's agent-identity public key (base64url), or undefined if none is
65
+ * configured. Hand it to a granter so they can `bindAgent` a mandate to you;
66
+ * only an engine holding the matching private key can then authorize it.
67
+ */
68
+ get agentPublicKey(): string | undefined;
69
+ /** GRANT — a principal authorizes an agent: scoped, capped, short-lived. */
70
+ grant(opts: GrantOptions): Mandate;
71
+ /** ATTENUATE — narrow a mandate for a sub-agent. Never widens. */
72
+ attenuate(token: MandateToken, delegationKey: KeyObject | undefined, opts: AttenuateOptions): Mandate;
73
+ /**
74
+ * Mint a possession proof for `token` using `delegationKey` (the holder's
75
+ * terminal key). Throws if the key is absent — you cannot act on a mandate you
76
+ * only hold the public token for.
77
+ */
78
+ provePossession(token: MandateToken, delegationKey: KeyObject, action: string, nonce?: string, agentKeys?: KeyObject[]): Proof;
79
+ /** Holder path: `mandate.authorize()` routes here, minting a PoP from its key. */
80
+ authorizeAsHolder(token: MandateToken, action: string, delegationKey: KeyObject | undefined): Promise<void>;
81
+ /**
82
+ * AUTHORIZE — verify a token, prove the presenter possesses the chain's
83
+ * terminal key, then check a concrete action. The `proof` is required: it
84
+ * binds the presenter to the exact (untruncated) chain and a fresh timestamp,
85
+ * which is what closes trailing-block truncation and stops a serialized token
86
+ * from being a reusable bearer credential. Produce one with `provePossession`
87
+ * (or, across the wire, `agent-authority/a2a`'s `present`).
88
+ */
89
+ authorize(token: MandateToken, action: string, proof?: Proof): Promise<void>;
90
+ /**
91
+ * INSPECT — advisory check of signature, revocation, expiry, and scope WITHOUT
92
+ * a possession proof. Use for "would this token allow X?" tooling (CLI,
93
+ * dashboards, `check_authority`). It does NOT prove the caller holds the
94
+ * mandate, does not consume rate budget, and writes no audit record — never
95
+ * gate a real action on it; use `authorize` for that.
96
+ */
97
+ inspect(token: MandateToken, action: string): Promise<{
98
+ allowed: boolean;
99
+ reason?: string;
100
+ }>;
101
+ /** Shared signature + revocation + expiry + scope check (no PoP, no side effects). */
102
+ private evaluate;
103
+ /** REVOKE — kill a mandate (and, transitively, everything downstream). */
104
+ revoke(id: string): Promise<void>;
105
+ /** AUDIT — fetch the hash-chained audit trail for a mandate's chain. */
106
+ audit(id: string): Promise<AuditEntry[]>;
107
+ /** Verify the integrity of the entire audit log. */
108
+ verifyAuditLog(): Promise<AuditIntegrity>;
109
+ /**
110
+ * Sign an anchor over the audit log's current head (C4). Store checkpoints
111
+ * somewhere the log's writer can't reach (another host, object storage, a
112
+ * ledger): a later `verifyAuditCheckpoint` then detects tail-deletion and
113
+ * full-chain rewrites, which the unkeyed hash chain alone cannot.
114
+ */
115
+ checkpointAudit(): Promise<AuditCheckpoint>;
116
+ /**
117
+ * Verify the log against a previously-taken checkpoint: the checkpoint's
118
+ * signature must verify under a trusted key, the chain must replay, and the
119
+ * entry at `checkpoint.seq` must still carry exactly the anchored hash.
120
+ */
121
+ verifyAuditCheckpoint(checkpoint: AuditCheckpoint): Promise<AuditIntegrity & {
122
+ reason?: string;
123
+ }>;
124
+ /**
125
+ * Rotate the issuer key with overlap: returns a NEW engine with a fresh
126
+ * keypair that shares this engine's stores/config and still trusts every
127
+ * previously trusted key (including the old one), so existing mandates keep
128
+ * verifying while new grants are signed by the new key. Distribute the new
129
+ * `publicKey` to verifiers, wait out the longest outstanding mandate expiry,
130
+ * then end the overlap with `untrustKey(oldPublicKey)`.
131
+ */
132
+ rotate(): Behalf;
133
+ /** Currently trusted issuer public keys (own key included). */
134
+ get trustedKeys(): string[];
135
+ /** Trust an additional issuer key (e.g. a peer's, or a pre-staged next key). */
136
+ trustKey(publicKey: string): void;
137
+ /** End a rotation overlap. Refuses to remove this engine's own key. */
138
+ untrustKey(publicKey: string): boolean;
139
+ /**
140
+ * Re-hydrate a Mandate from a serialized string. Accepts both forms:
141
+ * - `serialize()` (public token) → inspect/verify only; cannot authorize,
142
+ * prove, or attenuate (no delegation key).
143
+ * - `serializeWithKey()` (token + delegation key) → a full holder credential
144
+ * that can authorize, prove, and attenuate.
145
+ */
146
+ import(serialized: string): Mandate;
147
+ /**
148
+ * Decrypt and import a sealed holder credential (see
149
+ * `mandate.sealForRecipient`) using the recipient's X25519 sealing keypair.
150
+ * Equivalent to `import(unseal(sealed, recipient))`.
151
+ */
152
+ importSealed(sealed: string, recipient: SealKeyPair): Mandate;
153
+ /**
154
+ * Throws {@link IntegrityError} if the issuer is untrusted or the Ed25519
155
+ * signature chain does not verify. Pure public-key checks — no secrets.
156
+ */
157
+ verifySignature(token: MandateToken): void;
158
+ private static _default;
159
+ static get default(): Behalf;
160
+ /** Replace the default instance (e.g. to inject a persistent store). */
161
+ static configure(config: BehalfConfig): Behalf;
162
+ static grant(opts: GrantOptions): Mandate;
163
+ static revoke(id: string): Promise<void>;
164
+ static audit(id: string): Promise<AuditEntry[]>;
165
+ static import(serialized: string): Mandate;
166
+ }
167
+ /** Thrown when attenuating a mandate that has no in-memory delegation key. */
168
+ export declare class BehalfDelegationError extends BehalfError {
169
+ constructor();
170
+ }
171
+ /** Construct an isolated engine (own key + stores). */
172
+ export declare function createBehalf(config?: BehalfConfig): Behalf;
173
+ //# sourceMappingURL=behalf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"behalf.d.ts","sourceRoot":"","sources":["../src/behalf.ts"],"names":[],"mappings":"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"}