/** * The §13.1 endpoint-serve CREDENTIAL LIFECYCLE over a plain KV — the shared core home for the * endpoint credential family (`epgate..` + `epcred... * `) so BOTH the auth session ledger and the manager's endpoint-serve wiring drive * ONE implementation (fact H3 / P2 item 1 "1a-gate"; the manager cannot import * implementations/auth, AGENTS.md one-way deps, so the KV binding lives in core — the same * guarded-core lift as the Unit B lifecycle-saga). * * This module is the raw-KV credential-ledger primitives + the endpoint-serve mint fence + the * production issuance barrier. It carries NO auth-store branding: a caller supplies the bound KV + * space (the auth session ledger unwraps its `SessionAuthStore`; the manager binds its own auth * bucket). The KEY GRAMMAR + row parsers stay in `lifecycle-state.ts`; this module is the CAS * operations over them. */ import type { KV } from "@nats-io/kv"; import { type EndpointRepairCursor } from "./lifecycle-state.js"; import type { EpIssuanceGate, EpServeLedgerRow } from "./endpoint-service.js"; /** Create a credential-ledger row CREATE-ONLY, idempotent iff BYTE-IDENTICAL: staging a key that * already exists succeeds only when the stored bytes match (a retry of the SAME issuance), and * CONFLICTs when they differ (a staged name never silently re-binds the row revocation/audit * relies on). A create loss whose cause is not a CAS conflict fails the mint CLOSED. */ export declare function createRowByteIdempotent(kv: KV, key: string, value: unknown): Promise; /** CAS a credential-ledger row `active` -> `revoked` at its observed revision (retrying on a CAS * loss). Idempotent on an already-revoked row; FAILS LOUD on an absent/DEL row — a vanished * never-delete ledger row is corruption, never a "never staged" idempotence case. */ export declare function markLedgerRowRevoked(kv: KV, key: string): Promise<"revoked" | "already-revoked">; /** Read ONE instance's issuance-gate generation over a bound auth KV. A READ, never a freeze. * * This is the observation seam {@link registerServiceInstance}'s `observeHolderGeneration` and * {@link deregisterServiceInstance}'s `observeGeneration` both want, shipped once so a caller does * not hand-roll a parse of an authority row. It is a plain reader and grants nothing: the caller's * own credential decides whether the row is readable, and an unreadable one THROWS, which both * seams classify as a refusal. * * Its argument is an instanceId rather than a bound pair because the caller reading a FOREIGN * holder's gate knows that id only from the governance slot it just read. Absence throws for the * same reason a DEL marker does: a registration behind a gate that does not exist is not a state * this may reason about, so it fails closed rather than answering a number. */ export declare function readEndpointGateGeneration(kv: KV, args: { endpoint: string; instanceId: string; }): Promise; /** The §13.1 endpoint-serve MINT FENCE over a bound KV — the `EpIssuanceGate` core's serve mint * (`mintCreds`, profile `endpoint-serve`) fences its release on: it stages the per-JWT `epcred. * ..` row, then a revision-pinned identical-bytes TOUCH of the * `epgate..` key (a barrier that moved the gate since observation makes the * mint LOSE). Lifted from the auth session ledger (fact H3) so both the auth session redemption * and the manager's endpoint-serve wiring drive ONE fence; the auth `kvServeIssuanceGate` wraps * this by unwrapping its branded `SessionAuthStore` to `(kv, space)`. `space` is carried on the * observed gate for the core mint's space-bond defense (the KV IS the space bucket). */ export declare function serveIssuanceGateKv(kv: KV, space: string, args: { endpoint: string; instanceId: string; }): EpIssuanceGate; /** Stage a SIBLING credential into an instance's §13.1 family under the SAME open-and-commit fence * {@link import("./endpoint-service.js").finalizeServeIssuance} runs for the serve credential * itself. A "sibling" is any credential minted beside the serve cred against the same * `epgate..` and staged into the same `epcred..` * family (the manager's goal-writer and its §13.6 session credentials): the family IS the * takeover/retirement barrier's revocation unit, so a sibling that joins it without the fence is a * credential the barrier can never revoke. * * THE PASSED-IN OBSERVATION IS THE AUTHORITY AND IS NEVER REPLACED BY A RE-READ. A sibling's grant * is built FROM the observed coordinates (the serving epoch above all), so what this commit must * fence is the gate the credential was MINTED against. Fencing whatever the gate says NOW would be * the hole: the dangerous case is not a frozen gate but a COMPLETED takeover — freeze → * revoke/evict → reopen leaves the gate `open` again at a NEW generation, so a mint that re-pinned * to the successor would win its CAS and release a JWT minted against the PREDECESSOR's coordinates * into the family the barrier has just finished reconciling. This function re-reads the gate ONLY * to CLASSIFY a lost CAS, and every field of that re-read is compared against the ORIGINAL * observation: a successor coordinate can therefore never be adopted, it can only be refused. * * Order, identical to the serve mint: require `open` -> stage the row -> revision-pinned commit -> * release ONLY on the win. A frozen gate refuses before anything is written; a gate that MOVED * between the caller's observe and this commit loses the CAS, and the staged row is revoked so no * active row is left in a family the barrier already enumerated. A revoke failure is surfaced, not * swallowed — the reconciliation debt must be visible. The caller releases the credential only if * this resolves. */ export declare function commitSiblingIssuance(gate: import("./endpoint-service.js").EpIssuanceGate, observed: import("./endpoint-service.js").EpGateState, row: EpServeLedgerRow): Promise; export declare function loadEndpointRepairCursor(kv: KV, endpoint: string, instanceId: string): Promise<{ cursor: EndpointRepairCursor; revision: number; } | null>; /** Create-or-CAS the repair cursor. `expectedRevision` null means create-only (or identical retry). * A CAS loss is loud: the caller leaves the gate frozen rather than skipping on uncommitted progress. */ export declare function saveEndpointRepairCursor(kv: KV, endpoint: string, instanceId: string, cursor: EndpointRepairCursor, expectedRevision: number | null): Promise; /** Revision-pinned cleanup after the gate has reopened. A cleanup failure is safe to retain: every * future repair rebinds the cursor to its own gate revision before it may skip a holder. */ export declare function deleteEndpointRepairCursor(kv: KV, endpoint: string, instanceId: string, expectedRevision: number): Promise; /** True only when the stored cursor is the SAME freeze op, freeze token, and holder set. */ export declare function repairCursorMatches(cursor: EndpointRepairCursor, binding: { opId: string; freezeToken: number; holders: readonly string[]; }): boolean; /** Provision the endpoint's issuance gate OPEN (create-only), the §13.1 pre-registration a * `registerServiceInstance` writes behind — "a registration writes only behind the * provisioner-created gate". Born `open` at generation 0 / epoch 0 / registrationRevision 0 / * nameAuthorityRevision 0, bound to the serving connection principal (the eviction target every * `epcred` row copies). Create-only + idempotent-if-identical: a second provision of the SAME * (endpoint, instanceId, principal) is a retry, a DIFFERENT principal is a conflict (an instance * token is never re-bound). */ export declare function provisionEndpointGateOpen(kv: KV, args: { endpoint: string; instanceId: string; principal: string; }): Promise; /** The §13.1 endpoint-registration ISSUANCE BARRIER over a bound KV — the production barrier a * `registerServiceInstance` drives (observe -> freeze -> enumerate -> revoke -> [evict] -> reopen), * freezing this instance's `epgate..` so no serve mint can win against the * surface the registration is about to supersede. * * For a FRESH first registration the enumerated `epcred` family is EMPTY, so revoke/evict are not * exercised — but they are REAL code (a later takeover/re-registration of a LIVE instance MUST * revoke the prior serve family and verify-evict its holders; a stub would silently skip that). * `evict` is INJECTED: cluster-verified eviction is the $SYS CONNZ+KICK machinery (D5 slice 4), * not this module's job. The DEFAULT is FAIL-CLOSED `() => false` — "no evictor ⇒ eviction cannot * be VERIFIED ⇒ report not-verified" — so the saga's own guard (`if (!evict) throw`) leaves the * gate FROZEN for reconciliation on a takeover with no real evictor, never silently reopening into * split-brain. It is ONLY consulted on a NON-EMPTY family (a takeover); a fresh registration's * empty family never invokes it, so this default never touches the 1a-gate path — it enforces the * guard the moment a live predecessor exists. A caller with the real $SYS evictor injects it. The * freeze/reopen CAS is the real fence: a barrier that moved the gate makes a racing mint LOSE. */ export declare function endpointRegistrationBarrier(kv: KV, space: string, args: { endpoint: string; instanceId: string; opId: string; evict?: (holderPrincipal: string) => Promise | boolean; }): import("./endpoint-service.js").EpIssuanceBarrier; //# sourceMappingURL=endpoint-serve-kv.d.ts.map