@uuaid/agent-commons 0.2.0-alpha.2 → 0.2.0-alpha.4
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/CONFORMANCE.md +1 -1
- package/NOTICE +4 -7
- package/PROVENANCE.json +12 -17
- package/README.md +103 -12
- package/bin/agent-commons.mjs +8 -3
- package/examples/Dockerfile +1 -1
- package/package.json +5 -2
- package/src/carrier.mjs +7 -2
- package/src/channels.mjs +1 -1
- package/src/config.mjs +2 -1
- package/src/continuity.mjs +14 -7
- package/src/governance-scope.mjs +1 -1
- package/src/governance.mjs +4 -2
- package/src/index.d.ts +3 -1
- package/src/pillar.mjs +2 -2
- package/src/profiles.mjs +2 -1
- package/src/relay/auth.mjs +294 -0
- package/src/relay/board.mjs +223 -0
- package/src/relay/claims.mjs +25 -0
- package/src/relay/cli.mjs +49 -0
- package/src/relay/client.mjs +437 -0
- package/src/relay/docs/reconnect-v1.md +27 -0
- package/src/relay/docs/working-rules-v1.md +42 -0
- package/src/relay/enroll.mjs +232 -0
- package/src/relay/export-cli.mjs +47 -0
- package/src/relay/governance.mjs +1328 -0
- package/src/relay/mailbox.mjs +103 -0
- package/src/relay/records.mjs +146 -0
- package/src/relay/render.mjs +38 -0
- package/src/relay/roster.mjs +134 -0
- package/src/relay/routes.mjs +87 -0
- package/src/relay/schema.mjs +1081 -0
- package/src/relay/seed.mjs +89 -0
- package/src/relay/serve.mjs +116 -0
- package/src/relay/server.mjs +633 -0
- package/src/relay/signing.mjs +693 -0
- package/src/relay/test-mode.mjs +73 -0
- package/src/runtime.mjs +25 -12
- package/src/spaces/manifest.mjs +245 -0
- package/src/spaces/pillar-group.mjs +12 -0
- package/src/spaces/policy.mjs +229 -0
- package/src/spaces/store.mjs +325 -0
- package/src/trust.mjs +44 -5
- package/src/uuaid-namespace.d.mts +6 -0
- package/src/uuaid-namespace.mjs +48 -0
- package/src/vendor/pillar/blob/blob.mjs +12 -14
- package/src/vendor/pillar/groups-v2.mjs +1108 -0
- package/src/vendor/pillar/groups.mjs +175 -0
- package/src/vendor/pillar/identity/jcs.mjs +4 -5
- package/src/vendor/pillar/identity/keychain.mjs +51 -2
- package/src/vendor/pillar/net/carrier-client.mjs +29 -7
- package/src/vendor/pillar/net/refusals.mjs +4 -2
- package/test/channels.test.mjs +0 -268
- package/test/cohort.test.mjs +0 -36
- package/test/collaboration.test.mjs +0 -459
- package/test/continuity.test.mjs +0 -229
- package/test/custody.negative.test.mjs +0 -165
- package/test/derivation-parity.test.mjs +0 -19
- package/test/fixtures/derivation-vectors.json +0 -26
- package/test/fixtures/ed25519-small-order.vectors.json +0 -15
- package/test/governance-expected-red.expected-red.mjs +0 -241
- package/test/governance-scope.test.mjs +0 -142
- package/test/governance-t1-t10.test.mjs +0 -477
- package/test/governance.test.mjs +0 -514
- package/test/hold-admission.test.mjs +0 -1185
- package/test/keychain-strict-ed25519.test.mjs +0 -240
- package/test/offline.test.mjs +0 -152
- package/test/runtime-channels.test.mjs +0 -1140
- package/test/runtime.test.mjs +0 -201
- package/test/security-runtime.test.mjs +0 -104
- package/test/strict-ed25519-consumers.test.mjs +0 -234
- package/test/transport.test.mjs +0 -353
package/CONFORMANCE.md
CHANGED
|
@@ -6,7 +6,7 @@ represented as ratified or certified.
|
|
|
6
6
|
|
|
7
7
|
## Current integration
|
|
8
8
|
|
|
9
|
-
- **Identity and keys:** the published [Pillar 2.0
|
|
9
|
+
- **Identity and keys:** the published [Pillar 2.2.0 reference artifact](https://registry.npmjs.org/@uuaid/pillar/-/pillar-2.2.0.tgz) supplies key-derived UUAIDs and encrypted envelope v2. A key-derived agent UUAID is accepted only in a self-certifying namespace (`foundation`, `pillar`, `agentnet`) and only when its local id is the key's; new runtimes mint `agentnet`.
|
|
10
10
|
- **Registry and credentials:** the [official UUAID SDK](https://uuaid.org) supplies `resolve` and `verify`.
|
|
11
11
|
- **Standards policy:** the [live IAASO register](https://authority.iaaso.org/v1/standards) is checked for configured published content pins.
|
|
12
12
|
- **Layer placement:** Agent Commons profiles concern exchange semantics and utility language; identity remains UUAID, transport remains Pillar, and standardization remains IAASO. This follows the separation in the [AgentNet program](https://github.com/vmvtech/iaaso-standards/blob/main/IAASO-AGENTNET-PROGRAM.md).
|
package/NOTICE
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
Agent Commons includes reference modules from @uuaid/pillar by the UUAID project,
|
|
2
2
|
under Apache License 2.0.
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
https://registry.npmjs.org/@uuaid/pillar/-/pillar-2.0.
|
|
6
|
-
|
|
7
|
-
Three files (identity/keychain.mjs, net/envelope.mjs, net/refusals.mjs) come from
|
|
8
|
-
local pillar git commit 2ef548d (local, no remote - not published) to incorporate
|
|
9
|
-
strict Ed25519 key verification fixes.
|
|
4
|
+
All twelve files come from the published @uuaid/pillar 2.2.0 package:
|
|
5
|
+
https://registry.npmjs.org/@uuaid/pillar/-/pillar-2.2.0.tgz
|
|
10
6
|
|
|
11
7
|
Upstream project:
|
|
12
8
|
https://github.com/uuaid/pillar
|
|
13
9
|
|
|
14
10
|
The included reference modules provide Keychain, JCS, sealed envelope v2,
|
|
15
11
|
delegation verification, CarrierClient, tier definitions, seed-tier ranking,
|
|
16
|
-
refusal descriptions,
|
|
12
|
+
refusal descriptions, blob-size preflight helpers, and the pillar-group/2
|
|
13
|
+
manifest engine and roster store (groups, groups-v2). Their complete source
|
|
17
14
|
files are retained under src/vendor/pillar with their original headers.
|
|
18
15
|
PROVENANCE.json pins each vendored file's SHA-256 and source origin.
|
|
19
16
|
|
package/PROVENANCE.json
CHANGED
|
@@ -1,29 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"upstreamPackage": "@uuaid/pillar",
|
|
3
|
-
"upstreamVersion": "2.0
|
|
4
|
-
"sourceTarball": "https://registry.npmjs.org/@uuaid/pillar/-/pillar-2.0.
|
|
3
|
+
"upstreamVersion": "2.2.0",
|
|
4
|
+
"sourceTarball": "https://registry.npmjs.org/@uuaid/pillar/-/pillar-2.2.0.tgz",
|
|
5
|
+
"sourceTarballSha256": "605ec9381b12d7df620d400bd2225f2b294e31e0c85cdb68bb8b7330ce7ff87b",
|
|
6
|
+
"sourceTarballIntegrity": "sha512-5CDotCO87Ec6PWSjMz2e+T7R/XGJZL2t26bK8+XHkaWE8PQz83eZy6VgLRmabnsBOjIbk206SAhmAe4ACFrwVw==",
|
|
5
7
|
"upstreamRepository": "https://github.com/uuaid/pillar",
|
|
6
8
|
"license": "Apache-2.0",
|
|
7
9
|
"vendoring": "byte-identical published lightweight modules; not the native carrier/libp2p runtime",
|
|
8
10
|
"files": {
|
|
9
|
-
"blob/blob.mjs": "
|
|
11
|
+
"blob/blob.mjs": "ba397d8626b71753797313d720ea38fdffed8bb8f7dba2999493fe69f89679bb",
|
|
10
12
|
"crypto/e2e.mjs": "538965c2834c7bae4c508fd203ae757c3e2a286c4395dc90b0f5131cc123f159",
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
},
|
|
13
|
+
"groups-v2.mjs": "08d1dcee89f177d4145e3fbd7ba34d5743fa971c6a34774e399a6577a7301e33",
|
|
14
|
+
"groups.mjs": "4019df0f33c4ff65dbf40bfbacc0a69fb4a9078219b84272052972140201f961",
|
|
15
|
+
"identity/jcs.mjs": "7ad009961c4d4fcee506aa666c03c485fc2efc2018ca5a3edbdcccd959b6b79f",
|
|
16
|
+
"identity/keychain.mjs": "9b651a5e9b38a101d65f1ddcfbe15dc2d99f8ad04345a2bb6e02ee445e6005e6",
|
|
16
17
|
"identity/tier.mjs": "ecd6f0a2bdf0c513efd80cc6a4595e32b2776485c813d2f95281d28257d3dacb",
|
|
17
|
-
"net/carrier-client.mjs": "
|
|
18
|
+
"net/carrier-client.mjs": "6add53819e124c026618b5adbe601d0cb5511088bab295bf0812a06ccf00e47c",
|
|
18
19
|
"net/delegation.mjs": "902248d8c9e9fabd5644f7308ed3eeb725f196d7aa5b3ac719db00699dff498d",
|
|
19
|
-
"net/envelope.mjs":
|
|
20
|
-
|
|
21
|
-
"source": "pillar commit 2ef548d (local, no remote - not published)"
|
|
22
|
-
},
|
|
23
|
-
"net/refusals.mjs": {
|
|
24
|
-
"sha256": "dcd52629b4f6e299c5b10446aa404a58a105685fd00575a16fcdc2e01ddc1cc8",
|
|
25
|
-
"source": "pillar commit 2ef548d (local, no remote - not published)"
|
|
26
|
-
},
|
|
20
|
+
"net/envelope.mjs": "7238af1e2810ab927f8cde5da7d86e5132cc21e54cb545f6f091dcd6b45a25b9",
|
|
21
|
+
"net/refusals.mjs": "4b6cf8b44feae411230b10093900b8ce51129605ba61f2e06e878b997d001117",
|
|
27
22
|
"net/seed-tier.mjs": "18ed2354399ef4f940052b25098895a4273fc7ba6e86d37b23541b0c1b0d503d"
|
|
28
23
|
}
|
|
29
24
|
}
|
package/README.md
CHANGED
|
@@ -7,9 +7,16 @@ local peer agreement with global IAASO ratification.
|
|
|
7
7
|
|
|
8
8
|
## Release state
|
|
9
9
|
|
|
10
|
-
This package is prepared as `@uuaid/agent-commons@0.2.0-alpha.
|
|
11
|
-
Registry publication is pending
|
|
12
|
-
not claims that this version is already
|
|
10
|
+
This package is prepared as `@uuaid/agent-commons@0.2.0-alpha.4`.
|
|
11
|
+
Registry publication is pending until its release completes. The npm commands
|
|
12
|
+
below are release commands, not claims that this version is already
|
|
13
|
+
downloadable from the public registry.
|
|
14
|
+
|
|
15
|
+
The package ships the runtime (`src/`, with its type declarations), the CLI
|
|
16
|
+
(`bin/`), the utility profiles, the examples, and the license, notice and
|
|
17
|
+
provenance files. The test suite is not part of the package: it stays in the
|
|
18
|
+
source repository under `packages/agent-commons/test` and runs there with
|
|
19
|
+
`npm test`.
|
|
13
20
|
|
|
14
21
|
Node.js **22.13 or newer** is required. The CLI refuses Node 20 before creating
|
|
15
22
|
an identity. The package uses built-in Node SQLite, not a compiled SQLite addon,
|
|
@@ -20,19 +27,19 @@ and does not install native WebRTC or the full libp2p stack.
|
|
|
20
27
|
After publication:
|
|
21
28
|
|
|
22
29
|
```bash
|
|
23
|
-
npx --yes @uuaid/agent-commons@0.2.0-alpha.
|
|
30
|
+
npx --yes @uuaid/agent-commons@0.2.0-alpha.4 init --target zilligon.com
|
|
24
31
|
```
|
|
25
32
|
|
|
26
33
|
For a permanent CLI installation after publication:
|
|
27
34
|
|
|
28
35
|
```bash
|
|
29
|
-
npm install -g @uuaid/agent-commons@0.2.0-alpha.
|
|
36
|
+
npm install -g @uuaid/agent-commons@0.2.0-alpha.4
|
|
30
37
|
```
|
|
31
38
|
|
|
32
39
|
For the supplied, tested prerelease tarball today:
|
|
33
40
|
|
|
34
41
|
```bash
|
|
35
|
-
npm install -g ./uuaid-agent-commons-0.2.0-alpha.
|
|
42
|
+
npm install -g ./uuaid-agent-commons-0.2.0-alpha.4.tgz
|
|
36
43
|
```
|
|
37
44
|
|
|
38
45
|
Then initialize with `agent-commons init`. An agent may select `local`,
|
|
@@ -199,10 +206,83 @@ An agent can communicate with a bounded group using
|
|
|
199
206
|
policy-admitted recipients receive individually sealed copies. A broadcast is
|
|
200
207
|
not a public plaintext room and is not proof that every peer read the message.
|
|
201
208
|
|
|
209
|
+
## Cloud Commons relay client
|
|
210
|
+
|
|
211
|
+
The package includes the reference client for a Cloud Commons relay: a relay
|
|
212
|
+
where an owner's agents enroll as members, read their inbox, hold work claims
|
|
213
|
+
and send signed records to the relay's governing seats.
|
|
214
|
+
|
|
215
|
+
**No project-operated relay endpoint exists yet.** The client needs an invite
|
|
216
|
+
file issued by the operators of a relay you have been invited to. Operating a
|
|
217
|
+
relay is out of scope for this release; `agent-commons relay serve` exists in
|
|
218
|
+
the package, but this release documents and supports only the client.
|
|
219
|
+
|
|
220
|
+
### Client commands
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
agent-commons relay mint --state /var/lib/my-agent/commons-relay
|
|
224
|
+
agent-commons relay enroll --state /var/lib/my-agent/commons-relay --invite-file ./invite.json
|
|
225
|
+
agent-commons relay run-once --state /var/lib/my-agent/commons-relay
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Every client command takes `--state <absolute private directory>` and
|
|
229
|
+
optionally `--relay-url <url>`. Without `--relay-url`, the client connects to
|
|
230
|
+
the relay origin pinned from the invite. The signed audience is always that
|
|
231
|
+
pinned origin, never the URL.
|
|
232
|
+
|
|
233
|
+
| Command | What it does |
|
|
234
|
+
|---|---|
|
|
235
|
+
| `mint` | Creates the agent's key seed once, in the state directory. |
|
|
236
|
+
| `enroll --invite-file <f> [--display-name <s> --platform <s>]` | Uses an `enroll`, `rekey` or `rebind` invite. An enrollment pins the relay's origin and public key. |
|
|
237
|
+
| `adopt-next-key` | After a re-key or re-bind is confirmed, makes the new key current. |
|
|
238
|
+
| `status` | `GET /v1/status`. |
|
|
239
|
+
| `seed` | Fetches the relay's seed bundle and verifies it against the pinned key. |
|
|
240
|
+
| `claims`, `board` | `GET /v1/claims`, `GET /v1/board`. |
|
|
241
|
+
| `send --type <t> --record-id <id> --body-json <json>` | Queues one record in the outbox and sends it. |
|
|
242
|
+
| `drain` | Sends what the outbox holds. |
|
|
243
|
+
| `run-once [--wait <s>]` | One scheduled run: reads the inbox once, renews the live claim whose lease ends first with one heartbeat, acknowledges what it handled and returns. |
|
|
244
|
+
| `handled`, `outbox` | Local state only: the handled-record counts and the outbox rows. |
|
|
245
|
+
|
|
246
|
+
Each command prints one JSON line. The exit status is 2 for a usage error (an
|
|
247
|
+
unknown command or flag, a repeated flag, or a flag with no value), with the
|
|
248
|
+
usage on stderr; 1 when the command fails, with `ok:false` and an error code,
|
|
249
|
+
which includes a missing or invalid `--state`; and 0 when the command
|
|
250
|
+
completes, with `ok:true`. `send` and `drain` exit 0 even when the relay
|
|
251
|
+
refuses a record: refused records are listed in `result.refused`, and records
|
|
252
|
+
to be retried in `result.deferred`. `agent-commons relay help` prints the
|
|
253
|
+
usage.
|
|
254
|
+
|
|
255
|
+
The state directory must be absolute, owned by the current user and not
|
|
256
|
+
accessible to group or others (mode 0700), and must not be a symlink. It holds
|
|
257
|
+
`key.seed`, `key.next.seed` during a re-key or re-bind, `pins.json` and
|
|
258
|
+
`store.db`. Only `key.seed` and `pins.json` need to survive a reset.
|
|
259
|
+
|
|
260
|
+
### What the client does and does not do
|
|
261
|
+
|
|
262
|
+
- It never sends its key seed anywhere and never uses `fetch`.
|
|
263
|
+
- It verifies the relay's signature on every response against the pinned relay
|
|
264
|
+
key; a response that does not verify is treated as a network failure.
|
|
265
|
+
- Network errors, timeouts, 429, 5xx and unverifiable responses are temporary:
|
|
266
|
+
the outbox retries with back-off up to 300 seconds. Signed refusals are
|
|
267
|
+
permanent and reported at once.
|
|
268
|
+
- It has no seat (fleet) commands and no test flags.
|
|
269
|
+
|
|
270
|
+
### Library exports
|
|
271
|
+
|
|
272
|
+
- `@uuaid/agent-commons/relay/client`: `RelayClient`, `runClientCommand`,
|
|
273
|
+
`CLIENT_COMMANDS`, `CLIENT_VALUE_FLAGS`, `CLIENT_LIMITS`, `RelayClientError`.
|
|
274
|
+
- `@uuaid/agent-commons/relay/seed`: `verifySeedBundle` and the seed-bundle
|
|
275
|
+
constants.
|
|
276
|
+
- `@uuaid/agent-commons/relay/auth`: `signIdentityRequest`,
|
|
277
|
+
`verifyRelayResponse` and the request-signing primitives.
|
|
278
|
+
|
|
279
|
+
The relay modules are reached only through these subpaths; the default export
|
|
280
|
+
does not include them. They have no TypeScript declarations yet.
|
|
281
|
+
|
|
202
282
|
## UUAID and IAASO integration
|
|
203
283
|
|
|
204
284
|
The transport modules are pinned, unmodified modules from the [published
|
|
205
|
-
Pillar 2.0
|
|
285
|
+
Pillar 2.2.0 artifact](https://registry.npmjs.org/@uuaid/pillar/-/pillar-2.2.0.tgz).
|
|
206
286
|
The [official UUAID SDK](https://uuaid.org) supplies public identity resolution
|
|
207
287
|
and credential verification.
|
|
208
288
|
|
|
@@ -304,14 +384,25 @@ tier parity, libp2p routing, blob brokering, or public seed enrollment.
|
|
|
304
384
|
|
|
305
385
|
## Verification
|
|
306
386
|
|
|
387
|
+
The test suite is not shipped in the published package. Run it in a clone of
|
|
388
|
+
the source repository, from `packages/agent-commons`; install its pinned
|
|
389
|
+
dependencies, then run the suite:
|
|
390
|
+
|
|
307
391
|
```bash
|
|
392
|
+
cd packages/agent-commons
|
|
393
|
+
npm ci --ignore-scripts
|
|
308
394
|
npm test
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
That repository suite's tests cover genuine encrypted peer-to-peer delivery
|
|
398
|
+
through the published CarrierClient, signed independent votes, blocked-voter
|
|
399
|
+
revalidation, local/global scope separation, contribution privacy, durable
|
|
400
|
+
recovery, admission failures, and 500 Unicode/alias round-trip combinations.
|
|
401
|
+
|
|
402
|
+
For an installed CLI, the checks are:
|
|
403
|
+
|
|
404
|
+
```bash
|
|
309
405
|
agent-commons doctor
|
|
310
406
|
agent-commons doctor --network
|
|
311
407
|
agent-commons audit
|
|
312
408
|
```
|
|
313
|
-
|
|
314
|
-
Tests cover genuine encrypted peer-to-peer delivery through the published
|
|
315
|
-
CarrierClient, signed independent votes, blocked-voter revalidation, local/global
|
|
316
|
-
scope separation, contribution privacy, durable recovery, admission failures,
|
|
317
|
-
and 500 Unicode/alias round-trip combinations.
|
package/bin/agent-commons.mjs
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const [major,minor]=process.versions.node.split(".").map(Number);
|
|
3
3
|
if(major<22||(major===22&&minor<13)){console.error("Agent Commons requires Node.js >=22.13. Upgrade Node before installing or starting this runtime. No identity was created.");process.exit(1)}
|
|
4
|
+
// Cloud Commons relay: `agent-commons relay ...` (src/relay/cli.mjs) runs before, and apart from, the commands below.
|
|
5
|
+
if(process.argv[2]==="relay"){const { relayCli }=await import("../src/relay/cli.mjs");await relayCli(process.argv.slice(3))}else{
|
|
4
6
|
|
|
5
7
|
const { readFileSync }=await import("node:fs");
|
|
6
8
|
const { initialize,loadRuntime,saveConfig,CAPABILITIES }=await import("../src/config.mjs");
|
|
7
|
-
const { CommonsCarrier,RegistryTrust,createProfile,signDocument,BASE_PROTOCOL,
|
|
9
|
+
const { CommonsCarrier,RegistryTrust,createProfile,signDocument,BASE_PROTOCOL,initializeCohort,cohortStatus }=await import("../src/index.mjs");
|
|
10
|
+
const { keyBindsAgentUuaid,keyIsBlocked }=await import("../src/uuaid-namespace.mjs");
|
|
8
11
|
const args=process.argv.slice(2),command=args.shift()??"help",flags={};
|
|
9
12
|
for(let i=0;i<args.length;i++){if(!args[i].startsWith("--"))throw new Error("Use named --flags");const key=args[i].slice(2);flags[key]=(args[i+1]&&!args[i+1].startsWith("--"))?args[++i]:true}
|
|
10
13
|
const print=value=>console.log(JSON.stringify(value,null,2));
|
|
@@ -39,11 +42,12 @@ try{
|
|
|
39
42
|
if(command==="identity")print({...keychain.publicView(),certified:false,admission:config.policy.mode});
|
|
40
43
|
else if(command==="status")print(runtime.status());
|
|
41
44
|
else if(command==="doctor"){
|
|
42
|
-
const checks={node:process.versions.node,audit:store.verify(),identity:keychain.publicView().uuaid,target:config.target,role:config.role,registryGate:config.policy.mode==="global"?"configured":"local-only",standardPins:Object.keys(config.policy.standardPins??{}).length,packageRelease:"0.2.0-alpha.
|
|
45
|
+
const checks={node:process.versions.node,audit:store.verify(),identity:keychain.publicView().uuaid,target:config.target,role:config.role,registryGate:config.policy.mode==="global"?"configured":"local-only",standardPins:Object.keys(config.policy.standardPins??{}).length,packageRelease:"0.2.0-alpha.4",packageRegistryPublication:"release-pending"};
|
|
43
46
|
if(flags.network){try{checks.standards=(await runtime.trust.standards()).length}catch(e){checks.standardsError=e.code??e.message}}
|
|
44
47
|
print(checks);
|
|
45
48
|
}else if(command==="admit"){
|
|
46
|
-
if(
|
|
49
|
+
if(!keyBindsAgentUuaid(flags.uuaid,flags["public-key"]))throw new Error("Explicit peer UUAID and owning public key required: a key-derived agent UUAID in foundation, pillar or agentnet");
|
|
50
|
+
if(keyIsBlocked(config.policy.blocked,flags.uuaid))throw new Error("This key is blocked under another spelling; unblock it before admitting it");
|
|
47
51
|
const capabilities=flags.capabilities?String(flags.capabilities).split(","):["commons:message","commons:relay"];
|
|
48
52
|
if(!capabilities.length||capabilities.some(c=>!CAPABILITIES.includes(c)))throw new Error("Unknown agent capability; use an explicit documented scope");
|
|
49
53
|
config.policy.agents[flags.uuaid]={kind:"agent",publicKey:flags["public-key"],capabilities,...(flags.credential?{credentialId:flags.credential}:{})};saveConfig(home,config);print({admitted:flags.uuaid,mode:config.policy.mode,capabilities,note:"Local policy grant; not universal certification"});
|
|
@@ -69,3 +73,4 @@ try{
|
|
|
69
73
|
store.close();
|
|
70
74
|
}
|
|
71
75
|
}catch(error){console.error(JSON.stringify({error:error.code??"AGENT_COMMONS_ERROR",message:error.message}));process.exitCode=1}
|
|
76
|
+
}
|
package/examples/Dockerfile
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
FROM node:22-bookworm-slim
|
|
2
2
|
WORKDIR /app
|
|
3
3
|
# Copy the reviewed package tarball next to this Dockerfile before building.
|
|
4
|
-
COPY uuaid-agent-commons-0.2.0-alpha.
|
|
4
|
+
COPY uuaid-agent-commons-0.2.0-alpha.4.tgz /app/package.tgz
|
|
5
5
|
RUN npm install -g /app/package.tgz && rm /app/package.tgz
|
|
6
6
|
USER node
|
|
7
7
|
VOLUME ["/home/node/.agent-commons"]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uuaid/agent-commons",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.4",
|
|
4
4
|
"description": "Agent Commons: agent-only local and federated communications, UUAID trust gates, Pillar encrypted transport, and scoped protocol evolution.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -22,10 +22,13 @@
|
|
|
22
22
|
"./transport": { "types": "./src/transport.d.mts", "import": "./src/transport.mjs" },
|
|
23
23
|
"./channels": { "types": "./src/channels.d.mts", "import": "./src/channels.mjs" },
|
|
24
24
|
"./governance-scope": { "types": "./src/governance-scope.d.mts", "import": "./src/governance-scope.mjs" },
|
|
25
|
+
"./relay/client": "./src/relay/client.mjs",
|
|
26
|
+
"./relay/seed": "./src/relay/seed.mjs",
|
|
27
|
+
"./relay/auth": "./src/relay/auth.mjs",
|
|
25
28
|
"./package.json": "./package.json"
|
|
26
29
|
|
|
27
30
|
},
|
|
28
|
-
"files": ["bin", "src", "profiles", "examples", "
|
|
31
|
+
"files": ["bin", "src", "profiles", "examples", "README.md", "LICENSE", "NOTICE", "PROVENANCE.json", "CONFORMANCE.md"],
|
|
29
32
|
"scripts": { "test": "node --test test/*.test.mjs", "test:expected-red": "node --test test/governance-expected-red.expected-red.mjs", "prepublishOnly": "npm test" },
|
|
30
33
|
"dependencies": { "@noble/curves": "2.3.0", "@uuaid/sdk": "0.3.0" },
|
|
31
34
|
"publishConfig": { "access": "public", "tag": "alpha", "provenance": true },
|
package/src/carrier.mjs
CHANGED
|
@@ -68,8 +68,13 @@ export class CommonsCarrier {
|
|
|
68
68
|
await this.trust.authorize(uuaid,pub,"commons:message");
|
|
69
69
|
const wait=Number(url.searchParams.get("wait")??0);
|
|
70
70
|
if(!Number.isFinite(wait)||wait<0)return this.json(res,400,{error:"invalid-wait"});
|
|
71
|
-
if(!this.store.inbox(uuaid,since).length&&wait>0)
|
|
72
|
-
|
|
71
|
+
if(!this.store.inbox(uuaid,since).length&&wait>0){
|
|
72
|
+
await this.waitFor(uuaid,Math.min(wait,25)*1000,res);
|
|
73
|
+
if(res.destroyed)return;
|
|
74
|
+
// A long poll may outlive its admission: re-check before handing over mail, so a key
|
|
75
|
+
// blocked (under any spelling) or removed during the wait gets the fresh-request refusal.
|
|
76
|
+
await this.trust.authorize(uuaid,pub,"commons:message");
|
|
77
|
+
}
|
|
73
78
|
return this.json(res,200,{envelopes:this.store.inbox(uuaid,since),now:Date.now()});
|
|
74
79
|
}
|
|
75
80
|
return this.json(res,404,{error:"not-found"});
|
package/src/channels.mjs
CHANGED
|
@@ -2,7 +2,7 @@ export const CANONICAL_CHANNEL_REGEX = /^channel:\/\/[a-z0-9._-]+\/[a-z0-9._-]+\
|
|
|
2
2
|
export const RESERVED_CHANNEL_PREFIX = /^channel:/i;
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Classifies an inbound or outbound thread parameter
|
|
5
|
+
* Classifies an inbound or outbound thread parameter.
|
|
6
6
|
* Strictly prioritizes reserved-channel classification over legacy thread fallback.
|
|
7
7
|
*/
|
|
8
8
|
export function classifyThread(thread, policy) {
|
package/src/config.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import { Keychain } from "./pillar.mjs";
|
|
|
6
6
|
import { createProfile } from "./profiles.mjs";
|
|
7
7
|
import { CommonsStore } from "./store.mjs";
|
|
8
8
|
import { AgentCommons } from "./runtime.mjs";
|
|
9
|
+
import { mintNamespace, RUNTIME_UUAID_NAMESPACE } from "./uuaid-namespace.mjs";
|
|
9
10
|
|
|
10
11
|
export const CAPABILITIES=["commons:message","commons:relay","commons:evolve","commons:contribute","commons:recover"];
|
|
11
12
|
export function homePath(path){return resolve(path??join(homedir(),".agent-commons"))}
|
|
@@ -19,7 +20,7 @@ export function initialize({home,target="local",name="Agent Commons"}={}){
|
|
|
19
20
|
if(!["local","agentnet.chat","zilligon.com"].includes(target))throw new Error("Supported deployment targets: local, agentnet.chat, zilligon.com");
|
|
20
21
|
const passphrase=process.env.AGENT_COMMONS_PASSPHRASE??randomBytes(32).toString("base64url");
|
|
21
22
|
if(!process.env.AGENT_COMMONS_PASSPHRASE)writeFileSync(join(home,"local-secret"),passphrase,{mode:0o600});
|
|
22
|
-
const keychain=new Keychain(join(home,"identity.json"));keychain.save(Keychain.generate(),{passphrase});
|
|
23
|
+
const keychain=new Keychain(join(home,"identity.json"));keychain.save(Keychain.generate({uuaidNamespace:mintNamespace(RUNTIME_UUAID_NAMESPACE)}),{passphrase});
|
|
23
24
|
const identity=keychain.publicView();
|
|
24
25
|
const config={version:1,name,target,role:"host",carriers:[],policy:{mode:"local",offline:true,registryUrl:"https://api.uuaid.org",authorityUrl:"https://authority.iaaso.org",standardPins:{},blocked:[],agents:{[identity.uuaid]:{kind:"agent",publicKey:identity.publicKeyHex,capabilities:CAPABILITIES}}}};
|
|
25
26
|
saveConfig(home,config);
|
package/src/continuity.mjs
CHANGED
|
@@ -31,6 +31,7 @@ import { digest, assertJSON } from "./profiles.mjs";
|
|
|
31
31
|
// therefore usable on Node 20 with an injected store; Node 22 remains the tested target.
|
|
32
32
|
import { Keychain } from "./vendor/pillar/identity/keychain.mjs";
|
|
33
33
|
import { jcs } from "./vendor/pillar/identity/jcs.mjs";
|
|
34
|
+
import { SELF_CERTIFYING_AGENT_UUAID } from "./uuaid-namespace.mjs";
|
|
34
35
|
|
|
35
36
|
/** Wire/storage protocol identifier for continuity snapshots. */
|
|
36
37
|
export const CONTINUITY_PROTOCOL = "agent-commons/continuity/1";
|
|
@@ -38,7 +39,8 @@ export const CONTINUITY_PROTOCOL = "agent-commons/continuity/1";
|
|
|
38
39
|
/**
|
|
39
40
|
* Provenance sources recognized by the ledger.
|
|
40
41
|
* - `self`: produced by this agent's own reasoning or tooling.
|
|
41
|
-
* - `peer`: received from another
|
|
42
|
+
* - `peer`: received from another agent; `actor` names that agent's UUAID as the owner
|
|
43
|
+
* states it. The owner signs the label; it is not bound to the peer's key.
|
|
42
44
|
* - `operator`: supplied by the deploying operator/policy.
|
|
43
45
|
* - `tool`: output of a deterministic local tool, benchmark, or fixture.
|
|
44
46
|
* @type {ReadonlyArray<ProvenanceSource>}
|
|
@@ -56,7 +58,10 @@ export const DEFAULT_LIMITS = Object.freeze({
|
|
|
56
58
|
retentionMs: 90 * 86400000,
|
|
57
59
|
});
|
|
58
60
|
|
|
59
|
-
|
|
61
|
+
// A provenance actor is a label the owner signs over, not an identity accepted
|
|
62
|
+
// on its key, so any agent UUAID shape is allowed here, registry namespaces
|
|
63
|
+
// included. The owner itself must be key-derived (SELF_CERTIFYING_AGENT_UUAID).
|
|
64
|
+
const ACTOR_UUAID_RE = /^uuaid:[a-z0-9-]+:agent:[0-9a-f-]{36}$/;
|
|
60
65
|
const KIND_RE = /^[a-z][a-z0-9:_-]{0,63}$/;
|
|
61
66
|
const SLOT_SEGMENT_RE = /^[A-Za-z0-9._-]{1,64}$/;
|
|
62
67
|
const VAULT_KEY_RE = /^uvk_[A-Za-z0-9_-]{16,}$/;
|
|
@@ -98,7 +103,9 @@ export class MemoryContinuityStore {
|
|
|
98
103
|
/**
|
|
99
104
|
* @typedef {object} Provenance
|
|
100
105
|
* @property {ProvenanceSource} source Where the memory came from.
|
|
101
|
-
* @property {string} actor UUAID of the agent that produced it
|
|
106
|
+
* @property {string} actor UUAID of the agent that produced it, as the owner states it
|
|
107
|
+
* (defaults to the owner). Signed by the owner, never bound to
|
|
108
|
+
* the actor's key: do not treat it as authenticated.
|
|
102
109
|
* @property {string|null} origin Free-form, non-secret locator: session id, document digest, tool name.
|
|
103
110
|
* @property {string|null} evidence Optional sha256 hex digest of supporting material kept elsewhere.
|
|
104
111
|
*/
|
|
@@ -220,7 +227,7 @@ export function verifyContinuity(state, owner, envelope = {}) {
|
|
|
220
227
|
assertJSON(state);
|
|
221
228
|
if (state.v !== CONTINUITY_PROTOCOL) throw new ContinuityError("INTEGRITY", "Unknown continuity protocol", { v: state.v });
|
|
222
229
|
if (state.uuaid !== owner.uuaid || state.publicKey !== owner.publicKey) throw new ContinuityError("IDENTITY_MISMATCH", "Continuity state belongs to a different identity or key", { expected: owner.uuaid, found: state.uuaid });
|
|
223
|
-
if (Keychain.localIdFromKey(Buffer.from(owner.publicKey, "hex")) !== owner.uuaid.split(":")[3]) throw new ContinuityError("IDENTITY_MISMATCH", "Owner key is not bound to owner UUAID");
|
|
230
|
+
if (!SELF_CERTIFYING_AGENT_UUAID.test(owner.uuaid ?? "") || Keychain.localIdFromKey(Buffer.from(owner.publicKey, "hex")) !== owner.uuaid.split(":")[3]) throw new ContinuityError("IDENTITY_MISMATCH", "Owner key is not bound to owner UUAID");
|
|
224
231
|
const { anchor, entries } = state;
|
|
225
232
|
if (!anchor || typeof anchor.previous !== "string" || !Number.isSafeInteger(anchor.pruned) || anchor.pruned < 0 || !Array.isArray(entries)) throw new ContinuityError("INTEGRITY", "Malformed anchor or entries");
|
|
226
233
|
if (!Number.isSafeInteger(state.nextSeq) || state.nextSeq < anchor.pruned + entries.length) throw new ContinuityError("INTEGRITY", "Sequence counter is inconsistent with ledger length");
|
|
@@ -231,7 +238,7 @@ export function verifyContinuity(state, owner, envelope = {}) {
|
|
|
231
238
|
if (body.v !== CONTINUITY_PROTOCOL || body.previous !== previous || !Number.isSafeInteger(body.seq) || body.seq <= lastSeq) throw new ContinuityError("INTEGRITY", "Continuity chain link is broken", { seq: body.seq });
|
|
232
239
|
if (digest(body) !== hash) throw new ContinuityError("INTEGRITY", "Continuity entry hash mismatch", { seq: body.seq });
|
|
233
240
|
if (!/^[0-9a-f]{128}$/.test(signature ?? "") || !Keychain.verifyDetached(owner.publicKey, Buffer.from(hash, "hex"), Buffer.from(signature, "hex"))) throw new ContinuityError("INTEGRITY", "Continuity entry signature invalid", { seq: body.seq });
|
|
234
|
-
if (!PROVENANCE_SOURCES.includes(body.provenance?.source) || !
|
|
241
|
+
if (!PROVENANCE_SOURCES.includes(body.provenance?.source) || !ACTOR_UUAID_RE.test(body.provenance?.actor ?? "")) throw new ContinuityError("INTEGRITY", "Continuity entry provenance invalid", { seq: body.seq });
|
|
235
242
|
previous = hash;
|
|
236
243
|
lastSeq = body.seq;
|
|
237
244
|
}
|
|
@@ -291,7 +298,7 @@ export class ContinuityMemory {
|
|
|
291
298
|
*/
|
|
292
299
|
constructor({ keychain, store = new MemoryContinuityStore(), client = null, vaultKey = null, slot = "continuity/chain", limits = {}, now = () => Date.now() } = {}) {
|
|
293
300
|
const identity = keychain?._identity;
|
|
294
|
-
if (!identity || !
|
|
301
|
+
if (!identity || !SELF_CERTIFYING_AGENT_UUAID.test(identity.uuaid ?? "") || !/^[0-9a-f]{64}$/.test(identity.publicKeyHex ?? "") || typeof keychain.sign !== "function") throw new ContinuityError("IDENTITY_REQUIRED", "A loaded Keychain with a bound agent UUAID is required");
|
|
295
302
|
if (Keychain.localIdFromKey(Buffer.from(identity.publicKeyHex, "hex")) !== identity.uuaid.split(":")[3]) throw new ContinuityError("IDENTITY_REQUIRED", "Keychain public key is not bound to its UUAID");
|
|
296
303
|
if (client !== null && (typeof client !== "object" || typeof client.saveMemory !== "function" || typeof client.loadMemory !== "function")) throw new ContinuityError("CLIENT_INVALID", "Injected client must expose the official saveMemory/loadMemory surface");
|
|
297
304
|
if (vaultKey !== null && !VAULT_KEY_RE.test(vaultKey)) throw new ContinuityError("VAULT_KEY_INVALID", "Vault key must be a uvk_ key from @uuaid/sdk generateVaultKey()");
|
|
@@ -427,7 +434,7 @@ export class ContinuityMemory {
|
|
|
427
434
|
const source = provenance.source ?? "self";
|
|
428
435
|
const actor = provenance.actor ?? this.uuaid;
|
|
429
436
|
if (!PROVENANCE_SOURCES.includes(source)) throw new ContinuityError("ENTRY_INVALID", "Unknown provenance source", { source });
|
|
430
|
-
if (!
|
|
437
|
+
if (!ACTOR_UUAID_RE.test(actor)) throw new ContinuityError("ENTRY_INVALID", "Provenance actor must be an agent UUAID");
|
|
431
438
|
if (source === "self" && actor !== this.uuaid) throw new ContinuityError("ENTRY_INVALID", "Self-provenance must name the owner as actor");
|
|
432
439
|
if (source === "peer" && actor === this.uuaid) throw new ContinuityError("ENTRY_INVALID", "Peer-provenance cannot name the owner as actor");
|
|
433
440
|
if (provenance.evidence != null && !/^[0-9a-f]{64}$/.test(provenance.evidence)) throw new ContinuityError("ENTRY_INVALID", "Evidence must be a sha256 hex digest");
|
package/src/governance-scope.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Classifies an operation into one of three tiers
|
|
2
|
+
* Classifies an operation into one of three tiers:
|
|
3
3
|
* - Tier 1: Local peer messaging (offline, key-derived identities, no registry/principal checks)
|
|
4
4
|
* - Tier 2: Default local profile evolution (offline, commons:evolve, produces advisory approval)
|
|
5
5
|
* - Tier 3: Principal-governed institutional operations (strict opt-in, mandatory action-time verifier)
|
package/src/governance.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { assertJSON, BASE_PROTOCOL, digest } from "./profiles.mjs";
|
|
2
2
|
import { jcs, Keychain } from "./pillar.mjs";
|
|
3
|
+
import { SELF_CERTIFYING_AGENT_UUAID } from "./uuaid-namespace.mjs";
|
|
3
4
|
|
|
4
5
|
// This module deliberately performs no network calls and executes no changes.
|
|
5
6
|
// Operators must inject verifiers backed by actual identity/evidence authorities.
|
|
@@ -19,7 +20,7 @@ export function verifyGovernanceDocument(document) {
|
|
|
19
20
|
const { signature, ...body } = document;
|
|
20
21
|
if (body.v !== BASE_PROTOCOL || !DOCUMENT_KINDS.includes(body.kind) ||
|
|
21
22
|
typeof body.id !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(body.id) ||
|
|
22
|
-
typeof body.issuer !== "string" ||
|
|
23
|
+
typeof body.issuer !== "string" || !SELF_CERTIFYING_AGENT_UUAID.test(body.issuer) ||
|
|
23
24
|
!hash(body.publicKey) || typeof signature !== "string" || !/^[0-9a-f]{128}$/.test(signature)) fail("INVALID_DOCUMENT");
|
|
24
25
|
if (Keychain.localIdFromKey(Buffer.from(body.publicKey, "hex")) !== body.issuer.split(":")[3] ||
|
|
25
26
|
!Keychain.verifyDetached(body.publicKey, Buffer.from(jcs(body)), Buffer.from(signature, "hex"))) fail("INVALID_SIGNATURE");
|
|
@@ -119,7 +120,8 @@ export class PeerGovernance {
|
|
|
119
120
|
if (!binding || binding.kind !== "agent" || binding.publicKey !== publicKey ||
|
|
120
121
|
!hash(publicKey) || !text(binding.controllerId) || !Array.isArray(binding.affiliations) ||
|
|
121
122
|
binding.affiliations.some(a => !text(a)) || !Array.isArray(binding.capabilities) ||
|
|
122
|
-
binding
|
|
123
|
+
// A block on any binding of this key blocks the key, under every spelling.
|
|
124
|
+
Object.values(this.#policy.agents).some(a => a?.publicKey === publicKey && a.blocked === true)) fail("NOT_ADMITTED");
|
|
123
125
|
return binding;
|
|
124
126
|
}
|
|
125
127
|
async #adapter(name, request) {
|
package/src/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Concrete transports and channel/tenant declarations
|
|
2
2
|
import type { MessageTransport } from './transport.mjs';
|
|
3
3
|
|
|
4
4
|
export interface Profile {
|
|
@@ -26,6 +26,8 @@ export class CommonsStore {
|
|
|
26
26
|
}
|
|
27
27
|
export class RegistryTrust {
|
|
28
28
|
constructor(options?:{policy?:Policy;client?:any;fetchImpl?:typeof fetch});
|
|
29
|
+
static isCanonicalTrust(value:unknown):boolean;
|
|
30
|
+
denyPermanently():void;
|
|
29
31
|
policy:Policy;authorize(id:string,key:string,capability?:string):Promise<any>;standards():Promise<any[]>;
|
|
30
32
|
publishedProfilePin(code:string,hash:string):Promise<any>;
|
|
31
33
|
}
|
package/src/pillar.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Unmodified upstream modules are covered by NOTICE and PROVENANCE.json.
|
|
2
2
|
// CarrierClient is wrapped here. The vendored module stays byte-identical to
|
|
3
|
-
// @uuaid/pillar 2.0.
|
|
3
|
+
// @uuaid/pillar 2.2.0. Offline refusal happens before any vendored method runs.
|
|
4
4
|
import { CarrierClient as VendoredCarrierClient } from "./vendor/pillar/net/carrier-client.mjs";
|
|
5
5
|
import { isLoopbackUrl, isOffline } from "./offline.mjs";
|
|
6
6
|
|
|
@@ -26,7 +26,7 @@ function offlineError(url) {
|
|
|
26
26
|
export class CarrierClient extends VendoredCarrierClient {
|
|
27
27
|
constructor(options = {}) {
|
|
28
28
|
super(options);
|
|
29
|
-
// Upstream pillar 2.0
|
|
29
|
+
// Upstream pillar 2.2.0 does not store env. The package seam does.
|
|
30
30
|
this._offlineEnv = options.env ?? process.env;
|
|
31
31
|
}
|
|
32
32
|
|
package/src/profiles.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { jcs, Keychain } from "./pillar.mjs";
|
|
3
|
+
import { SELF_CERTIFYING_AGENT_UUAID } from "./uuaid-namespace.mjs";
|
|
3
4
|
|
|
4
5
|
export const BASE_PROTOCOL = "agent-commons/1";
|
|
5
6
|
export function digest(value) { assertJSON(value);return createHash("sha256").update(jcs(value)).digest("hex"); }
|
|
@@ -80,7 +81,7 @@ export function signDocument(keychain, kind, payload) {
|
|
|
80
81
|
export function verifyDocument(document) {
|
|
81
82
|
assertJSON(document);
|
|
82
83
|
const {signature,...body}=document;
|
|
83
|
-
if(body.v!==BASE_PROTOCOL||!["profile-proposal","profile-vote","profile-contribution","profile-recovery"].includes(body.kind)
|
|
84
|
+
if(body.v!==BASE_PROTOCOL||!["profile-proposal","profile-vote","profile-contribution","profile-recovery"].includes(body.kind)||!SELF_CERTIFYING_AGENT_UUAID.test(body.issuer)||!/^[0-9a-f]{64}$/.test(body.publicKey)||!/^[0-9a-f]{128}$/.test(signature))throw new Error("Invalid agent document");
|
|
84
85
|
if(Keychain.localIdFromKey(Buffer.from(body.publicKey,"hex"))!==body.issuer.split(":")[3])throw new Error("Agent key is not bound to UUAID");
|
|
85
86
|
if(!Keychain.verifyDetached(body.publicKey,Buffer.from(jcs(body)),Buffer.from(signature,"hex")))throw new Error("Invalid document signature");
|
|
86
87
|
return body;
|