sealbridge 0.0.1 → 0.2.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 (208) hide show
  1. package/NOTICE +43 -0
  2. package/README.ja.md +397 -0
  3. package/README.md +567 -1
  4. package/dist/cli/dist/acp.d.ts +2 -0
  5. package/dist/cli/dist/acp.js +6 -0
  6. package/dist/cli/dist/acp.js.map +1 -0
  7. package/dist/cli/dist/approval-summary.d.ts +2 -0
  8. package/dist/cli/dist/approval-summary.js +32 -0
  9. package/dist/cli/dist/approval-summary.js.map +1 -0
  10. package/dist/cli/dist/board.d.ts +47 -0
  11. package/dist/cli/dist/board.js +255 -0
  12. package/dist/cli/dist/board.js.map +1 -0
  13. package/dist/cli/dist/contacts.d.ts +18 -0
  14. package/dist/cli/dist/contacts.js +154 -0
  15. package/dist/cli/dist/contacts.js.map +1 -0
  16. package/dist/cli/dist/doctor.d.ts +13 -0
  17. package/dist/cli/dist/doctor.js +30 -0
  18. package/dist/cli/dist/doctor.js.map +1 -0
  19. package/dist/cli/dist/error-messages.d.ts +2 -0
  20. package/dist/cli/dist/error-messages.js +24 -0
  21. package/dist/cli/dist/error-messages.js.map +1 -0
  22. package/dist/cli/dist/index.d.ts +2 -0
  23. package/dist/cli/dist/index.js +194 -0
  24. package/dist/cli/dist/index.js.map +1 -0
  25. package/dist/cli/dist/invite-share.d.ts +16 -0
  26. package/dist/cli/dist/invite-share.js +163 -0
  27. package/dist/cli/dist/invite-share.js.map +1 -0
  28. package/dist/cli/dist/mcp.d.ts +17 -0
  29. package/dist/cli/dist/mcp.js +105 -0
  30. package/dist/cli/dist/mcp.js.map +1 -0
  31. package/dist/cli/dist/network.d.ts +1 -0
  32. package/dist/cli/dist/network.js +396 -0
  33. package/dist/cli/dist/network.js.map +1 -0
  34. package/dist/cli/dist/public-settings.d.ts +27 -0
  35. package/dist/cli/dist/public-settings.js +33 -0
  36. package/dist/cli/dist/public-settings.js.map +1 -0
  37. package/dist/cli/dist/receipt.d.ts +39 -0
  38. package/dist/cli/dist/receipt.js +90 -0
  39. package/dist/cli/dist/receipt.js.map +1 -0
  40. package/dist/cli/dist/receive-hook.d.ts +2 -0
  41. package/dist/cli/dist/receive-hook.js +35 -0
  42. package/dist/cli/dist/receive-hook.js.map +1 -0
  43. package/dist/cli/dist/task-input.d.ts +2 -0
  44. package/dist/cli/dist/task-input.js +19 -0
  45. package/dist/cli/dist/task-input.js.map +1 -0
  46. package/dist/cli/dist/wake-hook.d.ts +3 -0
  47. package/dist/cli/dist/wake-hook.js +32 -0
  48. package/dist/cli/dist/wake-hook.js.map +1 -0
  49. package/dist/examples/two-owner-demo/alice-policy.json +4 -0
  50. package/dist/examples/two-owner-demo/bob-policy.json +4 -0
  51. package/dist/examples/two-owner-demo/dist/run-demo.d.ts +71 -0
  52. package/dist/examples/two-owner-demo/dist/run-demo.js +383 -0
  53. package/dist/examples/two-owner-demo/dist/run-demo.js.map +1 -0
  54. package/dist/package.json +1 -0
  55. package/dist/packages/adapters/dist/command.d.ts +122 -0
  56. package/dist/packages/adapters/dist/command.js +378 -0
  57. package/dist/packages/adapters/dist/command.js.map +1 -0
  58. package/dist/packages/adapters/dist/credential-proxy.d.ts +37 -0
  59. package/dist/packages/adapters/dist/credential-proxy.js +363 -0
  60. package/dist/packages/adapters/dist/credential-proxy.js.map +1 -0
  61. package/dist/packages/adapters/dist/index.d.ts +6 -0
  62. package/dist/packages/adapters/dist/index.js +6 -0
  63. package/dist/packages/adapters/dist/index.js.map +1 -0
  64. package/dist/packages/adapters/dist/mcp.d.ts +32 -0
  65. package/dist/packages/adapters/dist/mcp.js +87 -0
  66. package/dist/packages/adapters/dist/mcp.js.map +1 -0
  67. package/dist/packages/adapters/dist/openai.d.ts +23 -0
  68. package/dist/packages/adapters/dist/openai.js +81 -0
  69. package/dist/packages/adapters/dist/openai.js.map +1 -0
  70. package/dist/packages/adapters/dist/sandbox.d.ts +45 -0
  71. package/dist/packages/adapters/dist/sandbox.js +60 -0
  72. package/dist/packages/adapters/dist/sandbox.js.map +1 -0
  73. package/dist/packages/core/dist/capability.d.ts +36 -0
  74. package/dist/packages/core/dist/capability.js +203 -0
  75. package/dist/packages/core/dist/capability.js.map +1 -0
  76. package/dist/packages/core/dist/contract.d.ts +33 -0
  77. package/dist/packages/core/dist/contract.js +97 -0
  78. package/dist/packages/core/dist/contract.js.map +1 -0
  79. package/dist/packages/core/dist/crypto.d.ts +34 -0
  80. package/dist/packages/core/dist/crypto.js +329 -0
  81. package/dist/packages/core/dist/crypto.js.map +1 -0
  82. package/dist/packages/core/dist/discovery.d.ts +129 -0
  83. package/dist/packages/core/dist/discovery.js +527 -0
  84. package/dist/packages/core/dist/discovery.js.map +1 -0
  85. package/dist/packages/core/dist/edge.d.ts +4 -0
  86. package/dist/packages/core/dist/edge.js +6 -0
  87. package/dist/packages/core/dist/edge.js.map +1 -0
  88. package/dist/packages/core/dist/index.d.ts +9 -0
  89. package/dist/packages/core/dist/index.js +10 -0
  90. package/dist/packages/core/dist/index.js.map +1 -0
  91. package/dist/packages/core/dist/receipts.d.ts +9 -0
  92. package/dist/packages/core/dist/receipts.js +67 -0
  93. package/dist/packages/core/dist/receipts.js.map +1 -0
  94. package/dist/packages/core/dist/relay-auth.d.ts +40 -0
  95. package/dist/packages/core/dist/relay-auth.js +106 -0
  96. package/dist/packages/core/dist/relay-auth.js.map +1 -0
  97. package/dist/packages/core/dist/revocation.d.ts +58 -0
  98. package/dist/packages/core/dist/revocation.js +112 -0
  99. package/dist/packages/core/dist/revocation.js.map +1 -0
  100. package/dist/packages/core/dist/schema.d.ts +11 -0
  101. package/dist/packages/core/dist/schema.js +48 -0
  102. package/dist/packages/core/dist/schema.js.map +1 -0
  103. package/dist/packages/core/dist/types.d.ts +172 -0
  104. package/dist/packages/core/dist/types.js +2 -0
  105. package/dist/packages/core/dist/types.js.map +1 -0
  106. package/dist/packages/ledger/dist/index.d.ts +84 -0
  107. package/dist/packages/ledger/dist/index.js +269 -0
  108. package/dist/packages/ledger/dist/index.js.map +1 -0
  109. package/dist/packages/relay/dist/fetch.d.ts +250 -0
  110. package/dist/packages/relay/dist/fetch.js +1401 -0
  111. package/dist/packages/relay/dist/fetch.js.map +1 -0
  112. package/dist/packages/relay/dist/index.d.ts +103 -0
  113. package/dist/packages/relay/dist/index.js +855 -0
  114. package/dist/packages/relay/dist/index.js.map +1 -0
  115. package/dist/packages/relay/dist/journal.d.ts +77 -0
  116. package/dist/packages/relay/dist/journal.js +339 -0
  117. package/dist/packages/relay/dist/journal.js.map +1 -0
  118. package/dist/packages/relay/dist/notifications.d.ts +63 -0
  119. package/dist/packages/relay/dist/notifications.js +55 -0
  120. package/dist/packages/relay/dist/notifications.js.map +1 -0
  121. package/dist/packages/relay/dist/retry.d.ts +12 -0
  122. package/dist/packages/relay/dist/retry.js +59 -0
  123. package/dist/packages/relay/dist/retry.js.map +1 -0
  124. package/dist/packages/relay/dist/ssrf.d.ts +32 -0
  125. package/dist/packages/relay/dist/ssrf.js +213 -0
  126. package/dist/packages/relay/dist/ssrf.js.map +1 -0
  127. package/dist/packages/relay/dist/worker.d.ts +92 -0
  128. package/dist/packages/relay/dist/worker.js +460 -0
  129. package/dist/packages/relay/dist/worker.js.map +1 -0
  130. package/dist/packages/runtime/dist/a2a.d.ts +8 -0
  131. package/dist/packages/runtime/dist/a2a.js +69 -0
  132. package/dist/packages/runtime/dist/a2a.js.map +1 -0
  133. package/dist/packages/runtime/dist/adapter.d.ts +51 -0
  134. package/dist/packages/runtime/dist/adapter.js +2 -0
  135. package/dist/packages/runtime/dist/adapter.js.map +1 -0
  136. package/dist/packages/runtime/dist/approval-providers.d.ts +99 -0
  137. package/dist/packages/runtime/dist/approval-providers.js +269 -0
  138. package/dist/packages/runtime/dist/approval-providers.js.map +1 -0
  139. package/dist/packages/runtime/dist/daily-limit.d.ts +2 -0
  140. package/dist/packages/runtime/dist/daily-limit.js +54 -0
  141. package/dist/packages/runtime/dist/daily-limit.js.map +1 -0
  142. package/dist/packages/runtime/dist/gateway.d.ts +35 -0
  143. package/dist/packages/runtime/dist/gateway.js +92 -0
  144. package/dist/packages/runtime/dist/gateway.js.map +1 -0
  145. package/dist/packages/runtime/dist/inbox.d.ts +10 -0
  146. package/dist/packages/runtime/dist/inbox.js +24 -0
  147. package/dist/packages/runtime/dist/inbox.js.map +1 -0
  148. package/dist/packages/runtime/dist/index.d.ts +13 -0
  149. package/dist/packages/runtime/dist/index.js +14 -0
  150. package/dist/packages/runtime/dist/index.js.map +1 -0
  151. package/dist/packages/runtime/dist/mcp-server.d.ts +90 -0
  152. package/dist/packages/runtime/dist/mcp-server.js +647 -0
  153. package/dist/packages/runtime/dist/mcp-server.js.map +1 -0
  154. package/dist/packages/runtime/dist/mcp-tool-catalog.d.ts +3 -0
  155. package/dist/packages/runtime/dist/mcp-tool-catalog.js +507 -0
  156. package/dist/packages/runtime/dist/mcp-tool-catalog.js.map +1 -0
  157. package/dist/packages/runtime/dist/mcp.d.ts +73 -0
  158. package/dist/packages/runtime/dist/mcp.js +67 -0
  159. package/dist/packages/runtime/dist/mcp.js.map +1 -0
  160. package/dist/packages/runtime/dist/policy.d.ts +19 -0
  161. package/dist/packages/runtime/dist/policy.js +41 -0
  162. package/dist/packages/runtime/dist/policy.js.map +1 -0
  163. package/dist/packages/runtime/dist/quota.d.ts +39 -0
  164. package/dist/packages/runtime/dist/quota.js +147 -0
  165. package/dist/packages/runtime/dist/quota.js.map +1 -0
  166. package/dist/packages/runtime/dist/session-audit.d.ts +107 -0
  167. package/dist/packages/runtime/dist/session-audit.js +520 -0
  168. package/dist/packages/runtime/dist/session-audit.js.map +1 -0
  169. package/dist/packages/runtime/dist/session-timeouts.d.ts +33 -0
  170. package/dist/packages/runtime/dist/session-timeouts.js +62 -0
  171. package/dist/packages/runtime/dist/session-timeouts.js.map +1 -0
  172. package/dist/packages/runtime/dist/session.d.ts +323 -0
  173. package/dist/packages/runtime/dist/session.js +2252 -0
  174. package/dist/packages/runtime/dist/session.js.map +1 -0
  175. package/dist/packages/runtime/dist/task-journal.d.ts +36 -0
  176. package/dist/packages/runtime/dist/task-journal.js +104 -0
  177. package/dist/packages/runtime/dist/task-journal.js.map +1 -0
  178. package/dist/packages/runtime/dist/verifier.d.ts +64 -0
  179. package/dist/packages/runtime/dist/verifier.js +109 -0
  180. package/dist/packages/runtime/dist/verifier.js.map +1 -0
  181. package/dist/packages/sandbox/dist/container.d.ts +33 -0
  182. package/dist/packages/sandbox/dist/container.js +189 -0
  183. package/dist/packages/sandbox/dist/container.js.map +1 -0
  184. package/dist/packages/sandbox/dist/index.d.ts +34 -0
  185. package/dist/packages/sandbox/dist/index.js +179 -0
  186. package/dist/packages/sandbox/dist/index.js.map +1 -0
  187. package/dist/packages/sandbox/dist/permission.d.ts +58 -0
  188. package/dist/packages/sandbox/dist/permission.js +90 -0
  189. package/dist/packages/sandbox/dist/permission.js.map +1 -0
  190. package/dist/spec/README.ja.md +76 -0
  191. package/dist/spec/README.md +135 -0
  192. package/dist/spec/ack-receipt.schema.json +77 -0
  193. package/dist/spec/agent-card.schema.json +83 -0
  194. package/dist/spec/approval-receipt.schema.json +94 -0
  195. package/dist/spec/audit-entry.schema.json +101 -0
  196. package/dist/spec/capability-token.schema.json +133 -0
  197. package/dist/spec/envelope.schema.json +197 -0
  198. package/dist/spec/examples/sample-ack-receipt.json +16 -0
  199. package/dist/spec/examples/sample-agent-card.json +35 -0
  200. package/dist/spec/examples/sample-approval-receipt.json +18 -0
  201. package/dist/spec/examples/sample-audit-entry.json +37 -0
  202. package/dist/spec/examples/sample-capability-token.json +39 -0
  203. package/dist/spec/examples/sample-envelope.json +21 -0
  204. package/dist/spec/examples/sample-task-manifest.json +141 -0
  205. package/dist/spec/examples/sample-usage-receipt.json +19 -0
  206. package/dist/spec/task-manifest.schema.json +422 -0
  207. package/dist/spec/usage-receipt.schema.json +91 -0
  208. package/package.json +61 -5
