import { type JetStreamManager } from "@nats-io/jetstream"; import type { KV, Kvm } from "@nats-io/kv"; import type { NatsConnection } from "@nats-io/transport-node"; import { type EpCaller } from "./endpoint-subjects.js"; export { assertGeneration, isIssuedCaller, EP_RAIL_V1, type IssuedCaller } from "./endpoint-subjects.js"; /** The four caller coordinates plus the generation. `generation` is 32 lowercase hex characters * of issuer-chosen entropy (at least 128 bits); it is an identifier, never a bearer secret. */ export interface IssuedAuthorityRef extends EpCaller { readonly space: string; readonly generation: string; } /** A fresh generation: 128 bits, hex. Chosen by the ISSUER, never by the client. */ export declare function mintGeneration(): string; /** A fresh accepted-row token: same grammar, chosen by the party that will connect. */ export declare function mintAcceptedToken(): string; /** One direction's allow set. Explicit: the native fragment's omitted or empty `allow` means * UNRESTRICTED, and a ceiling that read that as "nothing" would be exactly the inversion a * copied ledger row produces, so the mode is always spelled out. */ export type IssuedSubjectAllow = { readonly mode: "all"; } | { readonly mode: "none"; } | { readonly mode: "patterns"; readonly patterns: readonly string[]; }; export interface IssuedSubjectPermission { readonly allow: IssuedSubjectAllow; readonly deny: readonly string[]; } /** The ceiling: what the credential may publish and subscribe, as the broker enforces it. */ export interface IssuedSubjectPermissions { readonly publish: IssuedSubjectPermission; readonly subscribe: IssuedSubjectPermission; } /** Read a stored permission value back, refusing every shape but the explicit one. */ export declare function readIssuedSubjectPermission(value: unknown): IssuedSubjectPermission; export declare function readIssuedSubjectPermissions(value: unknown): IssuedSubjectPermissions; /** The native NATS fragment (`{pub, sub}` with `allow`/`deny` lists) as the JWT carries it: an * omitted or empty allow is UNRESTRICTED subject to denies. This is the one importer, and it * takes the fragment, never a whole JWT: `resp` and queue-qualified rows need semantics of their * own and are refused rather than dropped. */ export declare function importNativeSubjectPermissions(value: unknown): IssuedSubjectPermissions; /** Does the ceiling permit ONE concrete subject? Deny wins; `none` permits nothing. */ export declare function issuedPermitsSubject(value: IssuedSubjectPermission, concrete: string): boolean; /** Does the ceiling permit a SUBSCRIPTION whose filter is `pattern` (which may carry wildcards)? * Every subject the filter could deliver must sit inside one allow entry, and no deny entry may * overlap it: a subscription is a claim over a set of subjects, so a partial cover is a refusal, * never a narrower grant invented on the caller's behalf. */ export declare function issuedPermitsPattern(value: IssuedSubjectPermission, pattern: string): boolean; /** The immutable coordinate of a gate an issuance depends on: one KV row in one bucket. */ export interface IssuedSourceRef { readonly space: string; readonly bucket: string; readonly key: string; } /** What the issuer persisted, create-only, before any material was returned. `expiresAt` (unix * seconds) is REQUIRED when `sources` is empty: an issuance bound to no gate is a one-shot * credential's, and its liveness is its expiry. */ export interface IssuedEvidence { readonly version: 1; readonly ref: IssuedAuthorityRef; readonly sources: readonly IssuedSourceRef[]; readonly permissions: IssuedSubjectPermissions; readonly expiresAt?: number; } export type IssuedAttemptState = "prepared" | "active" | "aborted" | "revoked"; export interface IssuedResolution { readonly evidence: IssuedEvidence; /** The attempt row's revision at the second (linearizing) read. */ readonly revision: number; } /** The per-space evidence store. `allow_direct=false`: every read here is a fence (leader-served, * revision-pinned), and Direct Get's follower reads would defeat read-your-writes. */ export declare function issuedBucket(space: string): string; /** The per-space accepted-row store. Its own bucket, and `allow_direct` ON: per-key scoping is * only expressible on a `DIRECT.GET` grant, and a stream-wide `STREAM.MSG.GET` on a shared bucket * would let a client read every other client's row. */ export declare function acceptedBucket(space: string): string; export declare function issuedEvidenceKey(ref: IssuedAuthorityRef): string; export declare function issuedAttemptKey(ref: IssuedAuthorityRef): string; /** The reverse index prefix for one source: `bysource.v1..`. * The digest keeps the key one token; the generation suffix on the full key is load-bearing, since * without it a reused root credential id would merge two issuances into one entry. */ export declare function issuedSourcePrefix(source: IssuedSourceRef): string; export declare function issuedSourceIndexKey(source: IssuedSourceRef, ref: IssuedAuthorityRef): string; export interface PreparedIssuance { readonly key: string; } /** The store bound to one space over one KV handle. The KV is the ISSUER's (a `issuer` or a * `issued-reader` credential); a peer holds no grant on this bucket. */ export interface IssuedStore { /** Persist the evidence and a `prepared` attempt, create-only. A generation that already exists * refuses before the CAS, and the CAS is the second line. Nothing is released here. */ stage(evidence: IssuedEvidence): Promise; /** Run the existing finalizer, then activate by CAS at the revision the prepare observed. If * the finalizer succeeds and the activation loses, the issuance is retired and the call throws: * no material may be released on it. */ release(prepared: PreparedIssuance, finalizeExisting: () => Promise): Promise; /** `prepared` becomes `aborted`, `active` becomes `revoked`; idempotent on a terminal state. */ retire(ref: IssuedAuthorityRef): Promise; /** The resolution a consumer acts on: evidence read, attempt `active`, every source live (or * the one-shot expiry not reached), and the attempt still `active` at the SAME revision on a * second read after the source await. That second read is the linearization point. A failed * read on either side is a refusal, never an absence of revocation. */ resolve(ref: IssuedAuthorityRef, sourceIsLive: (source: IssuedSourceRef) => Promise, now?: () => number): Promise; /** Retire every issuance indexed under one source. The caller freezes the source gate FIRST. */ retireSource(source: IssuedSourceRef): Promise; /** A RENEWAL under an existing generation: the evidence must exist, its attempt must be * `active`, and its recorded ceiling must EQUAL `permissions`. A generation is reused only when * its immutable ceiling still matches (SPEC 13.15); a changed ceiling is a fresh issuance on a * fresh generation and a new connection, never an update of this one. */ confirm(ref: IssuedAuthorityRef, permissions: IssuedSubjectPermissions): Promise; } /** What an issuing mint hands {@link mintCreds}: the store and accepted-row handles of an * `issuer` connection, the immutable source coordinates the evidence names, and the existing * finalizer (a ledger append) release runs BEFORE activation. `renew` reuses a generation whose * ceiling is unchanged and stages nothing. */ export interface IssuanceSeam { readonly mode: "issue" | "renew"; readonly store: IssuedStore; readonly accepted: KV; readonly sources: readonly IssuedSourceRef[]; readonly finalize?: (credentialId: string) => Promise; } /** The broker-enforced part of every authority store's immutability (SPEC 13.12): no rollup * header, no message delete, no purge. The records store carries the same three. */ export declare const AUTHORITY_STORE_IMMUTABLE_FLAGS: Readonly<{ allow_rollup_hdrs: false; deny_delete: true; deny_purge: true; }>; /** Write-once per key: one message per subject and `discard: new` applied per subject, so the * broker refuses the SECOND message on a key whatever it carries. Other keys are unaffected. */ export declare const WRITE_ONCE_PER_KEY: Readonly<{ max_msgs_per_subject: 1; discard: "new"; discard_new_per_subject: true; }>; /** Append-only per key: unlimited per-key history, nothing ever removed. A reader that wants the * row that was created reads the FIRST message on its key. */ export declare const APPEND_ONLY_PER_KEY: Readonly<{ max_msgs_per_subject: -1; }>; /** Every field of `shape` must read back from the broker as set; the mismatches are named. */ export declare function assertStoreShape(cfg: Record, shape: Record, bucket: string, spec: string): void; /** The two issued-authority streams, for the provisioner's create-or-verify inventory. */ export declare function issuedStoreStreamNames(space: string): string[]; export declare function openIssuedStore(kv: KV, jsm: JetStreamManager, space: string): IssuedStore; /** Create-or-verify the two issued-authority stores. Evidence: `allow_direct=false`, file, * no eviction. Accepted: `allow_direct=true`, file, no eviction. A drifted store fails loud. */ export declare function ensureIssuedStores(jsm: JetStreamManager, kvm: Kvm, space: string): Promise; export declare function acceptedKey(acceptedToken: string): string; /** The read grant an issued ceiling carries for exactly its own accepted row and nothing else. */ export declare function acceptedReadGrant(space: string, acceptedToken: string): string; /** Written by the issuer at release, create-only: a token is redeemed once. */ export declare function writeAcceptedRow(kv: KV, acceptedToken: string, ref: IssuedAuthorityRef): Promise; /** Read by the CLIENT over its own connection: the reference the ISSUER wrote, so a client that * proposed or was told a different generation learns the accepted one here. The broker admits * the read only under the ceiling's per-key grant, which is what binds the answer to the * credential this connection presented. */ export declare function readAcceptedRow(nc: NatsConnection, space: string, acceptedToken: string, timeoutMs?: number): Promise; /** The named refusal an endpoint returns for a legacy (unversioned) invocation of a command * that requires issued authority (SPEC ยง13.15 compatibility). */ /** `details[].kind` of the refusal a command requiring issued caller authority returns to a * LEGACY arrival: the request rode the unversioned rail, so no generation binds it (SPEC 13.15). */ export declare const EP_UNBOUND_CALLER_AUTHORITY = "ai.cotal.ep.unbound-caller-authority"; //# sourceMappingURL=issued-authority.d.ts.map