@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/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-OPJBI7WK.js +2175 -0
- 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 +179 -9
- package/dist/index.js +11 -1
- package/package.json +17 -7
- package/targets/supabase.ts +18 -2
- package/dist/chunk-PDQJJ3Q2.js +0 -880
- package/dist/chunk-PDQJJ3Q2.js.map +0 -1
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,
|
|
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
|
-
/**
|
|
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
|
-
|
|
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
|
-
*
|
|
222
|
-
*
|
|
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
|
-
|
|
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-
|
|
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.
|
|
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.
|
|
25
|
+
"node": ">=22.14"
|
|
25
26
|
},
|
|
26
27
|
"dependencies": {
|
|
27
|
-
"byollm": "0.1.0-alpha.
|
|
28
|
-
"
|
|
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.
|
|
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
|
}
|
package/targets/supabase.ts
CHANGED
|
@@ -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 {
|
|
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 = {
|