feat: add authorization bootstrap and HTTP enforcement

Wires Core's identity and authorization domains into apps/api over one
shared, validated PostgreSQL pool; adds an explicit, single-use,
concurrency-safe platform-owner bootstrap CLI; adds a smallest-possible
immutable permission registry with one Core-owned restricted permission
(core.authorization.grants.manage); and adds reusable Hono authorization
enforcement plus a narrowly scoped /api/v1/grants HTTP API for managing
explicit grants only, gated by an exact trusted Origin on every mutation.
This commit is contained in:
ekklesia
2026-07-23 11:16:32 -05:00
parent 72e082ff2f
commit 1363608552
33 changed files with 2962 additions and 63 deletions
+109
View File
@@ -0,0 +1,109 @@
import {
type PermissionBaseline,
type PermissionId,
parsePermissionId,
safeParsePermissionId,
} from "@ekklesia/contracts";
/**
* A registered permission declaration: the trusted pairing of a permission identifier with its
* one baseline. This is what distinguishes a *registered* permission from a merely well-formed
* `PermissionId` string — the evaluator itself (`createAuthorizationEvaluator`) still takes an
* explicit, caller-supplied baseline per pass 1's design; the registry is what a trusted caller
* (Core's own HTTP enforcement, eventually a module registration mechanism) consults so that
* baseline is never client-supplied or guessed.
*/
export interface PermissionDeclaration {
readonly id: PermissionId;
readonly baseline: PermissionBaseline;
/** Human-readable, for operators and audit review only — never interpreted by the evaluator or
* by enforcement. */
readonly description: string;
}
export interface PermissionRegistry {
readonly declarations: readonly PermissionDeclaration[];
/** Returns the declaration for a permission id, or `undefined` for anything malformed or not
* registered — the two are deliberately indistinguishable to callers, so "unknown permission"
* and "invalid permission" fail closed the same way. */
get(permissionId: unknown): PermissionDeclaration | undefined;
has(permissionId: unknown): boolean;
}
export type PermissionRegistryErrorCode = "PERMISSION_REGISTRY_DUPLICATE_DECLARATION";
/** Thrown only at composition time (registry construction), never per-request. Carries the
* offending permission id deliberately — this is a startup-time programmer/configuration error
* message, not a runtime response, and a namespaced permission id is not sensitive. */
export class PermissionRegistryError extends Error {
readonly code: PermissionRegistryErrorCode;
readonly permissionId: string;
constructor(permissionId: string) {
super(`Permission "${permissionId}" is already registered.`);
this.name = "PermissionRegistryError";
this.code = "PERMISSION_REGISTRY_DUPLICATE_DECLARATION";
this.permissionId = permissionId;
this.stack = `${this.name}: ${this.message}`;
Object.freeze(this);
}
}
/**
* Builds an immutable permission registry from a fixed list of declarations, rejecting any
* duplicate permission id outright at composition time. Deliberately the smallest mechanism that
* distinguishes "registered" from "well-formed": no module discovery, no dynamic registration, no
* network or filesystem I/O — a future module registration mechanism can merge its own
* declarations into a combined list and still call this same constructor, inheriting duplicate
* detection for free instead of reimplementing it.
*/
export function createPermissionRegistry(
declarations: readonly PermissionDeclaration[],
): PermissionRegistry {
const byId = new Map<PermissionId, PermissionDeclaration>();
for (const declaration of declarations) {
if (byId.has(declaration.id)) {
throw new PermissionRegistryError(declaration.id);
}
byId.set(declaration.id, Object.freeze({ ...declaration }));
}
const frozenDeclarations = Object.freeze([...byId.values()]);
return Object.freeze({
declarations: frozenDeclarations,
get(permissionId: unknown): PermissionDeclaration | undefined {
const parsed = safeParsePermissionId(permissionId);
return parsed.success ? byId.get(parsed.data) : undefined;
},
has(permissionId: unknown): boolean {
const parsed = safeParsePermissionId(permissionId);
return parsed.success && byId.has(parsed.data);
},
});
}
/** Restricted: delegated grant administration is itself a privileged action, never public or
* merely authenticated. A platform owner always passes the evaluator's existing owner bypass and
* can delegate this permission to another account deliberately via the explicit-grant API. */
export const CORE_AUTHORIZATION_GRANTS_MANAGE: PermissionId = parsePermissionId(
"core.authorization.grants.manage",
);
const CORE_PERMISSION_DECLARATIONS: readonly PermissionDeclaration[] = Object.freeze([
Object.freeze({
baseline: "restricted" as const,
description: "Create, list, and revoke another user's explicit authorization grants.",
id: CORE_AUTHORIZATION_GRANTS_MANAGE,
}),
]);
/**
* Core's own permission registry — the trusted source of truth `apps/api`'s authorization
* enforcement consults so an HTTP route can never supply its own baseline. Contains exactly the
* one Core-owned permission this slice needs; a future module registration mechanism is
* deliberately out of scope here and will compose its own declarations through
* `createPermissionRegistry` rather than through this constant.
*/
export const CORE_PERMISSION_REGISTRY: PermissionRegistry = createPermissionRegistry(
CORE_PERMISSION_DECLARATIONS,
);