@byollm/server 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 +256 -18
- package/bin/keygen.mjs +21 -0
- package/dist/chunk-36Y77FUD.js +678 -0
- package/dist/chunk-36Y77FUD.js.map +1 -0
- package/dist/{chunk-7RKXFPBZ.js → chunk-YBNLLQZT.js} +19 -5
- package/dist/chunk-YBNLLQZT.js.map +1 -0
- package/dist/delivery-C5mz_Ykm.d.ts +114 -0
- package/dist/{handlers-D7lWfwno.d.ts → handlers-CTV3Jc6Q.d.ts} +28 -5
- package/dist/index.d.ts +262 -30
- package/dist/index.js +704 -89
- package/dist/index.js.map +1 -1
- package/dist/next.d.ts +40 -8
- package/dist/next.js +8 -2
- package/dist/next.js.map +1 -1
- package/dist/store-Cx2_bck1.d.ts +500 -0
- package/dist/supabase/index.d.ts +2 -2
- package/dist/supabase/index.js +151 -51
- package/dist/supabase/index.js.map +1 -1
- package/package.json +16 -4
- package/supabase/migrations/20260809000000_byollm_runner.sql +22 -3
- package/supabase/migrations/20260819000000_drop_runner_token.sql +87 -0
- package/supabase/migrations/20260819010000_completed_by_lease_id.sql +25 -0
- package/supabase/migrations/20260821000000_rename_collected.sql +91 -0
- package/supabase/migrations/20260824000000_one_vocabulary.sql +109 -0
- package/supabase/migrations/20260825000000_job_service.sql +20 -0
- package/supabase/migrations/20260827000000_job_purpose.sql +36 -0
- package/dist/chunk-7RKXFPBZ.js.map +0 -1
- package/dist/chunk-HL6EYHQ7.js +0 -422
- package/dist/chunk-HL6EYHQ7.js.map +0 -1
- package/dist/delivery-36nIe-b3.d.ts +0 -73
- package/dist/store-D23N6iiP.d.ts +0 -255
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
> [!WARNING]
|
|
2
|
-
> **Alpha (`0.1.0-alpha.
|
|
2
|
+
> **Alpha (`0.1.0-alpha.100`) — under active development. Don't use this yet.**
|
|
3
3
|
>
|
|
4
4
|
> Install it deliberately: `npm install @byollm/server@alpha`.
|
|
5
5
|
>
|
|
@@ -11,7 +11,135 @@
|
|
|
11
11
|
> npm assigns `latest` on a first publish and won't let it be removed, so a
|
|
12
12
|
> bare install resolves here too. This notice is the only guard — deliberately
|
|
13
13
|
> not an npm deprecation, which would read as *abandoned* rather than *early*.
|
|
14
|
-
> Ask for `@alpha` explicitly so your lockfile records that you meant to
|
|
14
|
+
> Ask for `@alpha` explicitly so your lockfile records that you meant to.>
|
|
15
|
+
> **`alpha.15` is a breaking wire change, and it breaks daemons and relays —
|
|
16
|
+
> not app authors.** If you call `app.enqueue(...)` and read results, nothing
|
|
17
|
+
> in your code changes. If you run a daemon or an upstream, every package must
|
|
18
|
+
> move together: a mixed pair refuses on both sides, because both ends parse
|
|
19
|
+
> `.strict()`.
|
|
20
|
+
>
|
|
21
|
+
> What moved, all of it reconciling the frozen `byollm_009` with its code:
|
|
22
|
+
> `JobStub` gains `site` (the site's identity key id) and loses
|
|
23
|
+
> `audienceAllow`; `ResultRequest` gains `leaseId`; `HeartbeatResponse` loses
|
|
24
|
+
> `leases`, which nothing read; `WireErrorCode` gains `not-ready`,
|
|
25
|
+
> `clock-skew` and `forbidden`, and `403` is `forbidden` rather than
|
|
26
|
+
> `unauthorized`. `RESULT_PROVENANCE` is superseded by
|
|
27
|
+
> `PROVENANCE_NAMES_DEVICE`. See `byollm_009` Amendment A.>
|
|
28
|
+
> **`alpha.16` is a breaking wire change — daemons and relays again, not app
|
|
29
|
+
> authors.** `app.enqueue(...)` and reading results are unchanged. All five
|
|
30
|
+
> packages move together: both ends parse `.strict()`, so a mixed pair
|
|
31
|
+
> refuses.
|
|
32
|
+
>
|
|
33
|
+
> What moved, all of it Tier 2 of `cloud_008`: `model`, `backendClass` and
|
|
34
|
+
> `durationMs` come off `ResultRequest` and are sealed **inside** the result
|
|
35
|
+
> envelope as `SealedOutcome = { outcome, ran }` — so a daemon can no longer
|
|
36
|
+
> declare a model it did not sign, and a relay carries neither.
|
|
37
|
+
> `HeartbeatResponse` loses `leases` (nothing read it) and now reports real
|
|
38
|
+
> cancellations instead of an empty list. `WireErrorCode` gains `forbidden`
|
|
39
|
+
> for 403, leaving `unauthorized` at exactly 401. The relay gained a
|
|
40
|
+
> site-plane `cancel` endpoint, honours `stub.deadlineAt`, honours
|
|
41
|
+
> `stub.audience`, and remembers a refusal.>
|
|
42
|
+
> **`alpha.17` is additive** — no wire change. It exports `ReleaseReason`,
|
|
43
|
+
> which `RoutingStore.releaseLeases` names and the package did not export, so
|
|
44
|
+
> the interface was unimplementable outside this repo.>
|
|
45
|
+
> **`alpha.18` is a breaking wire change — daemons and relays, not app
|
|
46
|
+
> authors.** `app.enqueue(...)` and reading results are unchanged. All five
|
|
47
|
+
> packages move together.
|
|
48
|
+
>
|
|
49
|
+
> The **bearer token is gone**: off `PairPollResponse`, off the runner row,
|
|
50
|
+
> off the daemon's pairings file, out of the adapter's schema. It was minted,
|
|
51
|
+
> hashed and stored on two disks and never sent, looked up or compared —
|
|
52
|
+
> `REQUESTS_SIGNED_NOT_BEARER` was enforced by signatures the whole time. If
|
|
53
|
+
> you run the Supabase adapter, apply
|
|
54
|
+
> `20260819000000_drop_runner_token.sql`; `byollm_approve_pairing` now takes
|
|
55
|
+
> one argument. A pairings file written by an older daemon still loads.
|
|
56
|
+
>
|
|
57
|
+
> `model`, `backendClass` and `durationMs` moved **inside** the sealed result
|
|
58
|
+
> (`SealedOutcome = { outcome, ran }`), so a daemon cannot declare a model it
|
|
59
|
+
> did not sign and a relay carries none of them. Writing a `RoutingStore`?
|
|
60
|
+
> `releaseLeases` takes an optional `reason` and `complete` requires
|
|
61
|
+
> `leaseId`, and **an implementation that ignores either still typechecks** —
|
|
62
|
+
> run the store contract tests.>
|
|
63
|
+
> **`alpha.19` is additive on the wire and a behaviour change in every
|
|
64
|
+
> store.** `ResultResponse` gains an optional `duplicate`. Nothing is removed,
|
|
65
|
+
> so an older daemon keeps working — but the *order* two rules are checked in
|
|
66
|
+
> has changed, and a `RoutingStore` implementation must change with it.
|
|
67
|
+
>
|
|
68
|
+
> `complete` now checks **terminal state before holder**, scoped to the device
|
|
69
|
+
> that finished the job: a replay from that device is answered `duplicate:
|
|
70
|
+
> true` with a 2xx, and anyone else gets exactly the refusal they would get
|
|
71
|
+
> for a job that is not terminal. Previously `RESULT_IDEMPOTENT` held only
|
|
72
|
+
> because the lease is nulled on success, so the holder check tripped first —
|
|
73
|
+
> deleting the idempotency branch failed no test. Run the store contract
|
|
74
|
+
> tests; the compiler cannot see this.
|
|
75
|
+
>
|
|
76
|
+
> **`alpha.7` breaks nobody who is not implementing a relay.** `@byollm/relay`'s
|
|
77
|
+
> projection gains `devices` — the device keys a control plane has approved —
|
|
78
|
+
> and the relay refuses to pair a device that is not in it. `revoked` becomes
|
|
79
|
+
> `{owner, siteId}` instead of a composite string. Sites, daemons, stores and
|
|
80
|
+
> the wire format are all untouched; no runner re-pairs.
|
|
81
|
+
>
|
|
82
|
+
> **`alpha.5` broke store adapters, and nothing else.** If you implement
|
|
83
|
+
> `JobStore` yourself, two changes are required: a new `adopt(args)` method
|
|
84
|
+
> (record a lease granted by an upstream this store does not own), and
|
|
85
|
+
> `CompleteArgs.runnerId` is replaced by `holder` — a discriminated union
|
|
86
|
+
> naming either a runner or a lease. Apps, daemons and the wire format are
|
|
87
|
+
> unaffected; `@byollm/conformance` will tell you if you missed one.
|
|
88
|
+
>
|
|
89
|
+
> **`alpha.4` broke every integration.** Three things changed for you:
|
|
90
|
+
>
|
|
91
|
+
> 1. **`siteKeys` is required.** Run `npx @byollm/server@alpha keygen` once,
|
|
92
|
+
> set `BYOLLM_SITE_KEYS`, and pass it to `ByollmApp` and `createHandler`.
|
|
93
|
+
> Once — not per deploy, never at startup.
|
|
94
|
+
> 2. **`createHandler` takes a function.** `next build` imports route modules
|
|
95
|
+
> with no secrets present, so a config object fails the build.
|
|
96
|
+
> 3. **Every paired runner re-pairs.** Bearer tokens are replaced by per-request
|
|
97
|
+
> signatures against a pinned device key, so old tokens authenticate nothing.
|
|
98
|
+
>
|
|
99
|
+
> Your store adapter is unaffected: payloads and results are sealed before they
|
|
100
|
+
> reach it, and `JobStore` did not change.
|
|
101
|
+
|
|
102
|
+
<!-- release-note 0.1.0-alpha.21 -->
|
|
103
|
+
> [!NOTE]
|
|
104
|
+
> **`0.1.0-alpha.20` is not a complete release — do not pin it.** Four
|
|
105
|
+
> packages published and `@byollm/server` did not: a Sigstore
|
|
106
|
+
> transparency-log 409 on its provenance attestation. The workflow's
|
|
107
|
+
> "already published" guard correctly refuses to resume a partial publish,
|
|
108
|
+
> so `0.1.0-alpha.21` is that release, whole.
|
|
109
|
+
>
|
|
110
|
+
> If you run the Supabase adapter, `alpha.21` needs
|
|
111
|
+
> `20260819010000_completed_by_lease_id.sql`: alpha.19 shipped §3.6's
|
|
112
|
+
> ordering without the column it stores the grant in.
|
|
113
|
+
|
|
114
|
+
<!-- release-note 0.1.0-alpha.40 -->
|
|
115
|
+
**`byollm start` — stop keeping a terminal open.** The daemon can now run
|
|
116
|
+
under your computer's own supervisor and restart itself if it stops: a launchd
|
|
117
|
+
agent on macOS, a `systemd --user` unit on Linux, a logon task on Windows. All
|
|
118
|
+
user-level — no root, no system directories, and `byollm stop` takes it
|
|
119
|
+
away. `byollm status` gained a line saying whether it is actually supervised
|
|
120
|
+
right now, including the state that matters most: installed but not running,
|
|
121
|
+
which looks fine from an app's dashboard and serves nothing.
|
|
122
|
+
|
|
123
|
+
If you are running via `npx`, install properly first (`npm install -g
|
|
124
|
+
byollm@latest`) — `install` refuses to supervise a copy in npx's cache, because
|
|
125
|
+
npm deletes that directory and the service would fail at some later boot.
|
|
126
|
+
|
|
127
|
+
<!-- release-note 0.1.0-alpha.41 -->
|
|
128
|
+
**`onNoRunner` takes a string.** Your fallback answer is your own value, not
|
|
129
|
+
wire data, and handing back a whole result record for it was ceremony — the
|
|
130
|
+
README's own example got the shape wrong, which is how this was found.
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
const { outcome, fallback } = await job.result({
|
|
134
|
+
onNoRunner: () => runOnHostedModel(transcript),
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Whatever you return, `result()` labels it `fallback: true` — the stamp is
|
|
139
|
+
applied by the wait, not taken from you, so an answer that did not run on
|
|
140
|
+
somebody's device cannot be reported as though it did (`FALLBACK_LABELED`).
|
|
141
|
+
Both delivery channels do it, polling and Supabase Realtime. Records still
|
|
142
|
+
work; they just get labelled too.
|
|
15
143
|
|
|
16
144
|
# `@byollm/server`
|
|
17
145
|
|
|
@@ -30,44 +158,139 @@ npm install @byollm/server
|
|
|
30
158
|
```ts
|
|
31
159
|
// app/api/byollm/[...route]/route.ts
|
|
32
160
|
import { createHandler } from "@byollm/server/next";
|
|
33
|
-
import {
|
|
161
|
+
import { siteKeysFromEnv } from "@byollm/server";
|
|
162
|
+
import { getStore } from "@/lib/byollm";
|
|
34
163
|
|
|
35
|
-
export const { POST } = createHandler({
|
|
36
|
-
store,
|
|
164
|
+
export const { POST } = createHandler(() => ({
|
|
165
|
+
store: getStore(),
|
|
166
|
+
siteKeys: siteKeysFromEnv("BYOLLM_SITE_KEYS"),
|
|
37
167
|
verificationUrl: "https://your-app.com/settings/runners",
|
|
38
|
-
|
|
168
|
+
// Next serves this route under /api, so say where it is mounted. The
|
|
169
|
+
// handler matches the full path and will 404 without this.
|
|
170
|
+
basePath: "/api/byollm",
|
|
171
|
+
}));
|
|
39
172
|
```
|
|
40
173
|
|
|
174
|
+
**Pass a function, not an object.** `next build` imports every route module to
|
|
175
|
+
collect page data, in an environment that has no secrets. A config object is
|
|
176
|
+
constructed during that import, so the build fails on credentials it cannot
|
|
177
|
+
have. A function is not called until the first request.
|
|
178
|
+
|
|
179
|
+
Then pair against that same path — `byollm connect https://your-app.com/api`.
|
|
180
|
+
The daemon appends `/byollm/<endpoint>` to whatever origin it is given, so
|
|
181
|
+
connecting to the bare domain looks for `/byollm/claim` and finds nothing.
|
|
182
|
+
To serve at `/byollm` instead, put the route at `app/byollm/[...route]/route.ts`,
|
|
183
|
+
drop `basePath`, and pair against the bare domain.
|
|
184
|
+
|
|
41
185
|
**2. Pick a store.**
|
|
42
186
|
|
|
43
187
|
```ts
|
|
44
188
|
// lib/byollm.ts
|
|
45
|
-
import { ByollmApp, MemoryStore } from "@byollm/server";
|
|
189
|
+
import { ByollmApp, MemoryStore, siteKeysFromEnv } from "@byollm/server";
|
|
190
|
+
|
|
191
|
+
// Lazily, and memoized, for the same reason the mount takes a function: a
|
|
192
|
+
// module-scope `new` runs during `next build`.
|
|
193
|
+
let store: MemoryStore | undefined;
|
|
194
|
+
export function getStore(): MemoryStore {
|
|
195
|
+
return (store ??= new MemoryStore());
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let app: ByollmApp | undefined;
|
|
199
|
+
export function getApp(): ByollmApp {
|
|
200
|
+
return (app ??= new ByollmApp({
|
|
201
|
+
store: getStore(),
|
|
202
|
+
siteKeys: siteKeysFromEnv("BYOLLM_SITE_KEYS"),
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Generate that identity once, and keep it:
|
|
46
208
|
|
|
47
|
-
|
|
48
|
-
|
|
209
|
+
```bash
|
|
210
|
+
npx @byollm/server@alpha keygen # prints BYOLLM_SITE_KEYS=...
|
|
49
211
|
```
|
|
50
212
|
|
|
213
|
+
Once, not per deploy and never at startup — a daemon pins this identity when
|
|
214
|
+
its owner approves the pairing, and regenerating it means every paired device
|
|
215
|
+
must pair again. Generating at startup fails only under horizontal scale: each
|
|
216
|
+
instance would have a different identity, and a daemon would be refused by
|
|
217
|
+
whichever one it did not pair with.
|
|
218
|
+
|
|
51
219
|
**3. Enqueue.**
|
|
52
220
|
|
|
53
221
|
```ts
|
|
54
|
-
const job = await
|
|
222
|
+
const job = await getApp().enqueue({
|
|
55
223
|
kind: "llm.generate",
|
|
56
|
-
audience: "
|
|
57
|
-
owner:
|
|
224
|
+
audience: "private", // this user's own device only — the default
|
|
225
|
+
owner: ownerId, // who this is depends on your mode — see below
|
|
58
226
|
payload: { prompt: `Summarize this transcript:\n\n${transcript}` },
|
|
59
227
|
});
|
|
60
228
|
|
|
61
|
-
const { outcome } = await job.result({
|
|
229
|
+
const { outcome, fallback } = await job.result({
|
|
62
230
|
timeoutMs: 120_000,
|
|
231
|
+
// A string is enough — it is your own fallback answer, not wire data.
|
|
63
232
|
onNoRunner: () => runOnHostedModel(transcript),
|
|
64
233
|
});
|
|
234
|
+
|
|
235
|
+
// `fallback` is true when nobody's device ran it and this came from your
|
|
236
|
+
// own substitute. Say so wherever you show the answer: work that did not run
|
|
237
|
+
// on the user's compute must not be presented as though it did.
|
|
65
238
|
```
|
|
66
239
|
|
|
67
240
|
`result()` is sugar over your delivery channel with a timeout and a
|
|
68
241
|
`noRunnerAvailable` path — never a bare promise that hangs forever. If nobody
|
|
69
242
|
is online to run the job, you find out and can fall back.
|
|
70
243
|
|
|
244
|
+
## Who `owner` is — this differs by mode
|
|
245
|
+
|
|
246
|
+
`owner` names the person whose devices should do the work, and the two
|
|
247
|
+
connection modes do not use the same names for people. Getting this wrong is
|
|
248
|
+
the one integration mistake that produces no error: the job enqueues, returns
|
|
249
|
+
an id, and never routes.
|
|
250
|
+
|
|
251
|
+
**Direct** — daemons reach your own handlers, and you are the only party who
|
|
252
|
+
knows who anybody is. `owner` is your own user id, from your session, never
|
|
253
|
+
from the client. That is what the example above shows.
|
|
254
|
+
|
|
255
|
+
**Cloud** — daemons reach `hub.byollm.cloud`, which has its own identity
|
|
256
|
+
space: rosters, consents and budgets all speak **BYOLLM ids**. Your user id
|
|
257
|
+
means nothing there. `owner` must be the person's BYOLLM id.
|
|
258
|
+
|
|
259
|
+
Ask them for it. Every signed-in person can read it on their byollm.cloud
|
|
260
|
+
account page under **Your BYOLLM id**, with a copy button. Add a settings
|
|
261
|
+
field, have them paste it, store it against your own user record.
|
|
262
|
+
|
|
263
|
+
The id names them and authorises nothing — someone holding it can address work
|
|
264
|
+
to a person and cannot deliver it, because the consent row is what opens the
|
|
265
|
+
route. So a pasted id is not a credential you are being trusted with, and a
|
|
266
|
+
wrong one is harmless. Two things still matter: it must come from the person's
|
|
267
|
+
own account rather than being inferred by your app, and they must connect your
|
|
268
|
+
site on byollm.cloud before anything routes.
|
|
269
|
+
|
|
270
|
+
**A wrong id is silence, not an error.** The relay routes on a consented
|
|
271
|
+
`(site, owner)` pair, so a mistyped id — or one belonging to somebody who has
|
|
272
|
+
not connected your site — matches no route. The job waits, then expires. So
|
|
273
|
+
check ids when you receive them rather than when you enqueue:
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
const { available } = await getApp().runnerAvailability({
|
|
277
|
+
kind: "llm.generate",
|
|
278
|
+
owner: pastedByollmId,
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
if (!available) {
|
|
282
|
+
// Existence-neutral, deliberately.
|
|
283
|
+
return "That id has no devices for you. Check it, or connect this site on byollm.cloud.";
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Keep that message vague on purpose. A typo'd id and an id belonging to somebody
|
|
288
|
+
who has not connected you give the same answer, and that is the system working
|
|
289
|
+
rather than a limitation to route around — telling them apart would make this
|
|
290
|
+
call an account-existence oracle: probe a guess, sort the answers, enumerate.
|
|
291
|
+
Never write "no such account". If you ever get an answer that *does*
|
|
292
|
+
distinguish them, that is a bug worth reporting.
|
|
293
|
+
|
|
71
294
|
## The approval page
|
|
72
295
|
|
|
73
296
|
Pairing is a device-code exchange, so you need one page where a signed-in user
|
|
@@ -75,13 +298,13 @@ types the code their daemon showed them:
|
|
|
75
298
|
|
|
76
299
|
```ts
|
|
77
300
|
// The owner comes from YOUR session. A daemon can never assert who it is.
|
|
78
|
-
const runner = await
|
|
301
|
+
const runner = await getApp().approvePairing({
|
|
79
302
|
userCode: formData.get("code"),
|
|
80
303
|
owner: session.userId,
|
|
81
304
|
});
|
|
82
305
|
```
|
|
83
306
|
|
|
84
|
-
`app.pendingPairing(code)` tells you what they are about to approve —
|
|
307
|
+
`app.pendingPairing(code)` tells you what they are about to approve — device
|
|
85
308
|
label, platform, and which models it is offering — so the page can show it.
|
|
86
309
|
|
|
87
310
|
## Stores
|
|
@@ -103,6 +326,7 @@ import {
|
|
|
103
326
|
const store = supabaseStore({ client: serviceRoleClient });
|
|
104
327
|
const app = new ByollmApp({
|
|
105
328
|
store,
|
|
329
|
+
siteKeys: siteKeysFromEnv("BYOLLM_SITE_KEYS"),
|
|
106
330
|
delivery: supabaseRealtimeDelivery(serviceRoleClient),
|
|
107
331
|
});
|
|
108
332
|
```
|
|
@@ -118,12 +342,12 @@ everything the browser does.
|
|
|
118
342
|
## Two things the API makes you confront
|
|
119
343
|
|
|
120
344
|
**Community results are untrusted.** A `named`/`public` result came from
|
|
121
|
-
someone else's
|
|
345
|
+
someone else's device and can be anything. Every result carries provenance,
|
|
122
346
|
and `untrusted` is derived from the audience — you cannot mark volunteer
|
|
123
347
|
output as first-party:
|
|
124
348
|
|
|
125
349
|
```ts
|
|
126
|
-
const { outcome, provenance } = await
|
|
350
|
+
const { outcome, provenance } = await getApp().result(jobId);
|
|
127
351
|
if (provenance?.untrusted) {
|
|
128
352
|
// Do not render as trusted HTML. Do not feed to a privileged step.
|
|
129
353
|
// Disclose where it came from.
|
|
@@ -133,7 +357,7 @@ if (provenance?.untrusted) {
|
|
|
133
357
|
**Jobs can depend on each other.** `dependsOn: [jobId]` keeps a job
|
|
134
358
|
unclaimable until its dependencies are `ok`. One field and one claim
|
|
135
359
|
predicate, not a DAG engine — so the two halves of a piece of work can land on
|
|
136
|
-
two different people's
|
|
360
|
+
two different people's devices, in order, without your app orchestrating the
|
|
137
361
|
wait.
|
|
138
362
|
|
|
139
363
|
## Certifying an adapter
|
|
@@ -146,3 +370,17 @@ A server is byollm-compatible when the kit passes. See
|
|
|
146
370
|
[`@byollm/conformance`](../conformance).
|
|
147
371
|
|
|
148
372
|
MIT
|
|
373
|
+
|
|
374
|
+
<!-- family:start -->
|
|
375
|
+
|
|
376
|
+
## The rest of byollm
|
|
377
|
+
|
|
378
|
+
Six packages, and they are only interesting together:
|
|
379
|
+
|
|
380
|
+
- [`byollm`](https://www.npmjs.com/package/byollm) — the daemon — runs models on your own machine and answers for it
|
|
381
|
+
- [`@byollm/protocol`](https://www.npmjs.com/package/@byollm/protocol) — the wire: envelopes, signatures and the closed vocabularies both ends validate against
|
|
382
|
+
- [`@byollm/relay`](https://www.npmjs.com/package/@byollm/relay) — the broker that holds jobs between a site and a device, and can read neither
|
|
383
|
+
- [`@byollm/control-plane`](https://www.npmjs.com/package/@byollm/control-plane) — who may ask whom, and the policy store behind it
|
|
384
|
+
- [`@byollm/conformance`](https://www.npmjs.com/package/@byollm/conformance) — the kit that proves an implementation is one — including a posture audit that holds nothing but a URL
|
|
385
|
+
|
|
386
|
+
<!-- family:end -->
|
package/bin/keygen.mjs
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* `npx @byollm/server keygen` — make a site identity, once.
|
|
4
|
+
*
|
|
5
|
+
* Prints an env-file fragment. Deliberately not written to a file: key
|
|
6
|
+
* material that lands on disk by default tends to end up committed, and the
|
|
7
|
+
* one place it should live is wherever this deployment keeps its secrets.
|
|
8
|
+
*/
|
|
9
|
+
import { formatSiteKeys, generateSiteKeys } from "../dist/index.js";
|
|
10
|
+
|
|
11
|
+
if (process.argv.includes("--help") || process.argv.includes("-h")) {
|
|
12
|
+
process.stdout.write(
|
|
13
|
+
"usage: npx @byollm/server keygen\n\n" +
|
|
14
|
+
"Generates this site's byollm identity and prints it as an env line.\n" +
|
|
15
|
+
"Run it once. Store the result as a secret. Regenerating it makes every\n" +
|
|
16
|
+
"daemon that has paired with this site pair again.\n",
|
|
17
|
+
);
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
process.stdout.write(formatSiteKeys(generateSiteKeys()));
|