@byollm/conformance 0.1.0-alpha.1 → 0.1.0-alpha.100

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/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { JobKind, JobPayload, Audience, JobState, MustId } from '@byollm/protocol';
2
- import { Backend, BackendRequest, BackendResult, Runner, Allowlist, IngressLog } from 'byollm';
1
+ import { JobKind, JobPayload, Audience, JobState, MustId, StoredKeys, PublicIdentity } from '@byollm/protocol';
2
+ import { Backend, BackendRequest, BackendResult, Runner, IngressLog, SpendLedger, LoadedConfig } from 'byollm';
3
3
 
4
4
  /**
5
5
  * What a server implementation must expose to be certified.
@@ -145,8 +145,26 @@ declare function certify(target: ConformanceTarget, options?: {
145
145
  only?: readonly string[];
146
146
  onProgress?: (result: CheckResult) => void;
147
147
  }): Promise<CertificationReport>;
148
- /** MUSTs with no check asserting them. */
148
+ /**
149
+ * `conformance`-kind MUSTs with no check asserting them.
150
+ *
151
+ * This counts only the MUSTs the kit is *able* to assert. It used to count
152
+ * all of them, which made a permanent structural fact — the kit certifies a
153
+ * server, and a third of the MUSTs are properties of a daemon — look like a
154
+ * backlog of ten missing tests. A number that can never reach zero gets
155
+ * ignored, and a number that is ignored is not a check.
156
+ *
157
+ * This one should be zero, and CI keeps it there.
158
+ */
149
159
  declare function uncoveredMusts(checks?: readonly Check[]): MustId[];
160
+ /**
161
+ * MUSTs a check claims but which are not verifiable by conformance.
162
+ *
163
+ * The opposite error, and the one that would quietly overstate what a
164
+ * certification means: a check asserting an `operator`-kind MUST would put
165
+ * "verified" next to something no third party can check from outside.
166
+ */
167
+ declare function miscoveredMusts(checks?: readonly Check[]): MustId[];
150
168
  /** A human-readable report. */
151
169
  declare function formatReport(report: CertificationReport): string;
152
170
 
@@ -159,12 +177,20 @@ declare function formatReport(report: CertificationReport): string;
159
177
  * every respect except the thing at the very end of the call.
160
178
  */