@@ -0,0 +1,45 @@
1
+ import type { AgentAdapter, AgentTaskContext } from '@acp/runtime';
2
+ import type { Json } from '@acp/core';
3
+ /**
4
+ * Runs task JavaScript and reports the isolation the host runner actually used.
5
+ *
6
+ * `subprocess_restricted` (the default, and the fallback when Podman or Docker
7
+ * is absent) is a separate OS process. The child gets a sanitized environment
8
+ * (no inherited credentials or `NODE_OPTIONS`), a temporary scratch directory,
9
+ * a wall-clock timeout followed by SIGTERM then SIGKILL, and an output byte cap.
10
+ * `detectNodePermissionFlag` / `resolveNodePermissionArgs` in `@acp/sandbox`
11
+ * select `--permission` on Node.js >= 22.13 and on Node.js 23+, or
12
+ * `--experimental-permission` on earlier Node.js 20 and 22 releases, and pair
13
+ * that flag with `--allow-fs-read` and `--allow-fs-write` for the scratch
14
+ * directory. Those flags restrict filesystem, child-process, worker-thread, and
15
+ * native-addon access. They do not block `fetch`, `node:net`, `node:http`,
16
+ * `node:https`, `node:dns`, or `node:dgram`, and they are not a kernel network
17
+ * namespace. When this Node binary advertises neither flag, the child still
18
+ * starts and `sandboxBoundary.permissionModelActive` is false.
19
+ *
20
+ * Task source is evaluated with `node:vm` in that same child. Node.js documents
21
+ * that `node:vm` is not a security mechanism against untrusted code. A
22
+ * null-prototype context that omits `process` and `require`, and deleting
23
+ * `globalThis.fetch` and `WebSocket` in the wrapper, only shrinks the API the
24
+ * guest script sees.
25
+ *
26
+ * Untrusted third-party code requires `oci_container`
27
+ * (`isolationLevel: 'oci_container'`). That path runs Podman or Docker with
28
+ * `--network none`, a read-only root filesystem, `--cap-drop ALL`,
29
+ * `--security-opt no-new-privileges`, and PID and memory limits.
30
+ * `ExecutionAttestation.effectiveIsolationLevel` is `subprocess_restricted` or
31
+ * `oci_container` from the path that ran. `VerificationEngine` rejects a
32
+ * `subprocess_restricted` attestation when the contract requires `oci_container`,
33
+ * unless `sandboxPolicy.allowSubprocessFallback` is true.
34
+ */
35
+ export declare class SandboxAgentAdapter implements AgentAdapter {
36
+ readonly code: string;
37
+ readonly id = "sandbox";
38
+ readonly name = "Restricted JavaScript Worker";
39
+ readonly skills: {
40
+ id: string;
41
+ name: string;
42
+ }[];
43
+ constructor(code?: string);
44
+ executeTask(context: AgentTaskContext): Promise<Json>;
45
+ }
@@ -0,0 +1,60 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { runContainerOrSubprocessSandbox } from '../../sandbox/dist/index.js';
3
+ /**
4
+ * Runs task JavaScript and reports the isolation the host runner actually used.
5
+ *
6
+ * `subprocess_restricted` (the default, and the fallback when Podman or Docker
7
+ * is absent) is a separate OS process. The child gets a sanitized environment
8
+ * (no inherited credentials or `NODE_OPTIONS`), a temporary scratch directory,
9
+ * a wall-clock timeout followed by SIGTERM then SIGKILL, and an output byte cap.
10
+ * `detectNodePermissionFlag` / `resolveNodePermissionArgs` in `@acp/sandbox`
11
+ * select `--permission` on Node.js >= 22.13 and on Node.js 23+, or
12
+ * `--experimental-permission` on earlier Node.js 20 and 22 releases, and pair
13
+ * that flag with `--allow-fs-read` and `--allow-fs-write` for the scratch
14
+ * directory. Those flags restrict filesystem, child-process, worker-thread, and
15
+ * native-addon access. They do not block `fetch`, `node:net`, `node:http`,
16
+ * `node:https`, `node:dns`, or `node:dgram`, and they are not a kernel network
17
+ * namespace. When this Node binary advertises neither flag, the child still
18
+ * starts and `sandboxBoundary.permissionModelActive` is false.
19
+ *
20
+ * Task source is evaluated with `node:vm` in that same child. Node.js documents
21
+ * that `node:vm` is not a security mechanism against untrusted code. A
22
+ * null-prototype context that omits `process` and `require`, and deleting
23
+ * `globalThis.fetch` and `WebSocket` in the wrapper, only shrinks the API the
24
+ * guest script sees.
25
+ *
26
+ * Untrusted third-party code requires `oci_container`
27
+ * (`isolationLevel: 'oci_container'`). That path runs Podman or Docker with
28
+ * `--network none`, a read-only root filesystem, `--cap-drop ALL`,
29
+ * `--security-opt no-new-privileges`, and PID and memory limits.
30
+ * `ExecutionAttestation.effectiveIsolationLevel` is `subprocess_restricted` or
31
+ * `oci_container` from the path that ran. `VerificationEngine` rejects a
32
+ * `subprocess_restricted` attestation when the contract requires `oci_container`,
33
+ * unless `sandboxPolicy.allowSubprocessFallback` is true.
34
+ */
35
+ export class SandboxAgentAdapter {
36
+ code;
37
+ id = 'sandbox';
38
+ name = 'Restricted JavaScript Worker';
39
+ skills = [{ id: 'json-task', name: 'JSON task' }];
40
+ constructor(code = 'return input;') {
41
+ this.code = code;
42
+ }
43
+ async executeTask(context) {
44
+ const policy = context.manifest.sandboxPolicy;
45
+ if (policy.networkAccess !== 'none' || policy.filesystemScope !== 'ephemeral_workspace_only')
46
+ throw new Error('UNSUPPORTED_SANDBOX_POLICY');
47
+ const result = await runContainerOrSubprocessSandbox({
48
+ code: this.code,
49
+ input: context.input,
50
+ isolationLevel: policy.isolationLevel,
51
+ timeoutMs: context.manifest.budget.maxWallClockSeconds * 1000,
52
+ signal: context.signal,
53
+ onTool: (name, args) => context.callTool(name, args),
54
+ onUsage: (inputTokens, outputTokens) => context.reportUsage({ inputTokens, outputTokens }),
55
+ });
56
+ context.reportIsolation?.(result.effectiveIsolationLevel, result.sandboxBoundary);
57
+ return result.output;
58
+ }
59
+ }
60
+ //# sourceMappingURL=sandbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,OAAO,EAAE,+BAA+B,EAAE,MAAM,cAAc,CAAC;AAI/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,OAAO,mBAAmB;IAIT;IAHZ,EAAE,GAAG,SAAS,CAAC;IACf,IAAI,GAAG,8BAA8B,CAAC;IACtC,MAAM,GAAG,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;IAC3D,YAAqB,OAAO,eAAe;QAAtB,SAAI,GAAJ,IAAI,CAAkB;IAAG,CAAC;IAC/C,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;QAC9C,IAAI,MAAM,CAAC,aAAa,KAAK,MAAM,IAAI,MAAM,CAAC,eAAe,KAAK,0BAA0B;YAC1F,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,MAAM,+BAA+B,CAAC;YACnD,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,cAAc,EAAE,MAAM,CAAC,cAAc;YACrC,SAAS,EAAE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,mBAAmB,GAAG,IAAI;YAC7D,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAY,CAAC;YAC5D,OAAO,EAAE,CAAC,WAAW,EAAE,YAAY,EAAE,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC;SAC3F,CAAC,CAAC;QACH,OAAO,CAAC,eAAe,EAAE,CAAC,MAAM,CAAC,uBAAuB,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;QAClF,OAAO,MAAM,CAAC,MAAM,CAAC;IACvB,CAAC;CACF"}
@@ -0,0 +1,36 @@
1
+ import type { RevocationList } from './revocation.js';
2
+ import type { Capability, CapabilityToken, KeyPair, Schema } from './types.js';
3
+ export declare function violation(detail: string): never;
4
+ export declare function resourceWithin(child: string, parent: string): boolean;
5
+ export declare function schemaNarrows(child: Schema, parent: Schema): boolean;
6
+ export declare function assertAttenuation(parent: CapabilityToken, child: CapabilityToken): void;
7
+ export declare function createCapability(params: Omit<Capability, 'type' | 'id' | 'issuer' | 'parentHash'>, key: KeyPair, parent?: CapabilityToken): CapabilityToken;
8
+ export interface CapabilityContext {
9
+ rootDid: string;
10
+ audience: string;
11
+ taskId: string;
12
+ now?: number;
13
+ }
14
+ export declare function verifyCapabilityChain(chain: CapabilityToken[], context: CapabilityContext): CapabilityToken;
15
+ /**
16
+ * True when `issuerDid` signed this token or an ancestor in the chain.
17
+ * `payload.issuer` is ignored unless that token's signature verifies for the
18
+ * same DID. Callers must pass a chain that has already passed
19
+ * `verifyCapabilityChain` for the context being evaluated: a self-signed
20
+ * token controls only itself, not another issuer's token that reused its id.
21
+ */
22
+ export declare function capabilityIssuerControls(chain: CapabilityToken[], index: number, issuerDid: string): boolean;
23
+ export declare class CapabilityGuard {
24
+ private readonly registry?;
25
+ private counts;
26
+ private revoked;
27
+ constructor(registry?: RevocationList | undefined);
28
+ revokeTask(taskId: string): void;
29
+ authorize(chain: CapabilityToken[], context: CapabilityContext, tool: string, args: Record<string, unknown>, resource: string): void;
30
+ /**
31
+ * A notice revokes this token only when its issuer controls the token in
32
+ * this verified chain. A third party who minted a different token with the
33
+ * same id does not.
34
+ */
35
+ private capabilityTargetRevoked;
36
+ }
@@ -0,0 +1,203 @@
1
+ // SPDX-License-Identifier: Apache-2.0 AND MIT
2
+ // Adapted from newtro/delegateos src/core/dct.ts and chain.ts (MIT), Scott Smith.
3
+ import { randomUUID } from 'node:crypto';
4
+ import { canonicalize, digest, signPayload, verifySigned } from './crypto.js';
5
+ import { DEFAULT_CLOCK_SKEW_MS } from './discovery.js';
6
+ import { validateSchema, createAjv, assertSynchronousSchema } from './schema.js';
7
+ export function violation(detail) {
8
+ throw new Error('CAPABILITY_VIOLATION: ' + detail);
9
+ }
10
+ // Only exact URIs and terminal /* prefixes are supported. No glob inference.
11
+ export function resourceWithin(child, parent) {
12
+ if (child === parent)
13
+ return true;
14
+ if (!parent.endsWith('/*'))
15
+ return false;
16
+ const prefix = parent.slice(0, -1);
17
+ return (child.startsWith(prefix) &&
18
+ !child.slice(prefix.length).includes('..') &&
19
+ !/%2e|%2f|%5c/i.test(child) &&
20
+ !child.includes('\\'));
21
+ }
22
+ // General JSON Schema containment is undecidable. Accept identical schemas or
23
+ // explicit allOf inheritance only; reject all other rewrites, including equivalent ones.
24
+ function assertToolSchema(schema) {
25
+ assertSynchronousSchema(schema);
26
+ const check = (s) => {
27
+ if (!s || typeof s !== 'object')
28
+ return;
29
+ for (const [k, v] of Object.entries(s)) {
30
+ if (['$ref', '$dynamicRef', '$id', '$anchor', '$dynamicAnchor'].includes(k))
31
+ violation('tool schemas must be self-contained');
32
+ check(v);
33
+ }
34
+ };
35
+ check(schema);
36
+ createAjv().compile(schema);
37
+ }
38
+ export function schemaNarrows(child, parent) {
39
+ if (canonicalize(child) === canonicalize(parent))
40
+ return true;
41
+ return (Array.isArray(child.allOf) &&
42
+ child.allOf.some((s) => canonicalize(s) === canonicalize(parent)));
43
+ }
44
+ export function assertAttenuation(parent, child) {
45
+ const p = parent.payload, c = child.payload;
46
+ if (c.parentHash !== digest(parent) ||
47
+ p.audience !== c.issuer ||
48
+ p.taskId !== c.taskId ||
49
+ c.maxDepth > p.maxDepth - 1 ||
50
+ c.maxCalls > p.maxCalls ||
51
+ Date.parse(c.expiresAt) > Date.parse(p.expiresAt) ||
52
+ Date.parse(c.notBefore) < Date.parse(p.notBefore) ||
53
+ !resourceWithin(c.resource, p.resource))
54
+ violation('non-monotonic delegation');
55
+ for (const tool of c.allowedTools)
56
+ if (!p.allowedTools.includes(tool) ||
57
+ !c.argConstraints[tool] ||
58
+ !p.argConstraints[tool] ||
59
+ !schemaNarrows(c.argConstraints[tool], p.argConstraints[tool]))
60
+ violation('tool or argument schema escalation');
61
+ }
62
+ export function createCapability(params, key, parent) {
63
+ const token = signPayload({
64
+ ...params,
65
+ type: 'CapabilityToken',
66
+ id: randomUUID(),
67
+ issuer: key.did,
68
+ parentHash: parent ? digest(parent) : null,
69
+ }, key);
70
+ validateSchema('capability-token', token);
71
+ for (const tool of token.payload.allowedTools)
72
+ if (!token.payload.argConstraints[tool] ||
73
+ !createAjv().validateSchema(token.payload.argConstraints[tool]))
74
+ violation('missing or invalid tool schema');
75
+ for (const tool of token.payload.allowedTools)
76
+ assertToolSchema(token.payload.argConstraints[tool]);
77
+ if (parent)
78
+ assertAttenuation(parent, token);
79
+ return token;
80
+ }
81
+ export function verifyCapabilityChain(chain, context) {
82
+ if (!chain.length || chain.length > 32)
83
+ violation('invalid proof chain length');
84
+ for (let i = 0; i < chain.length; i++) {
85
+ const t = chain[i], p = t.payload;
86
+ try {
87
+ validateSchema('capability-token', t);
88
+ }
89
+ catch {
90
+ violation('malformed token');
91
+ }
92
+ if (!verifySigned(t, p.issuer) ||
93
+ p.taskId !== context.taskId ||
94
+ (context.now ?? Date.now()) + DEFAULT_CLOCK_SKEW_MS < Date.parse(p.notBefore) ||
95
+ (context.now ?? Date.now()) >= Date.parse(p.expiresAt) ||
96
+ Date.parse(p.notBefore) >= Date.parse(p.expiresAt))
97
+ violation('signature, task or time');
98
+ for (const name of p.allowedTools)
99
+ if (!p.argConstraints[name] || !createAjv().validateSchema(p.argConstraints[name]))
100
+ violation('invalid tool schema');
101
+ for (const tool of p.allowedTools)
102
+ assertToolSchema(p.argConstraints[tool]);
103
+ if (i === 0) {
104
+ if (p.issuer !== context.rootDid || p.parentHash !== null)
105
+ violation('untrusted root');
106
+ }
107
+ else
108
+ assertAttenuation(chain[i - 1], t);
109
+ }
110
+ const leaf = chain[chain.length - 1];
111
+ if (leaf.payload.audience !== context.audience)
112
+ violation('audience');
113
+ return leaf;
114
+ }
115
+ /**
116
+ * True when `issuerDid` signed this token or an ancestor in the chain.
117
+ * `payload.issuer` is ignored unless that token's signature verifies for the
118
+ * same DID. Callers must pass a chain that has already passed
119
+ * `verifyCapabilityChain` for the context being evaluated: a self-signed
120
+ * token controls only itself, not another issuer's token that reused its id.
121
+ */
122
+ export function capabilityIssuerControls(chain, index, issuerDid) {
123
+ if (!issuerDid)
124
+ return false;
125
+ for (let i = 0; i <= index && i < chain.length; i++) {
126
+ const token = chain[i];
127
+ if (!token?.payload || token.payload.issuer !== issuerDid)
128
+ continue;
129
+ if (!verifySigned(token, issuerDid))
130
+ continue;
131
+ return true;
132
+ }
133
+ return false;
134
+ }
135
+ function capabilityIdentity(token) {
136
+ return digest({ issuer: token.payload.issuer, id: token.payload.id });
137
+ }
138
+ export class CapabilityGuard {
139
+ registry;
140
+ counts = new Map();
141
+ revoked = new Set();
142
+ constructor(registry) {
143
+ this.registry = registry;
144
+ }
145
+ revokeTask(taskId) {
146
+ this.revoked.add(taskId);
147
+ }
148
+ authorize(chain, context, tool, args, resource) {
149
+ // Task revocation does not read the chain. Check it before maxCalls so a
150
+ // settled task still fails closed after its counters are exhausted.
151
+ if (this.revoked.has(context.taskId))
152
+ violation('revoked task');
153
+ // Verify before trusting payload.issuer or treating a shared token id as revoked.
154
+ const leaf = verifyCapabilityChain(chain, context);
155
+ if (this.registry) {
156
+ for (let index = 0; index < chain.length; index++) {
157
+ const token = chain[index];
158
+ // The signed id is stable across representations. Digest targets remain
159
+ // supported after verification has required canonical signature encoding.
160
+ if (this.capabilityTargetRevoked(chain, index, token.payload.id) ||
161
+ this.capabilityTargetRevoked(chain, index, digest(token)))
162
+ throw new Error('TOKEN_REVOKED');
163
+ }
164
+ }
165
+ if (!leaf.payload.allowedTools.includes(tool) ||
166
+ !resourceWithin(resource, leaf.payload.resource) ||
167
+ !createAjv().compile(leaf.payload.argConstraints[tool])(args))
168
+ violation('tool, scope or arguments');
169
+ // Reserve synchronously before any asynchronous tool side effect. Parent IDs
170
+ // share counters across siblings; a caller must reuse this owner-side guard.
171
+ for (const t of chain)
172
+ if ((this.counts.get(capabilityIdentity(t)) ?? 0) >= t.payload.maxCalls)
173
+ violation('maxCalls exhausted');
174
+ for (const t of chain) {
175
+ const id = capabilityIdentity(t);
176
+ this.counts.set(id, (this.counts.get(id) ?? 0) + 1);
177
+ }
178
+ }
179
+ /**
180
+ * A notice revokes this token only when its issuer controls the token in
181
+ * this verified chain. A third party who minted a different token with the
182
+ * same id does not.
183
+ */
184
+ capabilityTargetRevoked(chain, index, targetId) {
185
+ const registry = this.registry;
186
+ if (!registry || !targetId)
187
+ return false;
188
+ const source = registry;
189
+ if (typeof source.noticesFor !== 'function')
190
+ return registry.isRevoked('capability', targetId);
191
+ const notices = source.noticesFor('capability', targetId);
192
+ if (notices.some((notice) => capabilityIssuerControls(chain, index, notice.payload.issuerDid)))
193
+ return true;
194
+ if (typeof source.unexplainedRevocation === 'function')
195
+ return source.unexplainedRevocation('capability', targetId);
196
+ // Listed notices are authoritative. An empty list plus a bare bit is an
197
+ // opaque registry that cannot show the issuer.
198
+ if (notices.length > 0)
199
+ return false;
200
+ return registry.isRevoked('capability', targetId);
201
+ }
202
+ }
203
+ //# sourceMappingURL=capability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability.js","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { KeyPair, Signed, TaskManifest } from './types.js';
2
+ export interface TaskContract {
3
+ manifest: TaskManifest;
4
+ proposal: Signed<TaskManifest>;
5
+ acceptance: Signed<{
6
+ type: 'ACCEPT';
7
+ manifestDigest: string;
8
+ taskId: string;
9
+ }>;
10
+ contractDigest: string;
11
+ }
12
+ export declare function createContract(manifest: TaskManifest, proposal: Signed<TaskManifest>, worker: KeyPair): TaskContract;
13
+ export declare function verifyContract(c: TaskContract): boolean;
14
+ export type State = 'PROPOSED' | 'ACCEPTED' | 'IN_PROGRESS' | 'DELIVERED' | 'VERIFY_ACCEPT' | 'VERIFY_REJECT' | 'SETTLED' | 'CANCELLED' | 'DISPUTED';
15
+ export type ContractEvent = 'ACCEPT' | 'START' | 'DELIVER' | 'VERIFY_ACCEPT' | 'VERIFY_REJECT' | 'SETTLE' | 'CANCEL' | 'DISPUTE';
16
+ export declare const transitions: Partial<Record<State, Partial<Record<ContractEvent, State>>>>;
17
+ export declare function nextState(state: State, event: ContractEvent): State;
18
+ export interface Transition {
19
+ taskId: string;
20
+ contractDigest: string;
21
+ seq: number;
22
+ from: State;
23
+ event: ContractEvent;
24
+ }
25
+ export declare class ContractMachine {
26
+ #private;
27
+ readonly contract: TaskContract;
28
+ readonly history: Signed<Transition>[];
29
+ get state(): State;
30
+ constructor(contract: TaskContract);
31
+ event(event: ContractEvent, key: KeyPair): Signed<Transition>;
32
+ apply(signed: Signed<Transition>): State;
33
+ }
@@ -0,0 +1,97 @@
1
+ // SPDX-License-Identifier: Apache-2.0 AND MIT
2
+ // Adapted from newtro/delegateos src/core/contract.ts (MIT), Scott Smith.
3
+ import { digest, signPayload, verifySigned } from './crypto.js';
4
+ import { validateManifest } from './schema.js';
5
+ export function createContract(manifest, proposal, worker) {
6
+ validateManifest(manifest);
7
+ if (!manifest.worker ||
8
+ worker.did !== manifest.worker.ownerDid ||
9
+ digest(manifest) !== digest(proposal.payload) ||
10
+ !verifySigned(proposal, manifest.requester.ownerDid))
11
+ throw new Error('INVALID_CONTRACT');
12
+ const acceptance = signPayload({ type: 'ACCEPT', manifestDigest: digest(manifest), taskId: manifest.taskId }, worker);
13
+ return {
14
+ manifest: structuredClone(manifest),
15
+ proposal,
16
+ acceptance,
17
+ contractDigest: digest({ proposal, acceptance }),
18
+ };
19
+ }
20
+ export function verifyContract(c) {
21
+ try {
22
+ validateManifest(c.manifest);
23
+ return (!!c.manifest.worker &&
24
+ digest(c.manifest) === digest(c.proposal.payload) &&
25
+ verifySigned(c.proposal, c.manifest.requester.ownerDid) &&
26
+ verifySigned(c.acceptance, c.manifest.worker.ownerDid) &&
27
+ c.acceptance.payload.type === 'ACCEPT' &&
28
+ c.acceptance.payload.taskId === c.manifest.taskId &&
29
+ c.acceptance.payload.manifestDigest === digest(c.manifest) &&
30
+ c.contractDigest === digest({ proposal: c.proposal, acceptance: c.acceptance }));
31
+ }
32
+ catch {
33
+ return false;
34
+ }
35
+ }
36
+ export const transitions = {
37
+ PROPOSED: { ACCEPT: 'ACCEPTED', CANCEL: 'CANCELLED' },
38
+ ACCEPTED: { START: 'IN_PROGRESS', CANCEL: 'CANCELLED' },
39
+ IN_PROGRESS: { DELIVER: 'DELIVERED', CANCEL: 'CANCELLED' },
40
+ DELIVERED: {
41
+ VERIFY_ACCEPT: 'VERIFY_ACCEPT',
42
+ VERIFY_REJECT: 'VERIFY_REJECT',
43
+ DISPUTE: 'DISPUTED',
44
+ },
45
+ VERIFY_ACCEPT: { SETTLE: 'SETTLED' },
46
+ VERIFY_REJECT: { DISPUTE: 'DISPUTED', CANCEL: 'CANCELLED' },
47
+ DISPUTED: { SETTLE: 'SETTLED' },
48
+ };
49
+ export function nextState(state, event) {
50
+ const next = transitions[state]?.[event];
51
+ if (!next)
52
+ throw new Error('INVALID_STATE_TRANSITION');
53
+ return next;
54
+ }
55
+ export class ContractMachine {
56
+ contract;
57
+ #state = 'PROPOSED';
58
+ #seq = 0;
59
+ history = [];
60
+ get state() {
61
+ return this.#state;
62
+ }
63
+ constructor(contract) {
64
+ this.contract = contract;
65
+ if (!verifyContract(contract))
66
+ throw new Error('INVALID_CONTRACT');
67
+ }
68
+ event(event, key) {
69
+ return signPayload({
70
+ taskId: this.contract.manifest.taskId,
71
+ contractDigest: this.contract.contractDigest,
72
+ seq: this.#seq,
73
+ from: this.#state,
74
+ event,
75
+ }, key);
76
+ }
77
+ apply(signed) {
78
+ const p = signed.payload, m = this.contract.manifest;
79
+ const workerEvents = ['ACCEPT', 'START', 'DELIVER'];
80
+ const expected = workerEvents.includes(p.event) ? m.worker.ownerDid : m.requester.ownerDid;
81
+ const either = ['CANCEL', 'DISPUTE'].includes(p.event);
82
+ if (!verifySigned(signed) ||
83
+ (!either && signed.signature.signerDid !== expected) ||
84
+ (either &&
85
+ ![m.requester.ownerDid, m.worker.ownerDid].includes(signed.signature.signerDid)) ||
86
+ p.taskId !== m.taskId ||
87
+ p.contractDigest !== this.contract.contractDigest ||
88
+ p.seq !== this.#seq ||
89
+ p.from !== this.#state)
90
+ throw new Error('INVALID_TRANSITION_SIGNATURE');
91
+ this.#state = nextState(this.#state, p.event);
92
+ this.#seq++;
93
+ this.history.push(structuredClone(signed));
94
+ return this.#state;
95
+ }
96
+ }
97
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { KeyObject } from 'node:crypto';
2
+ import type { EncryptedEnvelopePayload, Json, KeyPair, Signature, Signed, SignedEnvelope } from './types.js';
3
+ export declare function canonicalize(v: unknown): string;
4
+ export declare function digestBytes(v: string | Uint8Array): string;
5
+ export declare function digest(v: unknown): string;
6
+ export declare function generateKeyPair(): KeyPair;
7
+ export declare function resolveDid(did: string): KeyObject;
8
+ export declare function signPayload<T>(payload: T, key: KeyPair, signedAt?: string): Signed<T>;
9
+ export declare function verifySigned<T>(value: Signed<T>, expectedDid?: string): boolean;
10
+ export declare function verifySignature(payload: unknown, signature: Signature, expectedDid?: string): boolean;
11
+ export interface EnvelopeHeaderContext {
12
+ taskId: string;
13
+ seq: number;
14
+ eventType: string;
15
+ senderDid: string;
16
+ recipientDid: string;
17
+ }
18
+ /**
19
+ * HKDF-SHA256 info for envelope payload encryption.
20
+ * Binds the canonical header to the ephemeral X25519 public key and the
21
+ * recipient static X25519 public key so the AES key is tied to this
22
+ * Diffie-Hellman transcript as well as the header. Both keys are the raw
23
+ * 32-byte values, encoded base64url. AES-GCM additional data stays the
24
+ * header alone, and the 12-byte nonce stays the HKDF salt.
25
+ */
26
+ export declare function envelopeHkdfInfo(context: EnvelopeHeaderContext, ephemeralPublicKey: Uint8Array, recipientPublicKey: Uint8Array): Buffer;
27
+ export declare function isEncryptedEnvelopePayload(payload: unknown): payload is EncryptedEnvelopePayload;
28
+ export declare function encryptEnvelopePayload(payload: Json, context: EnvelopeHeaderContext): EncryptedEnvelopePayload;
29
+ export declare function decryptEnvelopePayload<T = Json>(payload: Json, recipient: KeyPair, context: EnvelopeHeaderContext, options?: {
30
+ requireEncrypted?: boolean;
31
+ }): T;
32
+ export declare function openEnvelope<T = Json>(envelope: SignedEnvelope, recipient: KeyPair, options?: {
33
+ requireEncrypted?: boolean;
34
+ }): T;