agent-comms 3.6.0 → 3.8.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/dist/core/bridge-mesh.d.ts +4 -2
- package/dist/core/bridge-mesh.d.ts.map +1 -1
- package/dist/core/bridge-mesh.js +13 -4
- package/dist/core/bridge-mesh.js.map +1 -1
- package/dist/core/capability-bubble-up.d.ts +69 -0
- package/dist/core/capability-bubble-up.d.ts.map +1 -0
- package/dist/core/capability-bubble-up.js +138 -0
- package/dist/core/capability-bubble-up.js.map +1 -0
- package/dist/core/delegation-policy.d.ts +20 -0
- package/dist/core/delegation-policy.d.ts.map +1 -0
- package/dist/core/delegation-policy.js +10 -0
- package/dist/core/delegation-policy.js.map +1 -0
- package/dist/core/device-membership-verification.d.ts +28 -0
- package/dist/core/device-membership-verification.d.ts.map +1 -0
- package/dist/core/device-membership-verification.js +43 -0
- package/dist/core/device-membership-verification.js.map +1 -0
- package/dist/core/device-membership.d.ts +42 -0
- package/dist/core/device-membership.d.ts.map +1 -0
- package/dist/core/device-membership.js +53 -0
- package/dist/core/device-membership.js.map +1 -0
- package/dist/core/identity-store.d.ts +10 -0
- package/dist/core/identity-store.d.ts.map +1 -1
- package/dist/core/identity-store.js +44 -0
- package/dist/core/identity-store.js.map +1 -1
- package/dist/core/package-version.d.ts +6 -0
- package/dist/core/package-version.d.ts.map +1 -0
- package/dist/core/package-version.js +28 -0
- package/dist/core/package-version.js.map +1 -0
- package/dist/core/room-lifecycle.d.ts.map +1 -1
- package/dist/core/room-lifecycle.js +5 -4
- package/dist/core/room-lifecycle.js.map +1 -1
- package/dist/core/room-protocol.d.ts.map +1 -1
- package/dist/core/room-protocol.js +4 -3
- package/dist/core/room-protocol.js.map +1 -1
- package/dist/core/room-token-verification.d.ts +3 -0
- package/dist/core/room-token-verification.d.ts.map +1 -1
- package/dist/core/room-token-verification.js +4 -0
- package/dist/core/room-token-verification.js.map +1 -1
- package/dist/core/test/package-version.unit.test.d.ts +5 -0
- package/dist/core/test/package-version.unit.test.d.ts.map +1 -0
- package/dist/core/test/package-version.unit.test.js +30 -0
- package/dist/core/test/package-version.unit.test.js.map +1 -0
- package/dist/core/test/version-check.unit.test.d.ts +5 -0
- package/dist/core/test/version-check.unit.test.d.ts.map +1 -0
- package/dist/core/test/version-check.unit.test.js +176 -0
- package/dist/core/test/version-check.unit.test.js.map +1 -0
- package/dist/core/tool.d.ts +7 -1
- package/dist/core/tool.d.ts.map +1 -1
- package/dist/core/tool.js +34 -11
- package/dist/core/tool.js.map +1 -1
- package/dist/core/user-identity.d.ts +10 -0
- package/dist/core/user-identity.d.ts.map +1 -1
- package/dist/core/user-identity.js +60 -8
- package/dist/core/user-identity.js.map +1 -1
- package/dist/core/version-check.d.ts +36 -0
- package/dist/core/version-check.d.ts.map +1 -0
- package/dist/core/version-check.js +118 -0
- package/dist/core/version-check.js.map +1 -0
- package/dist/test/bridge-mesh-version-drift.test.d.ts +7 -0
- package/dist/test/bridge-mesh-version-drift.test.d.ts.map +1 -0
- package/dist/test/bridge-mesh-version-drift.test.js +98 -0
- package/dist/test/bridge-mesh-version-drift.test.js.map +1 -0
- package/dist/test/capability-bubble-up.test.d.ts +2 -0
- package/dist/test/capability-bubble-up.test.d.ts.map +1 -0
- package/dist/test/capability-bubble-up.test.js +350 -0
- package/dist/test/capability-bubble-up.test.js.map +1 -0
- package/dist/test/delegation-policy.test.d.ts +5 -0
- package/dist/test/delegation-policy.test.d.ts.map +1 -0
- package/dist/test/delegation-policy.test.js +52 -0
- package/dist/test/delegation-policy.test.js.map +1 -0
- package/dist/test/device-membership-verification.test.d.ts +2 -0
- package/dist/test/device-membership-verification.test.d.ts.map +1 -0
- package/dist/test/device-membership-verification.test.js +214 -0
- package/dist/test/device-membership-verification.test.js.map +1 -0
- package/dist/test/device-membership.test.d.ts +2 -0
- package/dist/test/device-membership.test.d.ts.map +1 -0
- package/dist/test/device-membership.test.js +201 -0
- package/dist/test/device-membership.test.js.map +1 -0
- package/dist/test/identity-store.test.js +76 -1
- package/dist/test/identity-store.test.js.map +1 -1
- package/dist/test/room-lifecycle.test.js +24 -1
- package/dist/test/room-lifecycle.test.js.map +1 -1
- package/dist/test/tool-version-reporting.test.d.ts +5 -0
- package/dist/test/tool-version-reporting.test.d.ts.map +1 -0
- package/dist/test/tool-version-reporting.test.js +67 -0
- package/dist/test/tool-version-reporting.test.js.map +1 -0
- package/dist/test/user-identity.test.js +54 -1
- package/dist/test/user-identity.test.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects when a newer agent-comms release exists on npm than the one currently running -- see issue #166. A stale checkout (e.g. a manually pulled git checkout left behind while npm moves on) surfaces its own drift via CommsTool's whoami/update output (see tool.ts) rather than silently splitting mesh generations. Every failure mode (offline, registry down, timeout, malformed response) is swallowed here: a bridge with no network access behaves exactly as if no newer release were known, never as an error.
|
|
3
|
+
*/
|
|
4
|
+
const NPM_REGISTRY_LATEST_VERSION_URL = "https://registry.npmjs.org/agent-comms/latest";
|
|
5
|
+
/** How long a single npm registry lookup may take before it's treated as a failure -- generous for a slow connection, short enough that a hung request can never pile up across successive interval checks. */
|
|
6
|
+
const REGISTRY_FETCH_TIMEOUT_MS = 5000;
|
|
7
|
+
const MS_PER_SECOND = 1000;
|
|
8
|
+
const SECONDS_PER_MINUTE = 60;
|
|
9
|
+
const MINUTES_PER_HOUR = 60;
|
|
10
|
+
const CHECK_INTERVAL_HOURS = 6;
|
|
11
|
+
/** How often VersionDriftChecker re-checks npm once started. Deliberately slow: this is a background courtesy warning that piggybacks on whoami/update output (see tool.ts), not a thing any caller waits on, so there's no benefit to checking more often than a bridge process is likely to stay running for. */
|
|
12
|
+
export const DEFAULT_CHECK_INTERVAL_MS = CHECK_INTERVAL_HOURS * MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MS_PER_SECOND;
|
|
13
|
+
function isNpmRegistryLatestResponse(value) {
|
|
14
|
+
if (typeof value !== "object" || value === null)
|
|
15
|
+
return false;
|
|
16
|
+
if (!("version" in value))
|
|
17
|
+
return false;
|
|
18
|
+
return typeof value.version === "string";
|
|
19
|
+
}
|
|
20
|
+
/** Fetches the "latest" dist-tag version currently published to npm, or undefined on any failure (offline, timeout, non-2xx, malformed body) -- callers never need their own try/catch around this. */
|
|
21
|
+
export async function fetchLatestPublishedVersion(registryUrl = NPM_REGISTRY_LATEST_VERSION_URL, fetchImpl = fetch) {
|
|
22
|
+
try {
|
|
23
|
+
const response = await fetchImpl(registryUrl, {
|
|
24
|
+
signal: AbortSignal.timeout(REGISTRY_FETCH_TIMEOUT_MS),
|
|
25
|
+
});
|
|
26
|
+
if (!response.ok)
|
|
27
|
+
return undefined;
|
|
28
|
+
const body = await response.json();
|
|
29
|
+
if (!isNpmRegistryLatestResponse(body))
|
|
30
|
+
return undefined;
|
|
31
|
+
return body.version;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const VERSION_CORE_PART_COUNT = 3;
|
|
38
|
+
/** Parses a "x.y.z[-prerelease][+build]" version string's numeric major/minor/patch parts, ignoring any prerelease/build suffix -- agent-comms releases are always plain x.y.z (semantic-release's own default), so a fuller semver-precedence comparator (prerelease ordering, build-metadata rules) would be speculative generality with no real input that needs it. Returns undefined for a string that isn't three dot-separated non-negative integers. */
|
|
39
|
+
function parseVersionCore(version) {
|
|
40
|
+
const core = version.split(/[+-]/, 1)[0] ?? "";
|
|
41
|
+
const parts = core.split(".");
|
|
42
|
+
if (parts.length !== VERSION_CORE_PART_COUNT)
|
|
43
|
+
return undefined;
|
|
44
|
+
const major = Number(parts[0]);
|
|
45
|
+
const minor = Number(parts[1]);
|
|
46
|
+
const patch = Number(parts[2]);
|
|
47
|
+
const isValid = [major, minor, patch].every((part) => Number.isInteger(part) && part >= 0);
|
|
48
|
+
if (!isValid)
|
|
49
|
+
return undefined;
|
|
50
|
+
return [major, minor, patch];
|
|
51
|
+
}
|
|
52
|
+
/** True when latest is a strictly greater release than current, by major.minor.patch precedence. Returns false for either string that doesn't parse as a plain x.y.z version -- a malformed comparison has no safe "yes, warn" answer. */
|
|
53
|
+
export function isNewerVersion(current, latest) {
|
|
54
|
+
const currentCore = parseVersionCore(current);
|
|
55
|
+
const latestCore = parseVersionCore(latest);
|
|
56
|
+
if (currentCore === undefined || latestCore === undefined)
|
|
57
|
+
return false;
|
|
58
|
+
const [currentMajor, currentMinor, currentPatch] = currentCore;
|
|
59
|
+
const [latestMajor, latestMinor, latestPatch] = latestCore;
|
|
60
|
+
if (latestMajor !== currentMajor)
|
|
61
|
+
return latestMajor > currentMajor;
|
|
62
|
+
if (latestMinor !== currentMinor)
|
|
63
|
+
return latestMinor > currentMinor;
|
|
64
|
+
return latestPatch > currentPatch;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Periodically (and once immediately on start()) compares the running version against npm's published "latest" dist-tag, caching the most recent successful lookup so CommsTool's whoami/update output can surface a "newer release available" note without either of them ever waiting on a network call. A failed check leaves the previously cached result untouched -- see this file's own header for why a failure is never surfaced as an error.
|
|
68
|
+
*/
|
|
69
|
+
export class VersionDriftChecker {
|
|
70
|
+
currentVersion;
|
|
71
|
+
intervalMs;
|
|
72
|
+
fetchLatestVersion;
|
|
73
|
+
latestKnownVersion;
|
|
74
|
+
timer;
|
|
75
|
+
constructor(options) {
|
|
76
|
+
this.currentVersion = options.currentVersion;
|
|
77
|
+
this.intervalMs = options.intervalMs ?? DEFAULT_CHECK_INTERVAL_MS;
|
|
78
|
+
this.fetchLatestVersion =
|
|
79
|
+
options.fetchLatestVersion ?? (async () => fetchLatestPublishedVersion());
|
|
80
|
+
}
|
|
81
|
+
/** Runs one check immediately (fire-and-forget -- start() itself never waits on the network) and schedules a recurring check every intervalMs. The interval is unref'd so it can never keep a bridge process alive on its own. No-op if already started. */
|
|
82
|
+
start() {
|
|
83
|
+
if (this.timer !== undefined)
|
|
84
|
+
return;
|
|
85
|
+
void this.check();
|
|
86
|
+
this.timer = setInterval(() => {
|
|
87
|
+
void this.check();
|
|
88
|
+
}, this.intervalMs);
|
|
89
|
+
this.timer.unref();
|
|
90
|
+
}
|
|
91
|
+
/** Stops the recurring check, if running. Safe to call even if start() was never called. */
|
|
92
|
+
stop() {
|
|
93
|
+
if (this.timer !== undefined) {
|
|
94
|
+
clearInterval(this.timer);
|
|
95
|
+
this.timer = undefined;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** The most recently observed newer release, or undefined when none is known -- either no successful check has completed yet, or the running version is already current (or ahead). */
|
|
99
|
+
getNewerVersionIfAny() {
|
|
100
|
+
if (this.latestKnownVersion === undefined)
|
|
101
|
+
return undefined;
|
|
102
|
+
return isNewerVersion(this.currentVersion, this.latestKnownVersion)
|
|
103
|
+
? this.latestKnownVersion
|
|
104
|
+
: undefined;
|
|
105
|
+
}
|
|
106
|
+
/** Runs a single check, tolerating a rejection from a caller-supplied fetchLatestVersion as well as the soft-failure (undefined) contract fetchLatestPublishedVersion's own default already guarantees -- start()'s own fire-and-forget call, and the recurring interval callback, both rely on this never throwing regardless of which fetch implementation is wired in. */
|
|
107
|
+
async check() {
|
|
108
|
+
try {
|
|
109
|
+
const latest = await this.fetchLatestVersion();
|
|
110
|
+
if (latest !== undefined)
|
|
111
|
+
this.latestKnownVersion = latest;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// Swallowed deliberately -- see this file's own header on failing soft.
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=version-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version-check.js","sourceRoot":"","sources":["../../src/core/version-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests proving createBridgeMesh wires a VersionDriftChecker into the CommsTool it builds (agent-comms#166) -- every real bridge (pi, claude-code, codex, mcp, opencode, cc-peer) constructs its store/tool through this one factory, so this is the single place that needs to prove the wiring works rather than duplicating the assertion per bridge.
|
|
3
|
+
*
|
|
4
|
+
* fetchLatestVersion is injected in every test here specifically so this file never makes a real network call to the npm registry -- see bridge-mesh.ts's own header on why that parameter exists.
|
|
5
|
+
*/
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=bridge-mesh-version-drift.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-mesh-version-drift.test.d.ts","sourceRoot":"","sources":["../../src/test/bridge-mesh-version-drift.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests proving createBridgeMesh wires a VersionDriftChecker into the CommsTool it builds (agent-comms#166) -- every real bridge (pi, claude-code, codex, mcp, opencode, cc-peer) constructs its store/tool through this one factory, so this is the single place that needs to prove the wiring works rather than duplicating the assertion per bridge.
|
|
3
|
+
*
|
|
4
|
+
* fetchLatestVersion is injected in every test here specifically so this file never makes a real network call to the npm registry -- see bridge-mesh.ts's own header on why that parameter exists.
|
|
5
|
+
*/
|
|
6
|
+
import * as fs from "node:fs";
|
|
7
|
+
import { tmpdir } from "node:os";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { test, expect } from "vitest";
|
|
10
|
+
import { createBridgeMesh } from "../core/bridge-mesh.js";
|
|
11
|
+
import { getOwnPackageVersion } from "../core/package-version.js";
|
|
12
|
+
import { buildAction } from "../core/bridge.js";
|
|
13
|
+
function tempSlot(harness) {
|
|
14
|
+
const dir = fs.mkdtempSync(path.join(tmpdir(), "agent-comms-bridge-mesh-version-drift-test-"));
|
|
15
|
+
return { harness, cwd: "/tmp/project", dir };
|
|
16
|
+
}
|
|
17
|
+
// Never rely on the default coordinator port (19876) in a test -- this machine routinely runs other, real agent-comms bridges (this very session's own MCP tool included) that are genuinely listening there, and a solo test store becoming a peer of that unrelated live mesh rather than its own coordinator hangs indefinitely on the first round trip that expects a same-test peer to answer. Matches the ephemeral port range bridge-mesh.test.ts's own multi-peer test already uses for the identical reason.
|
|
18
|
+
const TEST_COORDINATOR_PORT_BASE = 21_100;
|
|
19
|
+
const TEST_COORDINATOR_PORT_RANGE = 100;
|
|
20
|
+
function testCoordinatorPort() {
|
|
21
|
+
return (TEST_COORDINATOR_PORT_BASE +
|
|
22
|
+
Math.floor(Math.random() * TEST_COORDINATOR_PORT_RANGE));
|
|
23
|
+
}
|
|
24
|
+
const POLL_TIMEOUT_MS = 5000;
|
|
25
|
+
const POLL_INTERVAL_MS = 20;
|
|
26
|
+
/**
|
|
27
|
+
* Re-runs whoami until its content includes match or timeoutMs elapses -- VersionDriftChecker's own start() check is fire-and-forget (see version-check.ts), so there's no signal other than tool.handle's own output for when the checker's first check has actually landed.
|
|
28
|
+
*/
|
|
29
|
+
async function whoamiUntilContains(tool, ctx, match) {
|
|
30
|
+
const deadline = Date.now() + POLL_TIMEOUT_MS;
|
|
31
|
+
for (;;) {
|
|
32
|
+
const result = await tool.handle(ctx, buildAction({ action: "whoami" }));
|
|
33
|
+
if (result.content.includes(match))
|
|
34
|
+
return result.content;
|
|
35
|
+
if (Date.now() >= deadline) {
|
|
36
|
+
throw new Error(`Timed out after ${String(POLL_TIMEOUT_MS)}ms waiting for whoami output to include "${match}". Last content:\n${result.content}`);
|
|
37
|
+
}
|
|
38
|
+
await new Promise((resolve) => {
|
|
39
|
+
setTimeout(resolve, POLL_INTERVAL_MS);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
test("createBridgeMesh's tool reports an available update when the injected version checker finds a newer release", async () => {
|
|
44
|
+
const slot = tempSlot("test-harness-drift");
|
|
45
|
+
const newerVersion = "9999.0.0";
|
|
46
|
+
const { store, tool } = await createBridgeMesh(slot, testCoordinatorPort(), undefined, async () => Promise.resolve(newerVersion));
|
|
47
|
+
try {
|
|
48
|
+
await store.init();
|
|
49
|
+
const agent = await store.registerAgent({
|
|
50
|
+
name: "drift-test-agent",
|
|
51
|
+
harness: "test-harness-drift",
|
|
52
|
+
cwd: "/tmp/project",
|
|
53
|
+
pid: process.pid,
|
|
54
|
+
visibility: "visible",
|
|
55
|
+
tags: [],
|
|
56
|
+
});
|
|
57
|
+
const ctx = {
|
|
58
|
+
agentId: agent.id,
|
|
59
|
+
harness: "test-harness-drift",
|
|
60
|
+
cwd: "/tmp/project",
|
|
61
|
+
pid: process.pid,
|
|
62
|
+
};
|
|
63
|
+
const content = await whoamiUntilContains(tool, ctx, "Update available");
|
|
64
|
+
expect(content).toContain(`Version: ${getOwnPackageVersion()}`);
|
|
65
|
+
expect(content).toContain(`Update available: ${newerVersion}`);
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
await store.shutdown();
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
test("createBridgeMesh's tool reports its own version with no update line when no newer release is known", async () => {
|
|
72
|
+
const slot = tempSlot("test-harness-no-drift");
|
|
73
|
+
const { store, tool } = await createBridgeMesh(slot, testCoordinatorPort(), undefined, async () => Promise.resolve(undefined));
|
|
74
|
+
try {
|
|
75
|
+
await store.init();
|
|
76
|
+
const agent = await store.registerAgent({
|
|
77
|
+
name: "no-drift-test-agent",
|
|
78
|
+
harness: "test-harness-no-drift",
|
|
79
|
+
cwd: "/tmp/project",
|
|
80
|
+
pid: process.pid,
|
|
81
|
+
visibility: "visible",
|
|
82
|
+
tags: [],
|
|
83
|
+
});
|
|
84
|
+
const ctx = {
|
|
85
|
+
agentId: agent.id,
|
|
86
|
+
harness: "test-harness-no-drift",
|
|
87
|
+
cwd: "/tmp/project",
|
|
88
|
+
pid: process.pid,
|
|
89
|
+
};
|
|
90
|
+
const result = await tool.handle(ctx, buildAction({ action: "whoami" }));
|
|
91
|
+
expect(result.content).toContain(`Version: ${getOwnPackageVersion()}`);
|
|
92
|
+
expect(result.content).not.toContain("Update available");
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
await store.shutdown();
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
//# sourceMappingURL=bridge-mesh-version-drift.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-mesh-version-drift.test.js","sourceRoot":"","sources":["../../src/test/bridge-mesh-version-drift.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability-bubble-up.test.d.ts","sourceRoot":"","sources":["../../src/test/capability-bubble-up.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { webcrypto } from "node:crypto";
|
|
2
|
+
import { describe, it, expect, vi } from "vitest";
|
|
3
|
+
import { createNodeIdentity } from "wire-mesh-core/adapters/node-identity";
|
|
4
|
+
import { deviceIdToHex } from "wire-mesh-core/domain/device-id";
|
|
5
|
+
import { mintCapabilityToken } from "wire-mesh-core/domain/tokens";
|
|
6
|
+
import { createRevocationView } from "wire-mesh-core/domain/revocation-view";
|
|
7
|
+
import { verifyCapabilityToken } from "wire-mesh-core/domain/tokens";
|
|
8
|
+
import { resolveBubbleUpRoute, createBubbleUpCapabilityRequestHandler, } from "../core/capability-bubble-up.js";
|
|
9
|
+
const ES256 = -7;
|
|
10
|
+
const HOUR_MS = 3_600_000;
|
|
11
|
+
const NOW_MS = 1_893_456_000_000;
|
|
12
|
+
const EXPIRES_MS = NOW_MS + HOUR_MS;
|
|
13
|
+
const CAPABILITY = "exec:pty";
|
|
14
|
+
const SCOPE = { kind: "folder" };
|
|
15
|
+
function buf(bytes) {
|
|
16
|
+
return Uint8Array.from(bytes);
|
|
17
|
+
}
|
|
18
|
+
async function generateEs256Identity() {
|
|
19
|
+
const keyPair = await webcrypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"]);
|
|
20
|
+
const publicKeyBytes = new Uint8Array(await webcrypto.subtle.exportKey("raw", keyPair.publicKey));
|
|
21
|
+
return createNodeIdentity(keyPair.privateKey, publicKeyBytes, ES256);
|
|
22
|
+
}
|
|
23
|
+
function fixedClock(atMs) {
|
|
24
|
+
return { now: () => atMs };
|
|
25
|
+
}
|
|
26
|
+
let issuedTokenIds = 0;
|
|
27
|
+
function nextTokenId() {
|
|
28
|
+
issuedTokenIds += 1;
|
|
29
|
+
return buf([issuedTokenIds]);
|
|
30
|
+
}
|
|
31
|
+
async function mint(issuer, bearer, options = {}) {
|
|
32
|
+
const verdict = await mintCapabilityToken({
|
|
33
|
+
identity: issuer,
|
|
34
|
+
clock: fixedClock(options.mintedAt ?? NOW_MS),
|
|
35
|
+
tokenId: nextTokenId(),
|
|
36
|
+
bearer: bearer.deviceId,
|
|
37
|
+
capability: options.capability ?? CAPABILITY,
|
|
38
|
+
scope: options.scope ?? SCOPE,
|
|
39
|
+
expires: options.expires ?? EXPIRES_MS,
|
|
40
|
+
...(options.delegationsRemaining !== undefined
|
|
41
|
+
? { delegationsRemaining: options.delegationsRemaining }
|
|
42
|
+
: {}),
|
|
43
|
+
...(options.parent !== undefined ? { parent: options.parent } : {}),
|
|
44
|
+
});
|
|
45
|
+
if (!verdict.ok)
|
|
46
|
+
throw new Error(`mint failed: ${verdict.reason}`);
|
|
47
|
+
return verdict.token;
|
|
48
|
+
}
|
|
49
|
+
describe("resolveBubbleUpRoute", () => {
|
|
50
|
+
it("grants directly when the held token has depth remaining that narrows the request", async () => {
|
|
51
|
+
const root = await generateEs256Identity();
|
|
52
|
+
const holder = await generateEs256Identity();
|
|
53
|
+
const heldToken = await mint(root, holder, { delegationsRemaining: 2 });
|
|
54
|
+
const route = await resolveBubbleUpRoute({
|
|
55
|
+
heldToken,
|
|
56
|
+
identity: holder,
|
|
57
|
+
clock: fixedClock(NOW_MS),
|
|
58
|
+
revocation: createRevocationView(),
|
|
59
|
+
capability: CAPABILITY,
|
|
60
|
+
scope: SCOPE,
|
|
61
|
+
expires: EXPIRES_MS,
|
|
62
|
+
delegationsRemaining: 1,
|
|
63
|
+
});
|
|
64
|
+
expect(route.kind).toBe("grant");
|
|
65
|
+
if (route.kind === "grant") {
|
|
66
|
+
expect(route.token).toBe(heldToken);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
it("forwards to the held token's own issuer when its delegation depth is exhausted", async () => {
|
|
70
|
+
const root = await generateEs256Identity();
|
|
71
|
+
const holder = await generateEs256Identity();
|
|
72
|
+
const heldToken = await mint(root, holder, { delegationsRemaining: 0 });
|
|
73
|
+
const route = await resolveBubbleUpRoute({
|
|
74
|
+
heldToken,
|
|
75
|
+
identity: holder,
|
|
76
|
+
clock: fixedClock(NOW_MS),
|
|
77
|
+
revocation: createRevocationView(),
|
|
78
|
+
capability: CAPABILITY,
|
|
79
|
+
scope: SCOPE,
|
|
80
|
+
expires: EXPIRES_MS,
|
|
81
|
+
delegationsRemaining: 0,
|
|
82
|
+
});
|
|
83
|
+
expect(route.kind).toBe("forward");
|
|
84
|
+
if (route.kind === "forward") {
|
|
85
|
+
expect(deviceIdToHex(route.target)).toBe(deviceIdToHex(root.deviceId));
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
it("forwards to the held token's own issuer when the requested scope does not narrow", async () => {
|
|
89
|
+
const root = await generateEs256Identity();
|
|
90
|
+
const holder = await generateEs256Identity();
|
|
91
|
+
const heldToken = await mint(root, holder, {
|
|
92
|
+
scope: { kind: "room", path: "abc/general" },
|
|
93
|
+
delegationsRemaining: 3,
|
|
94
|
+
});
|
|
95
|
+
const route = await resolveBubbleUpRoute({
|
|
96
|
+
heldToken,
|
|
97
|
+
identity: holder,
|
|
98
|
+
clock: fixedClock(NOW_MS),
|
|
99
|
+
revocation: createRevocationView(),
|
|
100
|
+
capability: CAPABILITY,
|
|
101
|
+
scope: { kind: "room", path: "abc/other" },
|
|
102
|
+
expires: EXPIRES_MS,
|
|
103
|
+
delegationsRemaining: 1,
|
|
104
|
+
});
|
|
105
|
+
expect(route.kind).toBe("forward");
|
|
106
|
+
if (route.kind === "forward") {
|
|
107
|
+
expect(deviceIdToHex(route.target)).toBe(deviceIdToHex(root.deviceId));
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
it("reports no-route when this device holds no token for the capability at all", async () => {
|
|
111
|
+
const holder = await generateEs256Identity();
|
|
112
|
+
const route = await resolveBubbleUpRoute({
|
|
113
|
+
heldToken: undefined,
|
|
114
|
+
identity: holder,
|
|
115
|
+
clock: fixedClock(NOW_MS),
|
|
116
|
+
revocation: createRevocationView(),
|
|
117
|
+
capability: CAPABILITY,
|
|
118
|
+
scope: SCOPE,
|
|
119
|
+
expires: EXPIRES_MS,
|
|
120
|
+
});
|
|
121
|
+
expect(route).toEqual({ kind: "no-route", reason: "no-held-token" });
|
|
122
|
+
});
|
|
123
|
+
it("reports no-route when the held token has already expired", async () => {
|
|
124
|
+
const root = await generateEs256Identity();
|
|
125
|
+
const holder = await generateEs256Identity();
|
|
126
|
+
const heldToken = await mint(root, holder, {
|
|
127
|
+
mintedAt: NOW_MS - HOUR_MS,
|
|
128
|
+
expires: NOW_MS - 1,
|
|
129
|
+
delegationsRemaining: 2,
|
|
130
|
+
});
|
|
131
|
+
const route = await resolveBubbleUpRoute({
|
|
132
|
+
heldToken,
|
|
133
|
+
identity: holder,
|
|
134
|
+
clock: fixedClock(NOW_MS),
|
|
135
|
+
revocation: createRevocationView(),
|
|
136
|
+
capability: CAPABILITY,
|
|
137
|
+
scope: SCOPE,
|
|
138
|
+
expires: NOW_MS,
|
|
139
|
+
delegationsRemaining: 1,
|
|
140
|
+
});
|
|
141
|
+
expect(route).toEqual({ kind: "no-route", reason: "held-token-invalid" });
|
|
142
|
+
});
|
|
143
|
+
it("reports no-route rather than forwarding to itself when its held token is self-issued", async () => {
|
|
144
|
+
const holder = await generateEs256Identity();
|
|
145
|
+
const heldToken = await mint(holder, holder, { delegationsRemaining: 0 });
|
|
146
|
+
const route = await resolveBubbleUpRoute({
|
|
147
|
+
heldToken,
|
|
148
|
+
identity: holder,
|
|
149
|
+
clock: fixedClock(NOW_MS),
|
|
150
|
+
revocation: createRevocationView(),
|
|
151
|
+
capability: CAPABILITY,
|
|
152
|
+
scope: SCOPE,
|
|
153
|
+
expires: EXPIRES_MS,
|
|
154
|
+
delegationsRemaining: 0,
|
|
155
|
+
});
|
|
156
|
+
expect(route).toEqual({ kind: "no-route", reason: "self-issued" });
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
let nextRequestId = 0;
|
|
160
|
+
function fakeIncoming(params) {
|
|
161
|
+
nextRequestId += 1;
|
|
162
|
+
return {
|
|
163
|
+
requestId: nextRequestId,
|
|
164
|
+
command: { verb: CAPABILITY, params },
|
|
165
|
+
scope: SCOPE,
|
|
166
|
+
respond: vi.fn(async () => undefined),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
describe("createBubbleUpCapabilityRequestHandler", () => {
|
|
170
|
+
it("mints a delegated child token when this device can grant from its own held depth", async () => {
|
|
171
|
+
const root = await generateEs256Identity();
|
|
172
|
+
const holder = await generateEs256Identity();
|
|
173
|
+
const requester = await generateEs256Identity();
|
|
174
|
+
const heldToken = await mint(root, holder, { delegationsRemaining: 2 });
|
|
175
|
+
const sendManageRequest = vi.fn();
|
|
176
|
+
const handler = createBubbleUpCapabilityRequestHandler({
|
|
177
|
+
capability: CAPABILITY,
|
|
178
|
+
identity: holder,
|
|
179
|
+
clock: fixedClock(NOW_MS),
|
|
180
|
+
revocation: createRevocationView(),
|
|
181
|
+
bearerDevice: requester.deviceId,
|
|
182
|
+
expires: EXPIRES_MS,
|
|
183
|
+
delegationsRemaining: 1,
|
|
184
|
+
ownToken: () => heldToken,
|
|
185
|
+
session: { sendManageRequest },
|
|
186
|
+
});
|
|
187
|
+
const incoming = fakeIncoming({
|
|
188
|
+
verb: "capability.request",
|
|
189
|
+
capability: CAPABILITY,
|
|
190
|
+
});
|
|
191
|
+
await handler(incoming);
|
|
192
|
+
expect(sendManageRequest).not.toHaveBeenCalled();
|
|
193
|
+
expect(incoming.respond).toHaveBeenCalledTimes(1);
|
|
194
|
+
const outcome = incoming.respond.mock.calls[0]?.[0];
|
|
195
|
+
expect(outcome.result).toBe("ok");
|
|
196
|
+
const verdict = await verifyCapabilityToken(outcome["granted-token"], {
|
|
197
|
+
identity: holder,
|
|
198
|
+
clock: fixedClock(NOW_MS),
|
|
199
|
+
revocation: createRevocationView(),
|
|
200
|
+
expectedBearer: requester.deviceId,
|
|
201
|
+
});
|
|
202
|
+
expect(verdict.ok).toBe(true);
|
|
203
|
+
if (verdict.ok) {
|
|
204
|
+
expect(deviceIdToHex(verdict.rootIssuer)).toBe(deviceIdToHex(root.deviceId));
|
|
205
|
+
expect(verdict.depth).toBe(1);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
it("responds malformed for a request naming a different capability", async () => {
|
|
209
|
+
const holder = await generateEs256Identity();
|
|
210
|
+
const requester = await generateEs256Identity();
|
|
211
|
+
const handler = createBubbleUpCapabilityRequestHandler({
|
|
212
|
+
capability: CAPABILITY,
|
|
213
|
+
identity: holder,
|
|
214
|
+
clock: fixedClock(NOW_MS),
|
|
215
|
+
revocation: createRevocationView(),
|
|
216
|
+
bearerDevice: requester.deviceId,
|
|
217
|
+
expires: EXPIRES_MS,
|
|
218
|
+
ownToken: () => undefined,
|
|
219
|
+
session: { sendManageRequest: vi.fn() },
|
|
220
|
+
});
|
|
221
|
+
const incoming = fakeIncoming({
|
|
222
|
+
verb: "capability.request",
|
|
223
|
+
capability: "room:member",
|
|
224
|
+
});
|
|
225
|
+
await handler(incoming);
|
|
226
|
+
expect(incoming.respond).toHaveBeenCalledWith({
|
|
227
|
+
result: "error",
|
|
228
|
+
code: "malformed",
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
it("responds expired for an already-stale ask", async () => {
|
|
232
|
+
const holder = await generateEs256Identity();
|
|
233
|
+
const requester = await generateEs256Identity();
|
|
234
|
+
const handler = createBubbleUpCapabilityRequestHandler({
|
|
235
|
+
capability: CAPABILITY,
|
|
236
|
+
identity: holder,
|
|
237
|
+
clock: fixedClock(NOW_MS),
|
|
238
|
+
revocation: createRevocationView(),
|
|
239
|
+
bearerDevice: requester.deviceId,
|
|
240
|
+
expires: EXPIRES_MS,
|
|
241
|
+
ownToken: () => undefined,
|
|
242
|
+
session: { sendManageRequest: vi.fn() },
|
|
243
|
+
});
|
|
244
|
+
const incoming = fakeIncoming({
|
|
245
|
+
verb: "capability.request",
|
|
246
|
+
capability: CAPABILITY,
|
|
247
|
+
"valid-until": NOW_MS - 1,
|
|
248
|
+
});
|
|
249
|
+
await handler(incoming);
|
|
250
|
+
expect(incoming.respond).toHaveBeenCalledWith({
|
|
251
|
+
result: "error",
|
|
252
|
+
code: "expired",
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
it("forwards to the held token's own issuer and re-delegates the returned grant when its own depth is exhausted", async () => {
|
|
256
|
+
const root = await generateEs256Identity();
|
|
257
|
+
const holder = await generateEs256Identity();
|
|
258
|
+
const requester = await generateEs256Identity();
|
|
259
|
+
const heldToken = await mint(root, holder, { delegationsRemaining: 0 });
|
|
260
|
+
const upstreamGrant = await mint(root, holder, { delegationsRemaining: 1 });
|
|
261
|
+
const sendManageRequest = vi.fn(async () => ({
|
|
262
|
+
result: "ok",
|
|
263
|
+
"granted-token": upstreamGrant,
|
|
264
|
+
}));
|
|
265
|
+
const handler = createBubbleUpCapabilityRequestHandler({
|
|
266
|
+
capability: CAPABILITY,
|
|
267
|
+
identity: holder,
|
|
268
|
+
clock: fixedClock(NOW_MS),
|
|
269
|
+
revocation: createRevocationView(),
|
|
270
|
+
bearerDevice: requester.deviceId,
|
|
271
|
+
expires: EXPIRES_MS,
|
|
272
|
+
delegationsRemaining: 0,
|
|
273
|
+
ownToken: () => heldToken,
|
|
274
|
+
session: { sendManageRequest },
|
|
275
|
+
});
|
|
276
|
+
const incoming = fakeIncoming({
|
|
277
|
+
verb: "capability.request",
|
|
278
|
+
capability: CAPABILITY,
|
|
279
|
+
});
|
|
280
|
+
await handler(incoming);
|
|
281
|
+
expect(sendManageRequest).toHaveBeenCalledWith(expect.objectContaining({ verb: CAPABILITY }), SCOPE, root.deviceId, undefined, undefined);
|
|
282
|
+
const outcome = incoming.respond.mock.calls[0]?.[0];
|
|
283
|
+
expect(outcome.result).toBe("ok");
|
|
284
|
+
const verdict = await verifyCapabilityToken(outcome["granted-token"], {
|
|
285
|
+
identity: holder,
|
|
286
|
+
clock: fixedClock(NOW_MS),
|
|
287
|
+
revocation: createRevocationView(),
|
|
288
|
+
expectedBearer: requester.deviceId,
|
|
289
|
+
});
|
|
290
|
+
expect(verdict.ok).toBe(true);
|
|
291
|
+
if (verdict.ok) {
|
|
292
|
+
expect(deviceIdToHex(verdict.rootIssuer)).toBe(deviceIdToHex(root.deviceId));
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
it("relays the upstream denial unchanged when forwarding is refused", async () => {
|
|
296
|
+
const root = await generateEs256Identity();
|
|
297
|
+
const holder = await generateEs256Identity();
|
|
298
|
+
const requester = await generateEs256Identity();
|
|
299
|
+
const heldToken = await mint(root, holder, { delegationsRemaining: 0 });
|
|
300
|
+
const sendManageRequest = vi.fn(async () => ({
|
|
301
|
+
result: "error",
|
|
302
|
+
code: "denied",
|
|
303
|
+
message: "no more depth to give",
|
|
304
|
+
}));
|
|
305
|
+
const handler = createBubbleUpCapabilityRequestHandler({
|
|
306
|
+
capability: CAPABILITY,
|
|
307
|
+
identity: holder,
|
|
308
|
+
clock: fixedClock(NOW_MS),
|
|
309
|
+
revocation: createRevocationView(),
|
|
310
|
+
bearerDevice: requester.deviceId,
|
|
311
|
+
expires: EXPIRES_MS,
|
|
312
|
+
delegationsRemaining: 0,
|
|
313
|
+
ownToken: () => heldToken,
|
|
314
|
+
session: { sendManageRequest },
|
|
315
|
+
});
|
|
316
|
+
const incoming = fakeIncoming({
|
|
317
|
+
verb: "capability.request",
|
|
318
|
+
capability: CAPABILITY,
|
|
319
|
+
});
|
|
320
|
+
await handler(incoming);
|
|
321
|
+
expect(incoming.respond).toHaveBeenCalledWith({
|
|
322
|
+
result: "error",
|
|
323
|
+
code: "denied",
|
|
324
|
+
message: "no more depth to give",
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
it("responds denied when this device holds no token and has nothing to forward from", async () => {
|
|
328
|
+
const holder = await generateEs256Identity();
|
|
329
|
+
const requester = await generateEs256Identity();
|
|
330
|
+
const handler = createBubbleUpCapabilityRequestHandler({
|
|
331
|
+
capability: CAPABILITY,
|
|
332
|
+
identity: holder,
|
|
333
|
+
clock: fixedClock(NOW_MS),
|
|
334
|
+
revocation: createRevocationView(),
|
|
335
|
+
bearerDevice: requester.deviceId,
|
|
336
|
+
expires: EXPIRES_MS,
|
|
337
|
+
ownToken: () => undefined,
|
|
338
|
+
session: { sendManageRequest: vi.fn() },
|
|
339
|
+
});
|
|
340
|
+
const incoming = fakeIncoming({
|
|
341
|
+
verb: "capability.request",
|
|
342
|
+
capability: CAPABILITY,
|
|
343
|
+
});
|
|
344
|
+
await handler(incoming);
|
|
345
|
+
const outcome = incoming.respond.mock.calls[0]?.[0];
|
|
346
|
+
expect(outcome.result).toBe("error");
|
|
347
|
+
expect(outcome.code).toBe("denied");
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
//# sourceMappingURL=capability-bubble-up.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability-bubble-up.test.js","sourceRoot":"","sources":["../../src/test/capability-bubble-up.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the issuer-side delegation policy (core/delegation-policy, agent-comms#163): deciding which capability verbs an issuer never lets a bearer redelegate, and how a caller narrows that decision per bearer.
|
|
3
|
+
*/
|
|
4
|
+
export {};
|
|
5
|
+
//# sourceMappingURL=delegation-policy.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delegation-policy.test.d.ts","sourceRoot":"","sources":["../../src/test/delegation-policy.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|