import { Keys, WorkerRecord } from "./keys.js"; import { Transport } from "./proxy.js"; import { ProbeFailure } from "./challenge.js"; import { createPinboard } from "./server.js"; //#region src/registry.d.ts type RedisKV = createPinboard.RedisKV; type RegistryResult = ({ ok: true; } & T) | { ok: false; error: string; nack?: "reregister"; conflict?: true; /** The advertise URL was unreachable or answered 502/503/504: retry, not a rejection. */ unavailable?: true; forbidden?: true; }; type RegistryAuthorization = { account?: string; namespaceAllowed(namespace: string): boolean; worldAllowed(hostname: string, env: string): boolean; }; type HeartbeatResult = { ok: true; expiresInMs: number; draining: boolean; /** Remaining seconds of a timed admin drain; absent otherwise. */ drainDeadline?: number; /** Set when this heartbeat's reverse probe failed and marked the record. */ degraded?: ProbeFailure; messages: Record[]; } | { ok: false; error: string; nack?: "reregister" | "superseded"; forbidden?: true; /** Set when a challenge value mismatch voided the registration. */ voided?: { advertiseUrl: string; }; }; interface NamespaceEntry { id: string; sessionId: string; /** Epoch ms wind-down deadline on the placement, when set. */ expiry?: number; } interface NamespaceOverview { hostname: string; env: string; namespace: string; sessionIds: string[]; entries: NamespaceEntry[]; } interface Overview { workers: Omit[]; bindings: { /** (hostname, ns) -> env, keyed `${hostname}${ns}`. */ hostnames: Record; envs: Record; }; namespaces: NamespaceOverview[]; } interface WorldNamespace { hostname: string; env: string; ns: string; } declare class Registry { readonly leaseTtlMs: number; readonly hostnameAware: boolean; private readonly verifyTimeoutMs; private readonly redis; private readonly keys; private readonly now; private readonly fetchImpl; constructor(redis: RedisKV, keys?: Keys, leaseTtlMs?: number, now?: () => number, hostnameAware?: boolean, verifyTimeoutMs?: number, fetchImpl?: Transport.FetchLike); private buckets; /** draining, expiry, and the admin deadline per the drain config: a timed * drain past its deadline stops the expiry from advancing beyond * deadline + lease. `drainDeadline` is the remaining relative seconds of a * timed admin drain (0 once passed), null for cordons and self-drains. */ private drainState; /** * Mints the sessionId (the worker's whole lease/auth/placement identity), * after proving the advertiseUrl terminates at the registering process via * its challenge. A lost session registers fresh; its old placements * re-place through dead-owner eviction — except ids submitted in * `instances` (ns -> ids still held in the worker's memory), which reclaim * on process continuity only: * - rows still owned by `prevSessionId` re-bind to the new session, * keeping their epoch; a pending wind-down carries and re-notifies * - every other id is untouched, returned in `duplicates` for the worker * to evict locally; its row re-places through resolve on demand */ register(body: unknown, account?: string): Promise; reclaimed: number; duplicates: { ns: string; id: string; }[]; }>>; /** Live worker record (secret included) for a session, or null. */ worker(sessionId: string): Promise; private liveRecord; private authorized; private scopedRecord; private forbidden; /** Authenticates by sessionId, re-proves the advertiseUrl via the carried * challenge (an unreachable, non-2xx, or body-less probe still renews but * marks the record degraded: existing placements keep routing, new * placements skip it; a probe answering the wrong value voids the * registration — records dropped, 409 reregister), recomputes the drain * state, and writes the refreshed record. The ack echoes draining, plus the remaining seconds of * a timed admin drain as `drainDeadline`. */ heartbeat(body: unknown, authorization?: RegistryAuthorization): Promise; /** Message naming the displacing binding when a (hostname, ns) of this * record is now bound to a different env, else null. */ private supersededBy; /** Deregister-shaped record removal: ns-list entries and worker records * across all buckets, freeing the session's placements to dead-owner * eviction. */ private dropRecords; /** Compare-and-delete, batched: only the placing session frees its own * placements, and only at the quoted epoch. */ release(body: unknown, authorization?: RegistryAuthorization): Promise>; /** Graceful shutdown: removes the session from ns_workers and drops the * worker record; the worker batch-releases its placements beforehand. */ deregister(body: unknown, authorization?: RegistryAuthorization): Promise>; /** Live worker records including secrets; never serialized to clients. */ liveWorkers(authorization?: RegistryAuthorization): Promise; listWorkers(authorization?: RegistryAuthorization): Promise[]>; /** The (hostname, ns) -> env (keyed `${hostname}${ns}`) and * env -> takeover binding tables. */ bindings(): Promise; /** Every (hostname, env, namespace) with at least one registered worker. */ worlds(): Promise; /** Live workers, the binding tables, and per (hostname, namespace) its * sessions and placed entries (via HSCAN of the placements hashes). */ overview(authorization?: RegistryAuthorization): Promise; /** * Stamps the wind-down expiry on the placement row (never landing before * the current lease can expire; null revokes) and tells the owner via its * ctl queue so it can comply early. Unplaced id = no-op. Omitting id winds * down every placement in the (env, namespace); queued is then the count. */ windDown(body: unknown, authorization?: RegistryAuthorization): Promise>; private scopedPlacements; /** Queues a wind_down ctl message; deadlineMs absolute epoch ms, null revokes. */ queueWindDown(sessionId: string, ns: string, id: string, deadlineMs: number | null): Promise; /** Trims ctl messages the worker confirmed applying; unknown ids no-op. */ private ackControl; /** Peeks ctl messages without deleting — a lost response re-delivers on the * next heartbeat until the worker acks the msgIds — converting stored * absolute deadlines to the wire's relative seconds at peek time. */ peekControl(sessionId: string): Promise[]>; /** * Admin drain: writes the client config (the only home of the drain * deadline), so it works while the worker is down and survives * re-register. deadline is relative seconds, floored to the lease window; * null = cordon. Register and heartbeat acks carry the draining flag and, * for timed drains, the remaining seconds as `drainDeadline`. */ drain(body: unknown, authorization?: RegistryAuthorization): Promise>; undrain(body: unknown, authorization?: RegistryAuthorization): Promise>; private clientAllowed; } //#endregion export { HeartbeatResult, NamespaceEntry, NamespaceOverview, Overview, Registry, RegistryResult, WorldNamespace }; //# sourceMappingURL=registry.d.ts.map