import { EP_AUTHZ_MODES } from "./endpoint-subjects.js"; import type { EpCapability } from "./endpoint-grants.js"; import { type AnchorResolver, type SignerAnchor } from "./endpoint-signing.js"; /** A per-command target tuple inside a grant entry — a CLOSED set of three legal shapes * (§13.6): no components; `targetOwner` alone; or the full triple. Every other combination is * schema-invalid ({@link parseGrantCommand} enforces it). */ export interface HandleGrantCommand { name: string; /** The authorization mode, meaningful only for an owner-domain entry (`owner`|`child`|`ledger`; * `any` is schema-invalid in a handle; default `owner`). Schema-invalid on a no-target entry * and on an actor-pinned entry (the triple IS the mode). */ authz?: "owner" | "child" | "ledger"; targetOwner?: string; targetActor?: string; targetLifecycleUid?: string; } /** One grant entry: a set of commands on one endpoint (optionally one instance), plus read * subtrees. Every present signed component MUST be consumed by the compile target. */ export interface HandleGrant { endpoint: string; instanceId?: string; commands: HandleGrantCommand[]; reads?: string[]; } /** The §13.6 handle artifact (signed envelope; `sig` over the sig-absent RFC 8785 form). */ export interface CapabilityHandle { v: 1; id: string; space: string; issuer: { keyId: string; }; holder: { id: string; lifecycleUid: string; }; grants: HandleGrant[]; iat: number; nbf?: number; exp: number; parentDigest?: string; sturdy: boolean; /** Present iff a LIVE handle (`sturdy: false`) — binds the current process epoch. */ epoch?: number; sig: string; } /** The §13.6 default validity ceilings (space-configurable): live ≤ 24h, sturdy default 30d. */ export declare const HANDLE_MAX_LIVE_TTL_MS: number; export declare const HANDLE_MAX_STURDY_TTL_MS: number; /** Verification bounds (fail loud past each; a bound reached is a refusal, never a truncation). */ export declare const HANDLE_MAX_CHAIN_LENGTH = 16; export declare const HANDLE_MAX_BYTES: number; export declare const HANDLE_MAX_GRANTS = 64; export declare const HANDLE_MAX_COMMANDS_PER_GRANT = 64; export declare const HANDLE_MAX_READS_PER_GRANT = 64; /** Validate a handle artifact's SHAPE (the closed-tuple rules + envelope), returning the typed * frozen handle. This is the schema step; signature + chain + currency are the verify step * ({@link verifyHandleChain}) — and those verify the RAW presented artifact, never this * projection. A `sturdy: false` handle MUST carry `epoch`; a `sturdy: true` handle MUST NOT * (§13.6: live binds the process epoch, sturdy binds the lifecycle UID). */ export declare function parseHandle(raw: unknown): CapabilityHandle; /** The handle's content address (`sha256:` over the full artifact incl. `sig`) — the * identity a child's `parentDigest` references. Chain verification computes this over the * RAW presented artifact ({@link verifyHandleChain}); this export is for issuance (building * a child's `parentDigest` from the parent artifact you hold). */ export declare function handleDigest(handle: CapabilityHandle): string; /** The command's contract dimensions the compiler needs but cannot derive from the grant * entry alone (§13.6: "per the command's contract"): whether a NO-TARGET command compiles to * the untargeted form or the `.self` form, and whether the command submits via the journal. * The compiler is transport-thin and never guesses either. */ export interface CommandContract { noTargetForm: "untargeted" | "self"; journal: boolean; } export type CommandContractSeam = (endpoint: string, command: string) => CommandContract; /** The compiled equivalent-mint bundle: the request capabilities PLUS the read subtrees. Both * halves are signed components; neither is ever silently dropped. */ export interface CompiledHandleGrants { caps: EpCapability[]; /** The signed read subtrees (record-key / event-topic prefixes), ENDPOINT-BOUND — a subtree * is meaningless without the grant endpoint it was signed under, so two grants naming the * same subtree on different endpoints stay distinguishable. Deduplicated per * (endpoint, subtree); the redemption mints these as endpoint-scoped read rows exactly as * signed. */ reads: { endpoint: string; subtree: string; }[]; } /** Compile a handle's grants to the EpCapability set + read subtrees the equivalent minted * capability would receive (§13.6). This is what a redemption mints and what attenuation * intersects against; it NEVER widens and consumes EVERY signed component: * - `routes` is set EXPLICITLY: an instance entry compiles to the exact `ep.inst` rails * ONLY (`routes: []` — an instance pin never also grants the class rail); a class entry * compiles to `routes: ["one"]` (scatter `all` is not expressible in a handle); * - a NO-TARGET command's untargeted-vs-`.self` form and the `journal` rail come from the * REQUIRED command-contract seam (the compiler never guesses either); * - a JOURNAL-class command compiles to a journal-EXCLUSIVE capability (`routes: []`, no * request rails): journal submissions ride ONLY `epj` (§13.9), so emitting a request row * alongside would be equivalent-mint WIDENING. The frozen `epj` grammar has no instance * coordinate, so an instance-pinned journal command is UNREPRESENTABLE and refuses — the * signed instance pin is never silently dropped onto a class-wide journal row; * - `reads` are returned alongside, endpoint-bound, never dropped. */ export declare function compileHandleGrants(handle: CapabilityHandle, opts: { commandContract: CommandContractSeam; }): CompiledHandleGrants; /** Assert a CHILD handle is ⊆ its PARENT under the §13.6 normative containment order. Per grant * entry: endpoint equal (domain patterns are a future extension; this revision pins exact * endpoints); `instanceId` equal or newly pinned (never widened to absent); commands a * name-subset with per-command mode contained and target components equal-or-newly-pinned; * reads subject-prefix-contained. Per envelope: same space; validity window within the * parent's; `sturdy` only if the parent is sturdy. Throws `permission-denied` on any widening. */ export declare function assertHandleContainedIn(child: CapabilityHandle, parent: CapabilityHandle): void; /** Enforce §13.10 issuer authority for one handle: `handle.grants ⊆ anchor.scope` under the * SAME §13.6 containment order — endpoints, per-command modes, target components, instance * pins, and read subtrees are all ceiling dimensions; a flat endpoint/command list cannot * express them and is exactly the laundering this refuses. */ export declare function assertIssuerScopeCoversHandle(anchor: SignerAnchor, handle: CapabilityHandle): void; /** The revocation reader for STURDY handles: the `handle..` record's status * side (monotonic revocation state, §13.9). ONLY the literal `false` means "not revoked": * `true`, `undefined`, or an unreadable status all FAIL CLOSED as revoked. */ export type HandleRevocationReader = (issuerKeyId: string, id: string) => Promise | boolean; /** Verify a presented handle CHAIN inline (§13.6): the leaf plus every `parentDigest`-linked * ancestor, presented together (no ambient fetch). Signature and digest identity are checked * over the EXACT RAW presented artifacts (D28), never the parsed projection. For each link, * leaf to root: * - its `parentDigest` (if any) equals the digest of the NEXT presented RAW artifact (the * chain is the one presented, not a forgeable claim); * - each child is ⊆ its parent ({@link assertHandleContainedIn}); * - the ISSUER of a child is the PARENT's holder — anchor-registered with a `handles` role * whose STRUCTURED scope covers the link ({@link assertIssuerScopeCoversHandle}) AND * lifecycle-bound to the parent's holder (`ownerLifecycleUid`; owner text alone would let * a recycled alias issue off its predecessor's handles — absent binding fails closed); * - the signature verifies against the resolved anchor, within the anchor's window; * - EVERY link is currency-checked: window (`nbf ≤ now ≤ exp`), no future `iat`, TTL span * within the live/sturdy ceiling AND `exp ≤ now + ceiling` (clock-anchored: a backdated * `nbf`/forward-dated `iat` cannot manufacture validity beyond the ceiling), space match; * - EVERY sturdy link's revocation status is strict-`false`-checked (unreadable = revoked); * - a LIVE leaf binds `presenterEpoch`; a LIVE ANCESTOR requires `resolveHolderEpoch` to * fresh-check ITS holder's current epoch (a restarted intermediate kills the chain). * The walk is bounded: chain length ≤ {@link HANDLE_MAX_CHAIN_LENGTH}, every await within * `verifyBudgetMs` (default 5000). A ROOT handle (no parentDigest) is issued by an anchor * whose owner is the handle's issuer principal. Returns the leaf and its compiled * equivalent-mint bundle (never wider than any ancestor). */ export declare function verifyHandleChain(chain: unknown[], opts: { resolveAnchor: AnchorResolver; now: number; space: string; presenter: { id: string; lifecycleUid: string; epoch?: number; }; readRevocation: HandleRevocationReader; /** The command-contract seam the compiled bundle needs ({@link compileHandleGrants}). */ commandContract: CommandContractSeam; /** REQUIRED when the chain contains a LIVE non-leaf link: the link holder's CURRENT * process epoch from trusted authority (null = retired/unknown). */ resolveHolderEpoch?: (holder: { id: string; lifecycleUid: string; }) => Promise | number | null; maxLiveTtlMs?: number; maxSturdyTtlMs?: number; verifyBudgetMs?: number; }): Promise<{ leaf: CapabilityHandle; compiled: CompiledHandleGrants; }>; /** Serialize a handle to its canonical bytes (the wire/store form). Throws if the artifact is * not interchangeable I-JSON (the strict canonical path), so a non-canonicalizable handle can * never be persisted or presented. The parsed projection is byte-faithful to the signed form * (parse injects nothing and drops nothing), so these bytes re-verify. */ export declare function serializeHandle(handle: CapabilityHandle): Uint8Array; export { EP_AUTHZ_MODES }; //# sourceMappingURL=endpoint-handle.d.ts.map