@byollm/relay 0.1.0-alpha.59 → 0.1.0-alpha.60
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
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
> [!WARNING]
|
|
2
|
-
> **Alpha (`0.1.0-alpha.
|
|
2
|
+
> **Alpha (`0.1.0-alpha.60`) — under active development. Don't use this yet.**
|
|
3
3
|
>
|
|
4
4
|
> This is a walking skeleton. It routes real jobs between real daemons and real
|
|
5
5
|
> sites, and it is the fixture byollm_009 freezes against — but it keeps its
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
> packages published and `@byollm/server` did not: a Sigstore
|
|
73
73
|
> transparency-log 409 on its provenance attestation. The workflow's
|
|
74
74
|
> "already published" guard correctly refuses to resume a partial publish,
|
|
75
|
-
> so `0.1.0-alpha.
|
|
75
|
+
> so `0.1.0-alpha.60` is that release, whole.
|
|
76
76
|
>
|
|
77
77
|
> If you run the Supabase adapter, `alpha.21` needs
|
|
78
78
|
> `20260819010000_completed_by_lease_id.sql`: alpha.19 shipped §3.6's
|
|
@@ -183,7 +183,7 @@ daemons pin at pairing, verified against the `sites` half of the projection.
|
|
|
183
183
|
Nothing here trusts a `siteId` in a body or a query string.
|
|
184
184
|
|
|
185
185
|
That is newer than the rest of this package. The site plane took the caller's
|
|
186
|
-
word for who it was until `0.1.0-alpha.
|
|
186
|
+
word for who it was until `0.1.0-alpha.60`, which on a relay reachable from the
|
|
187
187
|
internet is an open enqueue endpoint into consenting users' devices and an
|
|
188
188
|
open read of who is online. It was blind the whole time — nothing could open a
|
|
189
189
|
payload — and blind is not the same as safe.
|
|
@@ -191,7 +191,7 @@ payload — and blind is not the same as safe.
|
|
|
191
191
|
If you are running this: the site plane is authenticated but this is still a
|
|
192
192
|
single-tenant relay with in-memory state. One site, one replica.
|
|
193
193
|
|
|
194
|
-
## Breaking in `0.1.0-alpha.
|
|
194
|
+
## Breaking in `0.1.0-alpha.60`: `RelayState` is async
|
|
195
195
|
|
|
196
196
|
Every method on `RelayState` now returns a `Promise`, and `Relay.sweep()` and
|
|
197
197
|
`debugPage()` with it. `RelayState.requeue` is private — it was only ever a
|
|
@@ -249,6 +249,7 @@ var RelayState = class {
|
|
|
249
249
|
const job = this.#grantFor(jobId, input.runnerId, leaseId);
|
|
250
250
|
if (!job || job.claimedBy?.runnerId !== input.runnerId) continue;
|
|
251
251
|
if (job.claimedBy.leaseId !== leaseId) continue;
|
|
252
|
+
if (job.state === "done") continue;
|
|
252
253
|
if (input.reason === "refused" && !job.refusedBy.includes(input.runnerId)) {
|
|
253
254
|
job.refusedBy.push(input.runnerId);
|
|
254
255
|
}
|
|
@@ -401,4 +402,4 @@ export {
|
|
|
401
402
|
routeKey,
|
|
402
403
|
RelayState
|
|
403
404
|
};
|
|
404
|
-
//# sourceMappingURL=chunk-
|
|
405
|
+
//# sourceMappingURL=chunk-OB6LPEEE.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/state.ts"],"sourcesContent":["import type {\n CapabilityMatrix,\n ClaimedStub,\n JobStub,\n PublicIdentity,\n SealedEnvelope,\n WithheldKind,\n} from \"@byollm/protocol\";\nimport { randomUUID } from \"node:crypto\";\nimport type { Grant, RoutingStore } from \"./store.js\";\n\n/**\n * The relay's routing state — byollm_009 §7, reachable at last.\n *\n * §7 described a state machine the direct plane could not produce. There, the\n * site and the upstream are the same party: it seals when it likes, and a job\n * is never claimed-but-unsealed. Here they are different parties, and the gap\n * between them is a state:\n *\n * ```\n * queued ──claim──▶ awaiting-payload ──sealed──▶ ready ──fetch──▶ running\n * ▲ │ │\n * └────────────────────┘ ▼\n * site never seals, or seals too late ok | error | canceled\n * ```\n *\n * The relay cannot seal, so it cannot shortcut this. A payload is encrypted\n * to *the device that claimed it*, and nobody knows which device that is until\n * the claim happens — which is precisely why claim-then-fetch makes a blind\n * relay possible at all. The window is the price.\n *\n * ## What the relay holds, and what it cannot\n *\n * Stubs (metadata the site chose to publish), sealed envelopes it cannot open,\n * and public keys. There is no field on any type in this file that could hold\n * a private key or a plaintext, which is `RELAY_BLIND` expressed as a data\n * model rather than as a policy.\n */\n\n/** Where a routed job is. */\nexport type RoutedState =\n \"queued\" | \"awaiting-payload\" | \"ready\" | \"running\" | \"done\";\n\n/**\n * How long a site has to seal after one of its jobs is claimed.\n *\n * **Distinct from the lease, and distinct from the job's TTL** — byollm_009\n * §7.1. Three clocks, three different questions:\n *\n * - the **TTL** asks how long the work is worth doing at all;\n * - the **lease** asks how long this device gets to run it;\n * - this asks how long we wait for a site that has gone away.\n *\n * Collapsing any pair of them looks harmless until a site restarts during a\n * deploy: with only a lease, the device sits politely holding a job whose\n * payload will never arrive, and the lease's whole minute is spent waiting on\n * a party that is not coming back. Short, because a site that is up answers in\n * milliseconds and a site that is down will not answer sooner for waiting.\n */\nexport const AWAITING_PAYLOAD_MS = 10_000;\n\n/**\n * How long a device waits before asking about a job it could not run — the\n * rate every transient refusal needs.\n *\n * **A transient needs a rate; a retry without a not-before is a spin.** A\n * release that is not `refused` requeues immediately and stays claimable by\n * the same device, which is right for a daemon saying \"not now, I am\n * restarting\" and catastrophic for a control plane saying \"that mapping\n * resolves to another of your machines\": the device re-claims at once, is\n * declined again, and the pair loops. Measured before it could happen —\n * twelve ticks produced twelve control-plane reads, which in a deployment is\n * twelve database queries for a job that was never going to run there.\n *\n * Thirty seconds is chosen against the only thing it delays: a person who\n * fixes a mapping and has work already queued. Half a minute is a wait\n * nobody notices, and one read per device per thirty seconds per stuck job is\n * a cost nobody notices either.\n *\n * Distinct from the three durations above it, and for a fourth kind of\n * reason: the TTL asks whether the work is still worth doing, the lease how\n * long this device gets, {@link AWAITING_PAYLOAD_MS} how long we wait for the\n * site — and this asks how long before we ask *this device* again.\n */\nexport const RETRY_AFTER_MS = 30_000;\n\n/** A job the relay is routing. Metadata and ciphertext, nothing else. */\n/** Why a daemon gave a job back. Only `refused` means \"not me, ever\". */\nexport type ReleaseReason =\n \"shutdown\" | \"pause\" | \"revoked\" | \"backend-down\" | \"refused\";\n\nexport interface RoutedJob {\n readonly id: string;\n /** Which site enqueued it — the party that will be asked to seal. */\n readonly siteId: string;\n /**\n * Everything the relay knows about the work, which is everything the site\n * chose to publish and not one field more (byollm_009 §6).\n */\n readonly stub: JobStub;\n state: RoutedState;\n /** Set from the claim; the site seals to these keys. */\n claimedBy?: {\n readonly runnerId: string;\n readonly owner: string;\n readonly device: PublicIdentity;\n readonly leaseId: string;\n readonly leaseExpiresAt: number;\n };\n /** When {@link AWAITING_PAYLOAD_MS} runs out for this claim. */\n awaitingUntil?: number;\n /**\n * Runners that released this job with reason `refused` — cloud_008 §2.1.\n *\n * `REFUSAL_NOT_REOFFERED`, which the relay did not implement: it dropped\n * `ReleaseRequest.reason` on the floor. The field's own docstring says why\n * that is not cosmetic — an upstream cannot evaluate a daemon's *local*\n * `named` allowlist, so it may legitimately offer work the daemon then\n * declines, and without a record the two spin between claim and release\n * forever. The direct plane has always kept this list.\n */\n refusedBy: string[];\n /**\n * Runners that may not be offered this job again *yet*, and from when.\n *\n * The middle ground {@link RoutingStore.releaseLeases} had no way to say.\n * `refusedBy` is forever and a bare release is immediate; a control plane\n * declining a job for a reason the world can change — an unfilled mapping\n * slot, a resolution that named another machine, a store that was briefly\n * unreachable — means neither. It means \"ask again later\", and later needs\n * a number.\n *\n * Keyed by runner because it is a fact about a pairing, not about the job:\n * the same job goes to another device immediately, which is the whole\n * point of not marking it refused.\n */\n retryAfter?: Record<string, number>;\n /**\n * The site withdrew this job — cloud_008 §2.2.\n *\n * A flag rather than a state, because a cancelled job that a device is\n * *running* is not finished: the daemon has to be told, abort its backend\n * call and report `canceled`, and the ordinary `complete` path then closes\n * it. Making it a state would strand the in-flight case between two\n * machines' ideas of what happened.\n */\n cancelled?: boolean;\n /** Sealed to the claiming device by the site. Opaque here. */\n payload?: SealedEnvelope;\n /** Sealed to the site by the device. Opaque here. */\n result?: SealedEnvelope;\n /**\n * The result's clear-text discriminator — byollm_009 §6.1.\n *\n * The one outcome fact the relay is given, and the reason it is given:\n * without it the relay cannot stop dispatching a finished job. A routing\n * hint and never a fact — the *site* verifies it against the sealed\n * outcome, because only the site can open the envelope. The relay acts on\n * it and is entitled to be wrong; a lying daemon costs it a dispatch\n * decision, not a security property.\n */\n disposition?: \"ok\" | \"error\" | \"canceled\";\n}\n\n/** A device the relay has seen recently. */\nexport interface Presence {\n readonly runnerId: string;\n readonly owner: string;\n readonly device: PublicIdentity;\n lastSeenAt: number;\n /**\n * What this machine last said it can run — cloud_009, 2026-08-24.\n *\n * **Capabilities are presence data.** They arrive on the same heartbeat as\n * everything else here, they go stale at the same moment and for the same\n * reason, and a machine that stops heartbeating has not stopped being able\n * to run Llama — it has stopped being somewhere we can ask. Keeping them\n * anywhere else would put capability truth outside the interface that owns\n * presence, and every next consumer (a member's usable set, a dashboard's\n * pulse, a degraded-state banner) would re-derive it from a different\n * place. That is the two-owners shape this codebase keeps paying for.\n *\n * Empty is a real answer, not a missing one: it is a paired machine with no\n * healthy backend, which is a legal state the whole connect-first ruling\n * exists to make visible rather than refuse.\n */\n capabilities: CapabilityMatrix;\n /**\n * Kinds this machine is deliberately *not* advertising — byollm_016.\n *\n * Presence data for the same reason capabilities are, and stored beside\n * them rather than derived: two services answering one kind with no\n * `defaults` entry is a state only the daemon can see, and the hub cannot\n * reconstruct it from the matrix — an absent kind and a withheld kind look\n * identical there. Without this field the owner's page can say \"nothing\n * serves llm.generate\", which is true and useless, instead of \"two services\n * answer it and you have not chosen\", which is the sentence that ends with\n * the owner doing something.\n *\n * Empty is the normal answer, and means every kind resolved.\n */\n withheld: readonly WithheldKind[];\n // `revoked` is deliberately absent — cloud_008 §2.3.\n //\n // It was a boolean on the *runner*, written at heartbeat and read by\n // nothing but the debug page. Enforcement had already moved to the\n // projection, because enforcing on this flag made revocation depend on the\n // client calling an endpoint: a daemon that simply never heartbeat went on\n // claiming forever. What survived was the cache, which is a stored copy of\n // a derived fact — this project's most-repeated bug, kept alive here as a\n // display value.\n //\n // It is also a concept multi-tenancy cannot express. Revocation is a fact\n // about an (owner, site) pair; a daemon serving two sites and revoked at\n // one is not \"a revoked runner\", and a boolean on the device has nowhere to\n // put the difference. Deleting it now is what stops cloud_009 inheriting a\n // field it would have to contradict.\n}\n\n/**\n * What a routing store must do, expressed as operations — cloud_006 §3.2.\n *\n * Every method below is a **decision plus its write**, never a read the caller\n * follows with a mutation. That is the whole point, and it is the difference\n * between an interface a shared store can implement and one it cannot.\n *\n * `claim` is the specimen. It used to live in `DaemonPlane` as\n * `jobs()` → filter → mutate, which is atomic for exactly one reason: Node is\n * single-threaded and these Maps are local, so nothing runs between the read\n * and the write. Neither survives a store on a network, and\n * `packages/relay/test/two-replicas.test.ts` holds the resulting race as a\n * failing assertion.\n *\n * So the rule for anything added here: **if a caller has to read, decide, and\n * write back, the operation is in the wrong place.** Move the decision in.\n *\n * ## Why the projection does not come with it\n *\n * `claim` takes `owners: string[]` rather than a projection or a predicate.\n * A closure cannot travel to Valkey, and the projection replicates for free\n * from the control plane — so the caller collapses it with\n * `Projection.ownersRunnableBy` and hands over data the store can match on.\n * That keeps the store ignorant of consent, which is also what keeps it\n * replaceable.\n */\nexport interface ClaimInput {\n readonly runnerId: string;\n readonly owner: string;\n readonly device: PublicIdentity;\n /**\n * The (site, owner) pairs this device may run work for — cloud_009 §3.\n *\n * **One set of pairs, not a set of sites and a set of owners.** Consent\n * binds a user to a site, so the two cannot travel separately: a device\n * whose owner consented to site A, serving a roster member who consented\n * to site B, would have every element of both sets and no consented route\n * between them. Two sets multiply; consent does not.\n *\n * Built by {@link Projection.routesFor} and matched with {@link routeKey},\n * so the relay and a store in another repository agree on the encoding by\n * calling the same function rather than by both spelling it out.\n *\n * This is the collapse that lets a claim stay one operation: a predicate\n * cannot travel to a store over a network, and a set can.\n */\n readonly routes: ReadonlySet<string>;\n /**\n * Kinds this device can run — and that is now the whole of the match.\n *\n * A relay routes by kind and by consent; **which service answers is not its\n * question**. A job names a purpose, a person's mapping names a service,\n * and a control plane joins them at claim. A relay that filtered on the\n * service would need the mapping, which is the one thing it is not supposed\n * to hold.\n *\n * This carried a companion, `serves`, holding the (kind, service) pairs a\n * device advertised, so a job naming a service reached only a device with\n * it. Sites stopped naming services (Amendment L) and the field became wire\n * nothing read — so it is gone rather than left for a reader to infer\n * meaning from.\n *\n * The cost is stated plainly: a job may be offered to a device whose owner\n * admits the person but whose machine their mapping did not name. The\n * control plane declines it as not-here and it goes back with a\n * {@link RETRY_AFTER_MS} wait, which is what that mechanism is for.\n */\n readonly kinds: ReadonlySet<string>;\n readonly max: number;\n readonly leaseMs: number;\n}\n\n/**\n * How a (site, owner) route is written, so two implementations agree.\n *\n * The same `\\u0000` the job key uses, for the same reason: it cannot appear\n * in a site id or an owner id, so this is a key rather than a parser.\n */\nexport const routeKey = (siteId: string, owner: string): string =>\n `${siteId}\\u0000${owner}`;\n\n/**\n * Where the store's sense of time comes from — cloud_006 §3.4.\n *\n * **The store owns its clock; callers do not pass one.** Every deadline the\n * relay decides — a lease's expiry, the `awaiting-payload` window, what a\n * sweep considers due — is now stamped by one source, and it is the same\n * source that will later stamp them for every replica.\n *\n * It used to be a parameter. `claim` took `now`, `sweep` took `now`, and each\n * plane called its own `now()` before calling in — which is fine in one\n * process and is the recurring bug the moment there are two. A lease granted\n * by a pod whose clock runs fast is short; the same lease swept by a pod whose\n * clock runs slow outlives it. Nobody is wrong and the lease has no length.\n *\n * A Valkey-backed store returns `TIME` here, so the deadline and the sweep\n * that enforces it are read from the same server. The injected clock stays for\n * tests, which is what lets them move time instead of sleeping.\n *\n * **What deliberately does not use this**: request-signature freshness. That\n * is checked against the *local* clock on purpose — it is a question about the\n * caller's clock versus this process's, `MAX_CLOCK_SKEW_MS` already tolerates\n * two minutes of disagreement, and a network round trip to timestamp every\n * inbound request would be a cost with no property behind it.\n */\nexport interface RelayStateOptions {\n readonly now?: () => number | Promise<number>;\n}\n\n/** Why a lease-scoped operation was refused, in the caller's vocabulary. */\nexport type HolderRefusal =\n | \"not-found\"\n | \"not-holder\"\n | \"stale-lease\"\n | \"not-ready\"\n /** The job already ended — V1-6. A replay must not reopen it. */\n | \"terminal\";\n\n/**\n * In-memory routing state.\n *\n * Deliberately not durable. The skeleton proves the protocol, and the\n * production hub replaces this with the closed multi-tenant router behind the\n * same shape (cloud_004 §9). Anything a restart loses here is a job that\n * returns to its site's queue — which is the behaviour a lapsed lease already\n * has to produce, so nothing new needs to be true for this to be safe.\n */\n/**\n * A job's key: the site that published it, and the id that site chose.\n *\n * `\\u0000` cannot appear in either half, so this is a key rather than a\n * parser — cloud_009 §3, and the reason the Valkey layout uses a distinct\n * prefix rather than a suffix on the old one.\n */\nconst keyOf = (siteId: string, jobId: string): string =>\n `${siteId}\\u0000${jobId}`;\n\nexport class RelayState implements RoutingStore {\n /**\n * Jobs by **(site, id)** — cloud_009 §3.\n *\n * A job id is a site's to choose, so two sites can choose the same one.\n * Keyed by the bare id, the second site's enqueue returned the first\n * site's job (cloud_008 finding 58), and the refusal that fixed it was a\n * cross-tenant existence oracle. Keyed by the pair, the collision does not\n * exist and there is nothing to refuse.\n *\n * `\\u0000` as the separator, because a site id is a uuid and a job id is\n * whatever a site chose — including, one day, a string with a colon in it.\n * A separator that cannot appear in either half is the difference between\n * a key and a parser.\n */\n readonly #jobs = new Map<string, RoutedJob>();\n\n /**\n * Grants by lease id, so a holder-scoped call needs no site — §3.\n *\n * `takePayload`, `complete`, `releaseLeases` and `renewLeases` carry a\n * `leaseId` the relay minted, which is unique across every site. That is\n * what lets those four signatures stay as they are: the caller names the\n * grant, and the grant names the job. A daemon never has to know a site id\n * to answer for work it holds.\n */\n readonly #byLease = new Map<string, RoutedJob>();\n\n /**\n * Jobs by bare id, across sites — the refusal path.\n *\n * The lease index alone answers the happy case and gets the refusals\n * wrong: a **stale** lease finds nothing, so `LEASE_HONORED`'s \"your grant\n * ended\" becomes \"no such job\", and a daemon that was slow is told\n * something untrue about the work it was doing. Distinguishing\n * `not-found`, `not-holder` and `stale-lease` needs the job even when the\n * lease named is over, and that is what this is for.\n *\n * A list rather than a single value: two sites may choose one id, which is\n * the whole reason `#jobs` is keyed by the pair.\n */\n readonly #byJobId = new Map<string, RoutedJob[]>();\n\n /**\n * The job a holder-scoped call is about, without a site id.\n *\n * The exact grant first, and **checked against the job the caller named**:\n * a lease id belonging to another job would otherwise hand over that job's\n * payload to somebody holding a valid-looking grant. Then the same job held\n * by this runner under an older grant, which is what `stale-lease` is.\n *\n * And then nothing — V1-8. There used to be a third step: any job with that\n * id, which produced `not-holder` where an absent job produces `not-found`.\n * Since job ids are chosen per site, a runner could name a bare id it had\n * no relationship with and learn from the status code whether some *other*\n * tenant had a job by that name. Finding 58's existence oracle, through the\n * holder door.\n *\n * The distinction it bought was never acted on: a daemon abandons the work\n * either way. So a caller now learns about jobs it holds or held, and about\n * nothing else.\n */\n #grantFor(\n jobId: string,\n runnerId: string,\n leaseId: string,\n ): RoutedJob | undefined {\n const exact = this.#byLease.get(leaseId);\n if (exact?.id === jobId) return exact;\n const candidates = this.#byJobId.get(jobId) ?? [];\n return candidates.find((job) => job.claimedBy?.runnerId === runnerId);\n }\n\n #index(job: RoutedJob): void {\n const bare = this.#byJobId.get(job.id);\n if (bare) {\n if (!bare.includes(job)) bare.push(job);\n } else {\n this.#byJobId.set(job.id, [job]);\n }\n }\n\n #forget(job: RoutedJob): void {\n this.#jobs.delete(keyOf(job.siteId, job.id));\n const bare = (this.#byJobId.get(job.id) ?? []).filter((it) => it !== job);\n if (bare.length === 0) this.#byJobId.delete(job.id);\n else this.#byJobId.set(job.id, bare);\n if (job.claimedBy) this.#byLease.delete(job.claimedBy.leaseId);\n }\n readonly #presence = new Map<string, Presence>();\n readonly #now: () => number | Promise<number>;\n\n constructor(options: RelayStateOptions = {}) {\n this.#now = options.now ?? Date.now;\n }\n\n /** The one clock every deadline in this store is stamped from. */\n async now(): Promise<number> {\n return this.#now();\n }\n\n /**\n * Take a stub for routing. The payload is not here and will not be.\n *\n * **Idempotent by job id, and that is a security property rather than a\n * convenience.** Site-plane calls are authenticated by signature, and\n * byollm_009 §4.2's argument for signing the request instead of a\n * server-issued nonce rests entirely on every write being idempotent per the\n * instance it names. This one was not: re-enqueueing a known id built a\n * fresh `queued` job over the top of the old one, discarding a live claim,\n * its lease and any payload the site had already sealed to a device. A\n * replayed enqueue inside the two-minute freshness window was therefore a\n * way to yank a job back from the machine running it — the `release` bug of\n * §4.2, rediscovered on the other plane.\n *\n * So a known id returns what is already routing, unchanged. A site that\n * restarts and republishes its queue is the normal case, and it must not\n * disturb work in flight.\n */\n enqueue(input: {\n id: string;\n siteId: string;\n stub: JobStub;\n }): Promise<RoutedJob> {\n // Idempotent by (site, id). The refusal that used to live here went with\n // the collision it refused — cloud_009 §3.\n const existing = this.#jobs.get(keyOf(input.siteId, input.id));\n if (existing) return Promise.resolve(existing);\n const job: RoutedJob = {\n id: input.id,\n siteId: input.siteId,\n stub: input.stub,\n state: \"queued\",\n refusedBy: [],\n };\n this.#jobs.set(keyOf(job.siteId, job.id), job);\n this.#index(job);\n return Promise.resolve(job);\n }\n\n job(siteId: string, jobId: string): Promise<RoutedJob | undefined> {\n return Promise.resolve(this.#jobs.get(keyOf(siteId, jobId)));\n }\n\n jobs(): Promise<RoutedJob[]> {\n return Promise.resolve([...this.#jobs.values()]);\n }\n\n /** Jobs a site must seal for, right now. */\n async awaiting(siteId: string): Promise<RoutedJob[]> {\n return (await this.jobs()).filter(\n (j) => j.siteId === siteId && j.state === \"awaiting-payload\",\n );\n }\n\n /** Sealed results waiting to go home. */\n async finished(siteId: string): Promise<RoutedJob[]> {\n return (await this.jobs()).filter(\n (j) =>\n j.siteId === siteId && j.state === \"done\" && j.result !== undefined,\n );\n }\n\n /**\n * Claim work — one operation, because it has to be.\n *\n * Moved here wholesale from `DaemonPlane`, where it was a scan followed by\n * per-job mutation. Nothing about the *decision* changed; what changed is\n * that a store can now implement it, because the filter and the write are\n * one call rather than a loop the caller drives.\n *\n * The order of the guards is worth preserving as-is when this becomes a Lua\n * script: cheapest first, and `owners` last because it is the only one that\n * needed the projection.\n */\n async claim(input: ClaimInput): Promise<ClaimedStub[]> {\n const now = await this.now();\n await this.sweep();\n\n const granted: ClaimedStub[] = [];\n for (const job of this.#jobs.values()) {\n if (granted.length >= input.max) break;\n if (job.state !== \"queued\") continue;\n // The route, as one lookup, because consent is about the pair — a\n // device whose owner consented to site A, serving a roster member who\n // consented to site B, is in both a set of sites and a set of owners\n // and has no consented route between them.\n if (!input.routes.has(routeKey(job.siteId, job.stub.owner))) continue;\n // By kind, and only by kind — Amendment L. Which of the owner's\n // services answers is the control plane's, resolved from this person's\n // mapping at claim; a relay that matched on it would need to hold the\n // mapping, and holding it is what a relay must not do.\n if (!input.kinds.has(job.stub.kind)) continue;\n // Already declined by this device — `REFUSAL_NOT_REOFFERED`, §2.1.\n if (job.refusedBy.includes(input.runnerId)) continue;\n // Declined for something that may have changed since, but not yet.\n if ((job.retryAfter?.[input.runnerId] ?? 0) > now) continue;\n // Withdrawn by the site — §2.2. Cheap, and before every other check.\n if (job.cancelled) continue;\n // The relay's half of AUDIENCE_BOTH_SIDES. The daemon re-checks its own\n // allowlist and may still refuse — this only ever narrows.\n\n // `self` means the owner's own machines, and the route set cannot\n // express that — cloud_008 §2.1.\n //\n // The routes are every (site, owner) this device may run for, which for\n // a Team owner's machine includes every roster member. Correct for\n // `public` and `named`, and wrong for\n // `self`: a roster member's private job was offered to the owner's\n // daemon, which refused it locally and released it, and the relay\n // offered it straight back. The ping-pong was the visible symptom; the\n // invisible one is that `self` — the audience a user picks *because*\n // they want their own machine — was the audience the relay ignored.\n if (job.stub.audience === \"private\" && job.stub.owner !== input.owner) {\n continue;\n }\n\n // A UUID, not a readable composite. The direct plane's lease ids are\n // UUIDs and the Supabase adapter's `lease_id` column is typed `uuid`, so\n // a relay minting `lease_<job>_<time>` would route perfectly against a\n // memory store and fail the moment a real site adopted the lease.\n const leaseId = randomUUID();\n job.state = \"awaiting-payload\";\n job.claimedBy = {\n runnerId: input.runnerId,\n owner: input.owner,\n device: input.device,\n leaseId,\n leaseExpiresAt: now + input.leaseMs,\n };\n // Not the lease: this bounds how long we wait for a *site*, not how long\n // the device may work. byollm_009 §7.1's third clock.\n job.awaitingUntil = now + AWAITING_PAYLOAD_MS;\n // The grant is findable by its own id, which is how a holder-scoped\n // call needs no site — cloud_009 §3.\n this.#byLease.set(leaseId, job);\n\n granted.push({\n ...job.stub,\n lease: {\n id: leaseId,\n runnerId: input.runnerId,\n expiresAt: job.claimedBy.leaseExpiresAt,\n },\n });\n }\n return granted;\n }\n\n /**\n * Hand over the sealed payload to the device that holds the lease.\n *\n * The read and the state transition are one operation for the same reason\n * `claim` is: `running` must be set by whoever was told the envelope, or two\n * replicas can both hand out the same work and both believe they were first.\n */\n takePayload(input: {\n jobId: string;\n runnerId: string;\n leaseId: string;\n }): Promise<{ envelope: SealedEnvelope } | { refused: HolderRefusal }> {\n const job = this.#grantFor(input.jobId, input.runnerId, input.leaseId);\n if (!job) return Promise.resolve({ refused: \"not-found\" });\n if (job.claimedBy?.runnerId !== input.runnerId) {\n return Promise.resolve({ refused: \"not-holder\" });\n }\n // LEASE_HONORED per *instance*: a stale lease id names a grant that is\n // over, and answering it would hand work to a previous holder.\n if (job.claimedBy.leaseId !== input.leaseId) {\n return Promise.resolve({ refused: \"stale-lease\" });\n }\n if (!job.payload) return Promise.resolve({ refused: \"not-ready\" });\n // Only a job that is still running gets handed its payload — V1-6.\n //\n // The holder and lease checks pass for a job this runner finished\n // moments ago, because completing does not end the grant. A replayed\n // fetch then set `state = 'running'` on a **done** job: `finished()`\n // stopped returning its result to the site, and the sweep requeued\n // completed work as though the device had died holding it. A duplicate\n // request undoing a finished job is the shape `RESULT_IDEMPOTENT` exists\n // to forbid, arriving through the other door.\n if (job.state !== \"ready\" && job.state !== \"running\") {\n return Promise.resolve({ refused: \"terminal\" });\n }\n job.state = \"running\";\n return Promise.resolve({ envelope: job.payload });\n }\n\n /**\n * Record a finished job.\n *\n * `RESULT_IDEMPOTENT` lives here rather than in the caller: a replayed\n * result must be a no-op decided by the same operation that would have\n * written it, or two replicas can both decide they were the first.\n */\n complete(input: {\n jobId: string;\n runnerId: string;\n leaseId: string;\n envelope: SealedEnvelope;\n disposition: \"ok\" | \"error\" | \"canceled\";\n }): Promise<\n | { accepted: boolean; duplicate?: boolean; state: RoutedState }\n | { refused: HolderRefusal }\n > {\n const job = this.#grantFor(input.jobId, input.runnerId, input.leaseId);\n if (!job) return Promise.resolve({ refused: \"not-found\" });\n if (job.claimedBy?.runnerId !== input.runnerId) {\n return Promise.resolve({ refused: \"not-holder\" });\n }\n // Terminal before holder — cloud_008 §3.6, and the same order in all four\n // stores.\n //\n // This file argued the opposite two days ago: that a result under a grant\n // that ended is a different device's work arriving late rather than a\n // replay, so the lease check should win. That is right for a job which is\n // **not** terminal, and the two orders only ever disagree about a *done*\n // job asked about under a stale grant — where \"already recorded\" is the\n // more useful of two true statements, and \"your lease is stale\" invents a\n // worry about an answer that is safely stored.\n //\n // The deciding argument is not comfort. byollm_009 §4's case for signing\n // requests rather than issuing nonces rests on every write being\n // idempotent per the instance it names, and on the direct plane\n // `RESULT_IDEMPOTENT` was holding only because `complete` nulls the lease\n // and the holder check tripped first. A MUST another MUST's security\n // argument leans on cannot hold by coincidence.\n //\n // Scoped to the device that finished it: anyone else falls through to the\n // holder check and gets the refusal they would get for a job that is not\n // terminal, so a job id is not a terminality probe.\n if (job.state === \"done\") {\n const sameGrant = job.claimedBy.leaseId === input.leaseId;\n return Promise.resolve(\n sameGrant\n ? { accepted: false, duplicate: true, state: job.state }\n : { refused: \"stale-lease\" },\n );\n }\n // LEASE_HONORED per instance — cloud_008 §1.4a.\n if (job.claimedBy.leaseId !== input.leaseId) {\n return Promise.resolve({ refused: \"stale-lease\" });\n }\n job.result = input.envelope;\n job.disposition = input.disposition;\n job.state = \"done\";\n return Promise.resolve({ accepted: true, state: job.state });\n }\n\n /** Give back leases this runner holds, naming each grant it means. */\n releaseLeases(input: {\n runnerId: string;\n leases: readonly { jobId: string; leaseId: string }[];\n reason?: ReleaseReason;\n retryAfter?: number;\n }): Promise<string[]> {\n const released: string[] = [];\n for (const { jobId, leaseId } of input.leases) {\n // Through the grant, like every other holder-scoped operation — the\n // caller names a lease and this store no longer keys jobs by a bare id.\n const job = this.#grantFor(jobId, input.runnerId, leaseId);\n if (!job || job.claimedBy?.runnerId !== input.runnerId) continue;\n if (job.claimedBy.leaseId !== leaseId) continue;\n // Recorded before the requeue, so the job goes back to the queue\n // already knowing not to come back here. A daemon releasing for\n // `shutdown` or `backend-down` is saying \"not now\"; `refused` is the\n // only one that means \"not me, ever\" — the others must stay claimable\n // by the same device or a restart would strand its own work.\n if (\n input.reason === \"refused\" &&\n !job.refusedBy.includes(input.runnerId)\n ) {\n job.refusedBy.push(input.runnerId);\n }\n // Recorded before the requeue for the same reason the refusal is: the\n // job goes back to the queue already knowing when it may come back\n // here, rather than being claimable for the instant in between.\n if (input.retryAfter !== undefined) {\n job.retryAfter = {\n ...job.retryAfter,\n [input.runnerId]: input.retryAfter,\n };\n }\n this.#requeue(job);\n released.push(jobId);\n }\n return Promise.resolve(released);\n }\n\n /**\n * Take a site's sealed payload for a claimed job.\n *\n * Refuses anything not `awaiting-payload`, which is what makes the timeout\n * mean something: a late seal must not land on a claim that has moved.\n */\n seal(input: {\n jobId: string;\n siteId: string;\n envelope: SealedEnvelope;\n }): Promise<\n | { state: RoutedState }\n | { refused: \"not-found\" | \"too-late\"; was?: RoutedState }\n > {\n const job = this.#jobs.get(keyOf(input.siteId, input.jobId));\n if (job?.siteId !== input.siteId) {\n return Promise.resolve({ refused: \"not-found\" });\n }\n if (job.state !== \"awaiting-payload\") {\n return Promise.resolve({ refused: \"too-late\", was: job.state });\n }\n job.payload = input.envelope;\n job.state = \"ready\";\n delete job.awaitingUntil;\n return Promise.resolve({ state: job.state });\n }\n\n /** {@link RoutingStore.cancel} — the site withdraws a job. */\n cancel(input: { jobId: string; siteId: string }): Promise<boolean> {\n const job = this.#jobs.get(keyOf(input.siteId, input.jobId));\n // Scoped to the caller's site for the same reason every other site-plane\n // operation is: a site must not be able to cancel somebody else's work by\n // guessing an id.\n if (job?.siteId !== input.siteId) return Promise.resolve(false);\n job.cancelled = true;\n // Not deleted, and not requeued. If a device holds it, that device has to\n // hear about it — which is what `cancelRequests` below is for.\n return Promise.resolve(true);\n }\n\n /** {@link RoutingStore.cancelRequests} — cancelled jobs this runner holds. */\n cancelRequests(runnerId: string): Promise<Grant[]> {\n return Promise.resolve(\n [...this.#jobs.values()]\n .filter(\n (job) =>\n job.cancelled === true && job.claimedBy?.runnerId === runnerId,\n )\n // The grant, not the id — V1-3. Two sites may have chosen the same\n // job id, and a daemon holding both cannot tell which of them a bare\n // id means.\n .map((job) => ({\n jobId: job.id,\n leaseId: job.claimedBy?.leaseId ?? \"\",\n }))\n .filter((grant) => grant.leaseId !== \"\"),\n );\n }\n\n /** {@link RoutingStore.renewLeases} — extend what is still held, name what is not. */\n async renewLeases(input: {\n runnerId: string;\n leases: readonly { jobId: string; leaseId: string }[];\n leaseMs: number;\n }): Promise<{\n renewed: { jobId: string; expiresAt: number }[];\n lost: Grant[];\n }> {\n // The store's clock, not the caller's — cloud_006 §3.4. A lease extended\n // against one replica's `Date.now()` and swept against another's is a\n // lease with no length.\n const now = await this.now();\n const renewed: { jobId: string; expiresAt: number }[] = [];\n const lost: Grant[] = [];\n\n for (const { jobId, leaseId } of input.leases) {\n const job = this.#grantFor(jobId, input.runnerId, leaseId);\n const held = job?.claimedBy;\n // The lease id *and* the runner. A lease id is a UUID so the runner\n // check is belt and braces, but \"this grant, held by you\" is the\n // sentence every other operation in this file checks, and a renewal is\n // the one that extends a hold rather than ending it.\n if (\n !job ||\n held?.leaseId !== leaseId ||\n held.runnerId !== input.runnerId\n ) {\n lost.push({ jobId, leaseId });\n continue;\n }\n // Replaced rather than mutated: the grant is a readonly record, which\n // is what stops any other operation here from quietly extending it.\n const expiresAt = now + input.leaseMs;\n job.claimedBy = { ...held, leaseExpiresAt: expiresAt };\n renewed.push({ jobId, expiresAt });\n }\n\n // `awaitingUntil` is deliberately untouched. That clock bounds how long we\n // wait for a *site* to seal, and a busy device has no bearing on it —\n // byollm_009 §7.1's third clock stays third.\n return { renewed, lost };\n }\n\n async seen(presence: Omit<Presence, \"lastSeenAt\">): Promise<Presence> {\n const lastSeenAt = await this.now();\n const existing = this.#presence.get(presence.runnerId);\n if (existing) {\n existing.lastSeenAt = lastSeenAt;\n // Refreshed, not merged. The heartbeat re-sends the whole matrix every\n // time precisely so a server never matches against a stale one, and a\n // record that kept the union would keep advertising a backend the\n // machine has since lost — which is worse than forgetting one it still\n // has, because work would route to it.\n existing.capabilities = presence.capabilities;\n // Refreshed for the same reason and in the same breath: a kind that\n // stopped being contended — because the owner chose a default, or one\n // service went away — must stop being reported as withheld, or the page\n // keeps asking for a decision that has already been made.\n existing.withheld = presence.withheld;\n return existing;\n }\n const fresh: Presence = { ...presence, lastSeenAt };\n this.#presence.set(presence.runnerId, fresh);\n return fresh;\n }\n\n presence(runnerId: string): Promise<Presence | undefined> {\n return Promise.resolve(this.#presence.get(runnerId));\n }\n\n /**\n * Lose a record, the way a real store does.\n *\n * A shared store drops presence for reasons this one never will — a TTL, a\n * reschedule, a restart — and the interesting behaviour is what the relay\n * does next. `ValkeyRoutingStore` has carried the same helper since\n * finding 52; this is its memory twin, so the case can be written once\n * against the implementation that is easy to reason about.\n */\n dropPresenceForTests(runnerId: string): Promise<void> {\n this.#presence.delete(runnerId);\n return Promise.resolve();\n }\n\n everyone(): Promise<Presence[]> {\n return Promise.resolve([...this.#presence.values()]);\n }\n\n /**\n * Return a job to the queue, forgetting the claim.\n *\n * The stub survives; nothing is lost. That is `LEASE_RECLAIMABLE` and it is\n * why the awaiting-payload timeout is cheap to fire: the worst case is that\n * a device did nothing for ten seconds and another one gets a turn.\n */\n #requeue(job: RoutedJob): void {\n job.state = \"queued\";\n // The grant ended; the index that names it must end with it, or a stale\n // lease id resolves to a job it no longer holds.\n if (job.claimedBy) this.#byLease.delete(job.claimedBy.leaseId);\n delete job.claimedBy;\n delete job.awaitingUntil;\n delete job.payload;\n }\n\n /**\n * Fire whatever the clock says is due, and report it.\n *\n * Returns the jobs it requeued so a caller can log or surface them — a\n * timeout that fires invisibly is indistinguishable from a job that was\n * never claimed, and those want very different debugging.\n */\n async sweep(): Promise<RoutedJob[]> {\n const now = await this.now();\n const requeued: RoutedJob[] = [];\n const expired: RoutedJob[] = [];\n for (const job of this.#jobs.values()) {\n // Past its deadline — cloud_008 §2.2, and `TTL_EXPIRY` on this plane.\n //\n // The relay never read `stub.deadlineAt`. Not \"read it and got the\n // arithmetic wrong\": the field travelled on every stub, byollm_009 §6\n // describes it as the bound on how long a ciphertext is worth carrying,\n // and nothing here ever looked at it. A job whose deadline passed went\n // on being offered to devices forever, and its sealed payload sat in\n // the relay for as long as the process lived.\n //\n // Dropped rather than marked terminal. The relay is a router and the\n // site holds the authoritative record; a stub nobody may run is not\n // routing state, and keeping a tombstone would be keeping the ciphertext\n // with it. A daemon mid-flight learns through `renewLeases`, which\n // reports a job the store no longer holds as `lost` — the path that\n // already exists for a lease that ended.\n if (job.stub.deadlineAt <= now) {\n this.#forget(job);\n expired.push(job);\n continue;\n }\n if (job.state === \"awaiting-payload\" && (job.awaitingUntil ?? 0) <= now) {\n this.#requeue(job);\n requeued.push(job);\n }\n const lease = job.claimedBy;\n if (\n lease &&\n (job.state === \"ready\" || job.state === \"running\") &&\n lease.leaseExpiresAt <= now\n ) {\n this.#requeue(job);\n requeued.push(job);\n }\n }\n // Both, because a caller that logs \"requeued\" and never mentions expiry\n // would report a shrinking queue with no reason for it.\n return [...requeued, ...expired];\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/state.ts"],"sourcesContent":["import type {\n CapabilityMatrix,\n ClaimedStub,\n JobStub,\n PublicIdentity,\n SealedEnvelope,\n WithheldKind,\n} from \"@byollm/protocol\";\nimport { randomUUID } from \"node:crypto\";\nimport type { Grant, RoutingStore } from \"./store.js\";\n\n/**\n * The relay's routing state — byollm_009 §7, reachable at last.\n *\n * §7 described a state machine the direct plane could not produce. There, the\n * site and the upstream are the same party: it seals when it likes, and a job\n * is never claimed-but-unsealed. Here they are different parties, and the gap\n * between them is a state:\n *\n * ```\n * queued ──claim──▶ awaiting-payload ──sealed──▶ ready ──fetch──▶ running\n * ▲ │ │\n * └────────────────────┘ ▼\n * site never seals, or seals too late ok | error | canceled\n * ```\n *\n * The relay cannot seal, so it cannot shortcut this. A payload is encrypted\n * to *the device that claimed it*, and nobody knows which device that is until\n * the claim happens — which is precisely why claim-then-fetch makes a blind\n * relay possible at all. The window is the price.\n *\n * ## What the relay holds, and what it cannot\n *\n * Stubs (metadata the site chose to publish), sealed envelopes it cannot open,\n * and public keys. There is no field on any type in this file that could hold\n * a private key or a plaintext, which is `RELAY_BLIND` expressed as a data\n * model rather than as a policy.\n */\n\n/** Where a routed job is. */\nexport type RoutedState =\n \"queued\" | \"awaiting-payload\" | \"ready\" | \"running\" | \"done\";\n\n/**\n * How long a site has to seal after one of its jobs is claimed.\n *\n * **Distinct from the lease, and distinct from the job's TTL** — byollm_009\n * §7.1. Three clocks, three different questions:\n *\n * - the **TTL** asks how long the work is worth doing at all;\n * - the **lease** asks how long this device gets to run it;\n * - this asks how long we wait for a site that has gone away.\n *\n * Collapsing any pair of them looks harmless until a site restarts during a\n * deploy: with only a lease, the device sits politely holding a job whose\n * payload will never arrive, and the lease's whole minute is spent waiting on\n * a party that is not coming back. Short, because a site that is up answers in\n * milliseconds and a site that is down will not answer sooner for waiting.\n */\nexport const AWAITING_PAYLOAD_MS = 10_000;\n\n/**\n * How long a device waits before asking about a job it could not run — the\n * rate every transient refusal needs.\n *\n * **A transient needs a rate; a retry without a not-before is a spin.** A\n * release that is not `refused` requeues immediately and stays claimable by\n * the same device, which is right for a daemon saying \"not now, I am\n * restarting\" and catastrophic for a control plane saying \"that mapping\n * resolves to another of your machines\": the device re-claims at once, is\n * declined again, and the pair loops. Measured before it could happen —\n * twelve ticks produced twelve control-plane reads, which in a deployment is\n * twelve database queries for a job that was never going to run there.\n *\n * Thirty seconds is chosen against the only thing it delays: a person who\n * fixes a mapping and has work already queued. Half a minute is a wait\n * nobody notices, and one read per device per thirty seconds per stuck job is\n * a cost nobody notices either.\n *\n * Distinct from the three durations above it, and for a fourth kind of\n * reason: the TTL asks whether the work is still worth doing, the lease how\n * long this device gets, {@link AWAITING_PAYLOAD_MS} how long we wait for the\n * site — and this asks how long before we ask *this device* again.\n */\nexport const RETRY_AFTER_MS = 30_000;\n\n/** A job the relay is routing. Metadata and ciphertext, nothing else. */\n/** Why a daemon gave a job back. Only `refused` means \"not me, ever\". */\nexport type ReleaseReason =\n \"shutdown\" | \"pause\" | \"revoked\" | \"backend-down\" | \"refused\";\n\nexport interface RoutedJob {\n readonly id: string;\n /** Which site enqueued it — the party that will be asked to seal. */\n readonly siteId: string;\n /**\n * Everything the relay knows about the work, which is everything the site\n * chose to publish and not one field more (byollm_009 §6).\n */\n readonly stub: JobStub;\n state: RoutedState;\n /** Set from the claim; the site seals to these keys. */\n claimedBy?: {\n readonly runnerId: string;\n readonly owner: string;\n readonly device: PublicIdentity;\n readonly leaseId: string;\n readonly leaseExpiresAt: number;\n };\n /** When {@link AWAITING_PAYLOAD_MS} runs out for this claim. */\n awaitingUntil?: number;\n /**\n * Runners that released this job with reason `refused` — cloud_008 §2.1.\n *\n * `REFUSAL_NOT_REOFFERED`, which the relay did not implement: it dropped\n * `ReleaseRequest.reason` on the floor. The field's own docstring says why\n * that is not cosmetic — an upstream cannot evaluate a daemon's *local*\n * `named` allowlist, so it may legitimately offer work the daemon then\n * declines, and without a record the two spin between claim and release\n * forever. The direct plane has always kept this list.\n */\n refusedBy: string[];\n /**\n * Runners that may not be offered this job again *yet*, and from when.\n *\n * The middle ground {@link RoutingStore.releaseLeases} had no way to say.\n * `refusedBy` is forever and a bare release is immediate; a control plane\n * declining a job for a reason the world can change — an unfilled mapping\n * slot, a resolution that named another machine, a store that was briefly\n * unreachable — means neither. It means \"ask again later\", and later needs\n * a number.\n *\n * Keyed by runner because it is a fact about a pairing, not about the job:\n * the same job goes to another device immediately, which is the whole\n * point of not marking it refused.\n */\n retryAfter?: Record<string, number>;\n /**\n * The site withdrew this job — cloud_008 §2.2.\n *\n * A flag rather than a state, because a cancelled job that a device is\n * *running* is not finished: the daemon has to be told, abort its backend\n * call and report `canceled`, and the ordinary `complete` path then closes\n * it. Making it a state would strand the in-flight case between two\n * machines' ideas of what happened.\n */\n cancelled?: boolean;\n /** Sealed to the claiming device by the site. Opaque here. */\n payload?: SealedEnvelope;\n /** Sealed to the site by the device. Opaque here. */\n result?: SealedEnvelope;\n /**\n * The result's clear-text discriminator — byollm_009 §6.1.\n *\n * The one outcome fact the relay is given, and the reason it is given:\n * without it the relay cannot stop dispatching a finished job. A routing\n * hint and never a fact — the *site* verifies it against the sealed\n * outcome, because only the site can open the envelope. The relay acts on\n * it and is entitled to be wrong; a lying daemon costs it a dispatch\n * decision, not a security property.\n */\n disposition?: \"ok\" | \"error\" | \"canceled\";\n}\n\n/** A device the relay has seen recently. */\nexport interface Presence {\n readonly runnerId: string;\n readonly owner: string;\n readonly device: PublicIdentity;\n lastSeenAt: number;\n /**\n * What this machine last said it can run — cloud_009, 2026-08-24.\n *\n * **Capabilities are presence data.** They arrive on the same heartbeat as\n * everything else here, they go stale at the same moment and for the same\n * reason, and a machine that stops heartbeating has not stopped being able\n * to run Llama — it has stopped being somewhere we can ask. Keeping them\n * anywhere else would put capability truth outside the interface that owns\n * presence, and every next consumer (a member's usable set, a dashboard's\n * pulse, a degraded-state banner) would re-derive it from a different\n * place. That is the two-owners shape this codebase keeps paying for.\n *\n * Empty is a real answer, not a missing one: it is a paired machine with no\n * healthy backend, which is a legal state the whole connect-first ruling\n * exists to make visible rather than refuse.\n */\n capabilities: CapabilityMatrix;\n /**\n * Kinds this machine is deliberately *not* advertising — byollm_016.\n *\n * Presence data for the same reason capabilities are, and stored beside\n * them rather than derived: two services answering one kind with no\n * `defaults` entry is a state only the daemon can see, and the hub cannot\n * reconstruct it from the matrix — an absent kind and a withheld kind look\n * identical there. Without this field the owner's page can say \"nothing\n * serves llm.generate\", which is true and useless, instead of \"two services\n * answer it and you have not chosen\", which is the sentence that ends with\n * the owner doing something.\n *\n * Empty is the normal answer, and means every kind resolved.\n */\n withheld: readonly WithheldKind[];\n // `revoked` is deliberately absent — cloud_008 §2.3.\n //\n // It was a boolean on the *runner*, written at heartbeat and read by\n // nothing but the debug page. Enforcement had already moved to the\n // projection, because enforcing on this flag made revocation depend on the\n // client calling an endpoint: a daemon that simply never heartbeat went on\n // claiming forever. What survived was the cache, which is a stored copy of\n // a derived fact — this project's most-repeated bug, kept alive here as a\n // display value.\n //\n // It is also a concept multi-tenancy cannot express. Revocation is a fact\n // about an (owner, site) pair; a daemon serving two sites and revoked at\n // one is not \"a revoked runner\", and a boolean on the device has nowhere to\n // put the difference. Deleting it now is what stops cloud_009 inheriting a\n // field it would have to contradict.\n}\n\n/**\n * What a routing store must do, expressed as operations — cloud_006 §3.2.\n *\n * Every method below is a **decision plus its write**, never a read the caller\n * follows with a mutation. That is the whole point, and it is the difference\n * between an interface a shared store can implement and one it cannot.\n *\n * `claim` is the specimen. It used to live in `DaemonPlane` as\n * `jobs()` → filter → mutate, which is atomic for exactly one reason: Node is\n * single-threaded and these Maps are local, so nothing runs between the read\n * and the write. Neither survives a store on a network, and\n * `packages/relay/test/two-replicas.test.ts` holds the resulting race as a\n * failing assertion.\n *\n * So the rule for anything added here: **if a caller has to read, decide, and\n * write back, the operation is in the wrong place.** Move the decision in.\n *\n * ## Why the projection does not come with it\n *\n * `claim` takes `owners: string[]` rather than a projection or a predicate.\n * A closure cannot travel to Valkey, and the projection replicates for free\n * from the control plane — so the caller collapses it with\n * `Projection.ownersRunnableBy` and hands over data the store can match on.\n * That keeps the store ignorant of consent, which is also what keeps it\n * replaceable.\n */\nexport interface ClaimInput {\n readonly runnerId: string;\n readonly owner: string;\n readonly device: PublicIdentity;\n /**\n * The (site, owner) pairs this device may run work for — cloud_009 §3.\n *\n * **One set of pairs, not a set of sites and a set of owners.** Consent\n * binds a user to a site, so the two cannot travel separately: a device\n * whose owner consented to site A, serving a roster member who consented\n * to site B, would have every element of both sets and no consented route\n * between them. Two sets multiply; consent does not.\n *\n * Built by {@link Projection.routesFor} and matched with {@link routeKey},\n * so the relay and a store in another repository agree on the encoding by\n * calling the same function rather than by both spelling it out.\n *\n * This is the collapse that lets a claim stay one operation: a predicate\n * cannot travel to a store over a network, and a set can.\n */\n readonly routes: ReadonlySet<string>;\n /**\n * Kinds this device can run — and that is now the whole of the match.\n *\n * A relay routes by kind and by consent; **which service answers is not its\n * question**. A job names a purpose, a person's mapping names a service,\n * and a control plane joins them at claim. A relay that filtered on the\n * service would need the mapping, which is the one thing it is not supposed\n * to hold.\n *\n * This carried a companion, `serves`, holding the (kind, service) pairs a\n * device advertised, so a job naming a service reached only a device with\n * it. Sites stopped naming services (Amendment L) and the field became wire\n * nothing read — so it is gone rather than left for a reader to infer\n * meaning from.\n *\n * The cost is stated plainly: a job may be offered to a device whose owner\n * admits the person but whose machine their mapping did not name. The\n * control plane declines it as not-here and it goes back with a\n * {@link RETRY_AFTER_MS} wait, which is what that mechanism is for.\n */\n readonly kinds: ReadonlySet<string>;\n readonly max: number;\n readonly leaseMs: number;\n}\n\n/**\n * How a (site, owner) route is written, so two implementations agree.\n *\n * The same `\\u0000` the job key uses, for the same reason: it cannot appear\n * in a site id or an owner id, so this is a key rather than a parser.\n */\nexport const routeKey = (siteId: string, owner: string): string =>\n `${siteId}\\u0000${owner}`;\n\n/**\n * Where the store's sense of time comes from — cloud_006 §3.4.\n *\n * **The store owns its clock; callers do not pass one.** Every deadline the\n * relay decides — a lease's expiry, the `awaiting-payload` window, what a\n * sweep considers due — is now stamped by one source, and it is the same\n * source that will later stamp them for every replica.\n *\n * It used to be a parameter. `claim` took `now`, `sweep` took `now`, and each\n * plane called its own `now()` before calling in — which is fine in one\n * process and is the recurring bug the moment there are two. A lease granted\n * by a pod whose clock runs fast is short; the same lease swept by a pod whose\n * clock runs slow outlives it. Nobody is wrong and the lease has no length.\n *\n * A Valkey-backed store returns `TIME` here, so the deadline and the sweep\n * that enforces it are read from the same server. The injected clock stays for\n * tests, which is what lets them move time instead of sleeping.\n *\n * **What deliberately does not use this**: request-signature freshness. That\n * is checked against the *local* clock on purpose — it is a question about the\n * caller's clock versus this process's, `MAX_CLOCK_SKEW_MS` already tolerates\n * two minutes of disagreement, and a network round trip to timestamp every\n * inbound request would be a cost with no property behind it.\n */\nexport interface RelayStateOptions {\n readonly now?: () => number | Promise<number>;\n}\n\n/** Why a lease-scoped operation was refused, in the caller's vocabulary. */\nexport type HolderRefusal =\n | \"not-found\"\n | \"not-holder\"\n | \"stale-lease\"\n | \"not-ready\"\n /** The job already ended — V1-6. A replay must not reopen it. */\n | \"terminal\";\n\n/**\n * In-memory routing state.\n *\n * Deliberately not durable. The skeleton proves the protocol, and the\n * production hub replaces this with the closed multi-tenant router behind the\n * same shape (cloud_004 §9). Anything a restart loses here is a job that\n * returns to its site's queue — which is the behaviour a lapsed lease already\n * has to produce, so nothing new needs to be true for this to be safe.\n */\n/**\n * A job's key: the site that published it, and the id that site chose.\n *\n * `\\u0000` cannot appear in either half, so this is a key rather than a\n * parser — cloud_009 §3, and the reason the Valkey layout uses a distinct\n * prefix rather than a suffix on the old one.\n */\nconst keyOf = (siteId: string, jobId: string): string =>\n `${siteId}\\u0000${jobId}`;\n\nexport class RelayState implements RoutingStore {\n /**\n * Jobs by **(site, id)** — cloud_009 §3.\n *\n * A job id is a site's to choose, so two sites can choose the same one.\n * Keyed by the bare id, the second site's enqueue returned the first\n * site's job (cloud_008 finding 58), and the refusal that fixed it was a\n * cross-tenant existence oracle. Keyed by the pair, the collision does not\n * exist and there is nothing to refuse.\n *\n * `\\u0000` as the separator, because a site id is a uuid and a job id is\n * whatever a site chose — including, one day, a string with a colon in it.\n * A separator that cannot appear in either half is the difference between\n * a key and a parser.\n */\n readonly #jobs = new Map<string, RoutedJob>();\n\n /**\n * Grants by lease id, so a holder-scoped call needs no site — §3.\n *\n * `takePayload`, `complete`, `releaseLeases` and `renewLeases` carry a\n * `leaseId` the relay minted, which is unique across every site. That is\n * what lets those four signatures stay as they are: the caller names the\n * grant, and the grant names the job. A daemon never has to know a site id\n * to answer for work it holds.\n */\n readonly #byLease = new Map<string, RoutedJob>();\n\n /**\n * Jobs by bare id, across sites — the refusal path.\n *\n * The lease index alone answers the happy case and gets the refusals\n * wrong: a **stale** lease finds nothing, so `LEASE_HONORED`'s \"your grant\n * ended\" becomes \"no such job\", and a daemon that was slow is told\n * something untrue about the work it was doing. Distinguishing\n * `not-found`, `not-holder` and `stale-lease` needs the job even when the\n * lease named is over, and that is what this is for.\n *\n * A list rather than a single value: two sites may choose one id, which is\n * the whole reason `#jobs` is keyed by the pair.\n */\n readonly #byJobId = new Map<string, RoutedJob[]>();\n\n /**\n * The job a holder-scoped call is about, without a site id.\n *\n * The exact grant first, and **checked against the job the caller named**:\n * a lease id belonging to another job would otherwise hand over that job's\n * payload to somebody holding a valid-looking grant. Then the same job held\n * by this runner under an older grant, which is what `stale-lease` is.\n *\n * And then nothing — V1-8. There used to be a third step: any job with that\n * id, which produced `not-holder` where an absent job produces `not-found`.\n * Since job ids are chosen per site, a runner could name a bare id it had\n * no relationship with and learn from the status code whether some *other*\n * tenant had a job by that name. Finding 58's existence oracle, through the\n * holder door.\n *\n * The distinction it bought was never acted on: a daemon abandons the work\n * either way. So a caller now learns about jobs it holds or held, and about\n * nothing else.\n */\n #grantFor(\n jobId: string,\n runnerId: string,\n leaseId: string,\n ): RoutedJob | undefined {\n const exact = this.#byLease.get(leaseId);\n if (exact?.id === jobId) return exact;\n const candidates = this.#byJobId.get(jobId) ?? [];\n return candidates.find((job) => job.claimedBy?.runnerId === runnerId);\n }\n\n #index(job: RoutedJob): void {\n const bare = this.#byJobId.get(job.id);\n if (bare) {\n if (!bare.includes(job)) bare.push(job);\n } else {\n this.#byJobId.set(job.id, [job]);\n }\n }\n\n #forget(job: RoutedJob): void {\n this.#jobs.delete(keyOf(job.siteId, job.id));\n const bare = (this.#byJobId.get(job.id) ?? []).filter((it) => it !== job);\n if (bare.length === 0) this.#byJobId.delete(job.id);\n else this.#byJobId.set(job.id, bare);\n if (job.claimedBy) this.#byLease.delete(job.claimedBy.leaseId);\n }\n readonly #presence = new Map<string, Presence>();\n readonly #now: () => number | Promise<number>;\n\n constructor(options: RelayStateOptions = {}) {\n this.#now = options.now ?? Date.now;\n }\n\n /** The one clock every deadline in this store is stamped from. */\n async now(): Promise<number> {\n return this.#now();\n }\n\n /**\n * Take a stub for routing. The payload is not here and will not be.\n *\n * **Idempotent by job id, and that is a security property rather than a\n * convenience.** Site-plane calls are authenticated by signature, and\n * byollm_009 §4.2's argument for signing the request instead of a\n * server-issued nonce rests entirely on every write being idempotent per the\n * instance it names. This one was not: re-enqueueing a known id built a\n * fresh `queued` job over the top of the old one, discarding a live claim,\n * its lease and any payload the site had already sealed to a device. A\n * replayed enqueue inside the two-minute freshness window was therefore a\n * way to yank a job back from the machine running it — the `release` bug of\n * §4.2, rediscovered on the other plane.\n *\n * So a known id returns what is already routing, unchanged. A site that\n * restarts and republishes its queue is the normal case, and it must not\n * disturb work in flight.\n */\n enqueue(input: {\n id: string;\n siteId: string;\n stub: JobStub;\n }): Promise<RoutedJob> {\n // Idempotent by (site, id). The refusal that used to live here went with\n // the collision it refused — cloud_009 §3.\n const existing = this.#jobs.get(keyOf(input.siteId, input.id));\n if (existing) return Promise.resolve(existing);\n const job: RoutedJob = {\n id: input.id,\n siteId: input.siteId,\n stub: input.stub,\n state: \"queued\",\n refusedBy: [],\n };\n this.#jobs.set(keyOf(job.siteId, job.id), job);\n this.#index(job);\n return Promise.resolve(job);\n }\n\n job(siteId: string, jobId: string): Promise<RoutedJob | undefined> {\n return Promise.resolve(this.#jobs.get(keyOf(siteId, jobId)));\n }\n\n jobs(): Promise<RoutedJob[]> {\n return Promise.resolve([...this.#jobs.values()]);\n }\n\n /** Jobs a site must seal for, right now. */\n async awaiting(siteId: string): Promise<RoutedJob[]> {\n return (await this.jobs()).filter(\n (j) => j.siteId === siteId && j.state === \"awaiting-payload\",\n );\n }\n\n /** Sealed results waiting to go home. */\n async finished(siteId: string): Promise<RoutedJob[]> {\n return (await this.jobs()).filter(\n (j) =>\n j.siteId === siteId && j.state === \"done\" && j.result !== undefined,\n );\n }\n\n /**\n * Claim work — one operation, because it has to be.\n *\n * Moved here wholesale from `DaemonPlane`, where it was a scan followed by\n * per-job mutation. Nothing about the *decision* changed; what changed is\n * that a store can now implement it, because the filter and the write are\n * one call rather than a loop the caller drives.\n *\n * The order of the guards is worth preserving as-is when this becomes a Lua\n * script: cheapest first, and `owners` last because it is the only one that\n * needed the projection.\n */\n async claim(input: ClaimInput): Promise<ClaimedStub[]> {\n const now = await this.now();\n await this.sweep();\n\n const granted: ClaimedStub[] = [];\n for (const job of this.#jobs.values()) {\n if (granted.length >= input.max) break;\n if (job.state !== \"queued\") continue;\n // The route, as one lookup, because consent is about the pair — a\n // device whose owner consented to site A, serving a roster member who\n // consented to site B, is in both a set of sites and a set of owners\n // and has no consented route between them.\n if (!input.routes.has(routeKey(job.siteId, job.stub.owner))) continue;\n // By kind, and only by kind — Amendment L. Which of the owner's\n // services answers is the control plane's, resolved from this person's\n // mapping at claim; a relay that matched on it would need to hold the\n // mapping, and holding it is what a relay must not do.\n if (!input.kinds.has(job.stub.kind)) continue;\n // Already declined by this device — `REFUSAL_NOT_REOFFERED`, §2.1.\n if (job.refusedBy.includes(input.runnerId)) continue;\n // Declined for something that may have changed since, but not yet.\n if ((job.retryAfter?.[input.runnerId] ?? 0) > now) continue;\n // Withdrawn by the site — §2.2. Cheap, and before every other check.\n if (job.cancelled) continue;\n // The relay's half of AUDIENCE_BOTH_SIDES. The daemon re-checks its own\n // allowlist and may still refuse — this only ever narrows.\n\n // `self` means the owner's own machines, and the route set cannot\n // express that — cloud_008 §2.1.\n //\n // The routes are every (site, owner) this device may run for, which for\n // a Team owner's machine includes every roster member. Correct for\n // `public` and `named`, and wrong for\n // `self`: a roster member's private job was offered to the owner's\n // daemon, which refused it locally and released it, and the relay\n // offered it straight back. The ping-pong was the visible symptom; the\n // invisible one is that `self` — the audience a user picks *because*\n // they want their own machine — was the audience the relay ignored.\n if (job.stub.audience === \"private\" && job.stub.owner !== input.owner) {\n continue;\n }\n\n // A UUID, not a readable composite. The direct plane's lease ids are\n // UUIDs and the Supabase adapter's `lease_id` column is typed `uuid`, so\n // a relay minting `lease_<job>_<time>` would route perfectly against a\n // memory store and fail the moment a real site adopted the lease.\n const leaseId = randomUUID();\n job.state = \"awaiting-payload\";\n job.claimedBy = {\n runnerId: input.runnerId,\n owner: input.owner,\n device: input.device,\n leaseId,\n leaseExpiresAt: now + input.leaseMs,\n };\n // Not the lease: this bounds how long we wait for a *site*, not how long\n // the device may work. byollm_009 §7.1's third clock.\n job.awaitingUntil = now + AWAITING_PAYLOAD_MS;\n // The grant is findable by its own id, which is how a holder-scoped\n // call needs no site — cloud_009 §3.\n this.#byLease.set(leaseId, job);\n\n granted.push({\n ...job.stub,\n lease: {\n id: leaseId,\n runnerId: input.runnerId,\n expiresAt: job.claimedBy.leaseExpiresAt,\n },\n });\n }\n return granted;\n }\n\n /**\n * Hand over the sealed payload to the device that holds the lease.\n *\n * The read and the state transition are one operation for the same reason\n * `claim` is: `running` must be set by whoever was told the envelope, or two\n * replicas can both hand out the same work and both believe they were first.\n */\n takePayload(input: {\n jobId: string;\n runnerId: string;\n leaseId: string;\n }): Promise<{ envelope: SealedEnvelope } | { refused: HolderRefusal }> {\n const job = this.#grantFor(input.jobId, input.runnerId, input.leaseId);\n if (!job) return Promise.resolve({ refused: \"not-found\" });\n if (job.claimedBy?.runnerId !== input.runnerId) {\n return Promise.resolve({ refused: \"not-holder\" });\n }\n // LEASE_HONORED per *instance*: a stale lease id names a grant that is\n // over, and answering it would hand work to a previous holder.\n if (job.claimedBy.leaseId !== input.leaseId) {\n return Promise.resolve({ refused: \"stale-lease\" });\n }\n if (!job.payload) return Promise.resolve({ refused: \"not-ready\" });\n // Only a job that is still running gets handed its payload — V1-6.\n //\n // The holder and lease checks pass for a job this runner finished\n // moments ago, because completing does not end the grant. A replayed\n // fetch then set `state = 'running'` on a **done** job: `finished()`\n // stopped returning its result to the site, and the sweep requeued\n // completed work as though the device had died holding it. A duplicate\n // request undoing a finished job is the shape `RESULT_IDEMPOTENT` exists\n // to forbid, arriving through the other door.\n if (job.state !== \"ready\" && job.state !== \"running\") {\n return Promise.resolve({ refused: \"terminal\" });\n }\n job.state = \"running\";\n return Promise.resolve({ envelope: job.payload });\n }\n\n /**\n * Record a finished job.\n *\n * `RESULT_IDEMPOTENT` lives here rather than in the caller: a replayed\n * result must be a no-op decided by the same operation that would have\n * written it, or two replicas can both decide they were the first.\n */\n complete(input: {\n jobId: string;\n runnerId: string;\n leaseId: string;\n envelope: SealedEnvelope;\n disposition: \"ok\" | \"error\" | \"canceled\";\n }): Promise<\n | { accepted: boolean; duplicate?: boolean; state: RoutedState }\n | { refused: HolderRefusal }\n > {\n const job = this.#grantFor(input.jobId, input.runnerId, input.leaseId);\n if (!job) return Promise.resolve({ refused: \"not-found\" });\n if (job.claimedBy?.runnerId !== input.runnerId) {\n return Promise.resolve({ refused: \"not-holder\" });\n }\n // Terminal before holder — cloud_008 §3.6, and the same order in all four\n // stores.\n //\n // This file argued the opposite two days ago: that a result under a grant\n // that ended is a different device's work arriving late rather than a\n // replay, so the lease check should win. That is right for a job which is\n // **not** terminal, and the two orders only ever disagree about a *done*\n // job asked about under a stale grant — where \"already recorded\" is the\n // more useful of two true statements, and \"your lease is stale\" invents a\n // worry about an answer that is safely stored.\n //\n // The deciding argument is not comfort. byollm_009 §4's case for signing\n // requests rather than issuing nonces rests on every write being\n // idempotent per the instance it names, and on the direct plane\n // `RESULT_IDEMPOTENT` was holding only because `complete` nulls the lease\n // and the holder check tripped first. A MUST another MUST's security\n // argument leans on cannot hold by coincidence.\n //\n // Scoped to the device that finished it: anyone else falls through to the\n // holder check and gets the refusal they would get for a job that is not\n // terminal, so a job id is not a terminality probe.\n if (job.state === \"done\") {\n const sameGrant = job.claimedBy.leaseId === input.leaseId;\n return Promise.resolve(\n sameGrant\n ? { accepted: false, duplicate: true, state: job.state }\n : { refused: \"stale-lease\" },\n );\n }\n // LEASE_HONORED per instance — cloud_008 §1.4a.\n if (job.claimedBy.leaseId !== input.leaseId) {\n return Promise.resolve({ refused: \"stale-lease\" });\n }\n job.result = input.envelope;\n job.disposition = input.disposition;\n job.state = \"done\";\n return Promise.resolve({ accepted: true, state: job.state });\n }\n\n /** Give back leases this runner holds, naming each grant it means. */\n releaseLeases(input: {\n runnerId: string;\n leases: readonly { jobId: string; leaseId: string }[];\n reason?: ReleaseReason;\n retryAfter?: number;\n }): Promise<string[]> {\n const released: string[] = [];\n for (const { jobId, leaseId } of input.leases) {\n // Through the grant, like every other holder-scoped operation — the\n // caller names a lease and this store no longer keys jobs by a bare id.\n const job = this.#grantFor(jobId, input.runnerId, leaseId);\n if (!job || job.claimedBy?.runnerId !== input.runnerId) continue;\n if (job.claimedBy.leaseId !== leaseId) continue;\n /**\n * A finished job is finished — byollm-review 2026-08-27.\n *\n * Terminal **before** holder, the ordering `complete` already uses: a\n * release naming a valid lease on a done job was flipping it back to\n * `queued`. The recorded result survives on the row and becomes\n * unreachable, because `finished()` filters on the state — so the site\n * never collects it, the job is offered again, and somebody's hardware\n * runs it a second time and overwrites the first answer.\n *\n * The daemon's own shutdown races exactly this. `shutdown()` snapshots\n * the active leases while a job is finishing, the abort lands, and a\n * release and a result for the same lease are in flight together. If\n * the result wins, this used to undo it.\n *\n * `takePayload` got this guard at V1-6 and `complete` at cloud_008\n * §3.6. This is the third holder-scoped door and it was the one left\n * open — which is the argument for the contract case beside it: three\n * doors, one rule, and the two that were shut were shut one at a time.\n */\n if (job.state === \"done\") continue;\n // Recorded before the requeue, so the job goes back to the queue\n // already knowing not to come back here. A daemon releasing for\n // `shutdown` or `backend-down` is saying \"not now\"; `refused` is the\n // only one that means \"not me, ever\" — the others must stay claimable\n // by the same device or a restart would strand its own work.\n if (\n input.reason === \"refused\" &&\n !job.refusedBy.includes(input.runnerId)\n ) {\n job.refusedBy.push(input.runnerId);\n }\n // Recorded before the requeue for the same reason the refusal is: the\n // job goes back to the queue already knowing when it may come back\n // here, rather than being claimable for the instant in between.\n if (input.retryAfter !== undefined) {\n job.retryAfter = {\n ...job.retryAfter,\n [input.runnerId]: input.retryAfter,\n };\n }\n this.#requeue(job);\n released.push(jobId);\n }\n return Promise.resolve(released);\n }\n\n /**\n * Take a site's sealed payload for a claimed job.\n *\n * Refuses anything not `awaiting-payload`, which is what makes the timeout\n * mean something: a late seal must not land on a claim that has moved.\n */\n seal(input: {\n jobId: string;\n siteId: string;\n envelope: SealedEnvelope;\n }): Promise<\n | { state: RoutedState }\n | { refused: \"not-found\" | \"too-late\"; was?: RoutedState }\n > {\n const job = this.#jobs.get(keyOf(input.siteId, input.jobId));\n if (job?.siteId !== input.siteId) {\n return Promise.resolve({ refused: \"not-found\" });\n }\n if (job.state !== \"awaiting-payload\") {\n return Promise.resolve({ refused: \"too-late\", was: job.state });\n }\n job.payload = input.envelope;\n job.state = \"ready\";\n delete job.awaitingUntil;\n return Promise.resolve({ state: job.state });\n }\n\n /** {@link RoutingStore.cancel} — the site withdraws a job. */\n cancel(input: { jobId: string; siteId: string }): Promise<boolean> {\n const job = this.#jobs.get(keyOf(input.siteId, input.jobId));\n // Scoped to the caller's site for the same reason every other site-plane\n // operation is: a site must not be able to cancel somebody else's work by\n // guessing an id.\n if (job?.siteId !== input.siteId) return Promise.resolve(false);\n job.cancelled = true;\n // Not deleted, and not requeued. If a device holds it, that device has to\n // hear about it — which is what `cancelRequests` below is for.\n return Promise.resolve(true);\n }\n\n /** {@link RoutingStore.cancelRequests} — cancelled jobs this runner holds. */\n cancelRequests(runnerId: string): Promise<Grant[]> {\n return Promise.resolve(\n [...this.#jobs.values()]\n .filter(\n (job) =>\n job.cancelled === true && job.claimedBy?.runnerId === runnerId,\n )\n // The grant, not the id — V1-3. Two sites may have chosen the same\n // job id, and a daemon holding both cannot tell which of them a bare\n // id means.\n .map((job) => ({\n jobId: job.id,\n leaseId: job.claimedBy?.leaseId ?? \"\",\n }))\n .filter((grant) => grant.leaseId !== \"\"),\n );\n }\n\n /** {@link RoutingStore.renewLeases} — extend what is still held, name what is not. */\n async renewLeases(input: {\n runnerId: string;\n leases: readonly { jobId: string; leaseId: string }[];\n leaseMs: number;\n }): Promise<{\n renewed: { jobId: string; expiresAt: number }[];\n lost: Grant[];\n }> {\n // The store's clock, not the caller's — cloud_006 §3.4. A lease extended\n // against one replica's `Date.now()` and swept against another's is a\n // lease with no length.\n const now = await this.now();\n const renewed: { jobId: string; expiresAt: number }[] = [];\n const lost: Grant[] = [];\n\n for (const { jobId, leaseId } of input.leases) {\n const job = this.#grantFor(jobId, input.runnerId, leaseId);\n const held = job?.claimedBy;\n // The lease id *and* the runner. A lease id is a UUID so the runner\n // check is belt and braces, but \"this grant, held by you\" is the\n // sentence every other operation in this file checks, and a renewal is\n // the one that extends a hold rather than ending it.\n if (\n !job ||\n held?.leaseId !== leaseId ||\n held.runnerId !== input.runnerId\n ) {\n lost.push({ jobId, leaseId });\n continue;\n }\n // Replaced rather than mutated: the grant is a readonly record, which\n // is what stops any other operation here from quietly extending it.\n const expiresAt = now + input.leaseMs;\n job.claimedBy = { ...held, leaseExpiresAt: expiresAt };\n renewed.push({ jobId, expiresAt });\n }\n\n // `awaitingUntil` is deliberately untouched. That clock bounds how long we\n // wait for a *site* to seal, and a busy device has no bearing on it —\n // byollm_009 §7.1's third clock stays third.\n return { renewed, lost };\n }\n\n async seen(presence: Omit<Presence, \"lastSeenAt\">): Promise<Presence> {\n const lastSeenAt = await this.now();\n const existing = this.#presence.get(presence.runnerId);\n if (existing) {\n existing.lastSeenAt = lastSeenAt;\n // Refreshed, not merged. The heartbeat re-sends the whole matrix every\n // time precisely so a server never matches against a stale one, and a\n // record that kept the union would keep advertising a backend the\n // machine has since lost — which is worse than forgetting one it still\n // has, because work would route to it.\n existing.capabilities = presence.capabilities;\n // Refreshed for the same reason and in the same breath: a kind that\n // stopped being contended — because the owner chose a default, or one\n // service went away — must stop being reported as withheld, or the page\n // keeps asking for a decision that has already been made.\n existing.withheld = presence.withheld;\n return existing;\n }\n const fresh: Presence = { ...presence, lastSeenAt };\n this.#presence.set(presence.runnerId, fresh);\n return fresh;\n }\n\n presence(runnerId: string): Promise<Presence | undefined> {\n return Promise.resolve(this.#presence.get(runnerId));\n }\n\n /**\n * Lose a record, the way a real store does.\n *\n * A shared store drops presence for reasons this one never will — a TTL, a\n * reschedule, a restart — and the interesting behaviour is what the relay\n * does next. `ValkeyRoutingStore` has carried the same helper since\n * finding 52; this is its memory twin, so the case can be written once\n * against the implementation that is easy to reason about.\n */\n dropPresenceForTests(runnerId: string): Promise<void> {\n this.#presence.delete(runnerId);\n return Promise.resolve();\n }\n\n everyone(): Promise<Presence[]> {\n return Promise.resolve([...this.#presence.values()]);\n }\n\n /**\n * Return a job to the queue, forgetting the claim.\n *\n * The stub survives; nothing is lost. That is `LEASE_RECLAIMABLE` and it is\n * why the awaiting-payload timeout is cheap to fire: the worst case is that\n * a device did nothing for ten seconds and another one gets a turn.\n */\n #requeue(job: RoutedJob): void {\n job.state = \"queued\";\n // The grant ended; the index that names it must end with it, or a stale\n // lease id resolves to a job it no longer holds.\n if (job.claimedBy) this.#byLease.delete(job.claimedBy.leaseId);\n delete job.claimedBy;\n delete job.awaitingUntil;\n delete job.payload;\n }\n\n /**\n * Fire whatever the clock says is due, and report it.\n *\n * Returns the jobs it requeued so a caller can log or surface them — a\n * timeout that fires invisibly is indistinguishable from a job that was\n * never claimed, and those want very different debugging.\n */\n async sweep(): Promise<RoutedJob[]> {\n const now = await this.now();\n const requeued: RoutedJob[] = [];\n const expired: RoutedJob[] = [];\n for (const job of this.#jobs.values()) {\n // Past its deadline — cloud_008 §2.2, and `TTL_EXPIRY` on this plane.\n //\n // The relay never read `stub.deadlineAt`. Not \"read it and got the\n // arithmetic wrong\": the field travelled on every stub, byollm_009 §6\n // describes it as the bound on how long a ciphertext is worth carrying,\n // and nothing here ever looked at it. A job whose deadline passed went\n // on being offered to devices forever, and its sealed payload sat in\n // the relay for as long as the process lived.\n //\n // Dropped rather than marked terminal. The relay is a router and the\n // site holds the authoritative record; a stub nobody may run is not\n // routing state, and keeping a tombstone would be keeping the ciphertext\n // with it. A daemon mid-flight learns through `renewLeases`, which\n // reports a job the store no longer holds as `lost` — the path that\n // already exists for a lease that ended.\n if (job.stub.deadlineAt <= now) {\n this.#forget(job);\n expired.push(job);\n continue;\n }\n if (job.state === \"awaiting-payload\" && (job.awaitingUntil ?? 0) <= now) {\n this.#requeue(job);\n requeued.push(job);\n }\n const lease = job.claimedBy;\n if (\n lease &&\n (job.state === \"ready\" || job.state === \"running\") &&\n lease.leaseExpiresAt <= now\n ) {\n this.#requeue(job);\n requeued.push(job);\n }\n }\n // Both, because a caller that logs \"requeued\" and never mentions expiry\n // would report a shrinking queue with no reason for it.\n return [...requeued, ...expired];\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
package/dist/store-contract.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
routeKey
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-OB6LPEEE.js";
|
|
4
4
|
|
|
5
5
|
// src/store-contract.ts
|
|
6
6
|
import { generateKeys, publicIdentityOf } from "@byollm/protocol";
|
|
@@ -269,6 +269,36 @@ function describeStoreContract(name, options) {
|
|
|
269
269
|
expect((await store.claim(claimArgs())).map((j) => j.id)).toEqual(["a"]);
|
|
270
270
|
await done();
|
|
271
271
|
});
|
|
272
|
+
it("will not un-finish a job that is already done", async () => {
|
|
273
|
+
const { store, done } = await make();
|
|
274
|
+
await store.enqueue({ id: "a", siteId: SITE, stub: stub("a") });
|
|
275
|
+
const [granted] = await store.claim(claimArgs());
|
|
276
|
+
const lease = granted.lease.id;
|
|
277
|
+
await store.takePayload({
|
|
278
|
+
jobId: "a",
|
|
279
|
+
runnerId: "runner_1",
|
|
280
|
+
leaseId: lease
|
|
281
|
+
});
|
|
282
|
+
await store.complete({
|
|
283
|
+
jobId: "a",
|
|
284
|
+
runnerId: "runner_1",
|
|
285
|
+
leaseId: lease,
|
|
286
|
+
envelope: ENVELOPE,
|
|
287
|
+
disposition: "ok"
|
|
288
|
+
});
|
|
289
|
+
const released = await store.releaseLeases({
|
|
290
|
+
runnerId: "runner_1",
|
|
291
|
+
leases: [{ jobId: "a", leaseId: lease }],
|
|
292
|
+
reason: "shutdown"
|
|
293
|
+
});
|
|
294
|
+
expect(released).toEqual([]);
|
|
295
|
+
expect((await store.job(SITE, "a"))?.state).toBe("done");
|
|
296
|
+
expect((await store.finished(SITE)).map((j) => j.id)).toEqual(["a"]);
|
|
297
|
+
expect(await store.claim(claimArgs({ runnerId: "runner_2" }))).toEqual(
|
|
298
|
+
[]
|
|
299
|
+
);
|
|
300
|
+
await done();
|
|
301
|
+
});
|
|
272
302
|
it("keeps a job claimable by a runner that only went away", async () => {
|
|
273
303
|
const { store, done } = await make();
|
|
274
304
|
await store.enqueue({ id: "a", siteId: SITE, stub: stub("a") });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/store-contract.ts"],"sourcesContent":["import { generateKeys, publicIdentityOf, type JobStub } from \"@byollm/protocol\";\nimport { describe, expect, it } from \"vitest\";\nimport { routeKey } from \"./state.js\";\nimport type { RoutingStore } from \"./store.js\";\n\n/**\n * The routing store's behaviour, written once and run against every\n * implementation — cloud_008 finding 54.\n *\n * There were two copies. This package tested `RelayState`; `byollm-cloud`\n * tested `ValkeyRoutingStore` with a file that opened by explaining it was\n * \"written once and parameterised… a second copy of the scenario is a second\n * place for two implementations to quietly diverge\" — and was itself the\n * second copy. It had drifted to sixteen cases against the eighteen in the\n * ledger, which is exactly the divergence the sentence predicted, happening\n * to the sentence.\n *\n * So the contract lives here, in the package that declares `RoutingStore`,\n * and both repositories import it. A store that cannot pass this is not a\n * routing store, whatever it implements.\n *\n * ## Why this ships in the published package\n *\n * Because the implementation that matters most is in another repository. A\n * contract only the author can run is a description; one a third party runs\n * against their own implementation is a contract. `@byollm/relay/store-contract`\n * is a subpath export so nothing that imports the relay itself pulls in a\n * test framework.\n *\n * ## What this cannot prove\n *\n * `CLAIM_ATOMIC` is a MUST and every case here would pass against a store\n * that read, decided and wrote in three steps — in one process there is\n * nothing to interleave. Concurrency belongs with the implementation that\n * has a network in it, and `byollm-cloud`'s suite runs it against Valkey with\n * many connections. Named here so the omission is a decision rather than an\n * oversight.\n */\n\n/** The control-plane site id these cases route under. */\nexport const CONTRACT_SITE = \"site_store\";\n\n/** A site's identity key id — byollm_009 Amendment A §A.3. */\nconst SITE_KEY_ID = \"BYOLLM-TEST-SITE-KEY-ID\";\n\nconst SITE = CONTRACT_SITE;\n\nconst stub = (id: string, owner = \"alice\"): JobStub => ({\n id,\n kind: \"llm.generate\",\n owner,\n site: SITE_KEY_ID,\n audience: \"private\",\n sizeClass: \"small\",\n streaming: false,\n deadlineAt: 4_102_444_800_000,\n});\n\n/** One advertised route, as a daemon describes it on every heartbeat. */\nconst capability = (model: string) => ({\n kind: \"llm.generate\" as const,\n service: model,\n backendId: \"ollama\" as const,\n backendClass: \"process\" as const,\n model,\n offerScope: \"private\" as const,\n});\n\nconst ENVELOPE = {\n ciphertext: \"AAAA\",\n recipientKeyId: \"r\",\n senderKeyId: \"s\",\n direction: \"payload\" as const,\n deadlineAt: 4_102_444_800_000,\n};\n\n/**\n * A claim, with its routes written out — cloud_009 §3.\n *\n * `routes` replaced `siteId` + `owners`, and the cases that pass `owners`\n * below now pass the pairs those owners are reachable through. That rewrite\n * is the contract's own statement of the rule: there is no way to express\n * \"this owner, any site\" here, because that is not a thing consent says.\n */\nconst claimArgs = (\n over: { owners?: string[]; sites?: string[] } & Record<string, unknown> = {},\n) => {\n const { owners = [\"alice\"], sites = [SITE], ...rest } = over;\n return {\n runnerId: \"runner_1\",\n owner: \"alice\",\n device: publicIdentityOf(generateKeys(Date.now())),\n kinds: new Set([\"llm.generate\"]),\n routes: new Set(\n sites.flatMap((site) => owners.map((owner) => routeKey(site, owner))),\n ),\n max: 10,\n leaseMs: 60_000,\n ...rest,\n };\n};\n\nexport interface StoreContractOptions {\n /**\n * A fresh, empty store, and how to dispose of it.\n *\n * Arrow-typed rather than a method, because the caller passes these\n * around: a method signature lets `this` travel with the call, and a\n * factory read off an options object is exactly where that goes wrong.\n */\n readonly make: () => Promise<{\n store: RoutingStore;\n done: () => Promise<void>;\n }>;\n /**\n * Whether this store writes stubs as bytes and reads them back.\n *\n * An in-process store holds typed objects and cannot hold a stub it cannot\n * parse; a serialising one can, because what it wrote may have been written\n * by a previous version. The case that covers it is skipped rather than\n * hidden — a reader should know which half of this contract each store is\n * proving (cloud_008 §2.1a).\n */\n readonly serialising?: boolean;\n /** Write a stub's raw bytes, bypassing serialisation. */\n readonly writeRawStub?: (\n store: RoutingStore,\n id: string,\n raw: string,\n ) => Promise<void>;\n}\n\n/** Run the contract. Call inside a suite; it declares its own `describe`. */\nexport function describeStoreContract(\n name: string,\n options: StoreContractOptions,\n): void {\n const { make, serialising, writeRawStub } = options;\n describe(`the routing store — ${name}`, () => {\n it(\"is idempotent by job id\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n await store.claim(claimArgs());\n // A republish must not disturb work in flight — byollm_009 §4.2's replay\n // argument rests on it.\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n expect((await store.job(SITE, \"a\"))?.state).toBe(\"awaiting-payload\");\n await done();\n });\n\n it(\"gives two sites the same id without either seeing the other\", async () => {\n // cloud_009 §3. Keyed by the bare id, the second site's enqueue returned\n // the *first* site's job (cloud_008 finding 58), and the refusal that\n // fixed it was a cross-tenant existence oracle: a site knows its own stub\n // is well-formed, so any answer it can tell apart from success confirms\n // somebody else holds that id. Keyed by (site, id), the collision does\n // not exist and there is nothing to answer.\n const { store, done } = await make();\n await store.enqueue({ id: \"shared\", siteId: SITE, stub: stub(\"shared\") });\n await store.enqueue({\n id: \"shared\",\n siteId: \"site_other\",\n stub: stub(\"shared\", \"bob\"),\n });\n\n // Two jobs, each its own site's.\n expect((await store.job(SITE, \"shared\"))?.stub.owner).toBe(\"alice\");\n expect((await store.job(\"site_other\", \"shared\"))?.stub.owner).toBe(\"bob\");\n\n // And a claim for one site does not see the other's, which is the\n // property the key exists for rather than a restatement of it.\n const granted = await store.claim(claimArgs());\n expect(granted.map((job) => job.id)).toEqual([\"shared\"]);\n expect(granted[0]?.owner).toBe(\"alice\");\n\n // The same site republishing is still absorbed — byollm_009 §4.2's\n // replay argument, which is what idempotence is for.\n const replay = await store.enqueue({\n id: \"shared\",\n siteId: SITE,\n stub: stub(\"shared\"),\n });\n expect(replay).toEqual(await store.job(SITE, \"shared\"));\n await done();\n });\n\n it(\"grants only what the owners set allows\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"mine\", siteId: SITE, stub: stub(\"mine\") });\n await store.enqueue({\n id: \"theirs\",\n siteId: SITE,\n stub: stub(\"theirs\", \"mallory\"),\n });\n const granted = await store.claim(claimArgs());\n expect(granted.map((job) => job.id)).toEqual([\"mine\"]);\n await done();\n });\n\n it(\"refuses a stale lease and honours the current one\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n await store.seal({ jobId: \"a\", siteId: SITE, envelope: ENVELOPE });\n\n expect(\n await store.takePayload({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: \"an-older-grant\",\n }),\n ).toEqual({ refused: \"stale-lease\" });\n\n expect(\n await store.takePayload({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n }),\n ).toEqual({ envelope: ENVELOPE });\n await done();\n });\n\n it(\"will not answer for one job with another job's lease\", async () => {\n // cloud_009 §3. Holder-scoped calls carry a lease id and no site, so the\n // store resolves the job *from the lease* — and a lease id belonging to a\n // different job must not be a way to reach that job's payload.\n //\n // A mutation that dropped the \"does this lease's job match the id the\n // caller named\" check survived every other case in this contract, on both\n // implementations, because nothing else here ever presents a mismatched\n // pair. The holder of a valid grant is exactly the party who could try\n // it.\n const { store, done } = await make();\n await store.enqueue({ id: \"mine\", siteId: SITE, stub: stub(\"mine\") });\n await store.enqueue({ id: \"other\", siteId: SITE, stub: stub(\"other\") });\n const granted = await store.claim(claimArgs());\n const mine = granted.find((job) => job.id === \"mine\");\n const other = granted.find((job) => job.id === \"other\");\n await store.seal({ jobId: \"other\", siteId: SITE, envelope: ENVELOPE });\n\n // The right runner, a real lease, the wrong job.\n expect(\n await store.takePayload({\n jobId: \"other\",\n runnerId: \"runner_1\",\n leaseId: mine!.lease.id,\n }),\n ).toEqual({ refused: \"stale-lease\" });\n\n // And the honest pairing still works, so this is not \"refuse everything\".\n expect(\n await store.takePayload({\n jobId: \"other\",\n runnerId: \"runner_1\",\n leaseId: other!.lease.id,\n }),\n ).toEqual({ envelope: ENVELOPE });\n await done();\n });\n\n it(\"renews a grant it still holds, and extends the sweep with it\", async () => {\n // cloud_008 §0.6, at the contract level: renewal has to be one operation,\n // because between a read and a write another replica can sweep the lease\n // — and the renewal would then resurrect a grant on a job already back in\n // the queue. In Valkey that is a script; in memory it is a method; the\n // guarantee is the same one, so it is tested here rather than twice.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs({ leaseMs: 1_000 }));\n\n const renewed = await store.renewLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: granted!.lease.id }],\n leaseMs: 60_000,\n });\n\n expect(renewed.lost).toEqual([]);\n expect(renewed.renewed.map((r) => r.jobId)).toEqual([\"a\"]);\n // The store's own clock did the arithmetic — the caller passed a duration,\n // never an instant, which is what keeps two replicas from measuring one\n // lease against two clocks (cloud_006 §3.4).\n expect(renewed.renewed[0]!.expiresAt).toBeGreaterThan(\n granted!.lease.expiresAt,\n );\n\n // And it is the *stored* grant that moved, not just the number returned.\n // Reporting a renewal without performing one is the mutation that\n // survived the first version of the relay's own test for this.\n expect((await store.job(SITE, \"a\"))?.claimedBy?.leaseExpiresAt).toBe(\n renewed.renewed[0]!.expiresAt,\n );\n await done();\n });\n\n it(\"reports a lease the runner no longer holds as lost, and renews nothing\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs({ leaseMs: 1_000 }));\n const before = (await store.job(SITE, \"a\"))?.claimedBy?.leaseExpiresAt;\n\n const renewed = await store.renewLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: \"a-grant-that-ended\" }],\n leaseMs: 60_000,\n });\n\n expect(renewed.renewed).toEqual([]);\n expect(renewed.lost).toEqual([\n { jobId: \"a\", leaseId: \"a-grant-that-ended\" },\n ]);\n // A refusal must not advance anything — the current holder's grant is\n // untouched by somebody else's stale renewal.\n expect((await store.job(SITE, \"a\"))?.claimedBy?.leaseExpiresAt).toBe(\n before,\n );\n expect((await store.job(SITE, \"a\"))?.claimedBy?.leaseId).toBe(\n granted!.lease.id,\n );\n await done();\n });\n\n it(\"does not hand out a stub it cannot parse\", async () => {\n // cloud_008 §2.1a, and the case is not hypothetical: two jobs enqueued\n // before alpha.15 had no `site`, came back in a claim, and the daemon's\n // `.strict()` parse rejected the whole response. Two dead rows denied\n // claims to every device on the hub, and the queue could not drain past\n // them.\n //\n // The store had `JSON.parse(stub) as JobStub` — trusting what it wrote,\n // when what it wrote was written by a previous version. A routing store\n // outlives the deployment that filled it, so a wire change to `JobStub`\n // is a data migration whether or not anyone plans one.\n //\n // Only meaningful against a store that serialises: `RelayState` holds\n // typed objects and cannot reach this state, so it passes by\n // construction — which is worth saying rather than hiding, because a\n // reader should know which half of this contract each store is proving.\n const { store, done } = await make();\n if (!serialising || writeRawStub === undefined) {\n await done();\n return;\n }\n\n await store.enqueue({ id: \"good\", siteId: SITE, stub: stub(\"good\") });\n await store.enqueue({ id: \"old\", siteId: SITE, stub: stub(\"old\") });\n // Exactly what alpha.14 wrote: a stub with no `site`.\n const legacy = { ...stub(\"old\") } as Record<string, unknown>;\n delete legacy[\"site\"];\n await writeRawStub(store, \"old\", JSON.stringify(legacy));\n\n const granted = await store.claim(claimArgs({ max: 10 }));\n\n // The good one is still handed out — \"return nothing\" would pass a test\n // that only checked the bad one was absent, and would stop all routing.\n expect(granted.map((job) => job.id)).toEqual([\"good\"]);\n // And the bad one is back in the queue rather than stuck holding a lease\n // nobody received.\n expect((await store.job(SITE, \"old\"))?.state).toBe(\"queued\");\n await done();\n });\n\n it(\"honours a self job's audience\", async () => {\n // cloud_008 §2.1 at the contract level. The owners set is everyone this\n // device may run for, which for a Team owner's machine is the whole\n // roster — right for public and named, wrong for the one audience a user\n // picks because they want their own machine.\n const { store, done } = await make();\n await store.enqueue({\n id: \"private\",\n siteId: SITE,\n stub: { ...stub(\"private\", \"alice\"), audience: \"private\" },\n });\n\n // A roster owner's machine: may run alice's work in general.\n const granted = await store.claim(\n claimArgs({ owner: \"owner\", owners: [\"owner\", \"alice\"] }),\n );\n\n expect(granted).toEqual([]);\n expect((await store.job(SITE, \"private\"))?.state).toBe(\"queued\");\n await done();\n });\n\n it(\"still offers that job to its owner's own machine\", async () => {\n const { store, done } = await make();\n await store.enqueue({\n id: \"private\",\n siteId: SITE,\n stub: { ...stub(\"private\", \"alice\"), audience: \"private\" },\n });\n const granted = await store.claim(\n claimArgs({ owner: \"alice\", owners: [\"alice\"] }),\n );\n expect(granted.map((job) => job.id)).toEqual([\"private\"]);\n await done();\n });\n\n it(\"does not offer a job back to the runner that refused it\", async () => {\n // `REFUSAL_NOT_REOFFERED`. `releaseLeases` gained an **optional** reason,\n // which means a store can ignore it and still typecheck — this class has\n // now diverged from the interface that way twice, so the contract is the\n // only thing that catches it.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n\n await store.releaseLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: granted!.lease.id }],\n reason: \"refused\",\n });\n\n expect(await store.claim(claimArgs())).toEqual([]);\n expect((await store.job(SITE, \"a\"))?.state).toBe(\"queued\");\n // And somebody else may still run it.\n expect(\n (await store.claim(claimArgs({ runnerId: \"runner_2\" }))).map(\n (j) => j.id,\n ),\n ).toEqual([\"a\"]);\n await done();\n });\n\n it(\"does not offer a job back to a runner before its not-before\", async () => {\n /**\n * The middle ground between \"not ever\" and \"immediately\".\n *\n * A control plane declining a job for something the world can change —\n * an unfilled mapping slot, a resolution naming another machine, a\n * store that was briefly unreachable — means neither. Released\n * immediately, the device re-claims at once and is declined again; a\n * spin, and in a deployment one database read per turn of it.\n *\n * Asserted with an explicit moment rather than by moving a clock,\n * because a store on the other side of a network has its own and this\n * contract cannot reach it.\n */\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n\n await store.releaseLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: granted!.lease.id }],\n retryAfter: Date.now() + 600_000,\n });\n\n expect(await store.claim(claimArgs())).toEqual([]);\n expect((await store.job(SITE, \"a\"))?.state).toBe(\"queued\");\n // And it is *not* a refusal: another device gets it at once, which is\n // the entire reason this is a different thing.\n expect(\n (await store.claim(claimArgs({ runnerId: \"runner_2\" }))).map(\n (j) => j.id,\n ),\n ).toEqual([\"a\"]);\n await done();\n });\n\n it(\"offers it again once the not-before has passed\", async () => {\n // The other half, and the one that makes it a rate rather than a\n // refusal: a person who fixes their mapping gets their queued work.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n\n await store.releaseLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: granted!.lease.id }],\n retryAfter: Date.now() - 1_000,\n });\n\n expect((await store.claim(claimArgs())).map((j) => j.id)).toEqual([\"a\"]);\n await done();\n });\n\n it(\"keeps a job claimable by a runner that only went away\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n\n await store.releaseLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: granted!.lease.id }],\n reason: \"shutdown\",\n });\n\n expect((await store.claim(claimArgs())).map((j) => j.id)).toEqual([\"a\"]);\n await done();\n });\n\n it(\"stops offering a job past its deadline, and drops the ciphertext\", async () => {\n // cloud_008 §2.2. `deadlineAt` travelled on every stub and nothing read\n // it, so expired jobs were offered forever and their sealed payloads kept\n // for as long as the store lived.\n const { store, done } = await make();\n await store.enqueue({\n id: \"late\",\n siteId: SITE,\n stub: { ...stub(\"late\"), deadlineAt: Date.now() - 1_000 },\n });\n\n expect(await store.claim(claimArgs())).toEqual([]);\n expect(await store.job(SITE, \"late\")).toBeUndefined();\n await done();\n });\n\n it(\"names a cancelled job to the device holding it, and offers it to nobody\", async () => {\n // cloud_008 §2.2.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n await store.claim(claimArgs());\n\n expect(await store.cancel({ jobId: \"a\", siteId: SITE })).toBe(true);\n // Named by grant — V1-3, so a daemon holding two sites' `a` knows\n // which one the site withdrew.\n const held = (await store.job(SITE, \"a\"))?.claimedBy?.leaseId;\n expect(await store.cancelRequests(\"runner_1\")).toEqual([\n { jobId: \"a\", leaseId: held },\n ]);\n\n // Scoped to the caller's site: a site must not reach another's work by\n // guessing an id.\n expect(await store.cancel({ jobId: \"a\", siteId: \"someone_else\" })).toBe(\n false,\n );\n await done();\n });\n\n it(\"records a result once\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n\n const first = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n envelope: ENVELOPE,\n disposition: \"ok\",\n });\n const replay = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n envelope: { ...ENVELOPE, ciphertext: \"different\" },\n disposition: \"error\",\n });\n\n expect(first).toEqual({ accepted: true, state: \"done\" });\n // `duplicate` — cloud_008 §3.6. Case 18 above is the one that says why.\n expect(replay).toEqual({\n accepted: false,\n duplicate: true,\n state: \"done\",\n });\n // The property, not the boolean: the second result did not overwrite.\n expect((await store.job(SITE, \"a\"))?.disposition).toBe(\"ok\");\n await done();\n });\n\n it(\"answers the device that finished it with `duplicate`\", async () => {\n // Contract case 18 — cloud_008 §3.6, and the eighteenth exists because\n // the compiler cannot see any of this: the return type's `duplicate` is\n // optional, so a store that never sets it typechecks perfectly while\n // enforcing `RESULT_IDEMPOTENT` by accident or not at all.\n //\n // A daemon whose acknowledgment was lost hears that its answer is already\n // recorded, rather than that its lease is stale — the second message\n // invents a worry about a result safely on disk.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n\n const first = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n envelope: ENVELOPE,\n disposition: \"ok\",\n });\n expect(first).toEqual({ accepted: true, state: \"done\" });\n\n const replay = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n envelope: { ...ENVELOPE, ciphertext: \"a-different-result\" },\n disposition: \"error\",\n });\n expect(replay).toEqual({\n accepted: false,\n duplicate: true,\n state: \"done\",\n });\n\n // A *different* device, on a job that is terminal, gets the refusal it\n // would get for one that is not — otherwise the two answers differ and a\n // job id becomes a terminality probe.\n const stranger = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_2\",\n leaseId: granted!.lease.id,\n envelope: ENVELOPE,\n disposition: \"ok\",\n });\n expect(stranger).toEqual({ refused: \"not-holder\" });\n\n // And the first answer is what survived, which is the property rather\n // than any of the booleans above.\n expect((await store.job(SITE, \"a\"))?.disposition).toBe(\"ok\");\n await done();\n });\n\n it(\"refuses a result produced under a grant that ended\", async () => {\n // cloud_008 §1.4a at the contract level, and it belongs here more than\n // anywhere: `RoutingStore.complete` gained `leaseId` and this class kept\n // typechecking without it, because a parameter object with fewer\n // properties is assignable to one with more. The relay would have\n // enforced LEASE_HONORED on the memory store and not on the store that\n // runs in production, with nothing red on the way.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n await store.claim(claimArgs());\n\n const late = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: \"a-grant-that-ended\",\n envelope: ENVELOPE,\n disposition: \"ok\",\n });\n\n expect(late).toEqual({ refused: \"stale-lease\" });\n // Untouched: the current holder can still finish it.\n expect((await store.job(SITE, \"a\"))?.state).toBe(\"awaiting-payload\");\n expect((await store.job(SITE, \"a\"))?.result).toBeUndefined();\n await done();\n });\n\n it(\"refuses a payload for a job that already finished [V1-6]\", async () => {\n // The holder and lease checks both pass for a job this runner finished\n // moments ago — completing does not end the grant. So a replayed fetch\n // set `state = 'running'` on a **done** job: the site stopped being able\n // to collect its result, and the sweep requeued completed work as though\n // the device had died holding it.\n //\n // Which is `RESULT_IDEMPOTENT` broken through the other door: a\n // duplicate request undoing a finished job.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n await store.seal({ jobId: \"a\", siteId: SITE, envelope: ENVELOPE });\n await store.takePayload({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n });\n await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n envelope: ENVELOPE,\n disposition: \"ok\",\n });\n\n const replay = await store.takePayload({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n });\n // Its own refusal, not `not-ready`: one says keep asking and this says\n // stop, and a daemon that read them alike would poll a finished job\n // until its lease ran out.\n expect(replay).toEqual({ refused: \"terminal\" });\n // And the finished job is still finished, which is the property.\n expect((await store.job(SITE, \"a\"))?.state).toBe(\"done\");\n await done();\n });\n\n it(\"tells a runner nothing about another site's job id [V1-8]\", async () => {\n // Job ids are chosen per site, so a bare id is a guess about somebody\n // else's namespace. The answer used to depend on whether that guess\n // landed: an id belonging to another tenant came back `not-holder`,\n // an id belonging to nobody came back `not-found`. Two status codes,\n // one bit of somebody else's business — finding 58's oracle, reached\n // through the holder door instead of the site plane.\n const { store, done } = await make();\n // Another tenant's job, under an id this caller can guess but a site it\n // does not route for.\n await store.enqueue({\n id: \"secret\",\n siteId: \"site_somebody_else\",\n stub: stub(\"secret\"),\n });\n await store.enqueue({ id: \"mine\", siteId: SITE, stub: stub(\"mine\") });\n const granted = await store.claim(claimArgs());\n const mine = granted.find((job) => job.id === \"mine\");\n\n // Asking about a job this runner does not hold, under a lease it does.\n const other = await store.takePayload({\n jobId: \"secret\",\n runnerId: \"runner_1\",\n leaseId: mine!.lease.id,\n });\n const absent = await store.takePayload({\n jobId: \"no-such-job-anywhere\",\n runnerId: \"runner_1\",\n leaseId: mine!.lease.id,\n });\n\n expect(other).toEqual(absent);\n expect(other).toEqual({ refused: \"not-found\" });\n await done();\n });\n\n // ── routing by kind ─────────────────────────────────────────────────\n //\n // The selection cases lived here: a job could name one of the owner's\n // services and a store had to offer it only to a device advertising that\n // exact (kind, service) pair. Amendment L removed the field — sites\n // declare purposes and people map them — so a store now matches on kind\n // alone, and the pairs it used to carry are gone from `ClaimInput`.\n //\n // What replaces those cases is the property that a store must *not* have\n // opinions it no longer has the inputs for.\n\n it(\"offers a job to any device that serves its kind\", async () => {\n // Which of the owner's services answers is resolved at claim by a\n // control plane, from a mapping this store never sees. A store that\n // narrowed further would be deciding something it cannot know, and the\n // job would sit queued behind a judgement nobody made.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n\n const claimed = await store.claim(\n claimArgs({ runnerId: \"runner_any\", kinds: new Set([\"llm.generate\"]) }),\n );\n expect(claimed.map((j) => j.id)).toEqual([\"a\"]);\n await done();\n });\n\n it(\"does not offer a job to a device that serves a different kind\", async () => {\n // The control. Kind is the whole of the match, so it has to actually\n // match — a store that offered everything to everybody would push the\n // narrowing onto a control plane that would then decline it, which is\n // a round trip and a wait for a fact this side already had.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n\n const claimed = await store.claim(\n claimArgs({ runnerId: \"runner_chat\", kinds: new Set([\"llm.chat\"]) }),\n );\n expect(claimed).toEqual([]);\n await done();\n });\n\n // ── presence ────────────────────────────────────────────────────────\n //\n // The seam had no contract coverage at all: presence was exercised\n // against `RelayState` and nothing held a shared store to the same\n // behaviour. That is the shape the pairing ceiling was found in — one\n // seam, two implementations, and the check driving the one nobody\n // deploys — so these land with `Presence.capabilities` rather than after\n // somebody notices.\n\n it(\"remembers a device, and what it says it can run\", async () => {\n const { store, done } = await make();\n const device = publicIdentityOf(generateKeys(3_000_000_000_000));\n await store.seen({\n runnerId: \"runner_1\",\n owner: \"alice\",\n device,\n capabilities: [capability(\"llama3\")],\n withheld: [],\n });\n\n const known = await store.presence(\"runner_1\");\n expect(known?.owner).toBe(\"alice\");\n expect(known?.device.identity).toBe(device.identity);\n expect(known?.capabilities.map((row) => row.model)).toEqual([\"llama3\"]);\n await done();\n });\n\n it(\"replaces the matrix rather than merging it\", async () => {\n // The heartbeat re-sends the whole matrix every time so a server never\n // matches against a stale one. A store that kept the union would go on\n // advertising a backend the machine has lost — and work would route to\n // it, which is worse than forgetting one it still has.\n const { store, done } = await make();\n const device = publicIdentityOf(generateKeys(3_000_000_000_001));\n const seen = { runnerId: \"runner_2\", owner: \"alice\", device };\n await store.seen({\n ...seen,\n capabilities: [capability(\"llama3\")],\n withheld: [],\n });\n await store.seen({\n ...seen,\n capabilities: [capability(\"mistral\")],\n withheld: [],\n });\n\n const known = await store.presence(\"runner_2\");\n expect(known?.capabilities.map((row) => row.model)).toEqual([\"mistral\"]);\n await done();\n });\n\n it(\"keeps an empty matrix, because empty is an answer\", async () => {\n // A paired machine with no healthy backend — the connect-first ruling's\n // whole point. A store that treated empty as \"nothing to record\" would\n // leave the last non-empty matrix standing and the machines page would\n // show models that are gone.\n const { store, done } = await make();\n const device = publicIdentityOf(generateKeys(3_000_000_000_002));\n const seen = { runnerId: \"runner_3\", owner: \"alice\", device };\n await store.seen({\n ...seen,\n capabilities: [capability(\"llama3\")],\n withheld: [],\n });\n await store.seen({ ...seen, capabilities: [], withheld: [] });\n\n expect((await store.presence(\"runner_3\"))?.capabilities).toEqual([]);\n await done();\n });\n\n it(\"remembers which kinds are withheld, and drops them when resolved\", async () => {\n // byollm_016. Two services answering one kind with no `defaults` entry\n // is a state only the daemon can see: from the matrix alone a withheld\n // kind and an absent one are the same shape, so a store that dropped\n // this field would leave the owner's page saying \"nothing serves\n // llm.generate\" — true, and useless, when the real sentence is \"two\n // services answer it and you have not chosen\".\n //\n // It lives in the contract rather than one store's tests because the\n // pairing ceiling was found exactly that way: one seam, two\n // implementations, and the check driving the one nobody deploys.\n const { store, done } = await make();\n const device = publicIdentityOf(generateKeys(3_000_000_000_004));\n const seen = { runnerId: \"runner_withheld\", owner: \"alice\", device };\n\n await store.seen({\n ...seen,\n capabilities: [],\n withheld: [\n {\n kind: \"llm.generate\",\n claimants: [\n { service: \"ollama\", offer: \"team\" },\n { service: \"mlx\", offer: \"private\" },\n ],\n },\n ],\n });\n\n const held = await store.presence(\"runner_withheld\");\n expect(held?.withheld.map((row) => row.kind)).toEqual([\"llm.generate\"]);\n // The claimants survive the round trip, because naming them is the\n // whole difference between a prompt and a complaint.\n expect(held?.withheld[0]?.claimants.map((c) => c.service)).toEqual([\n \"ollama\",\n \"mlx\",\n ]);\n\n // The owner chooses a default: the kind resolves and stops being\n // reported, or the page goes on asking for a decision already made.\n await store.seen({\n ...seen,\n capabilities: [capability(\"llama3\")],\n withheld: [],\n });\n expect((await store.presence(\"runner_withheld\"))?.withheld).toEqual([]);\n await done();\n });\n\n it(\"stamps presence from its own clock, and moves it on the next beat\", async () => {\n // `lastSeenAt` is liveness, and liveness is only true if something\n // advances it. It was written once at pairing and never again, so it\n // reported the pairing time under the name \"last seen\" — a field that\n // is quietly a different fact.\n const { store, done } = await make();\n const device = publicIdentityOf(generateKeys(3_000_000_000_003));\n const seen = { runnerId: \"runner_4\", owner: \"alice\", device };\n\n const before = await store.now();\n const first = await store.seen({\n ...seen,\n capabilities: [],\n withheld: [],\n });\n expect(first.lastSeenAt).toBeGreaterThanOrEqual(before);\n\n const later = await store.seen({\n ...seen,\n capabilities: [],\n withheld: [],\n });\n expect(later.lastSeenAt).toBeGreaterThanOrEqual(first.lastSeenAt);\n await done();\n });\n\n it(\"lists everyone it has seen\", async () => {\n const { store, done } = await make();\n await store.seen({\n runnerId: \"runner_5\",\n owner: \"alice\",\n device: publicIdentityOf(generateKeys(3_000_000_000_004)),\n capabilities: [],\n withheld: [],\n });\n await store.seen({\n runnerId: \"runner_6\",\n owner: \"bob\",\n device: publicIdentityOf(generateKeys(3_000_000_000_005)),\n capabilities: [],\n withheld: [],\n });\n\n const everyone = await store.everyone();\n const ids = everyone.map((row) => row.runnerId);\n expect(ids).toContain(\"runner_5\");\n expect(ids).toContain(\"runner_6\");\n await done();\n });\n\n it(\"does not know a runner it has never seen\", async () => {\n const { store, done } = await make();\n expect(await store.presence(\"runner_never\")).toBeUndefined();\n await done();\n });\n\n it(\"stamps deadlines from its own clock\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const before = await store.now();\n const [granted] = await store.claim(claimArgs({ leaseMs: 45_000 }));\n const after = await store.now();\n\n // Bracketed rather than exact: a real store's clock moves between the two\n // reads, which is the whole reason it is the store's clock and not ours.\n expect(granted!.lease.expiresAt).toBeGreaterThanOrEqual(before + 45_000);\n expect(granted!.lease.expiresAt).toBeLessThanOrEqual(after + 45_000);\n await done();\n });\n });\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/store-contract.ts"],"sourcesContent":["import { generateKeys, publicIdentityOf, type JobStub } from \"@byollm/protocol\";\nimport { describe, expect, it } from \"vitest\";\nimport { routeKey } from \"./state.js\";\nimport type { RoutingStore } from \"./store.js\";\n\n/**\n * The routing store's behaviour, written once and run against every\n * implementation — cloud_008 finding 54.\n *\n * There were two copies. This package tested `RelayState`; `byollm-cloud`\n * tested `ValkeyRoutingStore` with a file that opened by explaining it was\n * \"written once and parameterised… a second copy of the scenario is a second\n * place for two implementations to quietly diverge\" — and was itself the\n * second copy. It had drifted to sixteen cases against the eighteen in the\n * ledger, which is exactly the divergence the sentence predicted, happening\n * to the sentence.\n *\n * So the contract lives here, in the package that declares `RoutingStore`,\n * and both repositories import it. A store that cannot pass this is not a\n * routing store, whatever it implements.\n *\n * ## Why this ships in the published package\n *\n * Because the implementation that matters most is in another repository. A\n * contract only the author can run is a description; one a third party runs\n * against their own implementation is a contract. `@byollm/relay/store-contract`\n * is a subpath export so nothing that imports the relay itself pulls in a\n * test framework.\n *\n * ## What this cannot prove\n *\n * `CLAIM_ATOMIC` is a MUST and every case here would pass against a store\n * that read, decided and wrote in three steps — in one process there is\n * nothing to interleave. Concurrency belongs with the implementation that\n * has a network in it, and `byollm-cloud`'s suite runs it against Valkey with\n * many connections. Named here so the omission is a decision rather than an\n * oversight.\n */\n\n/** The control-plane site id these cases route under. */\nexport const CONTRACT_SITE = \"site_store\";\n\n/** A site's identity key id — byollm_009 Amendment A §A.3. */\nconst SITE_KEY_ID = \"BYOLLM-TEST-SITE-KEY-ID\";\n\nconst SITE = CONTRACT_SITE;\n\nconst stub = (id: string, owner = \"alice\"): JobStub => ({\n id,\n kind: \"llm.generate\",\n owner,\n site: SITE_KEY_ID,\n audience: \"private\",\n sizeClass: \"small\",\n streaming: false,\n deadlineAt: 4_102_444_800_000,\n});\n\n/** One advertised route, as a daemon describes it on every heartbeat. */\nconst capability = (model: string) => ({\n kind: \"llm.generate\" as const,\n service: model,\n backendId: \"ollama\" as const,\n backendClass: \"process\" as const,\n model,\n offerScope: \"private\" as const,\n});\n\nconst ENVELOPE = {\n ciphertext: \"AAAA\",\n recipientKeyId: \"r\",\n senderKeyId: \"s\",\n direction: \"payload\" as const,\n deadlineAt: 4_102_444_800_000,\n};\n\n/**\n * A claim, with its routes written out — cloud_009 §3.\n *\n * `routes` replaced `siteId` + `owners`, and the cases that pass `owners`\n * below now pass the pairs those owners are reachable through. That rewrite\n * is the contract's own statement of the rule: there is no way to express\n * \"this owner, any site\" here, because that is not a thing consent says.\n */\nconst claimArgs = (\n over: { owners?: string[]; sites?: string[] } & Record<string, unknown> = {},\n) => {\n const { owners = [\"alice\"], sites = [SITE], ...rest } = over;\n return {\n runnerId: \"runner_1\",\n owner: \"alice\",\n device: publicIdentityOf(generateKeys(Date.now())),\n kinds: new Set([\"llm.generate\"]),\n routes: new Set(\n sites.flatMap((site) => owners.map((owner) => routeKey(site, owner))),\n ),\n max: 10,\n leaseMs: 60_000,\n ...rest,\n };\n};\n\nexport interface StoreContractOptions {\n /**\n * A fresh, empty store, and how to dispose of it.\n *\n * Arrow-typed rather than a method, because the caller passes these\n * around: a method signature lets `this` travel with the call, and a\n * factory read off an options object is exactly where that goes wrong.\n */\n readonly make: () => Promise<{\n store: RoutingStore;\n done: () => Promise<void>;\n }>;\n /**\n * Whether this store writes stubs as bytes and reads them back.\n *\n * An in-process store holds typed objects and cannot hold a stub it cannot\n * parse; a serialising one can, because what it wrote may have been written\n * by a previous version. The case that covers it is skipped rather than\n * hidden — a reader should know which half of this contract each store is\n * proving (cloud_008 §2.1a).\n */\n readonly serialising?: boolean;\n /** Write a stub's raw bytes, bypassing serialisation. */\n readonly writeRawStub?: (\n store: RoutingStore,\n id: string,\n raw: string,\n ) => Promise<void>;\n}\n\n/** Run the contract. Call inside a suite; it declares its own `describe`. */\nexport function describeStoreContract(\n name: string,\n options: StoreContractOptions,\n): void {\n const { make, serialising, writeRawStub } = options;\n describe(`the routing store — ${name}`, () => {\n it(\"is idempotent by job id\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n await store.claim(claimArgs());\n // A republish must not disturb work in flight — byollm_009 §4.2's replay\n // argument rests on it.\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n expect((await store.job(SITE, \"a\"))?.state).toBe(\"awaiting-payload\");\n await done();\n });\n\n it(\"gives two sites the same id without either seeing the other\", async () => {\n // cloud_009 §3. Keyed by the bare id, the second site's enqueue returned\n // the *first* site's job (cloud_008 finding 58), and the refusal that\n // fixed it was a cross-tenant existence oracle: a site knows its own stub\n // is well-formed, so any answer it can tell apart from success confirms\n // somebody else holds that id. Keyed by (site, id), the collision does\n // not exist and there is nothing to answer.\n const { store, done } = await make();\n await store.enqueue({ id: \"shared\", siteId: SITE, stub: stub(\"shared\") });\n await store.enqueue({\n id: \"shared\",\n siteId: \"site_other\",\n stub: stub(\"shared\", \"bob\"),\n });\n\n // Two jobs, each its own site's.\n expect((await store.job(SITE, \"shared\"))?.stub.owner).toBe(\"alice\");\n expect((await store.job(\"site_other\", \"shared\"))?.stub.owner).toBe(\"bob\");\n\n // And a claim for one site does not see the other's, which is the\n // property the key exists for rather than a restatement of it.\n const granted = await store.claim(claimArgs());\n expect(granted.map((job) => job.id)).toEqual([\"shared\"]);\n expect(granted[0]?.owner).toBe(\"alice\");\n\n // The same site republishing is still absorbed — byollm_009 §4.2's\n // replay argument, which is what idempotence is for.\n const replay = await store.enqueue({\n id: \"shared\",\n siteId: SITE,\n stub: stub(\"shared\"),\n });\n expect(replay).toEqual(await store.job(SITE, \"shared\"));\n await done();\n });\n\n it(\"grants only what the owners set allows\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"mine\", siteId: SITE, stub: stub(\"mine\") });\n await store.enqueue({\n id: \"theirs\",\n siteId: SITE,\n stub: stub(\"theirs\", \"mallory\"),\n });\n const granted = await store.claim(claimArgs());\n expect(granted.map((job) => job.id)).toEqual([\"mine\"]);\n await done();\n });\n\n it(\"refuses a stale lease and honours the current one\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n await store.seal({ jobId: \"a\", siteId: SITE, envelope: ENVELOPE });\n\n expect(\n await store.takePayload({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: \"an-older-grant\",\n }),\n ).toEqual({ refused: \"stale-lease\" });\n\n expect(\n await store.takePayload({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n }),\n ).toEqual({ envelope: ENVELOPE });\n await done();\n });\n\n it(\"will not answer for one job with another job's lease\", async () => {\n // cloud_009 §3. Holder-scoped calls carry a lease id and no site, so the\n // store resolves the job *from the lease* — and a lease id belonging to a\n // different job must not be a way to reach that job's payload.\n //\n // A mutation that dropped the \"does this lease's job match the id the\n // caller named\" check survived every other case in this contract, on both\n // implementations, because nothing else here ever presents a mismatched\n // pair. The holder of a valid grant is exactly the party who could try\n // it.\n const { store, done } = await make();\n await store.enqueue({ id: \"mine\", siteId: SITE, stub: stub(\"mine\") });\n await store.enqueue({ id: \"other\", siteId: SITE, stub: stub(\"other\") });\n const granted = await store.claim(claimArgs());\n const mine = granted.find((job) => job.id === \"mine\");\n const other = granted.find((job) => job.id === \"other\");\n await store.seal({ jobId: \"other\", siteId: SITE, envelope: ENVELOPE });\n\n // The right runner, a real lease, the wrong job.\n expect(\n await store.takePayload({\n jobId: \"other\",\n runnerId: \"runner_1\",\n leaseId: mine!.lease.id,\n }),\n ).toEqual({ refused: \"stale-lease\" });\n\n // And the honest pairing still works, so this is not \"refuse everything\".\n expect(\n await store.takePayload({\n jobId: \"other\",\n runnerId: \"runner_1\",\n leaseId: other!.lease.id,\n }),\n ).toEqual({ envelope: ENVELOPE });\n await done();\n });\n\n it(\"renews a grant it still holds, and extends the sweep with it\", async () => {\n // cloud_008 §0.6, at the contract level: renewal has to be one operation,\n // because between a read and a write another replica can sweep the lease\n // — and the renewal would then resurrect a grant on a job already back in\n // the queue. In Valkey that is a script; in memory it is a method; the\n // guarantee is the same one, so it is tested here rather than twice.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs({ leaseMs: 1_000 }));\n\n const renewed = await store.renewLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: granted!.lease.id }],\n leaseMs: 60_000,\n });\n\n expect(renewed.lost).toEqual([]);\n expect(renewed.renewed.map((r) => r.jobId)).toEqual([\"a\"]);\n // The store's own clock did the arithmetic — the caller passed a duration,\n // never an instant, which is what keeps two replicas from measuring one\n // lease against two clocks (cloud_006 §3.4).\n expect(renewed.renewed[0]!.expiresAt).toBeGreaterThan(\n granted!.lease.expiresAt,\n );\n\n // And it is the *stored* grant that moved, not just the number returned.\n // Reporting a renewal without performing one is the mutation that\n // survived the first version of the relay's own test for this.\n expect((await store.job(SITE, \"a\"))?.claimedBy?.leaseExpiresAt).toBe(\n renewed.renewed[0]!.expiresAt,\n );\n await done();\n });\n\n it(\"reports a lease the runner no longer holds as lost, and renews nothing\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs({ leaseMs: 1_000 }));\n const before = (await store.job(SITE, \"a\"))?.claimedBy?.leaseExpiresAt;\n\n const renewed = await store.renewLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: \"a-grant-that-ended\" }],\n leaseMs: 60_000,\n });\n\n expect(renewed.renewed).toEqual([]);\n expect(renewed.lost).toEqual([\n { jobId: \"a\", leaseId: \"a-grant-that-ended\" },\n ]);\n // A refusal must not advance anything — the current holder's grant is\n // untouched by somebody else's stale renewal.\n expect((await store.job(SITE, \"a\"))?.claimedBy?.leaseExpiresAt).toBe(\n before,\n );\n expect((await store.job(SITE, \"a\"))?.claimedBy?.leaseId).toBe(\n granted!.lease.id,\n );\n await done();\n });\n\n it(\"does not hand out a stub it cannot parse\", async () => {\n // cloud_008 §2.1a, and the case is not hypothetical: two jobs enqueued\n // before alpha.15 had no `site`, came back in a claim, and the daemon's\n // `.strict()` parse rejected the whole response. Two dead rows denied\n // claims to every device on the hub, and the queue could not drain past\n // them.\n //\n // The store had `JSON.parse(stub) as JobStub` — trusting what it wrote,\n // when what it wrote was written by a previous version. A routing store\n // outlives the deployment that filled it, so a wire change to `JobStub`\n // is a data migration whether or not anyone plans one.\n //\n // Only meaningful against a store that serialises: `RelayState` holds\n // typed objects and cannot reach this state, so it passes by\n // construction — which is worth saying rather than hiding, because a\n // reader should know which half of this contract each store is proving.\n const { store, done } = await make();\n if (!serialising || writeRawStub === undefined) {\n await done();\n return;\n }\n\n await store.enqueue({ id: \"good\", siteId: SITE, stub: stub(\"good\") });\n await store.enqueue({ id: \"old\", siteId: SITE, stub: stub(\"old\") });\n // Exactly what alpha.14 wrote: a stub with no `site`.\n const legacy = { ...stub(\"old\") } as Record<string, unknown>;\n delete legacy[\"site\"];\n await writeRawStub(store, \"old\", JSON.stringify(legacy));\n\n const granted = await store.claim(claimArgs({ max: 10 }));\n\n // The good one is still handed out — \"return nothing\" would pass a test\n // that only checked the bad one was absent, and would stop all routing.\n expect(granted.map((job) => job.id)).toEqual([\"good\"]);\n // And the bad one is back in the queue rather than stuck holding a lease\n // nobody received.\n expect((await store.job(SITE, \"old\"))?.state).toBe(\"queued\");\n await done();\n });\n\n it(\"honours a self job's audience\", async () => {\n // cloud_008 §2.1 at the contract level. The owners set is everyone this\n // device may run for, which for a Team owner's machine is the whole\n // roster — right for public and named, wrong for the one audience a user\n // picks because they want their own machine.\n const { store, done } = await make();\n await store.enqueue({\n id: \"private\",\n siteId: SITE,\n stub: { ...stub(\"private\", \"alice\"), audience: \"private\" },\n });\n\n // A roster owner's machine: may run alice's work in general.\n const granted = await store.claim(\n claimArgs({ owner: \"owner\", owners: [\"owner\", \"alice\"] }),\n );\n\n expect(granted).toEqual([]);\n expect((await store.job(SITE, \"private\"))?.state).toBe(\"queued\");\n await done();\n });\n\n it(\"still offers that job to its owner's own machine\", async () => {\n const { store, done } = await make();\n await store.enqueue({\n id: \"private\",\n siteId: SITE,\n stub: { ...stub(\"private\", \"alice\"), audience: \"private\" },\n });\n const granted = await store.claim(\n claimArgs({ owner: \"alice\", owners: [\"alice\"] }),\n );\n expect(granted.map((job) => job.id)).toEqual([\"private\"]);\n await done();\n });\n\n it(\"does not offer a job back to the runner that refused it\", async () => {\n // `REFUSAL_NOT_REOFFERED`. `releaseLeases` gained an **optional** reason,\n // which means a store can ignore it and still typecheck — this class has\n // now diverged from the interface that way twice, so the contract is the\n // only thing that catches it.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n\n await store.releaseLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: granted!.lease.id }],\n reason: \"refused\",\n });\n\n expect(await store.claim(claimArgs())).toEqual([]);\n expect((await store.job(SITE, \"a\"))?.state).toBe(\"queued\");\n // And somebody else may still run it.\n expect(\n (await store.claim(claimArgs({ runnerId: \"runner_2\" }))).map(\n (j) => j.id,\n ),\n ).toEqual([\"a\"]);\n await done();\n });\n\n it(\"does not offer a job back to a runner before its not-before\", async () => {\n /**\n * The middle ground between \"not ever\" and \"immediately\".\n *\n * A control plane declining a job for something the world can change —\n * an unfilled mapping slot, a resolution naming another machine, a\n * store that was briefly unreachable — means neither. Released\n * immediately, the device re-claims at once and is declined again; a\n * spin, and in a deployment one database read per turn of it.\n *\n * Asserted with an explicit moment rather than by moving a clock,\n * because a store on the other side of a network has its own and this\n * contract cannot reach it.\n */\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n\n await store.releaseLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: granted!.lease.id }],\n retryAfter: Date.now() + 600_000,\n });\n\n expect(await store.claim(claimArgs())).toEqual([]);\n expect((await store.job(SITE, \"a\"))?.state).toBe(\"queued\");\n // And it is *not* a refusal: another device gets it at once, which is\n // the entire reason this is a different thing.\n expect(\n (await store.claim(claimArgs({ runnerId: \"runner_2\" }))).map(\n (j) => j.id,\n ),\n ).toEqual([\"a\"]);\n await done();\n });\n\n it(\"offers it again once the not-before has passed\", async () => {\n // The other half, and the one that makes it a rate rather than a\n // refusal: a person who fixes their mapping gets their queued work.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n\n await store.releaseLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: granted!.lease.id }],\n retryAfter: Date.now() - 1_000,\n });\n\n expect((await store.claim(claimArgs())).map((j) => j.id)).toEqual([\"a\"]);\n await done();\n });\n\n it(\"will not un-finish a job that is already done\", async () => {\n /**\n * The third holder-scoped door — byollm-review 2026-08-27.\n *\n * A release naming a valid lease on a **done** job flipped it back to\n * `queued`. The recorded result survives on the row and becomes\n * unreachable, because `finished()` filters on state — so the site\n * never collects it, the job is offered again, and somebody's hardware\n * runs it a second time and overwrites the first answer.\n *\n * Not hypothetical: the daemon's own shutdown races it. A release and a\n * result for one lease are in flight together, and if the result won,\n * this undid it.\n *\n * `takePayload` got a terminal guard at V1-6 and `complete` at\n * cloud_008 §3.6. This one was left open, and no contract case existed\n * for it — so neither implementation could fail for it. Three doors,\n * one rule, shut one at a time; this is the rule stated where every\n * store has to answer for it.\n */\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n const lease = granted!.lease.id;\n\n await store.takePayload({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: lease,\n });\n await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: lease,\n envelope: ENVELOPE,\n disposition: \"ok\",\n });\n\n const released = await store.releaseLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: lease }],\n reason: \"shutdown\",\n });\n\n // Released nothing, and said so — a caller that believed this had\n // requeued would be as wrong as the store that did.\n expect(released).toEqual([]);\n expect((await store.job(SITE, \"a\"))?.state).toBe(\"done\");\n\n // And the result is still collectable, which is the harm the state\n // change actually caused: `finished()` filters on state, so a job\n // flipped back to `queued` hid an answer that had already been paid\n // for.\n expect((await store.finished(SITE)).map((j) => j.id)).toEqual([\"a\"]);\n\n // Nor is it offered to anybody again. The second execution is the\n // expensive half — somebody's tokens, spent twice, for one job.\n expect(await store.claim(claimArgs({ runnerId: \"runner_2\" }))).toEqual(\n [],\n );\n await done();\n });\n\n it(\"keeps a job claimable by a runner that only went away\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n\n await store.releaseLeases({\n runnerId: \"runner_1\",\n leases: [{ jobId: \"a\", leaseId: granted!.lease.id }],\n reason: \"shutdown\",\n });\n\n expect((await store.claim(claimArgs())).map((j) => j.id)).toEqual([\"a\"]);\n await done();\n });\n\n it(\"stops offering a job past its deadline, and drops the ciphertext\", async () => {\n // cloud_008 §2.2. `deadlineAt` travelled on every stub and nothing read\n // it, so expired jobs were offered forever and their sealed payloads kept\n // for as long as the store lived.\n const { store, done } = await make();\n await store.enqueue({\n id: \"late\",\n siteId: SITE,\n stub: { ...stub(\"late\"), deadlineAt: Date.now() - 1_000 },\n });\n\n expect(await store.claim(claimArgs())).toEqual([]);\n expect(await store.job(SITE, \"late\")).toBeUndefined();\n await done();\n });\n\n it(\"names a cancelled job to the device holding it, and offers it to nobody\", async () => {\n // cloud_008 §2.2.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n await store.claim(claimArgs());\n\n expect(await store.cancel({ jobId: \"a\", siteId: SITE })).toBe(true);\n // Named by grant — V1-3, so a daemon holding two sites' `a` knows\n // which one the site withdrew.\n const held = (await store.job(SITE, \"a\"))?.claimedBy?.leaseId;\n expect(await store.cancelRequests(\"runner_1\")).toEqual([\n { jobId: \"a\", leaseId: held },\n ]);\n\n // Scoped to the caller's site: a site must not reach another's work by\n // guessing an id.\n expect(await store.cancel({ jobId: \"a\", siteId: \"someone_else\" })).toBe(\n false,\n );\n await done();\n });\n\n it(\"records a result once\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n\n const first = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n envelope: ENVELOPE,\n disposition: \"ok\",\n });\n const replay = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n envelope: { ...ENVELOPE, ciphertext: \"different\" },\n disposition: \"error\",\n });\n\n expect(first).toEqual({ accepted: true, state: \"done\" });\n // `duplicate` — cloud_008 §3.6. Case 18 above is the one that says why.\n expect(replay).toEqual({\n accepted: false,\n duplicate: true,\n state: \"done\",\n });\n // The property, not the boolean: the second result did not overwrite.\n expect((await store.job(SITE, \"a\"))?.disposition).toBe(\"ok\");\n await done();\n });\n\n it(\"answers the device that finished it with `duplicate`\", async () => {\n // Contract case 18 — cloud_008 §3.6, and the eighteenth exists because\n // the compiler cannot see any of this: the return type's `duplicate` is\n // optional, so a store that never sets it typechecks perfectly while\n // enforcing `RESULT_IDEMPOTENT` by accident or not at all.\n //\n // A daemon whose acknowledgment was lost hears that its answer is already\n // recorded, rather than that its lease is stale — the second message\n // invents a worry about a result safely on disk.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n\n const first = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n envelope: ENVELOPE,\n disposition: \"ok\",\n });\n expect(first).toEqual({ accepted: true, state: \"done\" });\n\n const replay = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n envelope: { ...ENVELOPE, ciphertext: \"a-different-result\" },\n disposition: \"error\",\n });\n expect(replay).toEqual({\n accepted: false,\n duplicate: true,\n state: \"done\",\n });\n\n // A *different* device, on a job that is terminal, gets the refusal it\n // would get for one that is not — otherwise the two answers differ and a\n // job id becomes a terminality probe.\n const stranger = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_2\",\n leaseId: granted!.lease.id,\n envelope: ENVELOPE,\n disposition: \"ok\",\n });\n expect(stranger).toEqual({ refused: \"not-holder\" });\n\n // And the first answer is what survived, which is the property rather\n // than any of the booleans above.\n expect((await store.job(SITE, \"a\"))?.disposition).toBe(\"ok\");\n await done();\n });\n\n it(\"refuses a result produced under a grant that ended\", async () => {\n // cloud_008 §1.4a at the contract level, and it belongs here more than\n // anywhere: `RoutingStore.complete` gained `leaseId` and this class kept\n // typechecking without it, because a parameter object with fewer\n // properties is assignable to one with more. The relay would have\n // enforced LEASE_HONORED on the memory store and not on the store that\n // runs in production, with nothing red on the way.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n await store.claim(claimArgs());\n\n const late = await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: \"a-grant-that-ended\",\n envelope: ENVELOPE,\n disposition: \"ok\",\n });\n\n expect(late).toEqual({ refused: \"stale-lease\" });\n // Untouched: the current holder can still finish it.\n expect((await store.job(SITE, \"a\"))?.state).toBe(\"awaiting-payload\");\n expect((await store.job(SITE, \"a\"))?.result).toBeUndefined();\n await done();\n });\n\n it(\"refuses a payload for a job that already finished [V1-6]\", async () => {\n // The holder and lease checks both pass for a job this runner finished\n // moments ago — completing does not end the grant. So a replayed fetch\n // set `state = 'running'` on a **done** job: the site stopped being able\n // to collect its result, and the sweep requeued completed work as though\n // the device had died holding it.\n //\n // Which is `RESULT_IDEMPOTENT` broken through the other door: a\n // duplicate request undoing a finished job.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const [granted] = await store.claim(claimArgs());\n await store.seal({ jobId: \"a\", siteId: SITE, envelope: ENVELOPE });\n await store.takePayload({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n });\n await store.complete({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n envelope: ENVELOPE,\n disposition: \"ok\",\n });\n\n const replay = await store.takePayload({\n jobId: \"a\",\n runnerId: \"runner_1\",\n leaseId: granted!.lease.id,\n });\n // Its own refusal, not `not-ready`: one says keep asking and this says\n // stop, and a daemon that read them alike would poll a finished job\n // until its lease ran out.\n expect(replay).toEqual({ refused: \"terminal\" });\n // And the finished job is still finished, which is the property.\n expect((await store.job(SITE, \"a\"))?.state).toBe(\"done\");\n await done();\n });\n\n it(\"tells a runner nothing about another site's job id [V1-8]\", async () => {\n // Job ids are chosen per site, so a bare id is a guess about somebody\n // else's namespace. The answer used to depend on whether that guess\n // landed: an id belonging to another tenant came back `not-holder`,\n // an id belonging to nobody came back `not-found`. Two status codes,\n // one bit of somebody else's business — finding 58's oracle, reached\n // through the holder door instead of the site plane.\n const { store, done } = await make();\n // Another tenant's job, under an id this caller can guess but a site it\n // does not route for.\n await store.enqueue({\n id: \"secret\",\n siteId: \"site_somebody_else\",\n stub: stub(\"secret\"),\n });\n await store.enqueue({ id: \"mine\", siteId: SITE, stub: stub(\"mine\") });\n const granted = await store.claim(claimArgs());\n const mine = granted.find((job) => job.id === \"mine\");\n\n // Asking about a job this runner does not hold, under a lease it does.\n const other = await store.takePayload({\n jobId: \"secret\",\n runnerId: \"runner_1\",\n leaseId: mine!.lease.id,\n });\n const absent = await store.takePayload({\n jobId: \"no-such-job-anywhere\",\n runnerId: \"runner_1\",\n leaseId: mine!.lease.id,\n });\n\n expect(other).toEqual(absent);\n expect(other).toEqual({ refused: \"not-found\" });\n await done();\n });\n\n // ── routing by kind ─────────────────────────────────────────────────\n //\n // The selection cases lived here: a job could name one of the owner's\n // services and a store had to offer it only to a device advertising that\n // exact (kind, service) pair. Amendment L removed the field — sites\n // declare purposes and people map them — so a store now matches on kind\n // alone, and the pairs it used to carry are gone from `ClaimInput`.\n //\n // What replaces those cases is the property that a store must *not* have\n // opinions it no longer has the inputs for.\n\n it(\"offers a job to any device that serves its kind\", async () => {\n // Which of the owner's services answers is resolved at claim by a\n // control plane, from a mapping this store never sees. A store that\n // narrowed further would be deciding something it cannot know, and the\n // job would sit queued behind a judgement nobody made.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n\n const claimed = await store.claim(\n claimArgs({ runnerId: \"runner_any\", kinds: new Set([\"llm.generate\"]) }),\n );\n expect(claimed.map((j) => j.id)).toEqual([\"a\"]);\n await done();\n });\n\n it(\"does not offer a job to a device that serves a different kind\", async () => {\n // The control. Kind is the whole of the match, so it has to actually\n // match — a store that offered everything to everybody would push the\n // narrowing onto a control plane that would then decline it, which is\n // a round trip and a wait for a fact this side already had.\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n\n const claimed = await store.claim(\n claimArgs({ runnerId: \"runner_chat\", kinds: new Set([\"llm.chat\"]) }),\n );\n expect(claimed).toEqual([]);\n await done();\n });\n\n // ── presence ────────────────────────────────────────────────────────\n //\n // The seam had no contract coverage at all: presence was exercised\n // against `RelayState` and nothing held a shared store to the same\n // behaviour. That is the shape the pairing ceiling was found in — one\n // seam, two implementations, and the check driving the one nobody\n // deploys — so these land with `Presence.capabilities` rather than after\n // somebody notices.\n\n it(\"remembers a device, and what it says it can run\", async () => {\n const { store, done } = await make();\n const device = publicIdentityOf(generateKeys(3_000_000_000_000));\n await store.seen({\n runnerId: \"runner_1\",\n owner: \"alice\",\n device,\n capabilities: [capability(\"llama3\")],\n withheld: [],\n });\n\n const known = await store.presence(\"runner_1\");\n expect(known?.owner).toBe(\"alice\");\n expect(known?.device.identity).toBe(device.identity);\n expect(known?.capabilities.map((row) => row.model)).toEqual([\"llama3\"]);\n await done();\n });\n\n it(\"replaces the matrix rather than merging it\", async () => {\n // The heartbeat re-sends the whole matrix every time so a server never\n // matches against a stale one. A store that kept the union would go on\n // advertising a backend the machine has lost — and work would route to\n // it, which is worse than forgetting one it still has.\n const { store, done } = await make();\n const device = publicIdentityOf(generateKeys(3_000_000_000_001));\n const seen = { runnerId: \"runner_2\", owner: \"alice\", device };\n await store.seen({\n ...seen,\n capabilities: [capability(\"llama3\")],\n withheld: [],\n });\n await store.seen({\n ...seen,\n capabilities: [capability(\"mistral\")],\n withheld: [],\n });\n\n const known = await store.presence(\"runner_2\");\n expect(known?.capabilities.map((row) => row.model)).toEqual([\"mistral\"]);\n await done();\n });\n\n it(\"keeps an empty matrix, because empty is an answer\", async () => {\n // A paired machine with no healthy backend — the connect-first ruling's\n // whole point. A store that treated empty as \"nothing to record\" would\n // leave the last non-empty matrix standing and the machines page would\n // show models that are gone.\n const { store, done } = await make();\n const device = publicIdentityOf(generateKeys(3_000_000_000_002));\n const seen = { runnerId: \"runner_3\", owner: \"alice\", device };\n await store.seen({\n ...seen,\n capabilities: [capability(\"llama3\")],\n withheld: [],\n });\n await store.seen({ ...seen, capabilities: [], withheld: [] });\n\n expect((await store.presence(\"runner_3\"))?.capabilities).toEqual([]);\n await done();\n });\n\n it(\"remembers which kinds are withheld, and drops them when resolved\", async () => {\n // byollm_016. Two services answering one kind with no `defaults` entry\n // is a state only the daemon can see: from the matrix alone a withheld\n // kind and an absent one are the same shape, so a store that dropped\n // this field would leave the owner's page saying \"nothing serves\n // llm.generate\" — true, and useless, when the real sentence is \"two\n // services answer it and you have not chosen\".\n //\n // It lives in the contract rather than one store's tests because the\n // pairing ceiling was found exactly that way: one seam, two\n // implementations, and the check driving the one nobody deploys.\n const { store, done } = await make();\n const device = publicIdentityOf(generateKeys(3_000_000_000_004));\n const seen = { runnerId: \"runner_withheld\", owner: \"alice\", device };\n\n await store.seen({\n ...seen,\n capabilities: [],\n withheld: [\n {\n kind: \"llm.generate\",\n claimants: [\n { service: \"ollama\", offer: \"team\" },\n { service: \"mlx\", offer: \"private\" },\n ],\n },\n ],\n });\n\n const held = await store.presence(\"runner_withheld\");\n expect(held?.withheld.map((row) => row.kind)).toEqual([\"llm.generate\"]);\n // The claimants survive the round trip, because naming them is the\n // whole difference between a prompt and a complaint.\n expect(held?.withheld[0]?.claimants.map((c) => c.service)).toEqual([\n \"ollama\",\n \"mlx\",\n ]);\n\n // The owner chooses a default: the kind resolves and stops being\n // reported, or the page goes on asking for a decision already made.\n await store.seen({\n ...seen,\n capabilities: [capability(\"llama3\")],\n withheld: [],\n });\n expect((await store.presence(\"runner_withheld\"))?.withheld).toEqual([]);\n await done();\n });\n\n it(\"stamps presence from its own clock, and moves it on the next beat\", async () => {\n // `lastSeenAt` is liveness, and liveness is only true if something\n // advances it. It was written once at pairing and never again, so it\n // reported the pairing time under the name \"last seen\" — a field that\n // is quietly a different fact.\n const { store, done } = await make();\n const device = publicIdentityOf(generateKeys(3_000_000_000_003));\n const seen = { runnerId: \"runner_4\", owner: \"alice\", device };\n\n const before = await store.now();\n const first = await store.seen({\n ...seen,\n capabilities: [],\n withheld: [],\n });\n expect(first.lastSeenAt).toBeGreaterThanOrEqual(before);\n\n const later = await store.seen({\n ...seen,\n capabilities: [],\n withheld: [],\n });\n expect(later.lastSeenAt).toBeGreaterThanOrEqual(first.lastSeenAt);\n await done();\n });\n\n it(\"lists everyone it has seen\", async () => {\n const { store, done } = await make();\n await store.seen({\n runnerId: \"runner_5\",\n owner: \"alice\",\n device: publicIdentityOf(generateKeys(3_000_000_000_004)),\n capabilities: [],\n withheld: [],\n });\n await store.seen({\n runnerId: \"runner_6\",\n owner: \"bob\",\n device: publicIdentityOf(generateKeys(3_000_000_000_005)),\n capabilities: [],\n withheld: [],\n });\n\n const everyone = await store.everyone();\n const ids = everyone.map((row) => row.runnerId);\n expect(ids).toContain(\"runner_5\");\n expect(ids).toContain(\"runner_6\");\n await done();\n });\n\n it(\"does not know a runner it has never seen\", async () => {\n const { store, done } = await make();\n expect(await store.presence(\"runner_never\")).toBeUndefined();\n await done();\n });\n\n it(\"stamps deadlines from its own clock\", async () => {\n const { store, done } = await make();\n await store.enqueue({ id: \"a\", siteId: SITE, stub: stub(\"a\") });\n const before = await store.now();\n const [granted] = await store.claim(claimArgs({ leaseMs: 45_000 }));\n const after = await store.now();\n\n // Bracketed rather than exact: a real store's clock moves between the two\n // reads, which is the whole reason it is the store's clock and not ours.\n expect(granted!.lease.expiresAt).toBeGreaterThanOrEqual(before + 45_000);\n expect(granted!.lease.expiresAt).toBeLessThanOrEqual(after + 45_000);\n await done();\n });\n });\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@byollm/relay",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.60",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The reference relay: routes sealed byollm jobs between sites and daemons, holding no decryption keys by construction.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"zod": "^4.1.13",
|
|
34
|
-
"@byollm/protocol": "0.1.0-alpha.
|
|
34
|
+
"@byollm/protocol": "0.1.0-alpha.60"
|
|
35
35
|
},
|
|
36
36
|
"publishConfig": {
|
|
37
37
|
"access": "public"
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@supabase/supabase-js": "^2.112.2",
|
|
41
41
|
"vitest": "^4.1.10",
|
|
42
|
-
"@byollm/
|
|
43
|
-
"@byollm/
|
|
44
|
-
"@byollm/
|
|
45
|
-
"byollm": "0.1.0-alpha.
|
|
42
|
+
"@byollm/control-plane": "0.1.0-alpha.60",
|
|
43
|
+
"@byollm/conformance": "0.1.0-alpha.60",
|
|
44
|
+
"@byollm/server": "0.1.0-alpha.60",
|
|
45
|
+
"byollm": "0.1.0-alpha.60"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"vitest": ">=3"
|