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