/** * Capability Model — Zone-scoped read/write authorization * * ADR 0022. Resolves the §5 "room = boundary" decision and the §2a refs * decision under one principle: partition, don't filter. * * The kernel understands only four capability levels and opaque `zoneId`s. * Zone *names* ("Workshop", "Lab", …) are a mutable `alias` fact and never * appear in kernel logic, so renaming a zone never requires a refactor. * * Grants are explicit (principal, zoneId, level) facts. Absence = `None` * (deny-by-default). Revocation retracts the grant fact; `None` is never * persisted. Nesting via `parentZone` yields a capability closure. * * **Writes mint ops; reads run against the materialized store.** The store is * derived — rebuilt by op replay on boot — so writing to it directly would give * grants that vanish on restart, never replicate to peers, are not hash-covered * and carry no provenance. An authorization change a peer cannot see is not a * boundary, so every mutation here goes through the journal as a first-class, * nameable op (`vcs:zoneDefine` / `zoneRename` / `grantSet` / `grantRetract`). * * **Authority is checked at mint AND enforced at ingest.** `assertOwner` stops * an honest caller from over-reaching on the trusted local path; * `enforceIngestAuthorization` stops a peer from minting a grant op directly and * integrating it past the boundary (ADR 0022 Phase 3). The two are the same * check at two ends of one pipe: hash integrity (already at ingest) plus * attribution (this module) converge on the single ingest gate. */ import type { EAVStore } from '../core/store/eav-store.js'; import type { EngineContext } from '../vcs/engine-context.js'; import type { VcsOp } from '../vcs/types.js'; import type { IdentityResolver } from './signing-middleware.js'; /** * Capability levels, ordered. Higher numeric value = broader access, so * "at least level L" is a simple ordinal comparison. * * `None` is the deny-by-default absence of a grant. It is NEVER persisted as a * fact (see ADR 0022 §1): revocation retracts the grant fact instead. */ export declare enum CapabilityLevel { None = 0,// absence of grant = default. Never persisted. Reader = 1,// read-only within the boundary Member = 2,// read + write within the boundary Owner = 3 } /** Authoritative zone id: `turtle:///zone/`. Immutable. */ export type ZoneId = string; export interface Zone { /** Immutable, authority-bearing identifier. */ zoneId: ZoneId; /** Mutable human name. Renaming edits this, never the id. */ alias: string; /** Level granted to anon (Reader = public; None = private). */ defaultVisibility: CapabilityLevel; /** Opaque parent zoneId for nesting → grant inheritance (closure). */ parentZone?: ZoneId; } /** A single explicit capability grant. */ export interface Grant { principal: string; zoneId: ZoneId; level: CapabilityLevel; } /** * Build an immutable, authority-bearing zone id. * `turtle:///zone/` — ownership is self-describing in the id. */ export declare function makeZoneId(ownerDid: string, uuid: string): ZoneId; /** Parse the owner DID out of a zoneId, or null if malformed. */ export declare function zoneOwnerDid(zoneId: ZoneId): string | null; /** * Define a zone. `alias` is the only mutable display field; `zoneId` is fixed. * The zone's owner (from the id) is auto-granted `Owner` so the zone is * administerable from creation; subsequent grants are Owner-gated. */ export declare function defineZone(ctx: EngineContext, zone: Zone): Promise; /** * Rename a zone. Edits only the `alias` fact — `zoneId` and all grants are * untouched. This is the rename-proof guarantee (ADR 0022 §2). */ export declare function renameZone(ctx: EngineContext, zoneId: ZoneId, alias: string): Promise; /** Load a zone definition from the store. */ export declare function getZone(store: EAVStore, zoneId: ZoneId): Zone | undefined; /** * Set a grant. Only an existing `Owner` of the zone may mutate grants * (ADR 0022 invariant). `level` of `None` is rejected — revoke via * `retractGrant` instead, so `None` is never persisted. */ export declare function setGrant(ctx: EngineContext, grant: Grant, actor: string): Promise; /** * Retract a grant (revocation). Owner-gated. Removes the grant fact; the * principal falls back to `defaultVisibility` / `None`. */ export declare function retractGrant(ctx: EngineContext, zoneId: ZoneId, principal: string, actor: string): Promise; /** * Resolve the effective capability level of `principal` over `zone`. * * `effective = max(defaultVisibility, direct grant, inherited grants…)` * * - The target zone's `defaultVisibility` is a **floor**, always included. * - Direct grant on the zone (if any). * - Inherited grants up the `parentZone` chain (closure, `allOf`-style). * - `None` if the zone does not exist (deny-by-default). * * Positive-only (union/max) — no explicit-deny override, which keeps resolution * precedence-free. A consequence worth stating: **you cannot grant someone less * than the zone's default.** If a principal must have less than the public * floor, that is a different zone, not a smaller grant. (Partition, don't * filter — the same rule the zone model is built on.) */ export declare function resolveCapability(store: EAVStore, principal: string, zoneId: ZoneId): CapabilityLevel; /** Op kinds that mutate zone authority and therefore require attribution. */ export declare const AUTH_OP_KINDS: Set; export interface IngestAuthResult { ok: boolean; reason?: 'unauthorized'; message?: string; } /** * Ingest-time authorization gate (ADR 0022 Phase 3). * * `integrateOps` already verifies op *integrity* (the hash) but, until now, * verified no *attribution* — so a peer could mint a `vcs:grantSet` / * `vcs:zoneDefine` op directly and the store would apply it, bypassing the * `assertOwner` check that only guards the trusted local mint path * (`setGrant` / `defineZone` → `applyOp`). This gate closes that: every * authorization-bearing op must be attributable to a principal the zone already * trusts at `Owner` (or, for `zoneDefine`, to the identity named in the * zoneId — authority is self-describing in the id). * * Attribution: the op must be signed. When an `IdentityResolver` is wired, the * signature is cryptographically verified and `signedBy` is trusted only if it * verifies; without a resolver the kernel can at best require a signature * envelope to exist (deny-by-default for unattributable auth ops). Full PKI * validation is the resolver-wiring follow-on (ADR 0020). */ export declare function enforceIngestAuthorization(store: EAVStore, op: VcsOp, resolver?: IdentityResolver): Promise; //# sourceMappingURL=capability.d.ts.map