161
179
  declare class EchoBackend implements Backend {
180
+ readonly stopReasons: {
181
+ kind: "unavailable";
182
+ why: string;
183
+ };
162
184
  readonly id: "openai-http";
163
185
  readonly class: "http";
164
186
  /** Prompts this backend was asked to run, in order. */
165
187
  readonly seen: string[];
166
188
  /** Set to make the next call hang, for lease and cancel checks. */
167
189
  hangMs: number;
190
+ /** Set false to simulate the model not being installed or not running. */
191
+ healthy: boolean;
192
+ /** What the backend reports it can serve. Empty means "does not enumerate". */
193
+ models: string[];
168
194
  health(): Promise<{
169
195
  healthy: boolean;
170
196
  models: string[];
@@ -174,11 +200,27 @@ declare class EchoBackend implements Backend {
174
200
  interface HarnessDaemon {
175
201
  readonly runner: Runner;
176
202
  readonly backend: EchoBackend;
177
- readonly allowlist: Allowlist;
178
203
  readonly runnerId: string;
179
204
  readonly owner: string;
205
+ /** This daemon's keys, so a check can sign as it — or deliberately not. */
206
+ readonly keys: StoredKeys;
207
+ /** This daemon's keys, and the site identities it pinned at pairing. */
208
+ identityKeys(): Promise<StoredKeys>;
209
+ /**
210
+ * The site a single-site check seals to and verifies against.
211
+ *
212
+ * A pairing covers a set now, and every check in this kit pairs with one
213
+ * upstream serving one site — so this is that entry, read from the set
214
+ * rather than kept beside it. Two copies of "which key opens this" is the
215
+ * bug the set exists to remove.
216
+ */
217
+ readonly sitePinned: PublicIdentity;
180
218
  readonly home: string;
181
219
  readonly ingress: IngressLog;
220
+ /** The owner's spend ledger, so a check can drive it past its ceiling. */
221
+ readonly spend: SpendLedger;
222
+ /** The resolved config — the effective offer scope lives here. */
223
+ readonly loaded: LoadedConfig;
182
224
  /** Stop cleanly: cancel in-flight work and clean up. */
183
225
  dispose(): Promise<void>;
184
226
  /**
@@ -198,12 +240,40 @@ interface HarnessDaemon {
198
240
  * pairing exchange, with the shipped allowlist and budget checks. Only the
199
241
  * model at the far end is substituted.
200
242
  */
243
+ /**
244
+ * A paid backend, and what the owner said about spending on it — byollm_007.
245
+ *
246
+ * The kit needs this because "who pays" is visible on the wire: a daemon
247
+ * advertises the *effective* offer scope, so a metered backend nobody
248
+ * consented to share shows up to the server as `self` and the server is
249
+ * obliged to act on that.
250
+ */
251
+ interface MeteredOptions {
252
+ /**
253
+ * `openai` takes its cost from the registry; `openai-http` has it inferred
254
+ * from {@link MeteredOptions.baseUrl}.
255
+ */
256
+ readonly provider?: "openai" | "openai-http";
257
+ readonly baseUrl?: string;
258
+ readonly acknowledged?: boolean;
259
+ readonly dailyCapCents?: number;
260
+ }
201
261
  declare function pairDaemon(target: ConformanceTarget, options: {
202
262
  owner: string;
203
263
  label?: string;
204
- offer?: "self" | "named" | "public";
264
+ /**
265
+ * **Required — no default.** A harness default is part of every test's
266
+ * claim (ruled 2026-08-26), and this one decides whether the device's
267
+ * admission check runs at all. The relay suite's equivalent defaulted to
268
+ * `public` and silently disabled admission in every cross-user check it
269
+ * had; this one defaulted to the safe direction and was still a value no
270
+ * reader of a call site could see.
271
+ */
272
+ offer: "private" | "team";
205
273
  /** Use the subscription-class backend, to exercise the self-lock. */
206
274
  subscription?: boolean;
275
+ /** Use a paid backend, to exercise the cost rules. */
276
+ metered?: MeteredOptions;
207
277
  }): Promise<HarnessDaemon>;
208
278
  /**
209
279
  * The id this target uses for a person, given the friendly name the checks
@@ -217,10 +287,110 @@ declare function waitFor(predicate: () => boolean | Promise<boolean>, options?:
217
287
  what?: string;
218
288
  }): Promise<void>;
219
289
  declare function sleep(ms: number): Promise<void>;
290
+ declare function advance(target: ConformanceTarget, ms: number): Promise<void>;
291
+
220
292
  /**
221
- * Move the target's clock forward, faking it if the target can and genuinely
222
- * waiting if it cannot.
293
+ * The deployment posture audit — what an outsider can do to a running relay.
294
+ *
295
+ * ## Why this exists as a separate surface
296
+ *
297
+ * `certify` drives a real daemon against a {@link ConformanceTarget}, and a
298
+ * target may be an in-process handler or an HTTP server: "deliberately
299
+ * transport-agnostic", which is the right call for certifying a *protocol*.
300
+ *
301
+ * It is also a blind spot, and byollm_009's ninth finding lived in it. Eight
302
+ * freeze-gate findings came from tests where the site reached the relay by
303
+ * calling `handle()` on an object it held a reference to — and a harness that
304
+ * invokes the system under test directly cannot see anything about how the
305
+ * system is *reached*. The site plane had no authentication at all. Nothing
306
+ * noticed, because nothing in the suite was ever a stranger.
307
+ *
308
+ * So this suite is a stranger. It holds no credential, no key the deployment
309
+ * knows, and no reference to any object inside it. It has a URL, which is
310
+ * exactly what an attacker has. Every check asks the question that form of
311
+ * access makes available:
312
+ *
313
+ * - can I enqueue work into someone's machines?
314
+ * - can I read who is online and what they are holding?
315
+ * - can I make a signature that is *well-formed* and be believed?
316
+ * - is anything served that should not be on the internet?
317
+ * - can I reach a handler by dressing a path up to look like one?
318
+ *
319
+ * ## What this is not
320
+ *
321
+ * Not a penetration test, and not exhaustive — it cannot be, because the next
322
+ * hole will be in whatever gets added next. It is the specific class that has
323
+ * already bitten, turned into something that runs. That is the same move as
324
+ * every other check in this kit: a finding becomes a check so its *shape*
325
+ * cannot recur silently.
326
+ *
327
+ * It is also deliberately **safe to run against production**: nothing here
328
+ * writes, nothing floods, and every request is one an ordinary scanner would
329
+ * make. A posture audit you are nervous about running is one nobody runs.
223
330
  */
224
- declare function advance(target: ConformanceTarget, ms: number): Promise<void>;
331
+ /** One thing a stranger tried. */
332
+ interface PostureCheck {
333
+ /** Stable id, cited in output. */
334
+ readonly id: string;
335
+ /** What a person should understand from a failure. */
336
+ readonly title: string;
337
+ /** MUSTs this exercises, where one applies. Empty is honest, not a gap. */
338
+ readonly cites: readonly string[];
339
+ run(context: PostureContext): Promise<PostureOutcome>;
340
+ }
341
+ interface PostureContext {
342
+ /** The origin, as an outsider would type it. */
343
+ readonly origin: string;
344
+ /** Where the daemon plane is mounted. */
345
+ readonly basePath: string;
346
+ /** Injectable, so a test can drive this without a network. */
347
+ readonly fetch: typeof fetch;
348
+ /**
349
+ * The origin's own address, behind whatever edge fronts it — `D008`.
350
+ *
351
+ * Optional because most deployments have no separate origin, and a check
352
+ * that guessed one would report a posture it never tested.
353
+ */
354
+ readonly originAddress?: string;
355
+ }
356
+ interface PostureOutcome {
357
+ readonly passed: boolean;
358
+ /**
359
+ * Did this check actually put its question? Absent means yes.
360
+ *
361
+ * A posture that could not be measured does not pass — a gate that waves
362
+ * through what it could not look at passes hardest when it is blindest —
363
+ * and it is not a breach either. Unproven is a third state.
364
+ */
365
+ readonly measured?: boolean;
366
+ /** What actually happened, in a sentence someone can act on. */
367
+ readonly detail: string;
368
+ }
369
+ interface PostureResult extends PostureOutcome {
370
+ readonly id: string;
371
+ readonly title: string;
372
+ readonly cites: readonly string[];
373
+ }
374
+ interface PostureReport {
375
+ readonly origin: string;
376
+ readonly passed: boolean;
377
+ readonly results: readonly PostureResult[];
378
+ }
379
+ declare const POSTURE_CHECKS: readonly PostureCheck[];
380
+ /**
381
+ * Audit a running deployment. Holds nothing it was not given a URL for.
382
+ *
383
+ * Every check runs even after one fails, because a posture report's job is to
384
+ * be a complete picture rather than the first thing that went wrong.
385
+ */
386
+ declare function auditDeployment(options: {
387
+ url: string;
388
+ basePath?: string;
389
+ /** The origin behind the edge, for `D008`. */
390
+ originAddress?: string;
391
+ fetch?: typeof fetch;
392
+ onProgress?: (result: PostureResult) => void;
393
+ }): Promise<PostureReport>;
394
+ declare function formatPostureReport(report: PostureReport): string;
225
395
 
226
- export { CHECKS, type CertificationReport, type Check, type CheckResult, type ConformanceTarget, EchoBackend, type HarnessDaemon, advance, certify, formatReport, ownerIdFor, pairDaemon, sleep, uncoveredMusts, waitFor };
396
+ export { CHECKS, type CertificationReport, type Check, type CheckResult, type ConformanceTarget, EchoBackend, type HarnessDaemon, POSTURE_CHECKS, type PostureCheck, type PostureContext, type PostureOutcome, type PostureReport, type PostureResult, advance, auditDeployment, certify, formatPostureReport, formatReport, miscoveredMusts, ownerIdFor, pairDaemon, sleep, uncoveredMusts, waitFor };
package/dist/index.js CHANGED
@@ -4,18 +4,28 @@ import {
4
4
  advance,
5
5
  certify,
6
6
  formatReport,
7
+ miscoveredMusts,
7
8
  ownerIdFor,
8
9
  pairDaemon,
9
10
  sleep,
10
11
  uncoveredMusts,
11
12
  waitFor
12
- } from "./chunk-PDQJJ3Q2.js";
13
+ } from "./chunk-OPJBI7WK.js";
14
+ import {
15
+ POSTURE_CHECKS,
16
+ auditDeployment,
17
+ formatPostureReport
18
+ } from "./chunk-2JXKI5TH.js";
13
19
  export {
14
20
  CHECKS,
15
21
  EchoBackend,
22
+ POSTURE_CHECKS,
16
23
  advance,
24
+ auditDeployment,
17
25
  certify,
26
+ formatPostureReport,
18
27
  formatReport,
28
+ miscoveredMusts,
19
29
  ownerIdFor,
20
30
  pairDaemon,
21
31
  sleep,
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@byollm/conformance",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.100",
4
4
  "description": "The BYOLLM compatibility contract — drive a real daemon against any server and assert every protocol MUST.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "bin": {
8
- "byollm-certify": "./dist/cli.js"
8
+ "byollm-certify": "./dist/cli.js",
9
+ "byollm-audit-deployment": "./dist/audit-cli.js"
9
10
  },
10
11
  "exports": {
11
12
  ".": {
@@ -21,23 +22,32 @@
21
22
  "README.md"
22
23
  ],
23
24
  "engines": {
24
- "node": ">=22.12"
25
+ "node": ">=22.14"
25
26
  },
26
27
  "dependencies": {
27
- "byollm": "0.1.0-alpha.1",
28
- "@byollm/protocol": "0.1.0-alpha.1"
28
+ "@byollm/protocol": "0.1.0-alpha.100",
29
+ "byollm": "0.1.0-alpha.100"
29
30
  },
30
31
  "devDependencies": {
31
32
  "@supabase/supabase-js": "^2.112.2",
32
- "@byollm/server": "0.1.0-alpha.1"
33
+ "@byollm/server": "0.1.0-alpha.100"
33
34
  },
34
35
  "publishConfig": {
35
36
  "access": "public"
36
37
  },
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/oftomorrowinc/byollm.git",
41
+ "directory": "packages/conformance"
42
+ },
43
+ "bugs": {
44
+ "url": "https://github.com/oftomorrowinc/byollm/issues"
45
+ },
37
46
  "scripts": {
38
47
  "build": "tsup",
39
48
  "clean": "rm -rf dist .tsbuild",
40
49
  "certify:reference": "vitest run --project conformance --root ../..",
41
- "certify:supabase": "node --experimental-strip-types targets/supabase.ts"
50
+ "certify:supabase": "node --experimental-strip-types targets/supabase.ts",
51
+ "audit:deployed": "node dist/audit-cli.js"
42
52
  }
43
53
  }
@@ -24,7 +24,11 @@ import {
24
24
  // The built package, not `../src`: Node's type stripping does not rewrite the
25
25
  // `.js` specifiers the source uses, and certifying the published entry points
26
26
  // is closer to what a consumer actually gets.
27
- import { ByollmApp, createFetchHandler } from "@byollm/server";
27
+ import {
28
+ ByollmApp,
29
+ createFetchHandler,
30
+ generateSiteKeys,
31
+ } from "@byollm/server";
28
32
  import { supabaseStore } from "@byollm/server/supabase";
29
33
 
30
34
  const SUPABASE_URL = process.env["SUPABASE_URL"] ?? "http://127.0.0.1:54421";
@@ -37,6 +41,12 @@ const ORIGIN = "https://supabase.byollm.test";
37
41
  /** Short, because a real Postgres clock cannot be faked forward. */
38
42
  const LEASE_MS = 2_000;
39
43
  const TTL_MS = 1_500;
44
+ /**
45
+ * Short for the same reason as the two above: with no fakeable clock, an
46
+ * expiry check waits for real. The product default is ten minutes, which is
47
+ * right for a human reading a code off a screen and wrong for a suite.
48
+ */
49
+ const PAIRING_TTL_MS = 2_000;
40
50
 
41
51
  if (SERVICE_KEY === "") {
42
52
  process.stderr.write(
@@ -89,12 +99,18 @@ async function ensureUser(name: string): Promise<string> {
89
99
 
90
100
  const toName = (id: string): string => ownerNames.get(id) ?? id;
91
101
 
102
+ // Generated per run: this target is one process, so there is no scale-out
103
+ // identity problem to model here. A real deployment supplies these.
104
+ const SITE_KEYS = generateSiteKeys();
105
+
92
106
  const store = supabaseStore({ client, defaultTtlMs: TTL_MS });
93
- const app = new ByollmApp({ store });
107
+ const app = new ByollmApp({ store, siteKeys: SITE_KEYS });
94
108
  const handler = createFetchHandler({
95
109
  store,
96
110
  verificationUrl: `${ORIGIN}/settings/runners`,
111
+ siteKeys: SITE_KEYS,
97
112
  leaseMs: LEASE_MS,
113
+ pairingTtlMs: PAIRING_TTL_MS,
98
114
  });
99
115
 
100
116
  const target: ConformanceTarget = {