@byollm/conformance 0.1.0-alpha.5 → 0.1.0-alpha.50

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.
@@ -94,9 +94,11 @@ function daemonConfig(options) {
94
94
  const baseUrl = metered ? metered.baseUrl : options.subscription ? void 0 : "http://127.0.0.1:11434/v1";
95
95
  return resolveConfig(
96
96
  DaemonConfig.parse({
97
- backends: {
97
+ services: {
98
98
  primary: {
99
- backend: backendId,
99
+ model: "echo-model",
100
+ kinds: ["llm.generate", "llm.chat"],
101
+ type: backendId,
100
102
  ...baseUrl === void 0 ? {} : { baseUrl },
101
103
  offer: options.offer,
102
104
  ...metered === void 0 ? {} : {
@@ -107,10 +109,6 @@ function daemonConfig(options) {
107
109
  }
108
110
  }
109
111
  },
110
- routes: {
111
- "llm.generate": { backend: "primary", model: "echo-model" },
112
- "llm.chat": { backend: "primary", model: "echo-model" }
113
- },
114
112
  concurrency: 4
115
113
  })
116
114
  );
@@ -118,7 +116,7 @@ function daemonConfig(options) {
118
116
  async function pairDaemon(target, options) {
119
117
  const home = await mkdtemp(join(tmpdir(), "byollm-conformance-"));
120
118
  const loaded = daemonConfig({
121
- offer: options.offer ?? "self",
119
+ offer: options.offer ?? "private",
122
120
  subscription: options.subscription ?? false,
123
121
  ...options.metered === void 0 ? {} : { metered: options.metered }
124
122
  });
@@ -140,6 +138,8 @@ async function pairDaemon(target, options) {
140
138
  const fetchImpl = (input, init) => target.fetch(new Request(input, init));
141
139
  const capabilities = loaded.routes.map((route) => ({
142
140
  kind: route.kind,
141
+ service: route.service,
142
+ isDefault: true,
143
143
  backendId: route.backendId,
144
144
  backendClass: route.backendClass,
145
145
  model: route.model,
@@ -203,8 +203,10 @@ async function pairDaemon(target, options) {
203
203
  owner: result.pairing.owner,
204
204
  identity: {
205
205
  keys: () => deviceIdentity.load(Date.now()),
206
- // Pinned at pairing, exactly as a real daemon does.
207
- sitePinned: result.pairing.site
206
+ // Pinned at pairing, exactly as a real daemon does — the set the
207
+ // upstream answered with, keyed by each site's identity key id
208
+ // (cloud_009 §5). A direct site is one entry.
209
+ sites: new Map(Object.entries(result.pairing.sites))
208
210
  },
209
211
  daemonVersion: "conformance",
210
212
  loaded,
@@ -220,10 +222,9 @@ async function pairDaemon(target, options) {
220
222
  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.site,
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
- plaintext: JSON.stringify(input.outcome),
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: "self"
482
+ audience: "private"
449
483
  });
450
484
  for (let attempt = 0; attempt < 20; attempt += 1) {
451
485
  try {
@@ -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: "self"
528
+ audience: "private"
495
529
  });
496
530
  await bob.runner.tick();
497
531
  await sleep(50);
@@ -562,6 +596,26 @@ var CHECKS = [
562
596
  daemon.backend.seen.length > before,
563
597
  "the advertised kind never reached the backend"
564
598
  );
599
+ const chat = await target.enqueue({
600
+ kind: "llm.chat",
601
+ payload: { messages: [{ role: "user", content: "not for you" }] },
602
+ owner: "alice"
603
+ });
604
+ const generateOnly = await claimRaw(target, daemon, [
605
+ {
606
+ kind: "llm.generate",
607
+ service: "local",
608
+ isDefault: true,
609
+ backendId: "openai-http",
610
+ backendClass: "http",
611
+ model: "echo-model",
612
+ offerScope: "private"
613
+ }
614
+ ]);
615
+ assert(
616
+ !generateOnly.some((offered) => offered.id === chat.id),
617
+ "a server offered `llm.chat` to a claim advertising only `llm.generate`"
618
+ );
565
619
  } finally {
566
620
  await daemon.dispose();
567
621
  }
@@ -579,7 +633,7 @@ var CHECKS = [
579
633
  owner: "alice"
580
634
  });
581
635
  dead.backend.hangMs = 6e4;
582
- await dead.runner.tick();
636
+ const firstLease = await claimOne(target, dead);
583
637
  await waitFor(
584
638
  async () => {
585
639
  const state = await target.job(job.id);
@@ -594,12 +648,43 @@ var CHECKS = [
594
648
  label: "alive"
595
649
  });
596
650
  try {
597
- await alive.runner.tick();
598
- await waitFor(async () => (await target.job(job.id))?.state === "ok", {
599
- what: "the reclaimed job to complete"
651
+ const reclaimed = await claimOne(target, alive);
652
+ assert(
653
+ reclaimed.id === job.id,
654
+ "the reclaiming daemon did not get the job"
655
+ );
656
+ const late = await postResult(target, dead, {
657
+ jobId: job.id,
658
+ leaseId: firstLease.lease.id,
659
+ outcome: { outcome: "ok", text: "from the machine that vanished" }
600
660
  });
661
+ const lateBody = await late.json().catch(() => ({}));
662
+ assert(
663
+ lateBody.accepted !== true,
664
+ "a site accepted a result from a runner whose lease had lapsed"
665
+ );
666
+ const midflight = await target.job(job.id);
667
+ assert(
668
+ !midflight?.outcome,
669
+ "a lapsed holder's result was recorded over a live grant"
670
+ );
671
+ const proper = await postResult(target, alive, {
672
+ jobId: job.id,
673
+ leaseId: reclaimed.lease.id,
674
+ outcome: { outcome: "ok", text: "from the machine that took over" }
675
+ });
676
+ assert(
677
+ proper.status === 200,
678
+ `the reclaiming daemon could not finish the job (${String(proper.status)})`
679
+ );
680
+ const final = await target.job(job.id);
681
+ assert(
682
+ final?.outcome?.text === "from the machine that took over",
683
+ "the reclaimed job did not record the current holder's result"
684
+ );
601
685
  } finally {
602
686
  await alive.dispose();
687
+ await dead.dispose();
603
688
  }
604
689
  }
605
690
  },
@@ -609,15 +694,15 @@ var CHECKS = [
609
694
  musts: ["AUDIENCE_BOTH_SIDES", "NAMED_LOCAL_ALLOWLIST"],
610
695
  async run(target) {
611
696
  const expected = {
612
- "self:self": false,
613
- "self:named": false,
614
- "self:public": false,
615
- "named:self": false,
616
- "named:named": false,
697
+ "private:private": false,
698
+ "private:team": false,
699
+ "private:public": false,
700
+ "team:private": false,
701
+ "team:team": false,
617
702
  // refused locally — allowlist is empty
618
- "named:public": true,
619
- "public:self": false,
620
- "public:named": false,
703
+ "team:public": true,
704
+ "public:private": false,
705
+ "public:team": false,
621
706
  // refused locally — allowlist is empty
622
707
  "public:public": true
623
708
  };
@@ -653,13 +738,13 @@ var CHECKS = [
653
738
  title: "a named job runs only once the daemon's own allowlist admits it",
654
739
  musts: ["NAMED_LOCAL_ALLOWLIST", "REFUSAL_NOT_REOFFERED"],
655
740
  async run(target) {
656
- const bob = await pairDaemon(target, { owner: "bob", offer: "named" });
741
+ const bob = await pairDaemon(target, { owner: "bob", offer: "team" });
657
742
  try {
658
743
  const refused = await target.enqueue({
659
744
  kind: "llm.generate",
660
745
  payload: prompt("before"),
661
746
  owner: "alice",
662
- audience: "named"
747
+ audience: "team"
663
748
  });
664
749
  await bob.runner.tick();
665
750
  await sleep(80);
@@ -667,12 +752,10 @@ var CHECKS = [
667
752
  (await target.job(refused.id))?.state !== "ok",
668
753
  "a named job ran without the daemon's local allowlist admitting it"
669
754
  );
670
- const before = bob.backend.seen.length;
671
- await bob.runner.tick();
672
- await sleep(50);
755
+ const reoffered = await claimRaw(target, bob);
673
756
  assert(
674
- bob.backend.seen.length === before,
675
- "a refused job was re-offered to the runner that refused it"
757
+ !reoffered.some((job) => job.id === refused.id),
758
+ "a server re-offered a job to the runner that refused it"
676
759
  );
677
760
  await bob.allowlist.add(
678
761
  { origin: target.origin, owner: await ownerIdFor(target, "alice") },
@@ -682,7 +765,7 @@ var CHECKS = [
682
765
  kind: "llm.generate",
683
766
  payload: prompt("after"),
684
767
  owner: "alice",
685
- audience: "named"
768
+ audience: "team"
686
769
  });
687
770
  await bob.runner.tick();
688
771
  await waitFor(
@@ -730,7 +813,7 @@ var CHECKS = [
730
813
  kind: "llm.generate",
731
814
  payload: prompt("my own work"),
732
815
  owner: "bob",
733
- audience: "self"
816
+ audience: "private"
734
817
  });
735
818
  await bob.runner.tick();
736
819
  await waitFor(async () => (await target.job(own.id))?.state === "ok", {
@@ -744,7 +827,12 @@ var CHECKS = [
744
827
  {
745
828
  id: "C008_REVOCATION",
746
829
  title: "a revoked daemon stops mid-queue",
747
- musts: ["REVOCATION_HONORED"],
830
+ // Both halves, and this check already proved both: the daemon learns it
831
+ // is revoked (`REVOCATION_HONORED`), *and* the upstream leaves the job
832
+ // queued rather than granting it (`REVOCATION_IMMEDIATE`). The second
833
+ // assertion was here and cited nothing — which is how a MUST comes to be
834
+ // declared in a spec, absent from the registry, and tested all along.
835
+ musts: ["REVOCATION_HONORED", "REVOCATION_IMMEDIATE"],
748
836
  async run(target) {
749
837
  const daemon = await pairDaemon(target, { owner: "alice" });
750
838
  try {
@@ -809,32 +897,60 @@ var CHECKS = [
809
897
  payload: prompt("once"),
810
898
  owner: "alice"
811
899
  });
812
- await daemon.runner.tick();
813
- await waitFor(async () => (await target.job(job.id))?.state === "ok", {
814
- what: "the job to complete"
900
+ const claimed = await claimOne(target, daemon);
901
+ assert(claimed.id === job.id, "the harness could not claim its job");
902
+ const first = await postResult(target, daemon, {
903
+ jobId: job.id,
904
+ leaseId: claimed.lease.id,
905
+ outcome: { outcome: "ok", text: "the answer that counts" }
815
906
  });
816
- const first = await target.job(job.id);
817
- const response = await target.fetch(
818
- new Request(`${target.origin}/byollm/result`, {
819
- method: "POST",
820
- headers: { "content-type": "application/json" },
821
- body: JSON.stringify({
822
- protocolVersion: "0",
823
- runnerId: daemon.runnerId,
824
- jobId: job.id,
825
- outcome: { outcome: "ok", text: "SECOND ANSWER" },
826
- model: "echo-model",
827
- backendClass: "http",
828
- durationMs: 1
829
- })
830
- })
907
+ assert(
908
+ first.status === 200,
909
+ `a site refused the first result (${String(first.status)})`
910
+ );
911
+ const replay = await postResult(target, daemon, {
912
+ jobId: job.id,
913
+ leaseId: claimed.lease.id,
914
+ outcome: { outcome: "ok", text: "SECOND ANSWER" }
915
+ });
916
+ assert(
917
+ replay.status === 200,
918
+ `a replayed result was rejected rather than ignored (${String(replay.status)})`
919
+ );
920
+ const body = await replay.json();
921
+ assert(
922
+ body.accepted === false,
923
+ "a site reported a replayed result as newly accepted"
924
+ );
925
+ assert(
926
+ body.duplicate === true,
927
+ "a replay from the device that finished the job was not called a duplicate"
831
928
  );
832
- void response;
833
929
  const after = await target.job(job.id);
834
930
  assert(
835
- after?.outcome?.text === first?.outcome?.text,
836
- "a second result overwrote the first"
931
+ after?.outcome?.text === "the answer that counts",
932
+ `a second result overwrote the first (${String(after?.outcome?.text)})`
837
933
  );
934
+ const stranger = await pairDaemon(target, { owner: "alice" });
935
+ try {
936
+ const foreign = await postResult(target, stranger, {
937
+ jobId: job.id,
938
+ leaseId: claimed.lease.id,
939
+ outcome: { outcome: "ok", text: "not this device's to answer" }
940
+ });
941
+ const foreignBody = await foreign.json().catch(() => ({}));
942
+ assert(
943
+ foreignBody["duplicate"] !== true,
944
+ "a site told a device that never held this job it was a duplicate"
945
+ );
946
+ const stillFirst = await target.job(job.id);
947
+ assert(
948
+ stillFirst?.outcome?.text === "the answer that counts",
949
+ "a stranger's result overwrote a terminal job"
950
+ );
951
+ } finally {
952
+ await stranger.dispose();
953
+ }
838
954
  } finally {
839
955
  await daemon.dispose();
840
956
  }
@@ -852,13 +968,13 @@ var CHECKS = [
852
968
  kind: "llm.generate",
853
969
  payload: prompt("step one"),
854
970
  owner: "bob",
855
- audience: "self"
971
+ audience: "private"
856
972
  });
857
973
  const second = await target.enqueue({
858
974
  kind: "llm.generate",
859
975
  payload: prompt("step two"),
860
976
  owner: "alice",
861
- audience: "self",
977
+ audience: "private",
862
978
  dependsOn: [first.id]
863
979
  });
864
980
  await alice.runner.tick();
@@ -949,7 +1065,11 @@ var CHECKS = [
949
1065
  {
950
1066
  id: "C014_RESULT_PROVENANCE",
951
1067
  title: "a community result arrives marked untrusted, a self result does not",
952
- musts: ["RESULT_PROVENANCE"],
1068
+ // `PROVENANCE_NAMES_DEVICE` supersedes `RESULT_PROVENANCE` — a
1069
+ // strengthening rather than a rename. C030 is the other half: a label
1070
+ // means nothing unless a result whose signature does not verify against
1071
+ // the granted device is refused rather than recorded.
1072
+ musts: ["PROVENANCE_NAMES_DEVICE"],
953
1073
  async run(target) {
954
1074
  const bob = await pairDaemon(target, { owner: "bob", offer: "public" });
955
1075
  try {
@@ -977,7 +1097,7 @@ var CHECKS = [
977
1097
  kind: "llm.generate",
978
1098
  payload: prompt("my own"),
979
1099
  owner: "bob",
980
- audience: "self"
1100
+ audience: "private"
981
1101
  });
982
1102
  await bob.runner.tick();
983
1103
  await waitFor(async () => (await target.job(own.id))?.state === "ok", {
@@ -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
- musts: ["PAIR_ONE_USER"],
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(
@@ -1054,7 +1180,15 @@ 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
- musts: ["METERED_DEFAULTS_SELF", "COST_NOT_CONFIGURABLE"],
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",
@@ -1066,7 +1200,7 @@ var CHECKS = [
1066
1200
  });
1067
1201
  try {
1068
1202
  assert(
1069
- bob.loaded.routes.every((route) => route.offerScope === "self"),
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(
@@ -1107,7 +1241,7 @@ var CHECKS = [
1107
1241
  kind: "llm.generate",
1108
1242
  payload: prompt("my own work"),
1109
1243
  owner: "bob",
1110
- audience: "self"
1244
+ audience: "private"
1111
1245
  });
1112
1246
  await bob.runner.tick();
1113
1247
  await waitFor(async () => (await target.job(own.id))?.state === "ok", {
@@ -1183,7 +1317,7 @@ var CHECKS = [
1183
1317
  kind: "llm.generate",
1184
1318
  payload: prompt("my own work, my own key"),
1185
1319
  owner: "bob",
1186
- audience: "self"
1320
+ audience: "private"
1187
1321
  });
1188
1322
  await bob.runner.tick();
1189
1323
  await waitFor(async () => (await target.job(own.id))?.state === "ok", {
@@ -1206,7 +1340,7 @@ var CHECKS = [
1206
1340
  kind: "llm.generate",
1207
1341
  payload: prompt("only once, please"),
1208
1342
  owner: "alice",
1209
- audience: "self"
1343
+ audience: "private"
1210
1344
  });
1211
1345
  await Promise.all([a.runner.tick(), b.runner.tick()]);
1212
1346
  await waitFor(async () => (await target.job(job.id))?.state === "ok", {
@@ -1334,7 +1468,7 @@ var CHECKS = [
1334
1468
  baseUrl: "http://evil.test/v1"
1335
1469
  },
1336
1470
  owner: "alice",
1337
- audience: "self"
1471
+ audience: "private"
1338
1472
  });
1339
1473
  } catch {
1340
1474
  refused = true;
@@ -1364,7 +1498,7 @@ var CHECKS = [
1364
1498
  kind: "llm.generate",
1365
1499
  payload: prompt("ordinary text"),
1366
1500
  owner: "alice",
1367
- audience: "self"
1501
+ audience: "private"
1368
1502
  });
1369
1503
  await daemon.runner.tick();
1370
1504
  await waitFor(async () => (await target.job(ok.id))?.state === "ok", {
@@ -1471,7 +1605,7 @@ var CHECKS = [
1471
1605
  };
1472
1606
  const pending = await poll();
1473
1607
  assert(
1474
- pending["site"] === void 0,
1608
+ pending["sites"] === void 0,
1475
1609
  "a pending poll disclosed the site's keys before anyone approved"
1476
1610
  );
1477
1611
  await target.approvePairing(pairing.userCode, "alice");
@@ -1480,8 +1614,19 @@ var CHECKS = [
1480
1614
  approved["status"] === "approved",
1481
1615
  `poll after approval said "${String(approved["status"])}"`
1482
1616
  );
1483
- const site = PublicIdentity.safeParse(approved["site"]);
1484
- assert(site.success, "the approval carried no usable site identity");
1617
+ const offered = approved["sites"];
1618
+ assert(
1619
+ typeof offered === "object" && offered !== null,
1620
+ "the approval carried no sites to pin"
1621
+ );
1622
+ const parsed = Object.values(offered).map(
1623
+ (value) => PublicIdentity.safeParse(value)
1624
+ );
1625
+ assert(
1626
+ parsed.length > 0 && parsed.every((entry) => entry.success),
1627
+ "the approval carried no usable site identity"
1628
+ );
1629
+ const site = parsed[0];
1485
1630
  assert(
1486
1631
  verifyPublicIdentity(site.data),
1487
1632
  "the site's encryption key is not signed by the identity it presented"
@@ -1571,7 +1716,7 @@ var CHECKS = [
1571
1716
  kind: "llm.generate",
1572
1717
  payload: prompt("run me once"),
1573
1718
  owner: "alice",
1574
- audience: "self"
1719
+ audience: "private"
1575
1720
  });
1576
1721
  const first = await claimOne(target, daemon);
1577
1722
  assert(
@@ -1611,7 +1756,7 @@ var CHECKS = [
1611
1756
  kind: "llm.generate",
1612
1757
  payload: prompt("this must not appear in a claim response"),
1613
1758
  owner: "alice",
1614
- audience: "self"
1759
+ audience: "private"
1615
1760
  });
1616
1761
  const stub = await claimOne(target, daemon);
1617
1762
  const asRecord = stub;
@@ -1676,7 +1821,7 @@ var CHECKS = [
1676
1821
  kind: "llm.generate",
1677
1822
  payload: prompt(secret),
1678
1823
  owner: "alice",
1679
- audience: "self"
1824
+ audience: "private"
1680
1825
  });
1681
1826
  const stored = await target.job(job.id);
1682
1827
  assert(
@@ -1754,7 +1899,11 @@ var CHECKS = [
1754
1899
  {
1755
1900
  id: "C030_SITE_REFUSES_UNSIGNED_RESULTS",
1756
1901
  title: "a site refuses a result not signed by the device that ran it",
1757
- musts: ["ENVELOPE_SEALED_AND_SIGNED"],
1902
+ // The proof-of-possession half of `PROVENANCE_NAMES_DEVICE`: attribution
1903
+ // by a signature that verifies against the device the lease was granted
1904
+ // to, rather than by a key id carried beside the result. Carrying an id
1905
+ // is not proving possession, and a forger writes whatever it likes.
1906
+ musts: ["ENVELOPE_SEALED_AND_SIGNED", "PROVENANCE_NAMES_DEVICE"],
1758
1907
  async run(target) {
1759
1908
  const daemon = await pairDaemon(target, { owner: "alice" });
1760
1909
  try {
@@ -1768,6 +1917,7 @@ var CHECKS = [
1768
1917
  const relay = generateKeys(Date.now());
1769
1918
  const forged = await postResult(target, daemon, {
1770
1919
  jobId: job.id,
1920
+ leaseId: claimed.lease.id,
1771
1921
  outcome: { outcome: "ok", text: "an answer the device never gave" },
1772
1922
  sealWith: relay
1773
1923
  });
@@ -1782,6 +1932,7 @@ var CHECKS = [
1782
1932
  );
1783
1933
  const real = await postResult(target, daemon, {
1784
1934
  jobId: job.id,
1935
+ leaseId: claimed.lease.id,
1785
1936
  outcome: { outcome: "ok", text: "the genuine answer" }
1786
1937
  });
1787
1938
  assert(
@@ -1790,6 +1941,7 @@ var CHECKS = [
1790
1941
  );
1791
1942
  const lying = await postResult(target, daemon, {
1792
1943
  jobId: job.id,
1944
+ leaseId: claimed.lease.id,
1793
1945
  outcome: {
1794
1946
  outcome: "error",
1795
1947
  code: "backend-error",
@@ -1806,11 +1958,98 @@ var CHECKS = [
1806
1958
  await daemon.dispose();
1807
1959
  }
1808
1960
  }
1961
+ },
1962
+ {
1963
+ id: "C032_SERVER_REFUSES_TO_OFFER",
1964
+ title: "a claim is not answered with work the claimer may not run",
1965
+ musts: ["AUDIENCE_BOTH_SIDES"],
1966
+ async run(target) {
1967
+ const bob = await pairDaemon(target, { owner: "bob", offer: "public" });
1968
+ try {
1969
+ const priv = await target.enqueue({
1970
+ kind: "llm.generate",
1971
+ payload: prompt("alice's own machines only"),
1972
+ owner: "alice",
1973
+ audience: "private"
1974
+ });
1975
+ const offered = await claimRaw(target, bob);
1976
+ assert(
1977
+ !offered.some((job) => job.id === priv.id),
1978
+ "a server offered a `self` job to a device its owner does not own"
1979
+ );
1980
+ const shared = await target.enqueue({
1981
+ kind: "llm.generate",
1982
+ payload: prompt("anyone may run this"),
1983
+ owner: "alice",
1984
+ audience: "public"
1985
+ });
1986
+ const second = await claimRaw(target, bob);
1987
+ assert(
1988
+ second.some((job) => job.id === shared.id),
1989
+ "a server withheld a `public` job from a public-offering device"
1990
+ );
1991
+ } finally {
1992
+ await bob.dispose();
1993
+ }
1994
+ }
1995
+ },
1996
+ {
1997
+ id: "C031_ROSTER_NOT_DISCLOSED",
1998
+ title: "a claimed stub carries no list of who may run the job",
1999
+ musts: ["ROSTER_NOT_DISCLOSED"],
2000
+ async run(target) {
2001
+ const daemon = await pairDaemon(target, { owner: "alice" });
2002
+ try {
2003
+ const job = await target.enqueue({
2004
+ kind: "llm.generate",
2005
+ payload: prompt("who else is on this roster"),
2006
+ owner: "alice",
2007
+ audience: "team",
2008
+ // The site restricts the job to people who are not this daemon's
2009
+ // owner. A stub that carried the list would be handing a routing
2010
+ // party the membership of alice's group.
2011
+ audienceAllow: ["alice", "carol", "erin"]
2012
+ });
2013
+ const claimed = await claimOne(target, daemon);
2014
+ assert(
2015
+ claimed.id === job.id,
2016
+ "the harness could not claim its own named job"
2017
+ );
2018
+ const asRecord = claimed;
2019
+ assert(
2020
+ asRecord["audienceAllow"] === void 0,
2021
+ "a claimed stub carried audienceAllow"
2022
+ );
2023
+ const parsed = ClaimedStub.safeParse(claimed);
2024
+ assert(
2025
+ parsed.success,
2026
+ "the claim response is not a valid stub, so its fields prove nothing"
2027
+ );
2028
+ const wire = JSON.stringify(claimed);
2029
+ for (const member of ["carol", "erin"]) {
2030
+ assert(
2031
+ !wire.includes(member),
2032
+ `a claimed stub disclosed roster member "${member}"`
2033
+ );
2034
+ }
2035
+ assert(
2036
+ claimed.audience === "team",
2037
+ "the stub lost the audience routing decides on"
2038
+ );
2039
+ assert(
2040
+ typeof claimed.owner === "string" && claimed.owner.length > 0,
2041
+ "the stub lost the owner"
2042
+ );
2043
+ } finally {
2044
+ await daemon.dispose();
2045
+ }
2046
+ }
1809
2047
  }
1810
2048
  ];
1811
2049
 
1812
2050
  // src/certify.ts
1813
2051
  import {
2052
+ kindsOf,
1814
2053
  MUSTS,
1815
2054
  MUST_IDS,
1816
2055
  mustsVerifiedBy
@@ -1854,7 +2093,7 @@ function uncoveredMusts(checks = CHECKS) {
1854
2093
  return mustsVerifiedBy("conformance").filter((id) => !covered.has(id));
1855
2094
  }
1856
2095
  function miscoveredMusts(checks = CHECKS) {
1857
- return [...new Set(checks.flatMap((check) => check.musts))].filter((id) => MUSTS[id].verifiedBy !== "conformance").sort();
2096
+ return [...new Set(checks.flatMap((check) => check.musts))].filter((id) => !kindsOf(MUSTS[id]).includes("conformance")).sort();
1858
2097
  }
1859
2098
  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
2099
  function formatReport(report) {
@@ -1882,13 +2121,13 @@ function formatReport(report) {
1882
2121
  }
1883
2122
  }
1884
2123
  const elsewhere = MUST_IDS.filter(
1885
- (id) => MUSTS[id].verifiedBy !== "conformance"
2124
+ (id) => !kindsOf(MUSTS[id]).includes("conformance")
1886
2125
  );
1887
2126
  if (elsewhere.length > 0) {
1888
2127
  lines.push("");
1889
2128
  lines.push(" Verified elsewhere, not by this kit:");
1890
2129
  for (const kind of ["adversarial", "construction", "operator"]) {
1891
- const ids = elsewhere.filter((id) => MUSTS[id].verifiedBy === kind);
2130
+ const ids = elsewhere.filter((id) => kindsOf(MUSTS[id]).includes(kind));
1892
2131
  if (ids.length === 0) continue;
1893
2132
  lines.push(` ${kind}: ${ids.join(", ")}`);
1894
2133
  }
@@ -1911,4 +2150,4 @@ export {
1911
2150
  miscoveredMusts,
1912
2151
  formatReport
1913
2152
  };
1914
- //# sourceMappingURL=chunk-EXNALQ5D.js.map
2153
+ //# sourceMappingURL=chunk-FCX5R4LZ.js.map