sealbridge 0.0.1 → 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.
- package/NOTICE +43 -0
- package/README.ja.md +375 -0
- package/README.md +466 -1
- package/dist/cli/dist/acp.d.ts +2 -0
- package/dist/cli/dist/acp.js +6 -0
- package/dist/cli/dist/acp.js.map +1 -0
- package/dist/cli/dist/board.d.ts +47 -0
- package/dist/cli/dist/board.js +255 -0
- package/dist/cli/dist/board.js.map +1 -0
- package/dist/cli/dist/contacts.d.ts +18 -0
- package/dist/cli/dist/contacts.js +154 -0
- package/dist/cli/dist/contacts.js.map +1 -0
- package/dist/cli/dist/doctor.d.ts +13 -0
- package/dist/cli/dist/doctor.js +30 -0
- package/dist/cli/dist/doctor.js.map +1 -0
- package/dist/cli/dist/index.d.ts +2 -0
- package/dist/cli/dist/index.js +193 -0
- package/dist/cli/dist/index.js.map +1 -0
- package/dist/cli/dist/invite-share.d.ts +16 -0
- package/dist/cli/dist/invite-share.js +169 -0
- package/dist/cli/dist/invite-share.js.map +1 -0
- package/dist/cli/dist/mcp.d.ts +17 -0
- package/dist/cli/dist/mcp.js +105 -0
- package/dist/cli/dist/mcp.js.map +1 -0
- package/dist/cli/dist/network.d.ts +1 -0
- package/dist/cli/dist/network.js +320 -0
- package/dist/cli/dist/network.js.map +1 -0
- package/dist/cli/dist/public-settings.d.ts +27 -0
- package/dist/cli/dist/public-settings.js +33 -0
- package/dist/cli/dist/public-settings.js.map +1 -0
- package/dist/cli/dist/receipt.d.ts +39 -0
- package/dist/cli/dist/receipt.js +90 -0
- package/dist/cli/dist/receipt.js.map +1 -0
- package/dist/cli/dist/receive-hook.d.ts +2 -0
- package/dist/cli/dist/receive-hook.js +35 -0
- package/dist/cli/dist/receive-hook.js.map +1 -0
- package/dist/examples/two-owner-demo/alice-policy.json +4 -0
- package/dist/examples/two-owner-demo/bob-policy.json +4 -0
- package/dist/examples/two-owner-demo/dist/run-demo.d.ts +71 -0
- package/dist/examples/two-owner-demo/dist/run-demo.js +383 -0
- package/dist/examples/two-owner-demo/dist/run-demo.js.map +1 -0
- package/dist/package.json +1 -0
- package/dist/packages/adapters/dist/command.d.ts +120 -0
- package/dist/packages/adapters/dist/command.js +365 -0
- package/dist/packages/adapters/dist/command.js.map +1 -0
- package/dist/packages/adapters/dist/credential-proxy.d.ts +37 -0
- package/dist/packages/adapters/dist/credential-proxy.js +363 -0
- package/dist/packages/adapters/dist/credential-proxy.js.map +1 -0
- package/dist/packages/adapters/dist/index.d.ts +6 -0
- package/dist/packages/adapters/dist/index.js +6 -0
- package/dist/packages/adapters/dist/index.js.map +1 -0
- package/dist/packages/adapters/dist/mcp.d.ts +32 -0
- package/dist/packages/adapters/dist/mcp.js +87 -0
- package/dist/packages/adapters/dist/mcp.js.map +1 -0
- package/dist/packages/adapters/dist/openai.d.ts +23 -0
- package/dist/packages/adapters/dist/openai.js +81 -0
- package/dist/packages/adapters/dist/openai.js.map +1 -0
- package/dist/packages/adapters/dist/sandbox.d.ts +45 -0
- package/dist/packages/adapters/dist/sandbox.js +60 -0
- package/dist/packages/adapters/dist/sandbox.js.map +1 -0
- package/dist/packages/core/dist/capability.d.ts +36 -0
- package/dist/packages/core/dist/capability.js +198 -0
- package/dist/packages/core/dist/capability.js.map +1 -0
- package/dist/packages/core/dist/contract.d.ts +33 -0
- package/dist/packages/core/dist/contract.js +97 -0
- package/dist/packages/core/dist/contract.js.map +1 -0
- package/dist/packages/core/dist/crypto.d.ts +34 -0
- package/dist/packages/core/dist/crypto.js +323 -0
- package/dist/packages/core/dist/crypto.js.map +1 -0
- package/dist/packages/core/dist/discovery.d.ts +124 -0
- package/dist/packages/core/dist/discovery.js +511 -0
- package/dist/packages/core/dist/discovery.js.map +1 -0
- package/dist/packages/core/dist/edge.d.ts +4 -0
- package/dist/packages/core/dist/edge.js +6 -0
- package/dist/packages/core/dist/edge.js.map +1 -0
- package/dist/packages/core/dist/index.d.ts +9 -0
- package/dist/packages/core/dist/index.js +10 -0
- package/dist/packages/core/dist/index.js.map +1 -0
- package/dist/packages/core/dist/receipts.d.ts +9 -0
- package/dist/packages/core/dist/receipts.js +67 -0
- package/dist/packages/core/dist/receipts.js.map +1 -0
- package/dist/packages/core/dist/relay-auth.d.ts +40 -0
- package/dist/packages/core/dist/relay-auth.js +106 -0
- package/dist/packages/core/dist/relay-auth.js.map +1 -0
- package/dist/packages/core/dist/revocation.d.ts +55 -0
- package/dist/packages/core/dist/revocation.js +88 -0
- package/dist/packages/core/dist/revocation.js.map +1 -0
- package/dist/packages/core/dist/schema.d.ts +11 -0
- package/dist/packages/core/dist/schema.js +48 -0
- package/dist/packages/core/dist/schema.js.map +1 -0
- package/dist/packages/core/dist/types.d.ts +172 -0
- package/dist/packages/core/dist/types.js +2 -0
- package/dist/packages/core/dist/types.js.map +1 -0
- package/dist/packages/ledger/dist/index.d.ts +84 -0
- package/dist/packages/ledger/dist/index.js +269 -0
- package/dist/packages/ledger/dist/index.js.map +1 -0
- package/dist/packages/relay/dist/fetch.d.ts +223 -0
- package/dist/packages/relay/dist/fetch.js +1210 -0
- package/dist/packages/relay/dist/fetch.js.map +1 -0
- package/dist/packages/relay/dist/index.d.ts +92 -0
- package/dist/packages/relay/dist/index.js +707 -0
- package/dist/packages/relay/dist/index.js.map +1 -0
- package/dist/packages/relay/dist/journal.d.ts +70 -0
- package/dist/packages/relay/dist/journal.js +310 -0
- package/dist/packages/relay/dist/journal.js.map +1 -0
- package/dist/packages/relay/dist/retry.d.ts +9 -0
- package/dist/packages/relay/dist/retry.js +39 -0
- package/dist/packages/relay/dist/retry.js.map +1 -0
- package/dist/packages/relay/dist/ssrf.d.ts +32 -0
- package/dist/packages/relay/dist/ssrf.js +213 -0
- package/dist/packages/relay/dist/ssrf.js.map +1 -0
- package/dist/packages/relay/dist/worker.d.ts +85 -0
- package/dist/packages/relay/dist/worker.js +411 -0
- package/dist/packages/relay/dist/worker.js.map +1 -0
- package/dist/packages/runtime/dist/a2a.d.ts +8 -0
- package/dist/packages/runtime/dist/a2a.js +69 -0
- package/dist/packages/runtime/dist/a2a.js.map +1 -0
- package/dist/packages/runtime/dist/adapter.d.ts +51 -0
- package/dist/packages/runtime/dist/adapter.js +2 -0
- package/dist/packages/runtime/dist/adapter.js.map +1 -0
- package/dist/packages/runtime/dist/approval-providers.d.ts +97 -0
- package/dist/packages/runtime/dist/approval-providers.js +269 -0
- package/dist/packages/runtime/dist/approval-providers.js.map +1 -0
- package/dist/packages/runtime/dist/daily-limit.d.ts +2 -0
- package/dist/packages/runtime/dist/daily-limit.js +54 -0
- package/dist/packages/runtime/dist/daily-limit.js.map +1 -0
- package/dist/packages/runtime/dist/gateway.d.ts +35 -0
- package/dist/packages/runtime/dist/gateway.js +92 -0
- package/dist/packages/runtime/dist/gateway.js.map +1 -0
- package/dist/packages/runtime/dist/inbox.d.ts +10 -0
- package/dist/packages/runtime/dist/inbox.js +24 -0
- package/dist/packages/runtime/dist/inbox.js.map +1 -0
- package/dist/packages/runtime/dist/index.d.ts +13 -0
- package/dist/packages/runtime/dist/index.js +14 -0
- package/dist/packages/runtime/dist/index.js.map +1 -0
- package/dist/packages/runtime/dist/mcp-server.d.ts +90 -0
- package/dist/packages/runtime/dist/mcp-server.js +631 -0
- package/dist/packages/runtime/dist/mcp-server.js.map +1 -0
- package/dist/packages/runtime/dist/mcp-tool-catalog.d.ts +3 -0
- package/dist/packages/runtime/dist/mcp-tool-catalog.js +491 -0
- package/dist/packages/runtime/dist/mcp-tool-catalog.js.map +1 -0
- package/dist/packages/runtime/dist/mcp.d.ts +73 -0
- package/dist/packages/runtime/dist/mcp.js +67 -0
- package/dist/packages/runtime/dist/mcp.js.map +1 -0
- package/dist/packages/runtime/dist/policy.d.ts +19 -0
- package/dist/packages/runtime/dist/policy.js +41 -0
- package/dist/packages/runtime/dist/policy.js.map +1 -0
- package/dist/packages/runtime/dist/quota.d.ts +39 -0
- package/dist/packages/runtime/dist/quota.js +147 -0
- package/dist/packages/runtime/dist/quota.js.map +1 -0
- package/dist/packages/runtime/dist/session-audit.d.ts +88 -0
- package/dist/packages/runtime/dist/session-audit.js +415 -0
- package/dist/packages/runtime/dist/session-audit.js.map +1 -0
- package/dist/packages/runtime/dist/session.d.ts +288 -0
- package/dist/packages/runtime/dist/session.js +1880 -0
- package/dist/packages/runtime/dist/session.js.map +1 -0
- package/dist/packages/runtime/dist/verifier.d.ts +64 -0
- package/dist/packages/runtime/dist/verifier.js +109 -0
- package/dist/packages/runtime/dist/verifier.js.map +1 -0
- package/dist/packages/sandbox/dist/container.d.ts +33 -0
- package/dist/packages/sandbox/dist/container.js +189 -0
- package/dist/packages/sandbox/dist/container.js.map +1 -0
- package/dist/packages/sandbox/dist/index.d.ts +34 -0
- package/dist/packages/sandbox/dist/index.js +179 -0
- package/dist/packages/sandbox/dist/index.js.map +1 -0
- package/dist/packages/sandbox/dist/permission.d.ts +58 -0
- package/dist/packages/sandbox/dist/permission.js +90 -0
- package/dist/packages/sandbox/dist/permission.js.map +1 -0
- package/dist/spec/README.ja.md +76 -0
- package/dist/spec/README.md +135 -0
- package/dist/spec/ack-receipt.schema.json +77 -0
- package/dist/spec/agent-card.schema.json +83 -0
- package/dist/spec/approval-receipt.schema.json +94 -0
- package/dist/spec/audit-entry.schema.json +101 -0
- package/dist/spec/capability-token.schema.json +133 -0
- package/dist/spec/envelope.schema.json +197 -0
- package/dist/spec/examples/sample-ack-receipt.json +16 -0
- package/dist/spec/examples/sample-agent-card.json +35 -0
- package/dist/spec/examples/sample-approval-receipt.json +18 -0
- package/dist/spec/examples/sample-audit-entry.json +37 -0
- package/dist/spec/examples/sample-capability-token.json +39 -0
- package/dist/spec/examples/sample-envelope.json +21 -0
- package/dist/spec/examples/sample-task-manifest.json +141 -0
- package/dist/spec/examples/sample-usage-receipt.json +19 -0
- package/dist/spec/task-manifest.schema.json +422 -0
- package/dist/spec/usage-receipt.schema.json +91 -0
- package/package.json +61 -5
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import { mkdtemp, realpath, writeFile, rm } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { spawn } from 'node:child_process';
|
|
6
|
+
import { resolveNodePermissionArgs } from './permission.js';
|
|
7
|
+
export { OCI_CONTAINER_BOUNDARY, detectNodePermissionFlag, prefersStableNodePermissionFlag, resolveNodePermissionArgs, subprocessSandboxBoundary, } from './permission.js';
|
|
8
|
+
// The parent process is the only one that holds owner keys and tool handlers.
|
|
9
|
+
// Task values cross that process boundary as JSON. Inside the child, `node:vm`
|
|
10
|
+
// evaluates task source in a null-prototype context. Node.js documents that
|
|
11
|
+
// `node:vm` is not a security mechanism: code that escapes the context runs as
|
|
12
|
+
// this child. `--permission` / `--experimental-permission` can deny filesystem,
|
|
13
|
+
// child-process, worker, and addon access. They do not deny network syscalls
|
|
14
|
+
// (`fetch`, `node:net`, `node:http`, `node:https`, `node:dns`, `node:dgram`).
|
|
15
|
+
// Deleting the Web globals below only hides those names in the wrapper.
|
|
16
|
+
export const sandboxWrapper = String.raw `
|
|
17
|
+
const vm = require('node:vm');
|
|
18
|
+
const fs = require('node:fs');
|
|
19
|
+
try { delete globalThis.fetch; } catch { /* name may be non-configurable */ }
|
|
20
|
+
try { delete globalThis.WebSocket; } catch { /* name may be non-configurable */ }
|
|
21
|
+
const config = JSON.parse(fs.readFileSync('task.json', 'utf8'));
|
|
22
|
+
const context = vm.createContext(Object.create(null), { codeGeneration: { strings: false, wasm: false }, microtaskMode: 'afterEvaluate' });
|
|
23
|
+
const bootstrap = 'globalThis.queue=[]; globalThis.waiters=new Map(); globalThis.serial=0; globalThis.finished=false; globalThis.callTool=(name,args)=>new Promise((resolve,reject)=>{const id=++serial;waiters.set(id,{resolve,reject});queue.push({id,name,args});}); globalThis.reportUsage=(inputTokens,outputTokens)=>callTool("$usage",{inputTokens,outputTokens}); globalThis.input=JSON.parse(' + JSON.stringify(JSON.stringify(config.input)) + ');';
|
|
24
|
+
vm.runInContext(bootstrap, context, { timeout: 100 });
|
|
25
|
+
new vm.Script('(async()=>{'+config.code+'})().then(value=>{globalThis.result=JSON.stringify({value:value===undefined?null:value});globalThis.finished=true;},error=>{globalThis.result=JSON.stringify({error:String(error)});globalThis.finished=true;});').runInContext(context, { timeout: config.timeoutMs });
|
|
26
|
+
const timer = setInterval(()=>{
|
|
27
|
+
try {
|
|
28
|
+
const queued=JSON.parse(vm.runInContext('JSON.stringify(queue.splice(0))',context,{timeout:100}));
|
|
29
|
+
for(const req of queued)process.send({type:'tool',...req});
|
|
30
|
+
if(vm.runInContext('finished',context,{timeout:100})){clearInterval(timer);process.send({type:'done',...JSON.parse(vm.runInContext('result',context,{timeout:100}))});process.disconnect();}
|
|
31
|
+
} catch(e){process.send({type:'done',error:String(e)});process.exit(1);}
|
|
32
|
+
},5);
|
|
33
|
+
process.on('message',msg=>{
|
|
34
|
+
try { const data=JSON.stringify(msg); vm.runInContext('{const r=JSON.parse('+JSON.stringify(data)+');const w=waiters.get(r.id);if(w){waiters.delete(r.id);r.error?w.reject(new Error(r.error)):w.resolve(r.value);}}',context,{timeout:100}); }
|
|
35
|
+
catch(e){process.send({type:'done',error:String(e)});process.exit(1);}
|
|
36
|
+
});
|
|
37
|
+
`;
|
|
38
|
+
export function sanitizedEnvironment(directory) {
|
|
39
|
+
// Build an allowlist from scratch: NODE_OPTIONS, proxy auth, cloud credentials,
|
|
40
|
+
// provider keys, SSH sockets and inherited PATH never enter the worker.
|
|
41
|
+
return { LANG: 'C.UTF-8', TZ: 'UTC', HOME: directory, TMPDIR: directory };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Argv after `process.execPath` for a `subprocess_restricted` child.
|
|
45
|
+
* Permission-model flags come from `resolveNodePermissionArgs`. `--no-addons`
|
|
46
|
+
* stays even when this Node binary supports neither permission flag.
|
|
47
|
+
*/
|
|
48
|
+
export function buildRestrictedSubprocessArgv(directory, entry, probe) {
|
|
49
|
+
return [
|
|
50
|
+
...resolveNodePermissionArgs(directory, probe).args,
|
|
51
|
+
'--no-addons',
|
|
52
|
+
'--disable-proto=throw',
|
|
53
|
+
'--max-old-space-size=64',
|
|
54
|
+
entry,
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
/** SIGTERM, then SIGKILL if the child is still alive. `killed` is true after either signal. */
|
|
58
|
+
export function terminateSubprocess(subprocess) {
|
|
59
|
+
if (subprocess.exitCode !== null)
|
|
60
|
+
return;
|
|
61
|
+
subprocess.kill('SIGTERM');
|
|
62
|
+
const timer = setTimeout(() => {
|
|
63
|
+
if (subprocess.exitCode === null)
|
|
64
|
+
subprocess.kill('SIGKILL');
|
|
65
|
+
}, 40);
|
|
66
|
+
timer.unref?.();
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* `subprocess_restricted` runner. Separate OS process, sanitized environment,
|
|
70
|
+
* scratch directory, timeout then SIGKILL, and an output byte cap. Node's
|
|
71
|
+
* permission model is applied when `detectNodePermissionFlag` finds a flag.
|
|
72
|
+
* `node:vm` inside the child is not a security mechanism, and the permission
|
|
73
|
+
* model does not block network syscalls. Untrusted code needs `oci_container`.
|
|
74
|
+
*/
|
|
75
|
+
export async function runSandbox(options) {
|
|
76
|
+
if (!Number.isSafeInteger(options.timeoutMs) ||
|
|
77
|
+
options.timeoutMs < 1 ||
|
|
78
|
+
options.timeoutMs > 604800000)
|
|
79
|
+
throw new Error('INVALID_TIMEOUT');
|
|
80
|
+
// macOS tmpdir() is /var/folders/..., a symlink to /private/var/folders/...
|
|
81
|
+
// Node's permission model realpath's the entry script and then stats '/var'
|
|
82
|
+
// unless --allow-fs-read already names the physical directory.
|
|
83
|
+
const directory = await realpath(await mkdtemp(join(tmpdir(), 'acp-task-')));
|
|
84
|
+
let child;
|
|
85
|
+
let exited = Promise.resolve();
|
|
86
|
+
try {
|
|
87
|
+
await writeFile(join(directory, 'runner.cjs'), sandboxWrapper, { mode: 0o600 });
|
|
88
|
+
await writeFile(join(directory, 'task.json'), JSON.stringify({
|
|
89
|
+
code: options.code,
|
|
90
|
+
input: options.input ?? null,
|
|
91
|
+
timeoutMs: options.timeoutMs,
|
|
92
|
+
}), { mode: 0o600 });
|
|
93
|
+
return await new Promise((resolve, reject) => {
|
|
94
|
+
const subprocess = spawn(process.execPath, buildRestrictedSubprocessArgv(directory, join(directory, 'runner.cjs')), {
|
|
95
|
+
cwd: directory,
|
|
96
|
+
env: sanitizedEnvironment(directory),
|
|
97
|
+
stdio: ['ignore', 'ignore', 'pipe', 'ipc'],
|
|
98
|
+
});
|
|
99
|
+
child = subprocess;
|
|
100
|
+
// `close` means stdio and the process handle are gone, so Windows can release cwd.
|
|
101
|
+
exited = new Promise((resolveExit) => {
|
|
102
|
+
const finished = () => resolveExit();
|
|
103
|
+
subprocess.once('exit', finished);
|
|
104
|
+
subprocess.once('close', finished);
|
|
105
|
+
});
|
|
106
|
+
let done = false, requests = 0, stderr = '';
|
|
107
|
+
const finish = (error, result) => {
|
|
108
|
+
if (done)
|
|
109
|
+
return;
|
|
110
|
+
done = true;
|
|
111
|
+
clearTimeout(timer);
|
|
112
|
+
options.signal?.removeEventListener('abort', abort);
|
|
113
|
+
terminateSubprocess(subprocess);
|
|
114
|
+
error ? reject(error) : resolve(result ?? null);
|
|
115
|
+
};
|
|
116
|
+
const abort = () => finish(new Error('SANDBOX_HALTED: ' + String(options.signal?.reason)));
|
|
117
|
+
const timer = setTimeout(() => finish(new Error('SANDBOX_TIMEOUT')), options.timeoutMs);
|
|
118
|
+
options.signal?.addEventListener('abort', abort, { once: true });
|
|
119
|
+
if (options.signal?.aborted)
|
|
120
|
+
abort();
|
|
121
|
+
subprocess.stderr?.on('data', (b) => {
|
|
122
|
+
stderr = (stderr + b.toString()).slice(-4096);
|
|
123
|
+
});
|
|
124
|
+
// Requests arrive from another OS process. The child has no tool handlers,
|
|
125
|
+
// capability counters, private keys or credentials, and cannot skip CapGuard.
|
|
126
|
+
let serial = Promise.resolve();
|
|
127
|
+
subprocess.on('message', (message) => {
|
|
128
|
+
serial = serial
|
|
129
|
+
.then(async () => {
|
|
130
|
+
if (done)
|
|
131
|
+
return;
|
|
132
|
+
const m = message;
|
|
133
|
+
// Output byte cap for one IPC message from the child.
|
|
134
|
+
if (JSON.stringify(m).length > 1048576)
|
|
135
|
+
return finish(new Error('SANDBOX_MESSAGE_LIMIT'));
|
|
136
|
+
if (m.type === 'done')
|
|
137
|
+
return finish(m.error ? new Error(m.error) : undefined, m.value);
|
|
138
|
+
if (m.type !== 'tool' || ++requests > 10000)
|
|
139
|
+
return finish(new Error('SANDBOX_PROTOCOL'));
|
|
140
|
+
try {
|
|
141
|
+
let value = null;
|
|
142
|
+
if (m.name === '$usage') {
|
|
143
|
+
if (!options.onUsage)
|
|
144
|
+
throw new Error('USAGE_ADAPTER_REQUIRED');
|
|
145
|
+
options.onUsage(m.args.inputTokens, m.args.outputTokens);
|
|
146
|
+
}
|
|
147
|
+
else
|
|
148
|
+
value = await options.onTool(m.name, m.args);
|
|
149
|
+
if (!done && subprocess.connected)
|
|
150
|
+
subprocess.send({ id: m.id, value });
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
finish(error instanceof Error ? error : new Error(String(error)));
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
.catch((e) => finish(e));
|
|
157
|
+
});
|
|
158
|
+
subprocess.on('error', (e) => finish(e));
|
|
159
|
+
subprocess.on('exit', (code) => {
|
|
160
|
+
if (!done)
|
|
161
|
+
finish(new Error('SANDBOX_EXIT: ' + code + ' ' + stderr));
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
finally {
|
|
166
|
+
try {
|
|
167
|
+
if (child)
|
|
168
|
+
terminateSubprocess(child);
|
|
169
|
+
// Wait until the child has actually exited, then give Windows a short retry window.
|
|
170
|
+
await Promise.race([exited, new Promise((resolve) => setTimeout(resolve, 250))]);
|
|
171
|
+
await rm(directory, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 });
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// EBUSY, EPERM, and EACCES while deleting the temp dir must not replace the result.
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
export * from './container.js';
|
|
179
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI spelling of Node's permission model.
|
|
3
|
+
* `--permission` is the stable flag on Node.js >= 22.13 and on Node.js 23+.
|
|
4
|
+
* Earlier Node.js 20 and 22 releases accept `--experimental-permission`.
|
|
5
|
+
* `--allow-fs-read` / `--allow-fs-write` are valid only together with one of these.
|
|
6
|
+
*/
|
|
7
|
+
export declare const NODE_PERMISSION_FLAGS: readonly ["--permission", "--experimental-permission"];
|
|
8
|
+
export type NodePermissionFlag = (typeof NODE_PERMISSION_FLAGS)[number];
|
|
9
|
+
/**
|
|
10
|
+
* What a sandbox run can honestly claim about Node's permission model.
|
|
11
|
+
* `kernelNetworkIsolated` is true only for `oci_container` (`--network none`).
|
|
12
|
+
* The permission model does not deny `fetch`, `node:net`, `node:http`,
|
|
13
|
+
* `node:https`, `node:dns`, or `node:dgram`.
|
|
14
|
+
* `permissionModelActive` means a permission flag was passed. It is false for
|
|
15
|
+
* `oci_container`, which uses the container runtime instead of that flag.
|
|
16
|
+
*/
|
|
17
|
+
export interface SandboxBoundaryMetadata {
|
|
18
|
+
nodePermissionFlag: NodePermissionFlag | null;
|
|
19
|
+
permissionModelActive: boolean;
|
|
20
|
+
kernelNetworkIsolated: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface NodePermissionProbe {
|
|
23
|
+
/** Flags the binary advertises. Defaults to `process.allowedNodeEnvironmentFlags`. */
|
|
24
|
+
flags?: Iterable<string>;
|
|
25
|
+
/** `process.versions.node`, with or without a leading `v`. */
|
|
26
|
+
nodeVersion?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface ResolvedNodePermission {
|
|
29
|
+
flag: NodePermissionFlag | null;
|
|
30
|
+
/** Empty when neither permission flag is supported. Never contains `--allow-fs-*` alone. */
|
|
31
|
+
args: string[];
|
|
32
|
+
boundary: SandboxBoundaryMetadata;
|
|
33
|
+
}
|
|
34
|
+
/** Boundary recorded for Podman/Docker `--network none` runs. */
|
|
35
|
+
export declare const OCI_CONTAINER_BOUNDARY: SandboxBoundaryMetadata;
|
|
36
|
+
export declare function parseNodeVersion(version: string): {
|
|
37
|
+
major: number;
|
|
38
|
+
minor: number;
|
|
39
|
+
patch: number;
|
|
40
|
+
};
|
|
41
|
+
/** True when the documented stable spelling is `--permission` (Node.js >= 22.13, or >= 23). */
|
|
42
|
+
export declare function prefersStableNodePermissionFlag(nodeVersion: string): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Chooses `--permission` or `--experimental-permission` for `process.execPath`.
|
|
45
|
+
* Returns null when `allowedNodeEnvironmentFlags` lists neither, so callers can
|
|
46
|
+
* start the child without `--allow-fs-*` and record `permissionModelActive: false`.
|
|
47
|
+
* Version selects the spelling when both flags are advertised. A binary that
|
|
48
|
+
* lists only one spelling uses that spelling.
|
|
49
|
+
*/
|
|
50
|
+
export declare function detectNodePermissionFlag(probe?: NodePermissionProbe): NodePermissionFlag | null;
|
|
51
|
+
/** Subprocess claim. `kernelNetworkIsolated` stays false even when the flag is active. */
|
|
52
|
+
export declare function subprocessSandboxBoundary(probe?: NodePermissionProbe): SandboxBoundaryMetadata;
|
|
53
|
+
/**
|
|
54
|
+
* Permission-model argv for one scratch directory.
|
|
55
|
+
* `--allow-fs-read` and `--allow-fs-write` are omitted when no permission flag exists,
|
|
56
|
+
* because Node rejects them unless `--permission` or `--experimental-permission` is active.
|
|
57
|
+
*/
|
|
58
|
+
export declare function resolveNodePermissionArgs(directory: string, probe?: NodePermissionProbe): ResolvedNodePermission;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* CLI spelling of Node's permission model.
|
|
4
|
+
* `--permission` is the stable flag on Node.js >= 22.13 and on Node.js 23+.
|
|
5
|
+
* Earlier Node.js 20 and 22 releases accept `--experimental-permission`.
|
|
6
|
+
* `--allow-fs-read` / `--allow-fs-write` are valid only together with one of these.
|
|
7
|
+
*/
|
|
8
|
+
export const NODE_PERMISSION_FLAGS = ['--permission', '--experimental-permission'];
|
|
9
|
+
/** Boundary recorded for Podman/Docker `--network none` runs. */
|
|
10
|
+
export const OCI_CONTAINER_BOUNDARY = {
|
|
11
|
+
nodePermissionFlag: null,
|
|
12
|
+
permissionModelActive: false,
|
|
13
|
+
kernelNetworkIsolated: true,
|
|
14
|
+
};
|
|
15
|
+
export function parseNodeVersion(version) {
|
|
16
|
+
const match = /^v?(\d+)\.(\d+)(?:\.(\d+))?/.exec(version.trim());
|
|
17
|
+
if (!match)
|
|
18
|
+
return { major: 0, minor: 0, patch: 0 };
|
|
19
|
+
return {
|
|
20
|
+
major: Number(match[1]),
|
|
21
|
+
minor: Number(match[2]),
|
|
22
|
+
patch: match[3] ? Number(match[3]) : 0,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/** True when the documented stable spelling is `--permission` (Node.js >= 22.13, or >= 23). */
|
|
26
|
+
export function prefersStableNodePermissionFlag(nodeVersion) {
|
|
27
|
+
const { major, minor } = parseNodeVersion(nodeVersion);
|
|
28
|
+
return major > 22 || (major === 22 && minor >= 13);
|
|
29
|
+
}
|
|
30
|
+
function advertisedFlags(probe) {
|
|
31
|
+
return new Set(probe?.flags ?? process.allowedNodeEnvironmentFlags);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Chooses `--permission` or `--experimental-permission` for `process.execPath`.
|
|
35
|
+
* Returns null when `allowedNodeEnvironmentFlags` lists neither, so callers can
|
|
36
|
+
* start the child without `--allow-fs-*` and record `permissionModelActive: false`.
|
|
37
|
+
* Version selects the spelling when both flags are advertised. A binary that
|
|
38
|
+
* lists only one spelling uses that spelling.
|
|
39
|
+
*/
|
|
40
|
+
export function detectNodePermissionFlag(probe) {
|
|
41
|
+
const flags = advertisedFlags(probe);
|
|
42
|
+
const version = probe?.nodeVersion ?? process.versions.node;
|
|
43
|
+
const hasStable = flags.has('--permission');
|
|
44
|
+
const hasExperimental = flags.has('--experimental-permission');
|
|
45
|
+
if (prefersStableNodePermissionFlag(version) && hasStable)
|
|
46
|
+
return '--permission';
|
|
47
|
+
if (hasExperimental)
|
|
48
|
+
return '--experimental-permission';
|
|
49
|
+
if (hasStable)
|
|
50
|
+
return '--permission';
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
/** Subprocess claim. `kernelNetworkIsolated` stays false even when the flag is active. */
|
|
54
|
+
export function subprocessSandboxBoundary(probe) {
|
|
55
|
+
const flag = detectNodePermissionFlag(probe);
|
|
56
|
+
return {
|
|
57
|
+
nodePermissionFlag: flag,
|
|
58
|
+
permissionModelActive: flag !== null,
|
|
59
|
+
kernelNetworkIsolated: false,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function assertScratchDirectory(directory) {
|
|
63
|
+
// A comma is a permission-model path separator, so it would widen the allowlist.
|
|
64
|
+
if (directory.length === 0 ||
|
|
65
|
+
directory === '/' ||
|
|
66
|
+
directory.includes(',') ||
|
|
67
|
+
directory.includes('\0'))
|
|
68
|
+
throw new Error('INVALID_SANDBOX_DIRECTORY');
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Permission-model argv for one scratch directory.
|
|
72
|
+
* `--allow-fs-read` and `--allow-fs-write` are omitted when no permission flag exists,
|
|
73
|
+
* because Node rejects them unless `--permission` or `--experimental-permission` is active.
|
|
74
|
+
*/
|
|
75
|
+
export function resolveNodePermissionArgs(directory, probe) {
|
|
76
|
+
const boundary = subprocessSandboxBoundary(probe);
|
|
77
|
+
if (!boundary.nodePermissionFlag)
|
|
78
|
+
return { flag: null, args: [], boundary };
|
|
79
|
+
assertScratchDirectory(directory);
|
|
80
|
+
return {
|
|
81
|
+
flag: boundary.nodePermissionFlag,
|
|
82
|
+
args: [
|
|
83
|
+
boundary.nodePermissionFlag,
|
|
84
|
+
'--allow-fs-read=' + directory,
|
|
85
|
+
'--allow-fs-write=' + directory,
|
|
86
|
+
],
|
|
87
|
+
boundary,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=permission.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission.js","sourceRoot":"","sources":["../src/permission.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Sealbridge Protocol Specification (`spec/`)
|
|
2
|
+
|
|
3
|
+
[English (README.md)](./README.md) | **日本語**
|
|
4
|
+
|
|
5
|
+
**結論**: 本ディレクトリはプログラミング言語に依存しないプロトコル仕様と JSON Schema (Draft 2020-12) の正本(Single Source of Truth)を定義する。すべてのプロトコルオブジェクトは **RFC 8785 (JCS: JSON Canonicalization Scheme)** で正規化されたバイト列に対して **SHA-256 ダイジェスト** と **Ed25519 署名** を計算する。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. 共通暗号・シリアライズ規則
|
|
10
|
+
|
|
11
|
+
1. **エンコーディング**: UTF-8 JSON(I-JSON サブセット準拠、浮動小数点の誤差を避けるため金額・トークン数・秒数はすべて整数 `integer` で表現する)。
|
|
12
|
+
2. **正規化(Canonicalization)**: 署名およびハッシュ計算の対象となるJSONオブジェクトは、[RFC 8785 (JCS)](https://www.rfc-editor.org/info/rfc8785/) に従ってキーを辞書順ソートし空白を除去した決定論的バイト列に変換する。
|
|
13
|
+
3. **識別子(Principal ID)**: オーナーおよびエージェントの識別子は W3C `did:key` 形式(Ed25519公開鍵のMultibase base58btcエンコード、例: `did:key:z6Mk...`)を使用する。
|
|
14
|
+
4. **ダイジェスト表記**: `"sha256:<64文字の16進小文字>"`(例: `"sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"`)。
|
|
15
|
+
5. **署名表記**: `"ed25519:<base64urlまたは128文字の16進小文字>"`。署名計算時は対象オブジェクトから `signature` / `signatures` フィールドを除いた状態でJCS正規化を行う。
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 2. コアスキーマ一覧
|
|
20
|
+
|
|
21
|
+
| ファイル名 | ステータス | 役割 |
|
|
22
|
+
| :--------------------------------------------------------- | :---------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
23
|
+
| [`task-manifest.schema.json`](./task-manifest.schema.json) | **Draft v0.1 (検証済)** | 依頼側(Requester)が提示するタスク仕様・入出力JSON Schema・受入基準・検証方法・予算上限・付与Capability・隔離要件の定義 |
|
|
24
|
+
| `capability-token.schema.json` | Planned (M0) | UCAN/Biscuit風の減衰可能スコープトークン定義(`issuer`, `audience`, `taskId`, `resources`, `allowedTools`, `argConstraints`, `maxInvocations`, `maxDelegationDepth`, `proofs`) |
|
|
25
|
+
| `envelope.schema.json` | Planned (M0) | Relay通過用の署名付きメッセージエンベロープ(`envelopeId`, `taskId`, `seq`, `eventType`, `senderDid`, `recipientDid`, `payload`, `signature`)および受領確認 `AckReceipt` |
|
|
26
|
+
| `audit-entry.schema.json` | Planned (M0) | 双方向署名ハッシュチェーン台帳エントリおよび `UsageReceipt` / `ApprovalReceipt` |
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 3. スキーマ検証コマンド
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx --yes -p ajv-cli@5 -p ajv-formats@3 ajv validate \
|
|
34
|
+
--spec=draft2020 \
|
|
35
|
+
-c ajv-formats \
|
|
36
|
+
-s spec/task-manifest.schema.json \
|
|
37
|
+
-d spec/examples/sample-task-manifest.json
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## M0–M1 実行可能なワイヤプロファイル
|
|
41
|
+
|
|
42
|
+
新しい `capability-token`、`usage-receipt`、`approval-receipt`、`ack-receipt`、`envelope`、`audit-entry` のスキーマは実装済みです。`pnpm validate:schemas` を実行すると、Ajv Draft 2020-12 で全スキーマをコンパイルし、すべてのサンプルを検証します。元の TaskManifest スキーマとサンプルは変更していません。
|
|
43
|
+
|
|
44
|
+
署名付きプロトコルオブジェクトは `{ "payload": <object>, "signature": <block> }` です。`D = "sha256:" + hex(SHA256(UTF8(JCS(payload))))` とします。ブロックには `signerDid`、`canonicalization: "RFC8785-JCS"`、`payloadDigest: D`、`signedAt`、パディング無しの `signature: "ed25519:" + base64url(64-byte-signature)` が入ります。Ed25519 署名の正確なプレイメージは次のとおりです。
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
UTF8(JCS({payload, signerDid, canonicalization, payloadDigest, signedAt}))
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
これはペイロードと署名メタデータを覆い、署名バイトそのものは署名しません。上の草案にある一般規則を、この形に精密化しています。JCS の入力は、有効な Unicode を持つ有限の JSON データでなければなりません。`undefined`、循環、疎な配列、非有限数、独自のオブジェクトインスタンス、孤立サロゲートは拒否します。JSON 成果物のコンテンツアドレスは `SHA256(UTF8(JCS(data)))` です。バイナリそのものには `digestBytes` を使います。did:key は Ed25519 公開鍵を、マルチコーデックのバイト `ed 01` と base58btc マルチベース `z` で符号化します。
|
|
51
|
+
|
|
52
|
+
Capability の証明は、根から葉へ並んだ署名付きトークンの配列です。各子は、完全に署名された親の SHA-256 ダイジェストへ `parentHash` を束縛し、発行者を親の audience に変え、タスク束縛を保ち、リソース・ツール・引数スキーマを狭め、maxCalls と期限を上げず、notBefore を過去へ戻さず、maxDepth を少なくとも 1 減らします。深さが 0 なら、さらに子を作れません。ワイヤ上の `maxCalls` / `maxDepth` は、マニフェストの付与である `maxInvocations` / `maxDelegationDepth` に対応します。引数スキーマはツール名をキーにします。同一スキーマか、直接の `allOf` 継承だけを受理します。対応するスコープとスキーマ構文はセキュリティモデルを見てください。
|
|
53
|
+
|
|
54
|
+
承認レシートのゲート名は、マニフェストのゲート名へ次のように対応します。
|
|
55
|
+
|
|
56
|
+
| レシートのゲート | マニフェストのゲート |
|
|
57
|
+
| -------------------- | ------------------------- |
|
|
58
|
+
| contract_acceptance | GATE_CONTRACT_ACCEPT |
|
|
59
|
+
| external_side_effect | GATE_EXTERNAL_SIDE_EFFECT |
|
|
60
|
+
| budget_increase | GATE_BUDGET_OVERRUN |
|
|
61
|
+
| redelegation | GATE_SUB_DELEGATION |
|
|
62
|
+
| final_verification | GATE_DELIVERY_ACCEPT |
|
|
63
|
+
|
|
64
|
+
オーナー署名の承認レシートは、タスク、ゲート、対象ダイジェストそのもの、APPROVED / REJECTED の決定を束縛します。ゲートのインスタンスはレシートの再生を拒否します。署名付き遷移は、契約ダイジェスト、タスク、直前の状態、0 始まりのシーケンスを束縛します。M1 の遷移プロファイルは PROPOSED、ACCEPTED、IN_PROGRESS、DELIVERED、VERIFY_ACCEPT、VERIFY_REJECT、SETTLED、CANCELLED、DISPUTED です。網羅表は `conformance/state-transitions.json` にあります。PROPOSE が PROPOSED を確立します。アーキテクチャにある交渉、再試行、タイムアウト専用の状態は、後の拡張です。
|
|
65
|
+
|
|
66
|
+
監査本体のフィールドは `taskId`、0 始まりの `seq`、`prevEntryHash`(起源では null)、`actorDid`、`receiverDid`、`eventType`、`payload` です。`entryHash = digest(body)` です。`actorSignature` は本体に署名し、`counterSignature` はそのハッシュ、タスク、双方を束縛する署名付き AckReceipt です。最終の署名付きチェックポイントは件数、head、行為者を固定し、相手がもう一度署名します。JSONL は `{"entry": ...}` レコードのあとに、1件の `{"checkpoint": ...}` レコードが続きます。バンドルは `{entries, checkpoint}` です。オフライン検証にはチェックポイントが必要です。より古い有効なバンドルへの巻き戻しを検出するには、独立して入手した最新の head が必要です。
|
|
67
|
+
|
|
68
|
+
妥当な `reportProgress` は途中の `metrics` を含められます。その使用量は、進捗フレームをキューへ入れる前にセッションのクォータへ計上されます。不正な `reportProgress` ペイロード(`INVALID_PROGRESS`)は、その `metrics` を適用しません。返された Promise は直ちに拒否され、セッションは粘着的な失敗を記録するため、その後の成果物で `SETTLED` にはなりません。アダプタが Promise 拒否を catch しても、依頼者は `PEER_REJECTED: {"code":"INVALID"}` を観測し、ワーカーは `INVALID_PROGRESS` で失敗します。
|
|
69
|
+
|
|
70
|
+
A2A の AgentCard、Task、Message、Artifact は SDK のワイヤ形を保ち、署名付き Sealbridge エンベロープの中に載ります。ブリッジは、インストール済みの A2A SDK 0.3 のワイヤプロファイルを使います。Sealbridge の契約とレシートは、A2A を書き換えるのではなく、オーバーレイのペイロードです。MCP の `tools/call` の要求と結果は公式 SDK を使い、サンドボックスの外で物理的にプロキシされます。未知のツールと、メタデータの無い迂回は拒否します。
|
|
71
|
+
|
|
72
|
+
## M2/M3 追加のディスカバリとセッションプロファイル
|
|
73
|
+
|
|
74
|
+
M0/M1 の JSON スキーマとサンプルは変更していません。署名付き AgentCard と招待は、同じ `Signed<T>` のプレイメージを使います。`AgentCard` には ownerDid、agentDid、relayEndpoint、skills(任意の JSON 入出力スキーマを含む)、costModes、name、description、version が入ります。カードに署名するのはオーナーです。`AgentInvite` は、完全に署名されたカードを、同じ ownerDid / agentDid と relayUrl、および issuedAt / expiresAt に束縛します。パーサは、無効な署名、一致しない識別子や経路、未来の発行時刻、期限切れを拒否します。URI の符号化は `acp-invite:<base64url(JSON(Signed<AgentInvite>))>` または `acp://invite?token=<same-base64url>` です。
|
|
75
|
+
|
|
76
|
+
セッションのエンベロープは、送信者ごとに単調なシーケンス番号を持ちます。各アプリケーションイベントのあとに、署名付き監査本体を運ぶ `AUDIT_ENTRY` エンベロープが続き、受信者は署名付き AckReceipt を運ぶ `AUDIT_ACK` を返します。双方が同一の AuditEntry を追記します。`CHECKPOINT` と `CHECKPOINT_ACK` は、最終の署名付きチェックポイントと副署を交換します。DELIVER には成果物、利用量、実行証明、およびその署名付き遷移が含まれます。証明は `effectiveIsolationLevel` と `sandboxBoundary`(`nodePermissionFlag`、`permissionModelActive`、`kernelNetworkIsolated`)を追加できます。`node:vm` はセキュリティ機構ではないです。Node の `--permission` / `--experimental-permission` は `fetch`、`node:net`、`node:http`、`node:https`、`node:dns`、`node:dgram` を止めません。`kernelNetworkIsolated` が真になるのは `oci_container`(`isolationLevel: 'oci_container'`)だけで、その経路はカーネルのネットワーク境界です(`--network none`、`--read-only`、`--cap-drop ALL`、`no-new-privileges`)。`subprocess_restricted` は権限フラグと `--allow-fs-read`、`--allow-fs-write` を組にすることがあります。元の M1 証明も有効なままです。
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Sealbridge Protocol Specification (`spec/`)
|
|
2
|
+
|
|
3
|
+
**English** | [日本語 (README.ja.md)](./README.ja.md)
|
|
4
|
+
|
|
5
|
+
**Conclusion**: This directory is the language-independent source of truth for the protocol specification and the JSON Schema (Draft 2020-12). Every protocol object computes a **SHA-256 digest** and an **Ed25519 signature** over bytes canonicalized with **RFC 8785 (JCS: JSON Canonicalization Scheme)**.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Shared cryptographic and serialization rules
|
|
10
|
+
|
|
11
|
+
1. **Encoding**: UTF-8 JSON (the I-JSON subset). Amounts, token counts, and durations are integers, so floating-point error does not enter the signed bytes.
|
|
12
|
+
2. **Canonicalization**: A JSON object that will be signed or hashed is converted to a deterministic byte string with keys sorted lexicographically and whitespace removed, following [RFC 8785 (JCS)](https://www.rfc-editor.org/info/rfc8785/).
|
|
13
|
+
3. **Principal ID**: Owner and agent identifiers use W3C `did:key` (the Ed25519 public key in multibase base58btc, for example `did:key:z6Mk...`).
|
|
14
|
+
4. **Digest form**: `"sha256:<64 lowercase hex characters>"` (for example `"sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"`).
|
|
15
|
+
5. **Signature form**: `"ed25519:<base64url or 128 lowercase hex characters>"`. Before signing, remove the `signature` / `signatures` fields from the object and then apply JCS.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 2. Core schemas
|
|
20
|
+
|
|
21
|
+
| File | Status | Role |
|
|
22
|
+
| :--------------------------------------------------------- | :------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
23
|
+
| [`task-manifest.schema.json`](./task-manifest.schema.json) | **Draft v0.1 (validated)** | The task specification the requester presents: input and output JSON Schema, acceptance criteria, verification method, budget cap, granted Capabilities, and isolation requirements |
|
|
24
|
+
| `capability-token.schema.json` | Planned (M0) | A UCAN/Biscuit-style attenuating scope token (`issuer`, `audience`, `taskId`, `resources`, `allowedTools`, `argConstraints`, `maxInvocations`, `maxDelegationDepth`, `proofs`) |
|
|
25
|
+
| `envelope.schema.json` | Planned (M0) | The signed message envelope that crosses the Relay (`envelopeId`, `taskId`, `seq`, `eventType`, `senderDid`, `recipientDid`, `payload`, `signature`) and the acknowledgement `AckReceipt` |
|
|
26
|
+
| `audit-entry.schema.json` | Planned (M0) | A bilateral signed hash-chain ledger entry, plus `UsageReceipt` / `ApprovalReceipt` |
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 3. Schema validation command
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx --yes -p ajv-cli@5 -p ajv-formats@3 ajv validate \
|
|
34
|
+
--spec=draft2020 \
|
|
35
|
+
-c ajv-formats \
|
|
36
|
+
-s spec/task-manifest.schema.json \
|
|
37
|
+
-d spec/examples/sample-task-manifest.json
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## M0–M1 executable wire profile
|
|
41
|
+
|
|
42
|
+
The new `capability-token`, `usage-receipt`, `approval-receipt`, `ack-receipt`,
|
|
43
|
+
`envelope` and `audit-entry` schemas are implemented. Run `pnpm validate:schemas`
|
|
44
|
+
to compile all schemas and validate every example using Ajv Draft 2020-12.
|
|
45
|
+
The original TaskManifest schema and example remain unchanged.
|
|
46
|
+
|
|
47
|
+
Signed protocol objects use `{ "payload": <object>, "signature": <block> }`.
|
|
48
|
+
Let `D = "sha256:" + hex(SHA256(UTF8(JCS(payload))))`. The block contains
|
|
49
|
+
`signerDid`, `canonicalization: "RFC8785-JCS"`, `payloadDigest: D`, `signedAt`, and
|
|
50
|
+
`signature: "ed25519:" + base64url(64-byte-signature)` without padding.
|
|
51
|
+
The exact Ed25519 signature preimage is:
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
UTF8(JCS({payload, signerDid, canonicalization, payloadDigest, signedAt}))
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
This covers the payload and signature metadata, and does not sign the signature
|
|
58
|
+
bytes themselves. It refines the draft's general rule above. JCS inputs must be
|
|
59
|
+
finite JSON data with valid Unicode; undefined, cycles, sparse arrays, nonfinite
|
|
60
|
+
numbers, custom object instances and lone surrogates are rejected. Content
|
|
61
|
+
addresses for JSON artifacts use `SHA256(UTF8(JCS(data)))`; `digestBytes` is
|
|
62
|
+
available for exact binary content. did:key encodes the Ed25519 public key with
|
|
63
|
+
multicodec bytes `ed 01` and base58btc multibase `z`.
|
|
64
|
+
|
|
65
|
+
Capability proofs are an ordered root-to-leaf array of signed tokens. Each child
|
|
66
|
+
binds `parentHash` to the SHA-256 digest of its complete signed parent, changes
|
|
67
|
+
issuer to the parent's audience, retains task binding, narrows the resource,
|
|
68
|
+
tools and argument schemas, never raises maxCalls or expiry, never backdates
|
|
69
|
+
notBefore, and decreases maxDepth by at least one. A zero depth forbids another
|
|
70
|
+
child. Wire token `maxCalls`/`maxDepth` correspond to manifest grant
|
|
71
|
+
`maxInvocations`/`maxDelegationDepth`. Argument schemas are indexed by tool name.
|
|
72
|
+
Only identical schemas or direct `allOf` inheritance are accepted. See the
|
|
73
|
+
security model for supported scope and schema syntax.
|
|
74
|
+
|
|
75
|
+
Approval receipt gate names map to the manifest's names:
|
|
76
|
+
|
|
77
|
+
| Receipt gate | Manifest gate |
|
|
78
|
+
| -------------------- | ------------------------- |
|
|
79
|
+
| contract_acceptance | GATE_CONTRACT_ACCEPT |
|
|
80
|
+
| external_side_effect | GATE_EXTERNAL_SIDE_EFFECT |
|
|
81
|
+
| budget_increase | GATE_BUDGET_OVERRUN |
|
|
82
|
+
| redelegation | GATE_SUB_DELEGATION |
|
|
83
|
+
| final_verification | GATE_DELIVERY_ACCEPT |
|
|
84
|
+
|
|
85
|
+
Owner-signed approval receipts bind the task, gate, exact target digest and
|
|
86
|
+
APPROVED/REJECTED decision. Gate instances reject receipt replay. Signed
|
|
87
|
+
transitions bind contract digest, task, prior state and zero-based sequence. The
|
|
88
|
+
M1 transition profile uses PROPOSED, ACCEPTED, IN_PROGRESS, DELIVERED,
|
|
89
|
+
VERIFY_ACCEPT, VERIFY_REJECT, SETTLED, CANCELLED and DISPUTED; the exhaustive table
|
|
90
|
+
is in `conformance/state-transitions.json`. PROPOSE establishes PROPOSED. Negotiation,
|
|
91
|
+
retry and timeout-specific states in the architecture remain later extensions.
|
|
92
|
+
|
|
93
|
+
Audit body fields are `taskId`, zero-based `seq`, `prevEntryHash` (null at genesis),
|
|
94
|
+
`actorDid`, `receiverDid`, `eventType`, `payload`. `entryHash = digest(body)`;
|
|
95
|
+
`actorSignature` signs the body and `counterSignature` is a signed AckReceipt
|
|
96
|
+
binding that hash, task and both parties. The final signed checkpoint fixes the
|
|
97
|
+
count, head and actors and is signed again by the other party. JSONL contains
|
|
98
|
+
`{"entry": ...}` records followed by one `{"checkpoint": ...}` record. Bundles
|
|
99
|
+
contain `{entries, checkpoint}`. The checkpoint is required for offline verification.
|
|
100
|
+
An independent latest head is required to detect rollback to an older valid bundle.
|
|
101
|
+
|
|
102
|
+
A valid `reportProgress` may include intermediate `metrics`, and those metrics are charged to the session quota before the progress frame is queued. An invalid `reportProgress` payload (`INVALID_PROGRESS`) does not apply its `metrics`. The returned promise rejects immediately, and the session records a sticky failure, so a later deliverable cannot settle. The requester observes `PEER_REJECTED: {"code":"INVALID"}` and the worker fails with `INVALID_PROGRESS`, even if the adapter catches the promise.
|
|
103
|
+
|
|
104
|
+
A2A AgentCard, Task, Message and Artifact retain SDK wire shapes, carried inside
|
|
105
|
+
signed Sealbridge envelopes. The bridge uses the installed A2A SDK 0.3 wire profile;
|
|
106
|
+
Sealbridge contract/receipt objects are overlay payloads rather than changes to A2A.
|
|
107
|
+
MCP `tools/call` requests and results use the official SDK and are physically
|
|
108
|
+
proxied outside the sandbox. Unknown tools and metadata-free bypasses are denied.
|
|
109
|
+
|
|
110
|
+
## M2/M3 additive discovery and session profile
|
|
111
|
+
|
|
112
|
+
The M0/M1 JSON schemas and examples remain unchanged. Signed AgentCards and
|
|
113
|
+
invites use the same `Signed<T>` preimage. `AgentCard` contains ownerDid,
|
|
114
|
+
agentDid, relayEndpoint, skills (including optional JSON input/output schemas),
|
|
115
|
+
costModes, name, description and version. The owner signs the card.
|
|
116
|
+
`AgentInvite` binds the complete signed card to the same ownerDid/agentDid and
|
|
117
|
+
relayUrl, plus issuedAt/expiresAt. Parsers reject invalid signatures, mismatched
|
|
118
|
+
identities/routes, future issuance and expiry. URI encoding is either
|
|
119
|
+
`acp-invite:<base64url(JSON(Signed<AgentInvite>))>` or
|
|
120
|
+
`acp://invite?token=<same-base64url>`.
|
|
121
|
+
|
|
122
|
+
Session envelopes have monotonic sequence numbers per sender. Each application
|
|
123
|
+
event is followed by an `AUDIT_ENTRY` envelope carrying a signed audit body;
|
|
124
|
+
the receiver returns `AUDIT_ACK` carrying its signed AckReceipt. Both sides
|
|
125
|
+
append the identical AuditEntry. `CHECKPOINT` and `CHECKPOINT_ACK` exchange the
|
|
126
|
+
final signed checkpoint and countersignature. DELIVER includes artifact, usage,
|
|
127
|
+
attestation and its signed transition. Attestations may add
|
|
128
|
+
`effectiveIsolationLevel` and `sandboxBoundary` (`nodePermissionFlag`,
|
|
129
|
+
`permissionModelActive`, `kernelNetworkIsolated`). `node:vm` is not a security mechanism,
|
|
130
|
+
and Node's `--permission` / `--experimental-permission` model does not block
|
|
131
|
+
`fetch`, `node:net`, `node:http`, `node:https`, `node:dns`, or `node:dgram`.
|
|
132
|
+
`kernelNetworkIsolated` is true only for `oci_container` (`isolationLevel: 'oci_container'`),
|
|
133
|
+
which is the kernel network boundary: `--network none`, `--read-only`, `--cap-drop ALL`, and `no-new-privileges`.
|
|
134
|
+
`subprocess_restricted` may add `--allow-fs-read` and `--allow-fs-write` with the
|
|
135
|
+
permission flag. Original M1 attestations remain valid.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://acp-spec.local/schemas/v0.1/ack-receipt.schema.json",
|
|
4
|
+
"title": "ack-receipt",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"payload",
|
|
9
|
+
"signature"
|
|
10
|
+
],
|
|
11
|
+
"properties": {
|
|
12
|
+
"payload": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"additionalProperties": false,
|
|
15
|
+
"required": [
|
|
16
|
+
"type",
|
|
17
|
+
"taskId",
|
|
18
|
+
"entryHash",
|
|
19
|
+
"actorDid",
|
|
20
|
+
"receiverDid"
|
|
21
|
+
],
|
|
22
|
+
"properties": {
|
|
23
|
+
"type": {
|
|
24
|
+
"const": "AckReceipt"
|
|
25
|
+
},
|
|
26
|
+
"taskId": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"minLength": 1
|
|
29
|
+
},
|
|
30
|
+
"entryHash": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"pattern": "^sha256:[a-f0-9]{64}$"
|
|
33
|
+
},
|
|
34
|
+
"actorDid": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"pattern": "^did:key:z[1-9A-HJ-NP-Za-km-z]{32,128}$"
|
|
37
|
+
},
|
|
38
|
+
"receiverDid": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"pattern": "^did:key:z[1-9A-HJ-NP-Za-km-z]{32,128}$"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"signature": {
|
|
45
|
+
"type": "object",
|
|
46
|
+
"additionalProperties": false,
|
|
47
|
+
"required": [
|
|
48
|
+
"signerDid",
|
|
49
|
+
"canonicalization",
|
|
50
|
+
"payloadDigest",
|
|
51
|
+
"signedAt",
|
|
52
|
+
"signature"
|
|
53
|
+
],
|
|
54
|
+
"properties": {
|
|
55
|
+
"signerDid": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"pattern": "^did:key:z[1-9A-HJ-NP-Za-km-z]{32,128}$"
|
|
58
|
+
},
|
|
59
|
+
"canonicalization": {
|
|
60
|
+
"const": "RFC8785-JCS"
|
|
61
|
+
},
|
|
62
|
+
"payloadDigest": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"pattern": "^sha256:[a-f0-9]{64}$"
|
|
65
|
+
},
|
|
66
|
+
"signedAt": {
|
|
67
|
+
"type": "string",
|
|
68
|
+
"format": "date-time"
|
|
69
|
+
},
|
|
70
|
+
"signature": {
|
|
71
|
+
"type": "string",
|
|
72
|
+
"pattern": "^ed25519:[A-Za-z0-9_-]{86}$"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|