@byollm/conformance 0.1.0-alpha.9 → 0.1.0-alpha.91
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 +121 -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-2JXKI5TH.js +476 -0
- package/dist/chunk-2JXKI5TH.js.map +1 -0
- package/dist/{chunk-EXNALQ5D.js → chunk-OPJBI7WK.js} +446 -185
- package/dist/chunk-OPJBI7WK.js.map +1 -0
- package/dist/cli.js +2 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +128 -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,
|
|
@@ -41,6 +40,13 @@ import {
|
|
|
41
40
|
DaemonConfig
|
|
42
41
|
} from "byollm";
|
|
43
42
|
var EchoBackend = class {
|
|
43
|
+
/* The conformance kit's own backend, answering the question every adapter
|
|
44
|
+
must — byollm_021. It echoes rather than generating, so there is no
|
|
45
|
+
model ceiling to hit and nothing to read. */
|
|
46
|
+
stopReasons = {
|
|
47
|
+
kind: "unavailable",
|
|
48
|
+
why: "the conformance echo backend generates nothing, so no model ever stops early"
|
|
49
|
+
};
|
|
44
50
|
id = "openai-http";
|
|
45
51
|
class = "http";
|
|
46
52
|
/** Prompts this backend was asked to run, in order. */
|
|
@@ -76,7 +82,6 @@ var EchoBackend = class {
|
|
|
76
82
|
ok: false,
|
|
77
83
|
code: "canceled",
|
|
78
84
|
message: "the job was canceled",
|
|
79
|
-
retryable: false,
|
|
80
85
|
durationMs: Date.now() - started
|
|
81
86
|
};
|
|
82
87
|
}
|
|
@@ -94,9 +99,11 @@ function daemonConfig(options) {
|
|
|
94
99
|
const baseUrl = metered ? metered.baseUrl : options.subscription ? void 0 : "http://127.0.0.1:11434/v1";
|
|
95
100
|
return resolveConfig(
|
|
96
101
|
DaemonConfig.parse({
|
|
97
|
-
|
|
102
|
+
services: {
|
|
98
103
|
primary: {
|
|
99
|
-
|
|
104
|
+
model: "echo-model",
|
|
105
|
+
kinds: ["llm.generate", "llm.chat"],
|
|
106
|
+
type: backendId,
|
|
100
107
|
...baseUrl === void 0 ? {} : { baseUrl },
|
|
101
108
|
offer: options.offer,
|
|
102
109
|
...metered === void 0 ? {} : {
|
|
@@ -107,10 +114,6 @@ function daemonConfig(options) {
|
|
|
107
114
|
}
|
|
108
115
|
}
|
|
109
116
|
},
|
|
110
|
-
routes: {
|
|
111
|
-
"llm.generate": { backend: "primary", model: "echo-model" },
|
|
112
|
-
"llm.chat": { backend: "primary", model: "echo-model" }
|
|
113
|
-
},
|
|
114
117
|
concurrency: 4
|
|
115
118
|
})
|
|
116
119
|
);
|
|
@@ -118,12 +121,10 @@ function daemonConfig(options) {
|
|
|
118
121
|
async function pairDaemon(target, options) {
|
|
119
122
|
const home = await mkdtemp(join(tmpdir(), "byollm-conformance-"));
|
|
120
123
|
const loaded = daemonConfig({
|
|
121
|
-
offer: options.offer
|
|
124
|
+
offer: options.offer,
|
|
122
125
|
subscription: options.subscription ?? false,
|
|
123
126
|
...options.metered === void 0 ? {} : { metered: options.metered }
|
|
124
127
|
});
|
|
125
|
-
const allowlist = new Allowlist(join(home, "allow.json"));
|
|
126
|
-
await allowlist.load();
|
|
127
128
|
const budgets = new Budgets(
|
|
128
129
|
join(home, "budgets.json"),
|
|
129
130
|
loaded.config.community
|
|
@@ -140,6 +141,7 @@ async function pairDaemon(target, options) {
|
|
|
140
141
|
const fetchImpl = (input, init) => target.fetch(new Request(input, init));
|
|
141
142
|
const capabilities = loaded.routes.map((route) => ({
|
|
142
143
|
kind: route.kind,
|
|
144
|
+
service: route.service,
|
|
143
145
|
backendId: route.backendId,
|
|
144
146
|
backendClass: route.backendClass,
|
|
145
147
|
model: route.model,
|
|
@@ -203,12 +205,13 @@ async function pairDaemon(target, options) {
|
|
|
203
205
|
owner: result.pairing.owner,
|
|
204
206
|
identity: {
|
|
205
207
|
keys: () => deviceIdentity.load(Date.now()),
|
|
206
|
-
// Pinned at pairing, exactly as a real daemon does
|
|
207
|
-
|
|
208
|
+
// Pinned at pairing, exactly as a real daemon does — the set the
|
|
209
|
+
// upstream answered with, keyed by each site's identity key id
|
|
210
|
+
// (cloud_009 §5). A direct site is one entry.
|
|
211
|
+
sites: new Map(Object.entries(result.pairing.sites))
|
|
208
212
|
},
|
|
209
213
|
daemonVersion: "conformance",
|
|
210
214
|
loaded,
|
|
211
|
-
allowlist,
|
|
212
215
|
budgets,
|
|
213
216
|
spend,
|
|
214
217
|
ingress,
|
|
@@ -217,13 +220,11 @@ async function pairDaemon(target, options) {
|
|
|
217
220
|
return {
|
|
218
221
|
runner,
|
|
219
222
|
backend,
|
|
220
|
-
allowlist,
|
|
221
223
|
runnerId: result.pairing.runnerId,
|
|
222
224
|
owner: result.pairing.owner,
|
|
223
|
-
token: result.pairing.token,
|
|
224
225
|
keys: await deviceIdentity.load(Date.now()),
|
|
225
226
|
identityKeys: () => deviceIdentity.load(Date.now()),
|
|
226
|
-
sitePinned: result.pairing.
|
|
227
|
+
sitePinned: Object.values(result.pairing.sites)[0],
|
|
227
228
|
home,
|
|
228
229
|
ingress,
|
|
229
230
|
spend,
|
|
@@ -309,6 +310,37 @@ async function claimOne(target, daemon) {
|
|
|
309
310
|
if (!job) throw new Error("claim returned no jobs");
|
|
310
311
|
return job;
|
|
311
312
|
}
|
|
313
|
+
async function claimRaw(target, daemon, capabilityOverride) {
|
|
314
|
+
const capabilities = capabilityOverride ?? await daemon.runner.detectCapabilities();
|
|
315
|
+
const body = JSON.stringify({
|
|
316
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
317
|
+
runnerId: daemon.runnerId,
|
|
318
|
+
capabilities,
|
|
319
|
+
max: 10
|
|
320
|
+
});
|
|
321
|
+
const signature = signRequest(daemon.keys, {
|
|
322
|
+
endpoint: "claim",
|
|
323
|
+
runnerId: daemon.runnerId,
|
|
324
|
+
issuedAt: Date.now(),
|
|
325
|
+
body
|
|
326
|
+
});
|
|
327
|
+
const response = await target.fetch(
|
|
328
|
+
new Request(`${target.origin}/byollm/claim`, {
|
|
329
|
+
method: "POST",
|
|
330
|
+
headers: {
|
|
331
|
+
"content-type": "application/json",
|
|
332
|
+
"x-byollm-runner": signature.runnerId,
|
|
333
|
+
"x-byollm-issued-at": String(signature.issuedAt),
|
|
334
|
+
"x-byollm-signature": signature.signature
|
|
335
|
+
},
|
|
336
|
+
body
|
|
337
|
+
})
|
|
338
|
+
);
|
|
339
|
+
if (response.status !== 200) {
|
|
340
|
+
throw new Error(`claim answered ${String(response.status)}`);
|
|
341
|
+
}
|
|
342
|
+
return (await response.json()).jobs;
|
|
343
|
+
}
|
|
312
344
|
async function releaseLease(target, daemon, jobId, leaseId) {
|
|
313
345
|
const body = JSON.stringify({
|
|
314
346
|
protocolVersion: PROTOCOL_VERSION,
|
|
@@ -383,7 +415,11 @@ async function postResult(target, daemon, input) {
|
|
|
383
415
|
const keys = await daemon.identityKeys();
|
|
384
416
|
const sealer = input.sealWith ?? keys;
|
|
385
417
|
const envelope = await seal({
|
|
386
|
-
|
|
418
|
+
// `{ outcome, ran }` — cloud_008 §2.5.
|
|
419
|
+
plaintext: JSON.stringify({
|
|
420
|
+
outcome: input.outcome,
|
|
421
|
+
ran: { model: "test-model", backendClass: "http", durationMs: 1 }
|
|
422
|
+
}),
|
|
387
423
|
senderKeys: sealer,
|
|
388
424
|
recipientEncryptionPublic: daemon.sitePinned.encryption,
|
|
389
425
|
context: {
|
|
@@ -401,11 +437,9 @@ async function postResult(target, daemon, input) {
|
|
|
401
437
|
protocolVersion: PROTOCOL_VERSION,
|
|
402
438
|
runnerId: daemon.runnerId,
|
|
403
439
|
jobId: input.jobId,
|
|
440
|
+
leaseId: input.leaseId,
|
|
404
441
|
envelope,
|
|
405
|
-
disposition: input.disposition ?? input.outcome.outcome
|
|
406
|
-
model: "conformance-model",
|
|
407
|
-
backendClass: "http",
|
|
408
|
-
durationMs: 1
|
|
442
|
+
disposition: input.disposition ?? input.outcome.outcome
|
|
409
443
|
});
|
|
410
444
|
const signature = signRequest(daemon.keys, {
|
|
411
445
|
endpoint: "result",
|
|
@@ -445,7 +479,7 @@ async function fetchGenuine(target, daemon, owner = "alice") {
|
|
|
445
479
|
// The target's own name for the user, not the id it mapped that to —
|
|
446
480
|
// passing a mapped id back in addresses a user the target never made.
|
|
447
481
|
owner,
|
|
448
|
-
audience: "
|
|
482
|
+
audience: "private"
|
|
449
483
|
});
|
|
450
484
|
for (let attempt = 0; attempt < 20; attempt += 1) {
|
|
451
485
|
try {
|
|
@@ -475,13 +509,19 @@ var CHECKS = [
|
|
|
475
509
|
title: "a runner token is bound to exactly the approving user",
|
|
476
510
|
musts: ["PAIR_ONE_USER", "PAIR_INTERACTIVE"],
|
|
477
511
|
async run(target) {
|
|
478
|
-
const alice = await pairDaemon(target, {
|
|
512
|
+
const alice = await pairDaemon(target, {
|
|
513
|
+
owner: "alice",
|
|
514
|
+
offer: "private"
|
|
515
|
+
});
|
|
479
516
|
try {
|
|
480
517
|
assert(
|
|
481
518
|
alice.owner === await ownerIdFor(target, "alice"),
|
|
482
519
|
`runner was bound to "${alice.owner}", not to the approving user`
|
|
483
520
|
);
|
|
484
|
-
const bob = await pairDaemon(target, {
|
|
521
|
+
const bob = await pairDaemon(target, {
|
|
522
|
+
owner: "bob",
|
|
523
|
+
offer: "private"
|
|
524
|
+
});
|
|
485
525
|
assert(
|
|
486
526
|
bob.owner !== alice.owner,
|
|
487
527
|
"two different approvers produced the same runner owner"
|
|
@@ -491,7 +531,7 @@ var CHECKS = [
|
|
|
491
531
|
kind: "llm.generate",
|
|
492
532
|
payload: prompt("alice's private prompt"),
|
|
493
533
|
owner: "alice",
|
|
494
|
-
audience: "
|
|
534
|
+
audience: "private"
|
|
495
535
|
});
|
|
496
536
|
await bob.runner.tick();
|
|
497
537
|
await sleep(50);
|
|
@@ -513,7 +553,10 @@ var CHECKS = [
|
|
|
513
553
|
title: "an enqueued job runs on the owner's daemon and the result comes back",
|
|
514
554
|
musts: ["CLAIM_REQUIRES_CAPABILITY", "RESULT_IDEMPOTENT"],
|
|
515
555
|
async run(target) {
|
|
516
|
-
const daemon = await pairDaemon(target, {
|
|
556
|
+
const daemon = await pairDaemon(target, {
|
|
557
|
+
owner: "alice",
|
|
558
|
+
offer: "private"
|
|
559
|
+
});
|
|
517
560
|
try {
|
|
518
561
|
const job = await target.enqueue({
|
|
519
562
|
kind: "llm.generate",
|
|
@@ -543,7 +586,10 @@ var CHECKS = [
|
|
|
543
586
|
title: "a daemon is never handed a kind it did not advertise",
|
|
544
587
|
musts: ["KIND_TYPED_ONLY", "CLAIM_REQUIRES_CAPABILITY"],
|
|
545
588
|
async run(target) {
|
|
546
|
-
const daemon = await pairDaemon(target, {
|
|
589
|
+
const daemon = await pairDaemon(target, {
|
|
590
|
+
owner: "alice",
|
|
591
|
+
offer: "private"
|
|
592
|
+
});
|
|
547
593
|
try {
|
|
548
594
|
const job = await target.enqueue({
|
|
549
595
|
kind: "llm.chat",
|
|
@@ -562,6 +608,25 @@ var CHECKS = [
|
|
|
562
608
|
daemon.backend.seen.length > before,
|
|
563
609
|
"the advertised kind never reached the backend"
|
|
564
610
|
);
|
|
611
|
+
const chat = await target.enqueue({
|
|
612
|
+
kind: "llm.chat",
|
|
613
|
+
payload: { messages: [{ role: "user", content: "not for you" }] },
|
|
614
|
+
owner: "alice"
|
|
615
|
+
});
|
|
616
|
+
const generateOnly = await claimRaw(target, daemon, [
|
|
617
|
+
{
|
|
618
|
+
kind: "llm.generate",
|
|
619
|
+
service: "local",
|
|
620
|
+
backendId: "openai-http",
|
|
621
|
+
backendClass: "http",
|
|
622
|
+
model: "echo-model",
|
|
623
|
+
offerScope: "private"
|
|
624
|
+
}
|
|
625
|
+
]);
|
|
626
|
+
assert(
|
|
627
|
+
!generateOnly.some((offered) => offered.id === chat.id),
|
|
628
|
+
"a server offered `llm.chat` to a claim advertising only `llm.generate`"
|
|
629
|
+
);
|
|
565
630
|
} finally {
|
|
566
631
|
await daemon.dispose();
|
|
567
632
|
}
|
|
@@ -572,14 +637,18 @@ var CHECKS = [
|
|
|
572
637
|
title: "a job whose runner vanished is offered again, losing nothing",
|
|
573
638
|
musts: ["LEASE_RECLAIMABLE", "LEASE_HONORED"],
|
|
574
639
|
async run(target) {
|
|
575
|
-
const dead = await pairDaemon(target, {
|
|
640
|
+
const dead = await pairDaemon(target, {
|
|
641
|
+
owner: "alice",
|
|
642
|
+
label: "dead",
|
|
643
|
+
offer: "private"
|
|
644
|
+
});
|
|
576
645
|
const job = await target.enqueue({
|
|
577
646
|
kind: "llm.generate",
|
|
578
647
|
payload: prompt("work"),
|
|
579
648
|
owner: "alice"
|
|
580
649
|
});
|
|
581
650
|
dead.backend.hangMs = 6e4;
|
|
582
|
-
await dead
|
|
651
|
+
const firstLease = await claimOne(target, dead);
|
|
583
652
|
await waitFor(
|
|
584
653
|
async () => {
|
|
585
654
|
const state = await target.job(job.id);
|
|
@@ -591,35 +660,61 @@ var CHECKS = [
|
|
|
591
660
|
await advance(target, target.leaseMs + 500);
|
|
592
661
|
const alive = await pairDaemon(target, {
|
|
593
662
|
owner: "alice",
|
|
594
|
-
label: "alive"
|
|
663
|
+
label: "alive",
|
|
664
|
+
offer: "private"
|
|
595
665
|
});
|
|
596
666
|
try {
|
|
597
|
-
await alive
|
|
598
|
-
|
|
599
|
-
|
|
667
|
+
const reclaimed = await claimOne(target, alive);
|
|
668
|
+
assert(
|
|
669
|
+
reclaimed.id === job.id,
|
|
670
|
+
"the reclaiming daemon did not get the job"
|
|
671
|
+
);
|
|
672
|
+
const late = await postResult(target, dead, {
|
|
673
|
+
jobId: job.id,
|
|
674
|
+
leaseId: firstLease.lease.id,
|
|
675
|
+
outcome: { outcome: "ok", text: "from the machine that vanished" }
|
|
676
|
+
});
|
|
677
|
+
const lateBody = await late.json().catch(() => ({}));
|
|
678
|
+
assert(
|
|
679
|
+
lateBody.accepted !== true,
|
|
680
|
+
"a site accepted a result from a runner whose lease had lapsed"
|
|
681
|
+
);
|
|
682
|
+
const midflight = await target.job(job.id);
|
|
683
|
+
assert(
|
|
684
|
+
!midflight?.outcome,
|
|
685
|
+
"a lapsed holder's result was recorded over a live grant"
|
|
686
|
+
);
|
|
687
|
+
const proper = await postResult(target, alive, {
|
|
688
|
+
jobId: job.id,
|
|
689
|
+
leaseId: reclaimed.lease.id,
|
|
690
|
+
outcome: { outcome: "ok", text: "from the machine that took over" }
|
|
600
691
|
});
|
|
692
|
+
assert(
|
|
693
|
+
proper.status === 200,
|
|
694
|
+
`the reclaiming daemon could not finish the job (${String(proper.status)})`
|
|
695
|
+
);
|
|
696
|
+
const final = await target.job(job.id);
|
|
697
|
+
assert(
|
|
698
|
+
final?.outcome?.text === "from the machine that took over",
|
|
699
|
+
"the reclaimed job did not record the current holder's result"
|
|
700
|
+
);
|
|
601
701
|
} finally {
|
|
602
702
|
await alive.dispose();
|
|
703
|
+
await dead.dispose();
|
|
603
704
|
}
|
|
604
705
|
}
|
|
605
706
|
},
|
|
606
707
|
{
|
|
607
708
|
id: "C005_AUDIENCE_MATRIX",
|
|
608
|
-
title: "all
|
|
709
|
+
title: "all four audience x offer-scope combinations behave as specified",
|
|
609
710
|
musts: ["AUDIENCE_BOTH_SIDES", "NAMED_LOCAL_ALLOWLIST"],
|
|
610
711
|
async run(target) {
|
|
611
712
|
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
|
|
713
|
+
"private:private": false,
|
|
714
|
+
"private:team": false,
|
|
715
|
+
"team:private": false,
|
|
716
|
+
"team:team": false
|
|
717
|
+
// refused locally — nothing admits alice
|
|
623
718
|
};
|
|
624
719
|
for (const audience of AUDIENCES) {
|
|
625
720
|
for (const offer of OFFER_SCOPES) {
|
|
@@ -650,16 +745,30 @@ var CHECKS = [
|
|
|
650
745
|
},
|
|
651
746
|
{
|
|
652
747
|
id: "C006_NAMED_LOCAL_ALLOWLIST",
|
|
653
|
-
|
|
748
|
+
/**
|
|
749
|
+
* Renamed with the release that made the sentence true — Amendment G, B2.
|
|
750
|
+
*
|
|
751
|
+
* The old title read "a named job runs only once the daemon's own
|
|
752
|
+
* allowlist admits it", which was true only under a generous reading of
|
|
753
|
+
* "own": the list was per-person and local, and a team member had to be
|
|
754
|
+
* enrolled on every machine by hand.
|
|
755
|
+
*
|
|
756
|
+
* The id does not change, per the id-stability law. What changes is the
|
|
757
|
+
* sentence, and it now names all three of the things a reader would
|
|
758
|
+
* otherwise take on faith — that the list is local, that its authority was
|
|
759
|
+
* established out of band, and that admission is a property of the asker
|
|
760
|
+
* rather than of the request.
|
|
761
|
+
*/
|
|
762
|
+
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
763
|
musts: ["NAMED_LOCAL_ALLOWLIST", "REFUSAL_NOT_REOFFERED"],
|
|
655
764
|
async run(target) {
|
|
656
|
-
const bob = await pairDaemon(target, { owner: "bob", offer: "
|
|
765
|
+
const bob = await pairDaemon(target, { owner: "bob", offer: "team" });
|
|
657
766
|
try {
|
|
658
767
|
const refused = await target.enqueue({
|
|
659
768
|
kind: "llm.generate",
|
|
660
769
|
payload: prompt("before"),
|
|
661
770
|
owner: "alice",
|
|
662
|
-
audience: "
|
|
771
|
+
audience: "team"
|
|
663
772
|
});
|
|
664
773
|
await bob.runner.tick();
|
|
665
774
|
await sleep(80);
|
|
@@ -667,27 +776,10 @@ var CHECKS = [
|
|
|
667
776
|
(await target.job(refused.id))?.state !== "ok",
|
|
668
777
|
"a named job ran without the daemon's local allowlist admitting it"
|
|
669
778
|
);
|
|
670
|
-
const
|
|
671
|
-
await bob.runner.tick();
|
|
672
|
-
await sleep(50);
|
|
779
|
+
const reoffered = await claimRaw(target, bob);
|
|
673
780
|
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" }
|
|
781
|
+
!reoffered.some((job) => job.id === refused.id),
|
|
782
|
+
"a server re-offered a job to the runner that refused it"
|
|
691
783
|
);
|
|
692
784
|
} finally {
|
|
693
785
|
await bob.dispose();
|
|
@@ -701,19 +793,15 @@ var CHECKS = [
|
|
|
701
793
|
async run(target) {
|
|
702
794
|
const bob = await pairDaemon(target, {
|
|
703
795
|
owner: "bob",
|
|
704
|
-
offer: "
|
|
796
|
+
offer: "team",
|
|
705
797
|
subscription: true
|
|
706
798
|
});
|
|
707
799
|
try {
|
|
708
|
-
await bob.allowlist.add(
|
|
709
|
-
{ origin: target.origin, owner: await ownerIdFor(target, "alice") },
|
|
710
|
-
Date.now()
|
|
711
|
-
);
|
|
712
800
|
const job = await target.enqueue({
|
|
713
801
|
kind: "llm.generate",
|
|
714
802
|
payload: prompt("someone else's work"),
|
|
715
803
|
owner: "alice",
|
|
716
|
-
audience: "
|
|
804
|
+
audience: "team"
|
|
717
805
|
});
|
|
718
806
|
await bob.runner.tick();
|
|
719
807
|
await sleep(80);
|
|
@@ -730,7 +818,7 @@ var CHECKS = [
|
|
|
730
818
|
kind: "llm.generate",
|
|
731
819
|
payload: prompt("my own work"),
|
|
732
820
|
owner: "bob",
|
|
733
|
-
audience: "
|
|
821
|
+
audience: "private"
|
|
734
822
|
});
|
|
735
823
|
await bob.runner.tick();
|
|
736
824
|
await waitFor(async () => (await target.job(own.id))?.state === "ok", {
|
|
@@ -744,9 +832,17 @@ var CHECKS = [
|
|
|
744
832
|
{
|
|
745
833
|
id: "C008_REVOCATION",
|
|
746
834
|
title: "a revoked daemon stops mid-queue",
|
|
747
|
-
|
|
835
|
+
// Both halves, and this check already proved both: the daemon learns it
|
|
836
|
+
// is revoked (`REVOCATION_HONORED`), *and* the upstream leaves the job
|
|
837
|
+
// queued rather than granting it (`REVOCATION_IMMEDIATE`). The second
|
|
838
|
+
// assertion was here and cited nothing — which is how a MUST comes to be
|
|
839
|
+
// declared in a spec, absent from the registry, and tested all along.
|
|
840
|
+
musts: ["REVOCATION_HONORED", "REVOCATION_IMMEDIATE"],
|
|
748
841
|
async run(target) {
|
|
749
|
-
const daemon = await pairDaemon(target, {
|
|
842
|
+
const daemon = await pairDaemon(target, {
|
|
843
|
+
owner: "alice",
|
|
844
|
+
offer: "private"
|
|
845
|
+
});
|
|
750
846
|
try {
|
|
751
847
|
await target.revokeRunner(daemon.runnerId);
|
|
752
848
|
const job = await target.enqueue({
|
|
@@ -774,7 +870,10 @@ var CHECKS = [
|
|
|
774
870
|
title: "cancel aborts a running job's backend call",
|
|
775
871
|
musts: ["CANCEL_HONORED"],
|
|
776
872
|
async run(target) {
|
|
777
|
-
const daemon = await pairDaemon(target, {
|
|
873
|
+
const daemon = await pairDaemon(target, {
|
|
874
|
+
owner: "alice",
|
|
875
|
+
offer: "private"
|
|
876
|
+
});
|
|
778
877
|
try {
|
|
779
878
|
daemon.backend.hangMs = 3e4;
|
|
780
879
|
const job = await target.enqueue({
|
|
@@ -802,39 +901,73 @@ var CHECKS = [
|
|
|
802
901
|
title: "the first terminal outcome wins",
|
|
803
902
|
musts: ["RESULT_IDEMPOTENT"],
|
|
804
903
|
async run(target) {
|
|
805
|
-
const daemon = await pairDaemon(target, {
|
|
904
|
+
const daemon = await pairDaemon(target, {
|
|
905
|
+
owner: "alice",
|
|
906
|
+
offer: "private"
|
|
907
|
+
});
|
|
806
908
|
try {
|
|
807
909
|
const job = await target.enqueue({
|
|
808
910
|
kind: "llm.generate",
|
|
809
911
|
payload: prompt("once"),
|
|
810
912
|
owner: "alice"
|
|
811
913
|
});
|
|
812
|
-
await daemon
|
|
813
|
-
|
|
814
|
-
|
|
914
|
+
const claimed = await claimOne(target, daemon);
|
|
915
|
+
assert(claimed.id === job.id, "the harness could not claim its job");
|
|
916
|
+
const first = await postResult(target, daemon, {
|
|
917
|
+
jobId: job.id,
|
|
918
|
+
leaseId: claimed.lease.id,
|
|
919
|
+
outcome: { outcome: "ok", text: "the answer that counts" }
|
|
815
920
|
});
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
921
|
+
assert(
|
|
922
|
+
first.status === 200,
|
|
923
|
+
`a site refused the first result (${String(first.status)})`
|
|
924
|
+
);
|
|
925
|
+
const replay = await postResult(target, daemon, {
|
|
926
|
+
jobId: job.id,
|
|
927
|
+
leaseId: claimed.lease.id,
|
|
928
|
+
outcome: { outcome: "ok", text: "SECOND ANSWER" }
|
|
929
|
+
});
|
|
930
|
+
assert(
|
|
931
|
+
replay.status === 200,
|
|
932
|
+
`a replayed result was rejected rather than ignored (${String(replay.status)})`
|
|
933
|
+
);
|
|
934
|
+
const body = await replay.json();
|
|
935
|
+
assert(
|
|
936
|
+
body.accepted === false,
|
|
937
|
+
"a site reported a replayed result as newly accepted"
|
|
938
|
+
);
|
|
939
|
+
assert(
|
|
940
|
+
body.duplicate === true,
|
|
941
|
+
"a replay from the device that finished the job was not called a duplicate"
|
|
831
942
|
);
|
|
832
|
-
void response;
|
|
833
943
|
const after = await target.job(job.id);
|
|
834
944
|
assert(
|
|
835
|
-
after?.outcome?.text ===
|
|
836
|
-
|
|
945
|
+
after?.outcome?.text === "the answer that counts",
|
|
946
|
+
`a second result overwrote the first (${String(after?.outcome?.text)})`
|
|
837
947
|
);
|
|
948
|
+
const stranger = await pairDaemon(target, {
|
|
949
|
+
owner: "alice",
|
|
950
|
+
offer: "private"
|
|
951
|
+
});
|
|
952
|
+
try {
|
|
953
|
+
const foreign = await postResult(target, stranger, {
|
|
954
|
+
jobId: job.id,
|
|
955
|
+
leaseId: claimed.lease.id,
|
|
956
|
+
outcome: { outcome: "ok", text: "not this device's to answer" }
|
|
957
|
+
});
|
|
958
|
+
const foreignBody = await foreign.json().catch(() => ({}));
|
|
959
|
+
assert(
|
|
960
|
+
foreignBody["duplicate"] !== true,
|
|
961
|
+
"a site told a device that never held this job it was a duplicate"
|
|
962
|
+
);
|
|
963
|
+
const stillFirst = await target.job(job.id);
|
|
964
|
+
assert(
|
|
965
|
+
stillFirst?.outcome?.text === "the answer that counts",
|
|
966
|
+
"a stranger's result overwrote a terminal job"
|
|
967
|
+
);
|
|
968
|
+
} finally {
|
|
969
|
+
await stranger.dispose();
|
|
970
|
+
}
|
|
838
971
|
} finally {
|
|
839
972
|
await daemon.dispose();
|
|
840
973
|
}
|
|
@@ -845,20 +978,23 @@ var CHECKS = [
|
|
|
845
978
|
title: "a dependent job waits for its dependency, across two daemons",
|
|
846
979
|
musts: ["DEPENDS_ON_GATING", "TTL_EXPIRY"],
|
|
847
980
|
async run(target) {
|
|
848
|
-
const alice = await pairDaemon(target, {
|
|
849
|
-
|
|
981
|
+
const alice = await pairDaemon(target, {
|
|
982
|
+
owner: "alice",
|
|
983
|
+
offer: "private"
|
|
984
|
+
});
|
|
985
|
+
const bob = await pairDaemon(target, { owner: "bob", offer: "private" });
|
|
850
986
|
try {
|
|
851
987
|
const first = await target.enqueue({
|
|
852
988
|
kind: "llm.generate",
|
|
853
989
|
payload: prompt("step one"),
|
|
854
990
|
owner: "bob",
|
|
855
|
-
audience: "
|
|
991
|
+
audience: "private"
|
|
856
992
|
});
|
|
857
993
|
const second = await target.enqueue({
|
|
858
994
|
kind: "llm.generate",
|
|
859
995
|
payload: prompt("step two"),
|
|
860
996
|
owner: "alice",
|
|
861
|
-
audience: "
|
|
997
|
+
audience: "private",
|
|
862
998
|
dependsOn: [first.id]
|
|
863
999
|
});
|
|
864
1000
|
await alice.runner.tick();
|
|
@@ -919,7 +1055,10 @@ var CHECKS = [
|
|
|
919
1055
|
title: "a dependent job's TTL starts when it becomes claimable, not at enqueue",
|
|
920
1056
|
musts: ["TTL_EXPIRY"],
|
|
921
1057
|
async run(target) {
|
|
922
|
-
const daemon = await pairDaemon(target, {
|
|
1058
|
+
const daemon = await pairDaemon(target, {
|
|
1059
|
+
owner: "alice",
|
|
1060
|
+
offer: "private"
|
|
1061
|
+
});
|
|
923
1062
|
try {
|
|
924
1063
|
daemon.backend.hangMs = target.ttlMs * 2;
|
|
925
1064
|
const first = await target.enqueue({
|
|
@@ -949,35 +1088,19 @@ var CHECKS = [
|
|
|
949
1088
|
{
|
|
950
1089
|
id: "C014_RESULT_PROVENANCE",
|
|
951
1090
|
title: "a community result arrives marked untrusted, a self result does not",
|
|
952
|
-
|
|
1091
|
+
// `PROVENANCE_NAMES_DEVICE` supersedes `RESULT_PROVENANCE` — a
|
|
1092
|
+
// strengthening rather than a rename. C030 is the other half: a label
|
|
1093
|
+
// means nothing unless a result whose signature does not verify against
|
|
1094
|
+
// the granted device is refused rather than recorded.
|
|
1095
|
+
musts: ["PROVENANCE_NAMES_DEVICE"],
|
|
953
1096
|
async run(target) {
|
|
954
|
-
const bob = await pairDaemon(target, { owner: "bob", offer: "
|
|
1097
|
+
const bob = await pairDaemon(target, { owner: "bob", offer: "team" });
|
|
955
1098
|
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
1099
|
const own = await target.enqueue({
|
|
977
1100
|
kind: "llm.generate",
|
|
978
1101
|
payload: prompt("my own"),
|
|
979
1102
|
owner: "bob",
|
|
980
|
-
audience: "
|
|
1103
|
+
audience: "private"
|
|
981
1104
|
});
|
|
982
1105
|
await bob.runner.tick();
|
|
983
1106
|
await waitFor(async () => (await target.job(own.id))?.state === "ok", {
|
|
@@ -997,7 +1120,10 @@ var CHECKS = [
|
|
|
997
1120
|
title: "every executed prompt is in the ingress log before it runs",
|
|
998
1121
|
musts: ["INGRESS_LOGGED_BEFORE_EXECUTION"],
|
|
999
1122
|
async run(target) {
|
|
1000
|
-
const daemon = await pairDaemon(target, {
|
|
1123
|
+
const daemon = await pairDaemon(target, {
|
|
1124
|
+
owner: "alice",
|
|
1125
|
+
offer: "private"
|
|
1126
|
+
});
|
|
1001
1127
|
let ticking = Promise.resolve();
|
|
1002
1128
|
try {
|
|
1003
1129
|
daemon.backend.hangMs = 3e4;
|
|
@@ -1031,7 +1157,13 @@ var CHECKS = [
|
|
|
1031
1157
|
{
|
|
1032
1158
|
id: "C016_UNAUTHENTICATED_REFUSED",
|
|
1033
1159
|
title: "the protocol endpoints refuse an unknown token",
|
|
1034
|
-
|
|
1160
|
+
// `CONSENT_BEFORE_ROUTE` on this plane. A relay has a consent record; a
|
|
1161
|
+
// direct site has pairing, and it is the same obligation — an upstream
|
|
1162
|
+
// routes to a device it has a record binding, and there is no discovery
|
|
1163
|
+
// path by which an unbound device receives work. Every endpoint is
|
|
1164
|
+
// checked rather than just `claim`, which is what makes it the absence
|
|
1165
|
+
// of a path rather than the absence of one door.
|
|
1166
|
+
musts: ["PAIR_ONE_USER", "CONSENT_BEFORE_ROUTE"],
|
|
1035
1167
|
async run(target) {
|
|
1036
1168
|
for (const endpoint of ["claim", "heartbeat", "result", "release"]) {
|
|
1037
1169
|
const response = await target.fetch(
|
|
@@ -1041,7 +1173,7 @@ var CHECKS = [
|
|
|
1041
1173
|
"content-type": "application/json",
|
|
1042
1174
|
authorization: "Bearer definitely-not-a-real-token"
|
|
1043
1175
|
},
|
|
1044
|
-
body: JSON.stringify({ protocolVersion:
|
|
1176
|
+
body: JSON.stringify({ protocolVersion: PROTOCOL_VERSION2 })
|
|
1045
1177
|
})
|
|
1046
1178
|
);
|
|
1047
1179
|
assert(
|
|
@@ -1054,11 +1186,19 @@ var CHECKS = [
|
|
|
1054
1186
|
{
|
|
1055
1187
|
id: "C017_METERED_DEFAULTS_SELF",
|
|
1056
1188
|
title: "a paid backend is not shared until its owner says so, with a ceiling",
|
|
1057
|
-
|
|
1189
|
+
// `EFFECTIVE_OFFER_ONLY` too: bob asks for `public`, what reaches the
|
|
1190
|
+
// server is `self`, and the server acts on what it was told rather than
|
|
1191
|
+
// on what was wanted. That *is* the effective-offer rule, proved here
|
|
1192
|
+
// without being named.
|
|
1193
|
+
musts: [
|
|
1194
|
+
"METERED_DEFAULTS_SELF",
|
|
1195
|
+
"COST_NOT_CONFIGURABLE",
|
|
1196
|
+
"EFFECTIVE_OFFER_ONLY"
|
|
1197
|
+
],
|
|
1058
1198
|
async run(target) {
|
|
1059
1199
|
const bob = await pairDaemon(target, {
|
|
1060
1200
|
owner: "bob",
|
|
1061
|
-
offer: "
|
|
1201
|
+
offer: "team",
|
|
1062
1202
|
// Pointed at localhost — which changes nothing, because a named
|
|
1063
1203
|
// provider's cost comes from the registry, not from an address
|
|
1064
1204
|
// ({@link MUSTS.COST_NOT_CONFIGURABLE}).
|
|
@@ -1066,22 +1206,18 @@ var CHECKS = [
|
|
|
1066
1206
|
});
|
|
1067
1207
|
try {
|
|
1068
1208
|
assert(
|
|
1069
|
-
bob.loaded.routes.every((route) => route.offerScope === "
|
|
1209
|
+
bob.loaded.routes.every((route) => route.offerScope === "private"),
|
|
1070
1210
|
"a metered backend was advertised beyond its owner without consent"
|
|
1071
1211
|
);
|
|
1072
1212
|
assert(
|
|
1073
1213
|
bob.loaded.routes.every((route) => route.cost === "metered"),
|
|
1074
1214
|
"a metered provider was read as free because of its base URL"
|
|
1075
1215
|
);
|
|
1076
|
-
await bob.allowlist.add(
|
|
1077
|
-
{ origin: target.origin, owner: await ownerIdFor(target, "alice") },
|
|
1078
|
-
Date.now()
|
|
1079
|
-
);
|
|
1080
1216
|
const job = await target.enqueue({
|
|
1081
1217
|
kind: "llm.generate",
|
|
1082
1218
|
payload: prompt("spend someone else's money"),
|
|
1083
1219
|
owner: "alice",
|
|
1084
|
-
audience: "
|
|
1220
|
+
audience: "team"
|
|
1085
1221
|
});
|
|
1086
1222
|
await bob.runner.tick();
|
|
1087
1223
|
await sleep(80);
|
|
@@ -1097,7 +1233,7 @@ var CHECKS = [
|
|
|
1097
1233
|
const availability = await target.runnerAvailability({
|
|
1098
1234
|
kind: "llm.generate",
|
|
1099
1235
|
owner: "alice",
|
|
1100
|
-
audience: "
|
|
1236
|
+
audience: "team"
|
|
1101
1237
|
});
|
|
1102
1238
|
assert(
|
|
1103
1239
|
!availability.available,
|
|
@@ -1107,7 +1243,7 @@ var CHECKS = [
|
|
|
1107
1243
|
kind: "llm.generate",
|
|
1108
1244
|
payload: prompt("my own work"),
|
|
1109
1245
|
owner: "bob",
|
|
1110
|
-
audience: "
|
|
1246
|
+
audience: "private"
|
|
1111
1247
|
});
|
|
1112
1248
|
await bob.runner.tick();
|
|
1113
1249
|
await waitFor(async () => (await target.job(own.id))?.state === "ok", {
|
|
@@ -1125,7 +1261,7 @@ var CHECKS = [
|
|
|
1125
1261
|
async run(target) {
|
|
1126
1262
|
const bob = await pairDaemon(target, {
|
|
1127
1263
|
owner: "bob",
|
|
1128
|
-
offer: "
|
|
1264
|
+
offer: "team",
|
|
1129
1265
|
metered: {
|
|
1130
1266
|
// The generic backend pointed at a remote address. No registry entry
|
|
1131
1267
|
// says what this costs; it is metered because of where it goes
|
|
@@ -1142,30 +1278,15 @@ var CHECKS = [
|
|
|
1142
1278
|
"a remote backend was treated as free"
|
|
1143
1279
|
);
|
|
1144
1280
|
assert(
|
|
1145
|
-
bob.loaded.routes.every((route) => route.offerScope === "
|
|
1281
|
+
bob.loaded.routes.every((route) => route.offerScope === "team"),
|
|
1146
1282
|
"a deliberately shared metered backend was narrowed anyway"
|
|
1147
1283
|
);
|
|
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
1284
|
await bob.spend.record("primary", 900, Date.now());
|
|
1164
1285
|
const second = await target.enqueue({
|
|
1165
1286
|
kind: "llm.generate",
|
|
1166
1287
|
payload: prompt("work past the ceiling"),
|
|
1167
1288
|
owner: "alice",
|
|
1168
|
-
audience: "
|
|
1289
|
+
audience: "team"
|
|
1169
1290
|
});
|
|
1170
1291
|
const seenBefore = bob.backend.seen.length;
|
|
1171
1292
|
await bob.runner.tick();
|
|
@@ -1183,7 +1304,7 @@ var CHECKS = [
|
|
|
1183
1304
|
kind: "llm.generate",
|
|
1184
1305
|
payload: prompt("my own work, my own key"),
|
|
1185
1306
|
owner: "bob",
|
|
1186
|
-
audience: "
|
|
1307
|
+
audience: "private"
|
|
1187
1308
|
});
|
|
1188
1309
|
await bob.runner.tick();
|
|
1189
1310
|
await waitFor(async () => (await target.job(own.id))?.state === "ok", {
|
|
@@ -1199,14 +1320,22 @@ var CHECKS = [
|
|
|
1199
1320
|
title: "two runners racing one job \u2014 exactly one gets it",
|
|
1200
1321
|
musts: ["CLAIM_ATOMIC"],
|
|
1201
1322
|
async run(target) {
|
|
1202
|
-
const a = await pairDaemon(target, {
|
|
1203
|
-
|
|
1323
|
+
const a = await pairDaemon(target, {
|
|
1324
|
+
owner: "alice",
|
|
1325
|
+
label: "laptop",
|
|
1326
|
+
offer: "private"
|
|
1327
|
+
});
|
|
1328
|
+
const b = await pairDaemon(target, {
|
|
1329
|
+
owner: "alice",
|
|
1330
|
+
label: "desktop",
|
|
1331
|
+
offer: "private"
|
|
1332
|
+
});
|
|
1204
1333
|
try {
|
|
1205
1334
|
const job = await target.enqueue({
|
|
1206
1335
|
kind: "llm.generate",
|
|
1207
1336
|
payload: prompt("only once, please"),
|
|
1208
1337
|
owner: "alice",
|
|
1209
|
-
audience: "
|
|
1338
|
+
audience: "private"
|
|
1210
1339
|
});
|
|
1211
1340
|
await Promise.all([a.runner.tick(), b.runner.tick()]);
|
|
1212
1341
|
await waitFor(async () => (await target.job(job.id))?.state === "ok", {
|
|
@@ -1281,7 +1410,10 @@ var CHECKS = [
|
|
|
1281
1410
|
title: "a runner advertises only what is installed and healthy",
|
|
1282
1411
|
musts: ["CAPABILITY_IS_DETECTED"],
|
|
1283
1412
|
async run(target) {
|
|
1284
|
-
const daemon = await pairDaemon(target, {
|
|
1413
|
+
const daemon = await pairDaemon(target, {
|
|
1414
|
+
owner: "alice",
|
|
1415
|
+
offer: "private"
|
|
1416
|
+
});
|
|
1285
1417
|
try {
|
|
1286
1418
|
daemon.backend.healthy = false;
|
|
1287
1419
|
const advertised = await daemon.runner.detectCapabilities();
|
|
@@ -1319,7 +1451,10 @@ var CHECKS = [
|
|
|
1319
1451
|
// check does not establish.
|
|
1320
1452
|
musts: ["KIND_NO_CODE"],
|
|
1321
1453
|
async run(target) {
|
|
1322
|
-
const daemon = await pairDaemon(target, {
|
|
1454
|
+
const daemon = await pairDaemon(target, {
|
|
1455
|
+
owner: "alice",
|
|
1456
|
+
offer: "private"
|
|
1457
|
+
});
|
|
1323
1458
|
const SMUGGLED = ["command", "argv", "model", "baseUrl"];
|
|
1324
1459
|
try {
|
|
1325
1460
|
let refused = false;
|
|
@@ -1334,7 +1469,7 @@ var CHECKS = [
|
|
|
1334
1469
|
baseUrl: "http://evil.test/v1"
|
|
1335
1470
|
},
|
|
1336
1471
|
owner: "alice",
|
|
1337
|
-
audience: "
|
|
1472
|
+
audience: "private"
|
|
1338
1473
|
});
|
|
1339
1474
|
} catch {
|
|
1340
1475
|
refused = true;
|
|
@@ -1364,7 +1499,7 @@ var CHECKS = [
|
|
|
1364
1499
|
kind: "llm.generate",
|
|
1365
1500
|
payload: prompt("ordinary text"),
|
|
1366
1501
|
owner: "alice",
|
|
1367
|
-
audience: "
|
|
1502
|
+
audience: "private"
|
|
1368
1503
|
});
|
|
1369
1504
|
await daemon.runner.tick();
|
|
1370
1505
|
await waitFor(async () => (await target.job(ok.id))?.state === "ok", {
|
|
@@ -1471,7 +1606,7 @@ var CHECKS = [
|
|
|
1471
1606
|
};
|
|
1472
1607
|
const pending = await poll();
|
|
1473
1608
|
assert(
|
|
1474
|
-
pending["
|
|
1609
|
+
pending["sites"] === void 0,
|
|
1475
1610
|
"a pending poll disclosed the site's keys before anyone approved"
|
|
1476
1611
|
);
|
|
1477
1612
|
await target.approvePairing(pairing.userCode, "alice");
|
|
@@ -1480,8 +1615,19 @@ var CHECKS = [
|
|
|
1480
1615
|
approved["status"] === "approved",
|
|
1481
1616
|
`poll after approval said "${String(approved["status"])}"`
|
|
1482
1617
|
);
|
|
1483
|
-
const
|
|
1484
|
-
assert(
|
|
1618
|
+
const offered = approved["sites"];
|
|
1619
|
+
assert(
|
|
1620
|
+
typeof offered === "object" && offered !== null,
|
|
1621
|
+
"the approval carried no sites to pin"
|
|
1622
|
+
);
|
|
1623
|
+
const parsed = Object.values(offered).map(
|
|
1624
|
+
(value) => PublicIdentity.safeParse(value)
|
|
1625
|
+
);
|
|
1626
|
+
assert(
|
|
1627
|
+
parsed.length > 0 && parsed.every((entry) => entry.success),
|
|
1628
|
+
"the approval carried no usable site identity"
|
|
1629
|
+
);
|
|
1630
|
+
const site = parsed[0];
|
|
1485
1631
|
assert(
|
|
1486
1632
|
verifyPublicIdentity(site.data),
|
|
1487
1633
|
"the site's encryption key is not signed by the identity it presented"
|
|
@@ -1493,7 +1639,10 @@ var CHECKS = [
|
|
|
1493
1639
|
title: "authentication is a signature over the request, not a secret",
|
|
1494
1640
|
musts: ["REQUESTS_SIGNED_NOT_BEARER"],
|
|
1495
1641
|
async run(target) {
|
|
1496
|
-
const daemon = await pairDaemon(target, {
|
|
1642
|
+
const daemon = await pairDaemon(target, {
|
|
1643
|
+
owner: "alice",
|
|
1644
|
+
offer: "private"
|
|
1645
|
+
});
|
|
1497
1646
|
try {
|
|
1498
1647
|
const body = JSON.stringify({
|
|
1499
1648
|
protocolVersion: PROTOCOL_VERSION2,
|
|
@@ -1565,13 +1714,16 @@ var CHECKS = [
|
|
|
1565
1714
|
title: "a release acts on the lease it names, not whatever lease exists",
|
|
1566
1715
|
musts: ["LEASE_SCOPED_BY_GRANT"],
|
|
1567
1716
|
async run(target) {
|
|
1568
|
-
const daemon = await pairDaemon(target, {
|
|
1717
|
+
const daemon = await pairDaemon(target, {
|
|
1718
|
+
owner: "alice",
|
|
1719
|
+
offer: "private"
|
|
1720
|
+
});
|
|
1569
1721
|
try {
|
|
1570
1722
|
const job = await target.enqueue({
|
|
1571
1723
|
kind: "llm.generate",
|
|
1572
1724
|
payload: prompt("run me once"),
|
|
1573
1725
|
owner: "alice",
|
|
1574
|
-
audience: "
|
|
1726
|
+
audience: "private"
|
|
1575
1727
|
});
|
|
1576
1728
|
const first = await claimOne(target, daemon);
|
|
1577
1729
|
assert(
|
|
@@ -1605,13 +1757,16 @@ var CHECKS = [
|
|
|
1605
1757
|
title: "a claim carries routing metadata and no work",
|
|
1606
1758
|
musts: ["STUB_METADATA_EXHAUSTIVE"],
|
|
1607
1759
|
async run(target) {
|
|
1608
|
-
const daemon = await pairDaemon(target, {
|
|
1760
|
+
const daemon = await pairDaemon(target, {
|
|
1761
|
+
owner: "alice",
|
|
1762
|
+
offer: "private"
|
|
1763
|
+
});
|
|
1609
1764
|
try {
|
|
1610
1765
|
await target.enqueue({
|
|
1611
1766
|
kind: "llm.generate",
|
|
1612
1767
|
payload: prompt("this must not appear in a claim response"),
|
|
1613
1768
|
owner: "alice",
|
|
1614
|
-
audience: "
|
|
1769
|
+
audience: "private"
|
|
1615
1770
|
});
|
|
1616
1771
|
const stub = await claimOne(target, daemon);
|
|
1617
1772
|
const asRecord = stub;
|
|
@@ -1670,13 +1825,16 @@ var CHECKS = [
|
|
|
1670
1825
|
musts: ["ENVELOPE_SEALED_AND_SIGNED"],
|
|
1671
1826
|
async run(target) {
|
|
1672
1827
|
const secret = "a prompt nobody should read from storage";
|
|
1673
|
-
const daemon = await pairDaemon(target, {
|
|
1828
|
+
const daemon = await pairDaemon(target, {
|
|
1829
|
+
owner: "alice",
|
|
1830
|
+
offer: "private"
|
|
1831
|
+
});
|
|
1674
1832
|
try {
|
|
1675
1833
|
const job = await target.enqueue({
|
|
1676
1834
|
kind: "llm.generate",
|
|
1677
1835
|
payload: prompt(secret),
|
|
1678
1836
|
owner: "alice",
|
|
1679
|
-
audience: "
|
|
1837
|
+
audience: "private"
|
|
1680
1838
|
});
|
|
1681
1839
|
const stored = await target.job(job.id);
|
|
1682
1840
|
assert(
|
|
@@ -1709,7 +1867,10 @@ var CHECKS = [
|
|
|
1709
1867
|
title: "a daemon refuses work not signed by the site it pinned",
|
|
1710
1868
|
musts: ["ENVELOPE_SEALED_AND_SIGNED"],
|
|
1711
1869
|
async run(target) {
|
|
1712
|
-
const daemon = await pairDaemon(target, {
|
|
1870
|
+
const daemon = await pairDaemon(target, {
|
|
1871
|
+
owner: "alice",
|
|
1872
|
+
offer: "private"
|
|
1873
|
+
});
|
|
1713
1874
|
try {
|
|
1714
1875
|
const keys = await daemon.identityKeys();
|
|
1715
1876
|
const relay = generateKeys(Date.now());
|
|
@@ -1754,9 +1915,16 @@ var CHECKS = [
|
|
|
1754
1915
|
{
|
|
1755
1916
|
id: "C030_SITE_REFUSES_UNSIGNED_RESULTS",
|
|
1756
1917
|
title: "a site refuses a result not signed by the device that ran it",
|
|
1757
|
-
|
|
1918
|
+
// The proof-of-possession half of `PROVENANCE_NAMES_DEVICE`: attribution
|
|
1919
|
+
// by a signature that verifies against the device the lease was granted
|
|
1920
|
+
// to, rather than by a key id carried beside the result. Carrying an id
|
|
1921
|
+
// is not proving possession, and a forger writes whatever it likes.
|
|
1922
|
+
musts: ["ENVELOPE_SEALED_AND_SIGNED", "PROVENANCE_NAMES_DEVICE"],
|
|
1758
1923
|
async run(target) {
|
|
1759
|
-
const daemon = await pairDaemon(target, {
|
|
1924
|
+
const daemon = await pairDaemon(target, {
|
|
1925
|
+
owner: "alice",
|
|
1926
|
+
offer: "private"
|
|
1927
|
+
});
|
|
1760
1928
|
try {
|
|
1761
1929
|
const job = await target.enqueue({
|
|
1762
1930
|
kind: "llm.generate",
|
|
@@ -1768,6 +1936,7 @@ var CHECKS = [
|
|
|
1768
1936
|
const relay = generateKeys(Date.now());
|
|
1769
1937
|
const forged = await postResult(target, daemon, {
|
|
1770
1938
|
jobId: job.id,
|
|
1939
|
+
leaseId: claimed.lease.id,
|
|
1771
1940
|
outcome: { outcome: "ok", text: "an answer the device never gave" },
|
|
1772
1941
|
sealWith: relay
|
|
1773
1942
|
});
|
|
@@ -1782,6 +1951,7 @@ var CHECKS = [
|
|
|
1782
1951
|
);
|
|
1783
1952
|
const real = await postResult(target, daemon, {
|
|
1784
1953
|
jobId: job.id,
|
|
1954
|
+
leaseId: claimed.lease.id,
|
|
1785
1955
|
outcome: { outcome: "ok", text: "the genuine answer" }
|
|
1786
1956
|
});
|
|
1787
1957
|
assert(
|
|
@@ -1790,6 +1960,7 @@ var CHECKS = [
|
|
|
1790
1960
|
);
|
|
1791
1961
|
const lying = await postResult(target, daemon, {
|
|
1792
1962
|
jobId: job.id,
|
|
1963
|
+
leaseId: claimed.lease.id,
|
|
1793
1964
|
outcome: {
|
|
1794
1965
|
outcome: "error",
|
|
1795
1966
|
code: "backend-error",
|
|
@@ -1806,11 +1977,101 @@ var CHECKS = [
|
|
|
1806
1977
|
await daemon.dispose();
|
|
1807
1978
|
}
|
|
1808
1979
|
}
|
|
1980
|
+
},
|
|
1981
|
+
{
|
|
1982
|
+
id: "C032_SERVER_REFUSES_TO_OFFER",
|
|
1983
|
+
title: "a claim is not answered with work the claimer may not run",
|
|
1984
|
+
musts: ["AUDIENCE_BOTH_SIDES"],
|
|
1985
|
+
async run(target) {
|
|
1986
|
+
const bob = await pairDaemon(target, { owner: "bob", offer: "team" });
|
|
1987
|
+
try {
|
|
1988
|
+
const priv = await target.enqueue({
|
|
1989
|
+
kind: "llm.generate",
|
|
1990
|
+
payload: prompt("alice's own machines only"),
|
|
1991
|
+
owner: "alice",
|
|
1992
|
+
audience: "private"
|
|
1993
|
+
});
|
|
1994
|
+
const offered = await claimRaw(target, bob);
|
|
1995
|
+
assert(
|
|
1996
|
+
!offered.some((job) => job.id === priv.id),
|
|
1997
|
+
"a server offered a `self` job to a device its owner does not own"
|
|
1998
|
+
);
|
|
1999
|
+
const shared = await target.enqueue({
|
|
2000
|
+
kind: "llm.generate",
|
|
2001
|
+
payload: prompt("anyone may run this"),
|
|
2002
|
+
owner: "alice",
|
|
2003
|
+
audience: "team"
|
|
2004
|
+
});
|
|
2005
|
+
const second = await claimRaw(target, bob);
|
|
2006
|
+
assert(
|
|
2007
|
+
second.some((job) => job.id === shared.id),
|
|
2008
|
+
"a server withheld a `public` job from a public-offering device"
|
|
2009
|
+
);
|
|
2010
|
+
} finally {
|
|
2011
|
+
await bob.dispose();
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
},
|
|
2015
|
+
{
|
|
2016
|
+
id: "C031_ROSTER_NOT_DISCLOSED",
|
|
2017
|
+
title: "a claimed stub carries no list of who may run the job",
|
|
2018
|
+
musts: ["ROSTER_NOT_DISCLOSED"],
|
|
2019
|
+
async run(target) {
|
|
2020
|
+
const daemon = await pairDaemon(target, {
|
|
2021
|
+
owner: "alice",
|
|
2022
|
+
offer: "private"
|
|
2023
|
+
});
|
|
2024
|
+
try {
|
|
2025
|
+
const job = await target.enqueue({
|
|
2026
|
+
kind: "llm.generate",
|
|
2027
|
+
payload: prompt("who else is on this roster"),
|
|
2028
|
+
owner: "alice",
|
|
2029
|
+
audience: "team",
|
|
2030
|
+
// The site restricts the job to people who are not this daemon's
|
|
2031
|
+
// owner. A stub that carried the list would be handing a routing
|
|
2032
|
+
// party the membership of alice's group.
|
|
2033
|
+
audienceAllow: ["alice", "carol", "erin"]
|
|
2034
|
+
});
|
|
2035
|
+
const claimed = await claimOne(target, daemon);
|
|
2036
|
+
assert(
|
|
2037
|
+
claimed.id === job.id,
|
|
2038
|
+
"the harness could not claim its own named job"
|
|
2039
|
+
);
|
|
2040
|
+
const asRecord = claimed;
|
|
2041
|
+
assert(
|
|
2042
|
+
asRecord["audienceAllow"] === void 0,
|
|
2043
|
+
"a claimed stub carried audienceAllow"
|
|
2044
|
+
);
|
|
2045
|
+
const parsed = ClaimedStub.safeParse(claimed);
|
|
2046
|
+
assert(
|
|
2047
|
+
parsed.success,
|
|
2048
|
+
"the claim response is not a valid stub, so its fields prove nothing"
|
|
2049
|
+
);
|
|
2050
|
+
const wire = JSON.stringify(claimed);
|
|
2051
|
+
for (const member of ["carol", "erin"]) {
|
|
2052
|
+
assert(
|
|
2053
|
+
!wire.includes(member),
|
|
2054
|
+
`a claimed stub disclosed roster member "${member}"`
|
|
2055
|
+
);
|
|
2056
|
+
}
|
|
2057
|
+
assert(
|
|
2058
|
+
claimed.audience === "team",
|
|
2059
|
+
"the stub lost the audience routing decides on"
|
|
2060
|
+
);
|
|
2061
|
+
assert(
|
|
2062
|
+
typeof claimed.owner === "string" && claimed.owner.length > 0,
|
|
2063
|
+
"the stub lost the owner"
|
|
2064
|
+
);
|
|
2065
|
+
} finally {
|
|
2066
|
+
await daemon.dispose();
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
1809
2069
|
}
|
|
1810
2070
|
];
|
|
1811
2071
|
|
|
1812
2072
|
// src/certify.ts
|
|
1813
2073
|
import {
|
|
2074
|
+
kindsOf,
|
|
1814
2075
|
MUSTS,
|
|
1815
2076
|
MUST_IDS,
|
|
1816
2077
|
mustsVerifiedBy
|
|
@@ -1854,7 +2115,7 @@ function uncoveredMusts(checks = CHECKS) {
|
|
|
1854
2115
|
return mustsVerifiedBy("conformance").filter((id) => !covered.has(id));
|
|
1855
2116
|
}
|
|
1856
2117
|
function miscoveredMusts(checks = CHECKS) {
|
|
1857
|
-
return [...new Set(checks.flatMap((check) => check.musts))].filter((id) => MUSTS[id].
|
|
2118
|
+
return [...new Set(checks.flatMap((check) => check.musts))].filter((id) => !kindsOf(MUSTS[id]).includes("conformance")).sort();
|
|
1858
2119
|
}
|
|
1859
2120
|
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
2121
|
function formatReport(report) {
|
|
@@ -1882,13 +2143,13 @@ function formatReport(report) {
|
|
|
1882
2143
|
}
|
|
1883
2144
|
}
|
|
1884
2145
|
const elsewhere = MUST_IDS.filter(
|
|
1885
|
-
(id) => MUSTS[id].
|
|
2146
|
+
(id) => !kindsOf(MUSTS[id]).includes("conformance")
|
|
1886
2147
|
);
|
|
1887
2148
|
if (elsewhere.length > 0) {
|
|
1888
2149
|
lines.push("");
|
|
1889
2150
|
lines.push(" Verified elsewhere, not by this kit:");
|
|
1890
2151
|
for (const kind of ["adversarial", "construction", "operator"]) {
|
|
1891
|
-
const ids = elsewhere.filter((id) => MUSTS[id].
|
|
2152
|
+
const ids = elsewhere.filter((id) => kindsOf(MUSTS[id]).includes(kind));
|
|
1892
2153
|
if (ids.length === 0) continue;
|
|
1893
2154
|
lines.push(` ${kind}: ${ids.join(", ")}`);
|
|
1894
2155
|
}
|
|
@@ -1911,4 +2172,4 @@ export {
|
|
|
1911
2172
|
miscoveredMusts,
|
|
1912
2173
|
formatReport
|
|
1913
2174
|
};
|
|
1914
|
-
//# sourceMappingURL=chunk-
|
|
2175
|
+
//# sourceMappingURL=chunk-OPJBI7WK.js.map
|