@byollm/conformance 0.1.0-alpha.8 → 0.1.0-alpha.80
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/README.md +107 -5
- package/dist/audit-cli.d.ts +1 -0
- package/dist/audit-cli.js +25 -0
- package/dist/audit-cli.js.map +1 -0
- package/dist/chunk-2EJHYOQS.js +469 -0
- package/dist/chunk-2EJHYOQS.js.map +1 -0
- package/dist/{chunk-EXNALQ5D.js → chunk-MSM6QP2N.js} +439 -185
- package/dist/chunk-MSM6QP2N.js.map +1 -0
- package/dist/cli.js +2 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +116 -7
- package/dist/index.js +9 -1
- package/package.json +8 -6
- package/dist/chunk-EXNALQ5D.js.map +0 -1
|
@@ -29,7 +29,6 @@ import {
|
|
|
29
29
|
signRequest
|
|
30
30
|
} from "@byollm/protocol";
|
|
31
31
|
import {
|
|
32
|
-
Allowlist,
|
|
33
32
|
Budgets,
|
|
34
33
|
IngressLog,
|
|
35
34
|
SpendLedger,
|
|
@@ -76,7 +75,6 @@ var EchoBackend = class {
|
|
|
76
75
|
ok: false,
|
|
77
76
|
code: "canceled",
|
|
78
77
|
message: "the job was canceled",
|
|
79
|
-
retryable: false,
|
|
80
78
|
durationMs: Date.now() - started
|
|
81
79
|
};
|
|
82
80
|
}
|
|
@@ -94,9 +92,11 @@ function daemonConfig(options) {
|
|
|
94
92
|
const baseUrl = metered ? metered.baseUrl : options.subscription ? void 0 : "http://127.0.0.1:11434/v1";
|
|
95
93
|
return resolveConfig(
|
|
96
94
|
DaemonConfig.parse({
|
|
97
|
-
|
|
95
|
+
services: {
|
|
98
96
|
primary: {
|
|
99
|
-
|
|
97
|
+
model: "echo-model",
|
|
98
|
+
kinds: ["llm.generate", "llm.chat"],
|
|
99
|
+
type: backendId,
|
|
100
100
|
...baseUrl === void 0 ? {} : { baseUrl },
|
|
101
101
|
offer: options.offer,
|
|
102
102
|
...metered === void 0 ? {} : {
|
|
@@ -107,10 +107,6 @@ function daemonConfig(options) {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
},
|
|
110
|
-
routes: {
|
|
111
|
-
"llm.generate": { backend: "primary", model: "echo-model" },
|
|
112
|
-
"llm.chat": { backend: "primary", model: "echo-model" }
|
|
113
|
-
},
|
|
114
110
|
concurrency: 4
|
|
115
111
|
})
|
|
116
112
|
);
|
|
@@ -118,12 +114,10 @@ function daemonConfig(options) {
|
|
|
118
114
|
async function pairDaemon(target, options) {
|
|
119
115
|
const home = await mkdtemp(join(tmpdir(), "byollm-conformance-"));
|
|
120
116
|
const loaded = daemonConfig({
|
|
121
|
-
offer: options.offer
|
|
117
|
+
offer: options.offer,
|
|
122
118
|
subscription: options.subscription ?? false,
|
|
123
119
|
...options.metered === void 0 ? {} : { metered: options.metered }
|
|
124
120
|
});
|
|
125
|
-
const allowlist = new Allowlist(join(home, "allow.json"));
|
|
126
|
-
await allowlist.load();
|
|
127
121
|
const budgets = new Budgets(
|
|
128
122
|
join(home, "budgets.json"),
|
|
129
123
|
loaded.config.community
|
|
@@ -140,6 +134,7 @@ async function pairDaemon(target, options) {
|
|
|
140
134
|
const fetchImpl = (input, init) => target.fetch(new Request(input, init));
|
|
141
135
|
const capabilities = loaded.routes.map((route) => ({
|
|
142
136
|
kind: route.kind,
|
|
137
|
+
service: route.service,
|
|
143
138
|
backendId: route.backendId,
|
|
144
139
|
backendClass: route.backendClass,
|
|
145
140
|
model: route.model,
|
|
@@ -203,12 +198,13 @@ async function pairDaemon(target, options) {
|
|
|
203
198
|
owner: result.pairing.owner,
|
|
204
199
|
identity: {
|
|
205
200
|
keys: () => deviceIdentity.load(Date.now()),
|
|
206
|
-
// Pinned at pairing, exactly as a real daemon does
|
|
207
|
-
|
|
201
|
+
// Pinned at pairing, exactly as a real daemon does — the set the
|
|
202
|
+
// upstream answered with, keyed by each site's identity key id
|
|
203
|
+
// (cloud_009 §5). A direct site is one entry.
|
|
204
|
+
sites: new Map(Object.entries(result.pairing.sites))
|
|
208
205
|
},
|
|
209
206
|
daemonVersion: "conformance",
|
|
210
207
|
loaded,
|
|
211
|
-
allowlist,
|
|
212
208
|
budgets,
|
|
213
209
|
spend,
|
|
214
210
|
ingress,
|
|
@@ -217,13 +213,11 @@ async function pairDaemon(target, options) {
|
|
|
217
213
|
return {
|
|
218
214
|
runner,
|
|
219
215
|
backend,
|
|
220
|
-
allowlist,
|
|
221
216
|
runnerId: result.pairing.runnerId,
|
|
222
217
|
owner: result.pairing.owner,
|
|
223
|
-
token: result.pairing.token,
|
|
224
218
|
keys: await deviceIdentity.load(Date.now()),
|
|
225
219
|
identityKeys: () => deviceIdentity.load(Date.now()),
|
|
226
|
-
sitePinned: result.pairing.
|
|
220
|
+
sitePinned: Object.values(result.pairing.sites)[0],
|
|
227
221
|
home,
|
|
228
222
|
ingress,
|
|
229
223
|
spend,
|
|
@@ -309,6 +303,37 @@ async function claimOne(target, daemon) {
|
|
|
309
303
|
if (!job) throw new Error("claim returned no jobs");
|
|
310
304
|
return job;
|
|
311
305
|
}
|
|
306
|
+
async function claimRaw(target, daemon, capabilityOverride) {
|
|
307
|
+
const capabilities = capabilityOverride ?? await daemon.runner.detectCapabilities();
|
|
308
|
+
const body = JSON.stringify({
|
|
309
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
310
|
+
runnerId: daemon.runnerId,
|
|
311
|
+
capabilities,
|
|
312
|
+
max: 10
|
|
313
|
+
});
|
|
314
|
+
const signature = signRequest(daemon.keys, {
|
|
315
|
+
endpoint: "claim",
|
|
316
|
+
runnerId: daemon.runnerId,
|
|
317
|
+
issuedAt: Date.now(),
|
|
318
|
+
body
|
|
319
|
+
});
|
|
320
|
+
const response = await target.fetch(
|
|
321
|
+
new Request(`${target.origin}/byollm/claim`, {
|
|
322
|
+
method: "POST",
|
|
323
|
+
headers: {
|
|
324
|
+
"content-type": "application/json",
|
|
325
|
+
"x-byollm-runner": signature.runnerId,
|
|
326
|
+
"x-byollm-issued-at": String(signature.issuedAt),
|
|
327
|
+
"x-byollm-signature": signature.signature
|
|
328
|
+
},
|
|
329
|
+
body
|
|
330
|
+
})
|
|
331
|
+
);
|
|
332
|
+
if (response.status !== 200) {
|
|
333
|
+
throw new Error(`claim answered ${String(response.status)}`);
|
|
334
|
+
}
|
|
335
|
+
return (await response.json()).jobs;
|
|
336
|
+
}
|
|
312
337
|
async function releaseLease(target, daemon, jobId, leaseId) {
|
|
313
338
|
const body = JSON.stringify({
|
|
314
339
|
protocolVersion: PROTOCOL_VERSION,
|
|
@@ -383,7 +408,11 @@ async function postResult(target, daemon, input) {
|
|
|
383
408
|
const keys = await daemon.identityKeys();
|
|
384
409
|
const sealer = input.sealWith ?? keys;
|
|
385
410
|
const envelope = await seal({
|
|
386
|
-
|
|
411
|
+
// `{ outcome, ran }` — cloud_008 §2.5.
|
|
412
|
+
plaintext: JSON.stringify({
|
|
413
|
+
outcome: input.outcome,
|
|
414
|
+
ran: { model: "test-model", backendClass: "http", durationMs: 1 }
|
|
415
|
+
}),
|
|
387
416
|
senderKeys: sealer,
|
|
388
417
|
recipientEncryptionPublic: daemon.sitePinned.encryption,
|
|
389
418
|
context: {
|
|
@@ -401,11 +430,9 @@ async function postResult(target, daemon, input) {
|
|
|
401
430
|
protocolVersion: PROTOCOL_VERSION,
|
|
402
431
|
runnerId: daemon.runnerId,
|
|
403
432
|
jobId: input.jobId,
|
|
433
|
+
leaseId: input.leaseId,
|
|
404
434
|
envelope,
|
|
405
|
-
disposition: input.disposition ?? input.outcome.outcome
|
|
406
|
-
model: "conformance-model",
|
|
407
|
-
backendClass: "http",
|
|
408
|
-
durationMs: 1
|
|
435
|
+
disposition: input.disposition ?? input.outcome.outcome
|
|
409
436
|
});
|
|
410
437
|
const signature = signRequest(daemon.keys, {
|
|
411
438
|
endpoint: "result",
|
|
@@ -445,7 +472,7 @@ async function fetchGenuine(target, daemon, owner = "alice") {
|
|
|
445
472
|
// The target's own name for the user, not the id it mapped that to —
|
|
446
473
|
// passing a mapped id back in addresses a user the target never made.
|
|
447
474
|
owner,
|
|
448
|
-
audience: "
|
|
475
|
+
audience: "private"
|
|
449
476
|
});
|
|
450
477
|
for (let attempt = 0; attempt < 20; attempt += 1) {
|
|
451
478
|
try {
|
|
@@ -475,13 +502,19 @@ var CHECKS = [
|
|
|
475
502
|
title: "a runner token is bound to exactly the approving user",
|
|
476
503
|
musts: ["PAIR_ONE_USER", "PAIR_INTERACTIVE"],
|
|
477
504
|
async run(target) {
|
|
478
|
-
const alice = await pairDaemon(target, {
|
|
505
|
+
const alice = await pairDaemon(target, {
|
|
506
|
+
owner: "alice",
|
|
507
|
+
offer: "private"
|
|
508
|
+
});
|
|
479
509
|
try {
|
|
480
510
|
assert(
|
|
481
511
|
alice.owner === await ownerIdFor(target, "alice"),
|
|
482
512
|
`runner was bound to "${alice.owner}", not to the approving user`
|
|
483
513
|
);
|
|
484
|
-
const bob = await pairDaemon(target, {
|
|
514
|
+
const bob = await pairDaemon(target, {
|
|
515
|
+
owner: "bob",
|
|
516
|
+
offer: "private"
|
|
517
|
+
});
|
|
485
518
|
assert(
|
|
486
519
|
bob.owner !== alice.owner,
|
|
487
520
|
"two different approvers produced the same runner owner"
|
|
@@ -491,7 +524,7 @@ var CHECKS = [
|
|
|
491
524
|
kind: "llm.generate",
|
|
492
525
|
payload: prompt("alice's private prompt"),
|
|
493
526
|
owner: "alice",
|
|
494
|
-
audience: "
|
|
527
|
+
audience: "private"
|
|
495
528
|
});
|
|
496
529
|
await bob.runner.tick();
|
|
497
530
|
await sleep(50);
|
|
@@ -513,7 +546,10 @@ var CHECKS = [
|
|
|
513
546
|
title: "an enqueued job runs on the owner's daemon and the result comes back",
|
|
514
547
|
musts: ["CLAIM_REQUIRES_CAPABILITY", "RESULT_IDEMPOTENT"],
|
|
515
548
|
async run(target) {
|
|
516
|
-
const daemon = await pairDaemon(target, {
|
|
549
|
+
const daemon = await pairDaemon(target, {
|
|
550
|
+
owner: "alice",
|
|
551
|
+
offer: "private"
|
|
552
|
+
});
|
|
517
553
|
try {
|
|
518
554
|
const job = await target.enqueue({
|
|
519
555
|
kind: "llm.generate",
|
|
@@ -543,7 +579,10 @@ var CHECKS = [
|
|
|
543
579
|
title: "a daemon is never handed a kind it did not advertise",
|
|
544
580
|
musts: ["KIND_TYPED_ONLY", "CLAIM_REQUIRES_CAPABILITY"],
|
|
545
581
|
async run(target) {
|
|
546
|
-
const daemon = await pairDaemon(target, {
|
|
582
|
+
const daemon = await pairDaemon(target, {
|
|
583
|
+
owner: "alice",
|
|
584
|
+
offer: "private"
|
|
585
|
+
});
|
|
547
586
|
try {
|
|
548
587
|
const job = await target.enqueue({
|
|
549
588
|
kind: "llm.chat",
|
|
@@ -562,6 +601,25 @@ var CHECKS = [
|
|
|
562
601
|
daemon.backend.seen.length > before,
|
|
563
602
|
"the advertised kind never reached the backend"
|
|
564
603
|
);
|
|
604
|
+
const chat = await target.enqueue({
|
|
605
|
+
kind: "llm.chat",
|
|
606
|
+
payload: { messages: [{ role: "user", content: "not for you" }] },
|
|
607
|
+
owner: "alice"
|
|
608
|
+
});
|
|
609
|
+
const generateOnly = await claimRaw(target, daemon, [
|
|
610
|
+
{
|
|
611
|
+
kind: "llm.generate",
|
|
612
|
+
service: "local",
|
|
613
|
+
backendId: "openai-http",
|
|
614
|
+
backendClass: "http",
|
|
615
|
+
model: "echo-model",
|
|
616
|
+
offerScope: "private"
|
|
617
|
+
}
|
|
618
|
+
]);
|
|
619
|
+
assert(
|
|
620
|
+
!generateOnly.some((offered) => offered.id === chat.id),
|
|
621
|
+
"a server offered `llm.chat` to a claim advertising only `llm.generate`"
|
|
622
|
+
);
|
|
565
623
|
} finally {
|
|
566
624
|
await daemon.dispose();
|
|
567
625
|
}
|
|
@@ -572,14 +630,18 @@ var CHECKS = [
|
|
|
572
630
|
title: "a job whose runner vanished is offered again, losing nothing",
|
|
573
631
|
musts: ["LEASE_RECLAIMABLE", "LEASE_HONORED"],
|
|
574
632
|
async run(target) {
|
|
575
|
-
const dead = await pairDaemon(target, {
|
|
633
|
+
const dead = await pairDaemon(target, {
|
|
634
|
+
owner: "alice",
|
|
635
|
+
label: "dead",
|
|
636
|
+
offer: "private"
|
|
637
|
+
});
|
|
576
638
|
const job = await target.enqueue({
|
|
577
639
|
kind: "llm.generate",
|
|
578
640
|
payload: prompt("work"),
|
|
579
641
|
owner: "alice"
|
|
580
642
|
});
|
|
581
643
|
dead.backend.hangMs = 6e4;
|
|
582
|
-
await dead
|
|
644
|
+
const firstLease = await claimOne(target, dead);
|
|
583
645
|
await waitFor(
|
|
584
646
|
async () => {
|
|
585
647
|
const state = await target.job(job.id);
|
|
@@ -591,35 +653,61 @@ var CHECKS = [
|
|
|
591
653
|
await advance(target, target.leaseMs + 500);
|
|
592
654
|
const alive = await pairDaemon(target, {
|
|
593
655
|
owner: "alice",
|
|
594
|
-
label: "alive"
|
|
656
|
+
label: "alive",
|
|
657
|
+
offer: "private"
|
|
595
658
|
});
|
|
596
659
|
try {
|
|
597
|
-
await alive
|
|
598
|
-
|
|
599
|
-
|
|
660
|
+
const reclaimed = await claimOne(target, alive);
|
|
661
|
+
assert(
|
|
662
|
+
reclaimed.id === job.id,
|
|
663
|
+
"the reclaiming daemon did not get the job"
|
|
664
|
+
);
|
|
665
|
+
const late = await postResult(target, dead, {
|
|
666
|
+
jobId: job.id,
|
|
667
|
+
leaseId: firstLease.lease.id,
|
|
668
|
+
outcome: { outcome: "ok", text: "from the machine that vanished" }
|
|
600
669
|
});
|
|
670
|
+
const lateBody = await late.json().catch(() => ({}));
|
|
671
|
+
assert(
|
|
672
|
+
lateBody.accepted !== true,
|
|
673
|
+
"a site accepted a result from a runner whose lease had lapsed"
|
|
674
|
+
);
|
|
675
|
+
const midflight = await target.job(job.id);
|
|
676
|
+
assert(
|
|
677
|
+
!midflight?.outcome,
|
|
678
|
+
"a lapsed holder's result was recorded over a live grant"
|
|
679
|
+
);
|
|
680
|
+
const proper = await postResult(target, alive, {
|
|
681
|
+
jobId: job.id,
|
|
682
|
+
leaseId: reclaimed.lease.id,
|
|
683
|
+
outcome: { outcome: "ok", text: "from the machine that took over" }
|
|
684
|
+
});
|
|
685
|
+
assert(
|
|
686
|
+
proper.status === 200,
|
|
687
|
+
`the reclaiming daemon could not finish the job (${String(proper.status)})`
|
|
688
|
+
);
|
|
689
|
+
const final = await target.job(job.id);
|
|
690
|
+
assert(
|
|
691
|
+
final?.outcome?.text === "from the machine that took over",
|
|
692
|
+
"the reclaimed job did not record the current holder's result"
|
|
693
|
+
);
|
|
601
694
|
} finally {
|
|
602
695
|
await alive.dispose();
|
|
696
|
+
await dead.dispose();
|
|
603
697
|
}
|
|
604
698
|
}
|
|
605
699
|
},
|
|
606
700
|
{
|
|
607
701
|
id: "C005_AUDIENCE_MATRIX",
|
|
608
|
-
title: "all
|
|
702
|
+
title: "all four audience x offer-scope combinations behave as specified",
|
|
609
703
|
musts: ["AUDIENCE_BOTH_SIDES", "NAMED_LOCAL_ALLOWLIST"],
|
|
610
704
|
async run(target) {
|
|
611
705
|
const expected = {
|
|
612
|
-
"
|
|
613
|
-
"
|
|
614
|
-
"
|
|
615
|
-
"
|
|
616
|
-
|
|
617
|
-
// refused locally — allowlist is empty
|
|
618
|
-
"named:public": true,
|
|
619
|
-
"public:self": false,
|
|
620
|
-
"public:named": false,
|
|
621
|
-
// refused locally — allowlist is empty
|
|
622
|
-
"public:public": true
|
|
706
|
+
"private:private": false,
|
|
707
|
+
"private:team": false,
|
|
708
|
+
"team:private": false,
|
|
709
|
+
"team:team": false
|
|
710
|
+
// refused locally — nothing admits alice
|
|
623
711
|
};
|
|
624
712
|
for (const audience of AUDIENCES) {
|
|
625
713
|
for (const offer of OFFER_SCOPES) {
|
|
@@ -650,16 +738,30 @@ var CHECKS = [
|
|
|
650
738
|
},
|
|
651
739
|
{
|
|
652
740
|
id: "C006_NAMED_LOCAL_ALLOWLIST",
|
|
653
|
-
|
|
741
|
+
/**
|
|
742
|
+
* Renamed with the release that made the sentence true — Amendment G, B2.
|
|
743
|
+
*
|
|
744
|
+
* The old title read "a named job runs only once the daemon's own
|
|
745
|
+
* allowlist admits it", which was true only under a generous reading of
|
|
746
|
+
* "own": the list was per-person and local, and a team member had to be
|
|
747
|
+
* enrolled on every machine by hand.
|
|
748
|
+
*
|
|
749
|
+
* The id does not change, per the id-stability law. What changes is the
|
|
750
|
+
* sentence, and it now names all three of the things a reader would
|
|
751
|
+
* otherwise take on faith — that the list is local, that its authority was
|
|
752
|
+
* established out of band, and that admission is a property of the asker
|
|
753
|
+
* rather than of the request.
|
|
754
|
+
*/
|
|
755
|
+
title: "a team job is refused by a device whose upstream cannot say who the asker is, and is not offered to it again",
|
|
654
756
|
musts: ["NAMED_LOCAL_ALLOWLIST", "REFUSAL_NOT_REOFFERED"],
|
|
655
757
|
async run(target) {
|
|
656
|
-
const bob = await pairDaemon(target, { owner: "bob", offer: "
|
|
758
|
+
const bob = await pairDaemon(target, { owner: "bob", offer: "team" });
|
|
657
759
|
try {
|
|
658
760
|
const refused = await target.enqueue({
|
|
659
761
|
kind: "llm.generate",
|
|
660
762
|
payload: prompt("before"),
|
|
661
763
|
owner: "alice",
|
|
662
|
-
audience: "
|
|
764
|
+
audience: "team"
|
|
663
765
|
});
|
|
664
766
|
await bob.runner.tick();
|
|
665
767
|
await sleep(80);
|
|
@@ -667,27 +769,10 @@ var CHECKS = [
|
|
|
667
769
|
(await target.job(refused.id))?.state !== "ok",
|
|
668
770
|
"a named job ran without the daemon's local allowlist admitting it"
|
|
669
771
|
);
|
|
670
|
-
const
|
|
671
|
-
await bob.runner.tick();
|
|
672
|
-
await sleep(50);
|
|
772
|
+
const reoffered = await claimRaw(target, bob);
|
|
673
773
|
assert(
|
|
674
|
-
|
|
675
|
-
"a
|
|
676
|
-
);
|
|
677
|
-
await bob.allowlist.add(
|
|
678
|
-
{ origin: target.origin, owner: await ownerIdFor(target, "alice") },
|
|
679
|
-
Date.now()
|
|
680
|
-
);
|
|
681
|
-
const allowed = await target.enqueue({
|
|
682
|
-
kind: "llm.generate",
|
|
683
|
-
payload: prompt("after"),
|
|
684
|
-
owner: "alice",
|
|
685
|
-
audience: "named"
|
|
686
|
-
});
|
|
687
|
-
await bob.runner.tick();
|
|
688
|
-
await waitFor(
|
|
689
|
-
async () => (await target.job(allowed.id))?.state === "ok",
|
|
690
|
-
{ what: "the allowed named job to run" }
|
|
774
|
+
!reoffered.some((job) => job.id === refused.id),
|
|
775
|
+
"a server re-offered a job to the runner that refused it"
|
|
691
776
|
);
|
|
692
777
|
} finally {
|
|
693
778
|
await bob.dispose();
|
|
@@ -701,19 +786,15 @@ var CHECKS = [
|
|
|
701
786
|
async run(target) {
|
|
702
787
|
const bob = await pairDaemon(target, {
|
|
703
788
|
owner: "bob",
|
|
704
|
-
offer: "
|
|
789
|
+
offer: "team",
|
|
705
790
|
subscription: true
|
|
706
791
|
});
|
|
707
792
|
try {
|
|
708
|
-
await bob.allowlist.add(
|
|
709
|
-
{ origin: target.origin, owner: await ownerIdFor(target, "alice") },
|
|
710
|
-
Date.now()
|
|
711
|
-
);
|
|
712
793
|
const job = await target.enqueue({
|
|
713
794
|
kind: "llm.generate",
|
|
714
795
|
payload: prompt("someone else's work"),
|
|
715
796
|
owner: "alice",
|
|
716
|
-
audience: "
|
|
797
|
+
audience: "team"
|
|
717
798
|
});
|
|
718
799
|
await bob.runner.tick();
|
|
719
800
|
await sleep(80);
|
|
@@ -730,7 +811,7 @@ var CHECKS = [
|
|
|
730
811
|
kind: "llm.generate",
|
|
731
812
|
payload: prompt("my own work"),
|
|
732
813
|
owner: "bob",
|
|
733
|
-
audience: "
|
|
814
|
+
audience: "private"
|
|
734
815
|
});
|
|
735
816
|
await bob.runner.tick();
|
|
736
817
|
await waitFor(async () => (await target.job(own.id))?.state === "ok", {
|
|
@@ -744,9 +825,17 @@ var CHECKS = [
|
|
|
744
825
|
{
|
|
745
826
|
id: "C008_REVOCATION",
|
|
746
827
|
title: "a revoked daemon stops mid-queue",
|
|
747
|
-
|
|
828
|
+
// Both halves, and this check already proved both: the daemon learns it
|
|
829
|
+
// is revoked (`REVOCATION_HONORED`), *and* the upstream leaves the job
|
|
830
|
+
// queued rather than granting it (`REVOCATION_IMMEDIATE`). The second
|
|
831
|
+
// assertion was here and cited nothing — which is how a MUST comes to be
|
|
832
|
+
// declared in a spec, absent from the registry, and tested all along.
|
|
833
|
+
musts: ["REVOCATION_HONORED", "REVOCATION_IMMEDIATE"],
|
|
748
834
|
async run(target) {
|
|
749
|
-
const daemon = await pairDaemon(target, {
|
|
835
|
+
const daemon = await pairDaemon(target, {
|
|
836
|
+
owner: "alice",
|
|
837
|
+
offer: "private"
|
|
838
|
+
});
|
|
750
839
|
try {
|
|
751
840
|
await target.revokeRunner(daemon.runnerId);
|
|
752
841
|
const job = await target.enqueue({
|
|
@@ -774,7 +863,10 @@ var CHECKS = [
|
|
|
774
863
|
title: "cancel aborts a running job's backend call",
|
|
775
864
|
musts: ["CANCEL_HONORED"],
|
|
776
865
|
async run(target) {
|
|
777
|
-
const daemon = await pairDaemon(target, {
|
|
866
|
+
const daemon = await pairDaemon(target, {
|
|
867
|
+
owner: "alice",
|
|
868
|
+
offer: "private"
|
|
869
|
+
});
|
|
778
870
|
try {
|
|
779
871
|
daemon.backend.hangMs = 3e4;
|
|
780
872
|
const job = await target.enqueue({
|
|
@@ -802,39 +894,73 @@ var CHECKS = [
|
|
|
802
894
|
title: "the first terminal outcome wins",
|
|
803
895
|
musts: ["RESULT_IDEMPOTENT"],
|
|
804
896
|
async run(target) {
|
|
805
|
-
const daemon = await pairDaemon(target, {
|
|
897
|
+
const daemon = await pairDaemon(target, {
|
|
898
|
+
owner: "alice",
|
|
899
|
+
offer: "private"
|
|
900
|
+
});
|
|
806
901
|
try {
|
|
807
902
|
const job = await target.enqueue({
|
|
808
903
|
kind: "llm.generate",
|
|
809
904
|
payload: prompt("once"),
|
|
810
905
|
owner: "alice"
|
|
811
906
|
});
|
|
812
|
-
await daemon
|
|
813
|
-
|
|
814
|
-
|
|
907
|
+
const claimed = await claimOne(target, daemon);
|
|
908
|
+
assert(claimed.id === job.id, "the harness could not claim its job");
|
|
909
|
+
const first = await postResult(target, daemon, {
|
|
910
|
+
jobId: job.id,
|
|
911
|
+
leaseId: claimed.lease.id,
|
|
912
|
+
outcome: { outcome: "ok", text: "the answer that counts" }
|
|
815
913
|
});
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
914
|
+
assert(
|
|
915
|
+
first.status === 200,
|
|
916
|
+
`a site refused the first result (${String(first.status)})`
|
|
917
|
+
);
|
|
918
|
+
const replay = await postResult(target, daemon, {
|
|
919
|
+
jobId: job.id,
|
|
920
|
+
leaseId: claimed.lease.id,
|
|
921
|
+
outcome: { outcome: "ok", text: "SECOND ANSWER" }
|
|
922
|
+
});
|
|
923
|
+
assert(
|
|
924
|
+
replay.status === 200,
|
|
925
|
+
`a replayed result was rejected rather than ignored (${String(replay.status)})`
|
|
926
|
+
);
|
|
927
|
+
const body = await replay.json();
|
|
928
|
+
assert(
|
|
929
|
+
body.accepted === false,
|
|
930
|
+
"a site reported a replayed result as newly accepted"
|
|
931
|
+
);
|
|
932
|
+
assert(
|
|
933
|
+
body.duplicate === true,
|
|
934
|
+
"a replay from the device that finished the job was not called a duplicate"
|
|
831
935
|
);
|
|
832
|
-
void response;
|
|
833
936
|
const after = await target.job(job.id);
|
|
834
937
|
assert(
|
|
835
|
-
after?.outcome?.text ===
|
|
836
|
-
|
|
938
|
+
after?.outcome?.text === "the answer that counts",
|
|
939
|
+
`a second result overwrote the first (${String(after?.outcome?.text)})`
|
|
837
940
|
);
|
|
941
|
+
const stranger = await pairDaemon(target, {
|
|
942
|
+
owner: "alice",
|
|
943
|
+
offer: "private"
|
|
944
|
+
});
|
|
945
|
+
try {
|
|
946
|
+
const foreign = await postResult(target, stranger, {
|
|
947
|
+
jobId: job.id,
|
|
948
|
+
leaseId: claimed.lease.id,
|
|
949
|
+
outcome: { outcome: "ok", text: "not this device's to answer" }
|
|
950
|
+
});
|
|
951
|
+
const foreignBody = await foreign.json().catch(() => ({}));
|
|
952
|
+
assert(
|
|
953
|
+
foreignBody["duplicate"] !== true,
|
|
954
|
+
"a site told a device that never held this job it was a duplicate"
|
|
955
|
+
);
|
|
956
|
+
const stillFirst = await target.job(job.id);
|
|
957
|
+
assert(
|
|
958
|
+
stillFirst?.outcome?.text === "the answer that counts",
|
|
959
|
+
"a stranger's result overwrote a terminal job"
|
|
960
|
+
);
|
|
961
|
+
} finally {
|
|
962
|
+
await stranger.dispose();
|
|
963
|
+
}
|
|
838
964
|
} finally {
|
|
839
965
|
await daemon.dispose();
|
|
840
966
|
}
|
|
@@ -845,20 +971,23 @@ var CHECKS = [
|
|
|
845
971
|
title: "a dependent job waits for its dependency, across two daemons",
|
|
846
972
|
musts: ["DEPENDS_ON_GATING", "TTL_EXPIRY"],
|
|
847
973
|
async run(target) {
|
|
848
|
-
const alice = await pairDaemon(target, {
|
|
849
|
-
|
|
974
|
+
const alice = await pairDaemon(target, {
|
|
975
|
+
owner: "alice",
|
|
976
|
+
offer: "private"
|
|
977
|
+
});
|
|
978
|
+
const bob = await pairDaemon(target, { owner: "bob", offer: "private" });
|
|
850
979
|
try {
|
|
851
980
|
const first = await target.enqueue({
|
|
852
981
|
kind: "llm.generate",
|
|
853
982
|
payload: prompt("step one"),
|
|
854
983
|
owner: "bob",
|
|
855
|
-
audience: "
|
|
984
|
+
audience: "private"
|
|
856
985
|
});
|
|
857
986
|
const second = await target.enqueue({
|
|
858
987
|
kind: "llm.generate",
|
|
859
988
|
payload: prompt("step two"),
|
|
860
989
|
owner: "alice",
|
|
861
|
-
audience: "
|
|
990
|
+
audience: "private",
|
|
862
991
|
dependsOn: [first.id]
|
|
863
992
|
});
|
|
864
993
|
await alice.runner.tick();
|
|
@@ -919,7 +1048,10 @@ var CHECKS = [
|
|
|
919
1048
|
title: "a dependent job's TTL starts when it becomes claimable, not at enqueue",
|
|
920
1049
|
musts: ["TTL_EXPIRY"],
|
|
921
1050
|
async run(target) {
|
|
922
|
-
const daemon = await pairDaemon(target, {
|
|
1051
|
+
const daemon = await pairDaemon(target, {
|
|
1052
|
+
owner: "alice",
|
|
1053
|
+
offer: "private"
|
|
1054
|
+
});
|
|
923
1055
|
try {
|
|
924
1056
|
daemon.backend.hangMs = target.ttlMs * 2;
|
|
925
1057
|
const first = await target.enqueue({
|
|
@@ -949,35 +1081,19 @@ var CHECKS = [
|
|
|
949
1081
|
{
|
|
950
1082
|
id: "C014_RESULT_PROVENANCE",
|
|
951
1083
|
title: "a community result arrives marked untrusted, a self result does not",
|
|
952
|
-
|
|
1084
|
+
// `PROVENANCE_NAMES_DEVICE` supersedes `RESULT_PROVENANCE` — a
|
|
1085
|
+
// strengthening rather than a rename. C030 is the other half: a label
|
|
1086
|
+
// means nothing unless a result whose signature does not verify against
|
|
1087
|
+
// the granted device is refused rather than recorded.
|
|
1088
|
+
musts: ["PROVENANCE_NAMES_DEVICE"],
|
|
953
1089
|
async run(target) {
|
|
954
|
-
const bob = await pairDaemon(target, { owner: "bob", offer: "
|
|
1090
|
+
const bob = await pairDaemon(target, { owner: "bob", offer: "team" });
|
|
955
1091
|
try {
|
|
956
|
-
const community = await target.enqueue({
|
|
957
|
-
kind: "llm.generate",
|
|
958
|
-
payload: prompt("run this anywhere"),
|
|
959
|
-
owner: "alice",
|
|
960
|
-
audience: "public"
|
|
961
|
-
});
|
|
962
|
-
await bob.runner.tick();
|
|
963
|
-
await waitFor(
|
|
964
|
-
async () => (await target.job(community.id))?.state === "ok",
|
|
965
|
-
{ what: "the community job to complete" }
|
|
966
|
-
);
|
|
967
|
-
const delivered = await target.job(community.id);
|
|
968
|
-
assert(
|
|
969
|
-
delivered?.provenance?.untrusted === true,
|
|
970
|
-
"a public result was not marked untrusted"
|
|
971
|
-
);
|
|
972
|
-
assert(
|
|
973
|
-
delivered.provenance.runnerOwner === "bob",
|
|
974
|
-
"the result did not carry the runner's owner"
|
|
975
|
-
);
|
|
976
1092
|
const own = await target.enqueue({
|
|
977
1093
|
kind: "llm.generate",
|
|
978
1094
|
payload: prompt("my own"),
|
|
979
1095
|
owner: "bob",
|
|
980
|
-
audience: "
|
|
1096
|
+
audience: "private"
|
|
981
1097
|
});
|
|
982
1098
|
await bob.runner.tick();
|
|
983
1099
|
await waitFor(async () => (await target.job(own.id))?.state === "ok", {
|
|
@@ -997,7 +1113,10 @@ var CHECKS = [
|
|
|
997
1113
|
title: "every executed prompt is in the ingress log before it runs",
|
|
998
1114
|
musts: ["INGRESS_LOGGED_BEFORE_EXECUTION"],
|
|
999
1115
|
async run(target) {
|
|
1000
|
-
const daemon = await pairDaemon(target, {
|
|
1116
|
+
const daemon = await pairDaemon(target, {
|
|
1117
|
+
owner: "alice",
|
|
1118
|
+
offer: "private"
|
|
1119
|
+
});
|
|
1001
1120
|
let ticking = Promise.resolve();
|
|
1002
1121
|
try {
|
|
1003
1122
|
daemon.backend.hangMs = 3e4;
|
|
@@ -1031,7 +1150,13 @@ var CHECKS = [
|
|
|
1031
1150
|
{
|
|
1032
1151
|
id: "C016_UNAUTHENTICATED_REFUSED",
|
|
1033
1152
|
title: "the protocol endpoints refuse an unknown token",
|
|
1034
|
-
|
|
1153
|
+
// `CONSENT_BEFORE_ROUTE` on this plane. A relay has a consent record; a
|
|
1154
|
+
// direct site has pairing, and it is the same obligation — an upstream
|
|
1155
|
+
// routes to a device it has a record binding, and there is no discovery
|
|
1156
|
+
// path by which an unbound device receives work. Every endpoint is
|
|
1157
|
+
// checked rather than just `claim`, which is what makes it the absence
|
|
1158
|
+
// of a path rather than the absence of one door.
|
|
1159
|
+
musts: ["PAIR_ONE_USER", "CONSENT_BEFORE_ROUTE"],
|
|
1035
1160
|
async run(target) {
|
|
1036
1161
|
for (const endpoint of ["claim", "heartbeat", "result", "release"]) {
|
|
1037
1162
|
const response = await target.fetch(
|
|
@@ -1041,7 +1166,7 @@ var CHECKS = [
|
|
|
1041
1166
|
"content-type": "application/json",
|
|
1042
1167
|
authorization: "Bearer definitely-not-a-real-token"
|
|
1043
1168
|
},
|
|
1044
|
-
body: JSON.stringify({ protocolVersion:
|
|
1169
|
+
body: JSON.stringify({ protocolVersion: PROTOCOL_VERSION2 })
|
|
1045
1170
|
})
|
|
1046
1171
|
);
|
|
1047
1172
|
assert(
|
|
@@ -1054,11 +1179,19 @@ var CHECKS = [
|
|
|
1054
1179
|
{
|
|
1055
1180
|
id: "C017_METERED_DEFAULTS_SELF",
|
|
1056
1181
|
title: "a paid backend is not shared until its owner says so, with a ceiling",
|
|
1057
|
-
|
|
1182
|
+
// `EFFECTIVE_OFFER_ONLY` too: bob asks for `public`, what reaches the
|
|
1183
|
+
// server is `self`, and the server acts on what it was told rather than
|
|
1184
|
+
// on what was wanted. That *is* the effective-offer rule, proved here
|
|
1185
|
+
// without being named.
|
|
1186
|
+
musts: [
|
|
1187
|
+
"METERED_DEFAULTS_SELF",
|
|
1188
|
+
"COST_NOT_CONFIGURABLE",
|
|
1189
|
+
"EFFECTIVE_OFFER_ONLY"
|
|
1190
|
+
],
|
|
1058
1191
|
async run(target) {
|
|
1059
1192
|
const bob = await pairDaemon(target, {
|
|
1060
1193
|
owner: "bob",
|
|
1061
|
-
offer: "
|
|
1194
|
+
offer: "team",
|
|
1062
1195
|
// Pointed at localhost — which changes nothing, because a named
|
|
1063
1196
|
// provider's cost comes from the registry, not from an address
|
|
1064
1197
|
// ({@link MUSTS.COST_NOT_CONFIGURABLE}).
|
|
@@ -1066,22 +1199,18 @@ var CHECKS = [
|
|
|
1066
1199
|
});
|
|
1067
1200
|
try {
|
|
1068
1201
|
assert(
|
|
1069
|
-
bob.loaded.routes.every((route) => route.offerScope === "
|
|
1202
|
+
bob.loaded.routes.every((route) => route.offerScope === "private"),
|
|
1070
1203
|
"a metered backend was advertised beyond its owner without consent"
|
|
1071
1204
|
);
|
|
1072
1205
|
assert(
|
|
1073
1206
|
bob.loaded.routes.every((route) => route.cost === "metered"),
|
|
1074
1207
|
"a metered provider was read as free because of its base URL"
|
|
1075
1208
|
);
|
|
1076
|
-
await bob.allowlist.add(
|
|
1077
|
-
{ origin: target.origin, owner: await ownerIdFor(target, "alice") },
|
|
1078
|
-
Date.now()
|
|
1079
|
-
);
|
|
1080
1209
|
const job = await target.enqueue({
|
|
1081
1210
|
kind: "llm.generate",
|
|
1082
1211
|
payload: prompt("spend someone else's money"),
|
|
1083
1212
|
owner: "alice",
|
|
1084
|
-
audience: "
|
|
1213
|
+
audience: "team"
|
|
1085
1214
|
});
|
|
1086
1215
|
await bob.runner.tick();
|
|
1087
1216
|
await sleep(80);
|
|
@@ -1097,7 +1226,7 @@ var CHECKS = [
|
|
|
1097
1226
|
const availability = await target.runnerAvailability({
|
|
1098
1227
|
kind: "llm.generate",
|
|
1099
1228
|
owner: "alice",
|
|
1100
|
-
audience: "
|
|
1229
|
+
audience: "team"
|
|
1101
1230
|
});
|
|
1102
1231
|
assert(
|
|
1103
1232
|
!availability.available,
|
|
@@ -1107,7 +1236,7 @@ var CHECKS = [
|
|
|
1107
1236
|
kind: "llm.generate",
|
|
1108
1237
|
payload: prompt("my own work"),
|
|
1109
1238
|
owner: "bob",
|
|
1110
|
-
audience: "
|
|
1239
|
+
audience: "private"
|
|
1111
1240
|
});
|
|
1112
1241
|
await bob.runner.tick();
|
|
1113
1242
|
await waitFor(async () => (await target.job(own.id))?.state === "ok", {
|
|
@@ -1125,7 +1254,7 @@ var CHECKS = [
|
|
|
1125
1254
|
async run(target) {
|
|
1126
1255
|
const bob = await pairDaemon(target, {
|
|
1127
1256
|
owner: "bob",
|
|
1128
|
-
offer: "
|
|
1257
|
+
offer: "team",
|
|
1129
1258
|
metered: {
|
|
1130
1259
|
// The generic backend pointed at a remote address. No registry entry
|
|
1131
1260
|
// says what this costs; it is metered because of where it goes
|
|
@@ -1142,30 +1271,15 @@ var CHECKS = [
|
|
|
1142
1271
|
"a remote backend was treated as free"
|
|
1143
1272
|
);
|
|
1144
1273
|
assert(
|
|
1145
|
-
bob.loaded.routes.every((route) => route.offerScope === "
|
|
1274
|
+
bob.loaded.routes.every((route) => route.offerScope === "team"),
|
|
1146
1275
|
"a deliberately shared metered backend was narrowed anyway"
|
|
1147
1276
|
);
|
|
1148
|
-
await bob.allowlist.add(
|
|
1149
|
-
{ origin: target.origin, owner: await ownerIdFor(target, "alice") },
|
|
1150
|
-
Date.now()
|
|
1151
|
-
);
|
|
1152
|
-
const first = await target.enqueue({
|
|
1153
|
-
kind: "llm.generate",
|
|
1154
|
-
payload: prompt("work bob agreed to pay for"),
|
|
1155
|
-
owner: "alice",
|
|
1156
|
-
audience: "public"
|
|
1157
|
-
});
|
|
1158
|
-
await bob.runner.tick();
|
|
1159
|
-
await waitFor(
|
|
1160
|
-
async () => (await target.job(first.id))?.state === "ok",
|
|
1161
|
-
{ what: "a consented metered job to run" }
|
|
1162
|
-
);
|
|
1163
1277
|
await bob.spend.record("primary", 900, Date.now());
|
|
1164
1278
|
const second = await target.enqueue({
|
|
1165
1279
|
kind: "llm.generate",
|
|
1166
1280
|
payload: prompt("work past the ceiling"),
|
|
1167
1281
|
owner: "alice",
|
|
1168
|
-
audience: "
|
|
1282
|
+
audience: "team"
|
|
1169
1283
|
});
|
|
1170
1284
|
const seenBefore = bob.backend.seen.length;
|
|
1171
1285
|
await bob.runner.tick();
|
|
@@ -1183,7 +1297,7 @@ var CHECKS = [
|
|
|
1183
1297
|
kind: "llm.generate",
|
|
1184
1298
|
payload: prompt("my own work, my own key"),
|
|
1185
1299
|
owner: "bob",
|
|
1186
|
-
audience: "
|
|
1300
|
+
audience: "private"
|
|
1187
1301
|
});
|
|
1188
1302
|
await bob.runner.tick();
|
|
1189
1303
|
await waitFor(async () => (await target.job(own.id))?.state === "ok", {
|
|
@@ -1199,14 +1313,22 @@ var CHECKS = [
|
|
|
1199
1313
|
title: "two runners racing one job \u2014 exactly one gets it",
|
|
1200
1314
|
musts: ["CLAIM_ATOMIC"],
|
|
1201
1315
|
async run(target) {
|
|
1202
|
-
const a = await pairDaemon(target, {
|
|
1203
|
-
|
|
1316
|
+
const a = await pairDaemon(target, {
|
|
1317
|
+
owner: "alice",
|
|
1318
|
+
label: "laptop",
|
|
1319
|
+
offer: "private"
|
|
1320
|
+
});
|
|
1321
|
+
const b = await pairDaemon(target, {
|
|
1322
|
+
owner: "alice",
|
|
1323
|
+
label: "desktop",
|
|
1324
|
+
offer: "private"
|
|
1325
|
+
});
|
|
1204
1326
|
try {
|
|
1205
1327
|
const job = await target.enqueue({
|
|
1206
1328
|
kind: "llm.generate",
|
|
1207
1329
|
payload: prompt("only once, please"),
|
|
1208
1330
|
owner: "alice",
|
|
1209
|
-
audience: "
|
|
1331
|
+
audience: "private"
|
|
1210
1332
|
});
|
|
1211
1333
|
await Promise.all([a.runner.tick(), b.runner.tick()]);
|
|
1212
1334
|
await waitFor(async () => (await target.job(job.id))?.state === "ok", {
|
|
@@ -1281,7 +1403,10 @@ var CHECKS = [
|
|
|
1281
1403
|
title: "a runner advertises only what is installed and healthy",
|
|
1282
1404
|
musts: ["CAPABILITY_IS_DETECTED"],
|
|
1283
1405
|
async run(target) {
|
|
1284
|
-
const daemon = await pairDaemon(target, {
|
|
1406
|
+
const daemon = await pairDaemon(target, {
|
|
1407
|
+
owner: "alice",
|
|
1408
|
+
offer: "private"
|
|
1409
|
+
});
|
|
1285
1410
|
try {
|
|
1286
1411
|
daemon.backend.healthy = false;
|
|
1287
1412
|
const advertised = await daemon.runner.detectCapabilities();
|
|
@@ -1319,7 +1444,10 @@ var CHECKS = [
|
|
|
1319
1444
|
// check does not establish.
|
|
1320
1445
|
musts: ["KIND_NO_CODE"],
|
|
1321
1446
|
async run(target) {
|
|
1322
|
-
const daemon = await pairDaemon(target, {
|
|
1447
|
+
const daemon = await pairDaemon(target, {
|
|
1448
|
+
owner: "alice",
|
|
1449
|
+
offer: "private"
|
|
1450
|
+
});
|
|
1323
1451
|
const SMUGGLED = ["command", "argv", "model", "baseUrl"];
|
|
1324
1452
|
try {
|
|
1325
1453
|
let refused = false;
|
|
@@ -1334,7 +1462,7 @@ var CHECKS = [
|
|
|
1334
1462
|
baseUrl: "http://evil.test/v1"
|
|
1335
1463
|
},
|
|
1336
1464
|
owner: "alice",
|
|
1337
|
-
audience: "
|
|
1465
|
+
audience: "private"
|
|
1338
1466
|
});
|
|
1339
1467
|
} catch {
|
|
1340
1468
|
refused = true;
|
|
@@ -1364,7 +1492,7 @@ var CHECKS = [
|
|
|
1364
1492
|
kind: "llm.generate",
|
|
1365
1493
|
payload: prompt("ordinary text"),
|
|
1366
1494
|
owner: "alice",
|
|
1367
|
-
audience: "
|
|
1495
|
+
audience: "private"
|
|
1368
1496
|
});
|
|
1369
1497
|
await daemon.runner.tick();
|
|
1370
1498
|
await waitFor(async () => (await target.job(ok.id))?.state === "ok", {
|
|
@@ -1471,7 +1599,7 @@ var CHECKS = [
|
|
|
1471
1599
|
};
|
|
1472
1600
|
const pending = await poll();
|
|
1473
1601
|
assert(
|
|
1474
|
-
pending["
|
|
1602
|
+
pending["sites"] === void 0,
|
|
1475
1603
|
"a pending poll disclosed the site's keys before anyone approved"
|
|
1476
1604
|
);
|
|
1477
1605
|
await target.approvePairing(pairing.userCode, "alice");
|
|
@@ -1480,8 +1608,19 @@ var CHECKS = [
|
|
|
1480
1608
|
approved["status"] === "approved",
|
|
1481
1609
|
`poll after approval said "${String(approved["status"])}"`
|
|
1482
1610
|
);
|
|
1483
|
-
const
|
|
1484
|
-
assert(
|
|
1611
|
+
const offered = approved["sites"];
|
|
1612
|
+
assert(
|
|
1613
|
+
typeof offered === "object" && offered !== null,
|
|
1614
|
+
"the approval carried no sites to pin"
|
|
1615
|
+
);
|
|
1616
|
+
const parsed = Object.values(offered).map(
|
|
1617
|
+
(value) => PublicIdentity.safeParse(value)
|
|
1618
|
+
);
|
|
1619
|
+
assert(
|
|
1620
|
+
parsed.length > 0 && parsed.every((entry) => entry.success),
|
|
1621
|
+
"the approval carried no usable site identity"
|
|
1622
|
+
);
|
|
1623
|
+
const site = parsed[0];
|
|
1485
1624
|
assert(
|
|
1486
1625
|
verifyPublicIdentity(site.data),
|
|
1487
1626
|
"the site's encryption key is not signed by the identity it presented"
|
|
@@ -1493,7 +1632,10 @@ var CHECKS = [
|
|
|
1493
1632
|
title: "authentication is a signature over the request, not a secret",
|
|
1494
1633
|
musts: ["REQUESTS_SIGNED_NOT_BEARER"],
|
|
1495
1634
|
async run(target) {
|
|
1496
|
-
const daemon = await pairDaemon(target, {
|
|
1635
|
+
const daemon = await pairDaemon(target, {
|
|
1636
|
+
owner: "alice",
|
|
1637
|
+
offer: "private"
|
|
1638
|
+
});
|
|
1497
1639
|
try {
|
|
1498
1640
|
const body = JSON.stringify({
|
|
1499
1641
|
protocolVersion: PROTOCOL_VERSION2,
|
|
@@ -1565,13 +1707,16 @@ var CHECKS = [
|
|
|
1565
1707
|
title: "a release acts on the lease it names, not whatever lease exists",
|
|
1566
1708
|
musts: ["LEASE_SCOPED_BY_GRANT"],
|
|
1567
1709
|
async run(target) {
|
|
1568
|
-
const daemon = await pairDaemon(target, {
|
|
1710
|
+
const daemon = await pairDaemon(target, {
|
|
1711
|
+
owner: "alice",
|
|
1712
|
+
offer: "private"
|
|
1713
|
+
});
|
|
1569
1714
|
try {
|
|
1570
1715
|
const job = await target.enqueue({
|
|
1571
1716
|
kind: "llm.generate",
|
|
1572
1717
|
payload: prompt("run me once"),
|
|
1573
1718
|
owner: "alice",
|
|
1574
|
-
audience: "
|
|
1719
|
+
audience: "private"
|
|
1575
1720
|
});
|
|
1576
1721
|
const first = await claimOne(target, daemon);
|
|
1577
1722
|
assert(
|
|
@@ -1605,13 +1750,16 @@ var CHECKS = [
|
|
|
1605
1750
|
title: "a claim carries routing metadata and no work",
|
|
1606
1751
|
musts: ["STUB_METADATA_EXHAUSTIVE"],
|
|
1607
1752
|
async run(target) {
|
|
1608
|
-
const daemon = await pairDaemon(target, {
|
|
1753
|
+
const daemon = await pairDaemon(target, {
|
|
1754
|
+
owner: "alice",
|
|
1755
|
+
offer: "private"
|
|
1756
|
+
});
|
|
1609
1757
|
try {
|
|
1610
1758
|
await target.enqueue({
|
|
1611
1759
|
kind: "llm.generate",
|
|
1612
1760
|
payload: prompt("this must not appear in a claim response"),
|
|
1613
1761
|
owner: "alice",
|
|
1614
|
-
audience: "
|
|
1762
|
+
audience: "private"
|
|
1615
1763
|
});
|
|
1616
1764
|
const stub = await claimOne(target, daemon);
|
|
1617
1765
|
const asRecord = stub;
|
|
@@ -1670,13 +1818,16 @@ var CHECKS = [
|
|
|
1670
1818
|
musts: ["ENVELOPE_SEALED_AND_SIGNED"],
|
|
1671
1819
|
async run(target) {
|
|
1672
1820
|
const secret = "a prompt nobody should read from storage";
|
|
1673
|
-
const daemon = await pairDaemon(target, {
|
|
1821
|
+
const daemon = await pairDaemon(target, {
|
|
1822
|
+
owner: "alice",
|
|
1823
|
+
offer: "private"
|
|
1824
|
+
});
|
|
1674
1825
|
try {
|
|
1675
1826
|
const job = await target.enqueue({
|
|
1676
1827
|
kind: "llm.generate",
|
|
1677
1828
|
payload: prompt(secret),
|
|
1678
1829
|
owner: "alice",
|
|
1679
|
-
audience: "
|
|
1830
|
+
audience: "private"
|
|
1680
1831
|
});
|
|
1681
1832
|
const stored = await target.job(job.id);
|
|
1682
1833
|
assert(
|
|
@@ -1709,7 +1860,10 @@ var CHECKS = [
|
|
|
1709
1860
|
title: "a daemon refuses work not signed by the site it pinned",
|
|
1710
1861
|
musts: ["ENVELOPE_SEALED_AND_SIGNED"],
|
|
1711
1862
|
async run(target) {
|
|
1712
|
-
const daemon = await pairDaemon(target, {
|
|
1863
|
+
const daemon = await pairDaemon(target, {
|
|
1864
|
+
owner: "alice",
|
|
1865
|
+
offer: "private"
|
|
1866
|
+
});
|
|
1713
1867
|
try {
|
|
1714
1868
|
const keys = await daemon.identityKeys();
|
|
1715
1869
|
const relay = generateKeys(Date.now());
|
|
@@ -1754,9 +1908,16 @@ var CHECKS = [
|
|
|
1754
1908
|
{
|
|
1755
1909
|
id: "C030_SITE_REFUSES_UNSIGNED_RESULTS",
|
|
1756
1910
|
title: "a site refuses a result not signed by the device that ran it",
|
|
1757
|
-
|
|
1911
|
+
// The proof-of-possession half of `PROVENANCE_NAMES_DEVICE`: attribution
|
|
1912
|
+
// by a signature that verifies against the device the lease was granted
|
|
1913
|
+
// to, rather than by a key id carried beside the result. Carrying an id
|
|
1914
|
+
// is not proving possession, and a forger writes whatever it likes.
|
|
1915
|
+
musts: ["ENVELOPE_SEALED_AND_SIGNED", "PROVENANCE_NAMES_DEVICE"],
|
|
1758
1916
|
async run(target) {
|
|
1759
|
-
const daemon = await pairDaemon(target, {
|
|
1917
|
+
const daemon = await pairDaemon(target, {
|
|
1918
|
+
owner: "alice",
|
|
1919
|
+
offer: "private"
|
|
1920
|
+
});
|
|
1760
1921
|
try {
|
|
1761
1922
|
const job = await target.enqueue({
|
|
1762
1923
|
kind: "llm.generate",
|
|
@@ -1768,6 +1929,7 @@ var CHECKS = [
|
|
|
1768
1929
|
const relay = generateKeys(Date.now());
|
|
1769
1930
|
const forged = await postResult(target, daemon, {
|
|
1770
1931
|
jobId: job.id,
|
|
1932
|
+
leaseId: claimed.lease.id,
|
|
1771
1933
|
outcome: { outcome: "ok", text: "an answer the device never gave" },
|
|
1772
1934
|
sealWith: relay
|
|
1773
1935
|
});
|
|
@@ -1782,6 +1944,7 @@ var CHECKS = [
|
|
|
1782
1944
|
);
|
|
1783
1945
|
const real = await postResult(target, daemon, {
|
|
1784
1946
|
jobId: job.id,
|
|
1947
|
+
leaseId: claimed.lease.id,
|
|
1785
1948
|
outcome: { outcome: "ok", text: "the genuine answer" }
|
|
1786
1949
|
});
|
|
1787
1950
|
assert(
|
|
@@ -1790,6 +1953,7 @@ var CHECKS = [
|
|
|
1790
1953
|
);
|
|
1791
1954
|
const lying = await postResult(target, daemon, {
|
|
1792
1955
|
jobId: job.id,
|
|
1956
|
+
leaseId: claimed.lease.id,
|
|
1793
1957
|
outcome: {
|
|
1794
1958
|
outcome: "error",
|
|
1795
1959
|
code: "backend-error",
|
|
@@ -1806,11 +1970,101 @@ var CHECKS = [
|
|
|
1806
1970
|
await daemon.dispose();
|
|
1807
1971
|
}
|
|
1808
1972
|
}
|
|
1973
|
+
},
|
|
1974
|
+
{
|
|
1975
|
+
id: "C032_SERVER_REFUSES_TO_OFFER",
|
|
1976
|
+
title: "a claim is not answered with work the claimer may not run",
|
|
1977
|
+
musts: ["AUDIENCE_BOTH_SIDES"],
|
|
1978
|
+
async run(target) {
|
|
1979
|
+
const bob = await pairDaemon(target, { owner: "bob", offer: "team" });
|
|
1980
|
+
try {
|
|
1981
|
+
const priv = await target.enqueue({
|
|
1982
|
+
kind: "llm.generate",
|
|
1983
|
+
payload: prompt("alice's own machines only"),
|
|
1984
|
+
owner: "alice",
|
|
1985
|
+
audience: "private"
|
|
1986
|
+
});
|
|
1987
|
+
const offered = await claimRaw(target, bob);
|
|
1988
|
+
assert(
|
|
1989
|
+
!offered.some((job) => job.id === priv.id),
|
|
1990
|
+
"a server offered a `self` job to a device its owner does not own"
|
|
1991
|
+
);
|
|
1992
|
+
const shared = await target.enqueue({
|
|
1993
|
+
kind: "llm.generate",
|
|
1994
|
+
payload: prompt("anyone may run this"),
|
|
1995
|
+
owner: "alice",
|
|
1996
|
+
audience: "team"
|
|
1997
|
+
});
|
|
1998
|
+
const second = await claimRaw(target, bob);
|
|
1999
|
+
assert(
|
|
2000
|
+
second.some((job) => job.id === shared.id),
|
|
2001
|
+
"a server withheld a `public` job from a public-offering device"
|
|
2002
|
+
);
|
|
2003
|
+
} finally {
|
|
2004
|
+
await bob.dispose();
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
},
|
|
2008
|
+
{
|
|
2009
|
+
id: "C031_ROSTER_NOT_DISCLOSED",
|
|
2010
|
+
title: "a claimed stub carries no list of who may run the job",
|
|
2011
|
+
musts: ["ROSTER_NOT_DISCLOSED"],
|
|
2012
|
+
async run(target) {
|
|
2013
|
+
const daemon = await pairDaemon(target, {
|
|
2014
|
+
owner: "alice",
|
|
2015
|
+
offer: "private"
|
|
2016
|
+
});
|
|
2017
|
+
try {
|
|
2018
|
+
const job = await target.enqueue({
|
|
2019
|
+
kind: "llm.generate",
|
|
2020
|
+
payload: prompt("who else is on this roster"),
|
|
2021
|
+
owner: "alice",
|
|
2022
|
+
audience: "team",
|
|
2023
|
+
// The site restricts the job to people who are not this daemon's
|
|
2024
|
+
// owner. A stub that carried the list would be handing a routing
|
|
2025
|
+
// party the membership of alice's group.
|
|
2026
|
+
audienceAllow: ["alice", "carol", "erin"]
|
|
2027
|
+
});
|
|
2028
|
+
const claimed = await claimOne(target, daemon);
|
|
2029
|
+
assert(
|
|
2030
|
+
claimed.id === job.id,
|
|
2031
|
+
"the harness could not claim its own named job"
|
|
2032
|
+
);
|
|
2033
|
+
const asRecord = claimed;
|
|
2034
|
+
assert(
|
|
2035
|
+
asRecord["audienceAllow"] === void 0,
|
|
2036
|
+
"a claimed stub carried audienceAllow"
|
|
2037
|
+
);
|
|
2038
|
+
const parsed = ClaimedStub.safeParse(claimed);
|
|
2039
|
+
assert(
|
|
2040
|
+
parsed.success,
|
|
2041
|
+
"the claim response is not a valid stub, so its fields prove nothing"
|
|
2042
|
+
);
|
|
2043
|
+
const wire = JSON.stringify(claimed);
|
|
2044
|
+
for (const member of ["carol", "erin"]) {
|
|
2045
|
+
assert(
|
|
2046
|
+
!wire.includes(member),
|
|
2047
|
+
`a claimed stub disclosed roster member "${member}"`
|
|
2048
|
+
);
|
|
2049
|
+
}
|
|
2050
|
+
assert(
|
|
2051
|
+
claimed.audience === "team",
|
|
2052
|
+
"the stub lost the audience routing decides on"
|
|
2053
|
+
);
|
|
2054
|
+
assert(
|
|
2055
|
+
typeof claimed.owner === "string" && claimed.owner.length > 0,
|
|
2056
|
+
"the stub lost the owner"
|
|
2057
|
+
);
|
|
2058
|
+
} finally {
|
|
2059
|
+
await daemon.dispose();
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
1809
2062
|
}
|
|
1810
2063
|
];
|
|
1811
2064
|
|
|
1812
2065
|
// src/certify.ts
|
|
1813
2066
|
import {
|
|
2067
|
+
kindsOf,
|
|
1814
2068
|
MUSTS,
|
|
1815
2069
|
MUST_IDS,
|
|
1816
2070
|
mustsVerifiedBy
|
|
@@ -1854,7 +2108,7 @@ function uncoveredMusts(checks = CHECKS) {
|
|
|
1854
2108
|
return mustsVerifiedBy("conformance").filter((id) => !covered.has(id));
|
|
1855
2109
|
}
|
|
1856
2110
|
function miscoveredMusts(checks = CHECKS) {
|
|
1857
|
-
return [...new Set(checks.flatMap((check) => check.musts))].filter((id) => MUSTS[id].
|
|
2111
|
+
return [...new Set(checks.flatMap((check) => check.musts))].filter((id) => !kindsOf(MUSTS[id]).includes("conformance")).sort();
|
|
1858
2112
|
}
|
|
1859
2113
|
var VERIFICATION_NOTE = "(`adversarial` = proved by the reference daemon's own suites; `construction` = true by code shape; `operator` = a deployment claim, verifiable only by audit or source. None is asserted by this run.)";
|
|
1860
2114
|
function formatReport(report) {
|
|
@@ -1882,13 +2136,13 @@ function formatReport(report) {
|
|
|
1882
2136
|
}
|
|
1883
2137
|
}
|
|
1884
2138
|
const elsewhere = MUST_IDS.filter(
|
|
1885
|
-
(id) => MUSTS[id].
|
|
2139
|
+
(id) => !kindsOf(MUSTS[id]).includes("conformance")
|
|
1886
2140
|
);
|
|
1887
2141
|
if (elsewhere.length > 0) {
|
|
1888
2142
|
lines.push("");
|
|
1889
2143
|
lines.push(" Verified elsewhere, not by this kit:");
|
|
1890
2144
|
for (const kind of ["adversarial", "construction", "operator"]) {
|
|
1891
|
-
const ids = elsewhere.filter((id) => MUSTS[id].
|
|
2145
|
+
const ids = elsewhere.filter((id) => kindsOf(MUSTS[id]).includes(kind));
|
|
1892
2146
|
if (ids.length === 0) continue;
|
|
1893
2147
|
lines.push(` ${kind}: ${ids.join(", ")}`);
|
|
1894
2148
|
}
|
|
@@ -1911,4 +2165,4 @@ export {
|
|
|
1911
2165
|
miscoveredMusts,
|
|
1912
2166
|
formatReport
|
|
1913
2167
|
};
|
|
1914
|
-
//# sourceMappingURL=chunk-
|
|
2168
|
+
//# sourceMappingURL=chunk-MSM6QP2N.js.map
|