/** * @factory/shared โ€” the MANDATORY AUDIT-EVENT catalog (TASK-0133). * * Single source of truth for the PRODUCT-SPEC ยง23 mandatory audit events * (plus the phase-12/13 security-mandatory additions). Two consumers: * * 1. `tests/integration/audit-completeness/` โ€” the completeness test imports * this table and, for EVERY entry, performs the `fire` operation in a * seeded workspace/org and asserts an `AuditLog` row with the `name` * action + every `metadata` field present. A missing emitter fails the * build (the phase exit criterion "audit log covers all mandatory events"). * 2. The audit viewer's action autocomplete (the org-admin API reads the same * list server-side via `@factory/shared`). * * `metadata` lists the REQUIRED metadata fields โ€” the catalog is the * contract, so an emitter that drops a listed field fails the completeness * test. Fields the emitter carries that are NOT listed are fine. */ export interface AuditCatalogEntry { /** The exact `AuditLog.action` string. */ name: string; /** One-line description (docs + UI autocomplete). */ description: string; /** * The operation that must fire it. The completeness test keys its dispatch * table off this id, so it is part of the contract (renaming it is a * breaking change for the test). */ fire: string; /** Required `AuditLog.metadata` fields. */ metadata: string[]; } export const AUDIT_CATALOG: AuditCatalogEntry[] = [ { name: "user.login", description: "Successful password sign-in.", fire: "login", metadata: ["email"] }, { name: "page.created", description: "A page was created.", fire: "page.create", metadata: ["title", "parentId", "type"] }, { name: "page.updated", description: "A page's title/content changed.", fire: "page.update", metadata: [] }, { name: "page.deleted", description: "A page was trashed (soft delete; descendants included).", fire: "page.trash", metadata: ["pageIds"] }, { name: "page.shared", description: "A share (public or per-user) was created for a page/database.", fire: "share.create", metadata: ["targetType", "targetId", "scope"] }, { name: "permission.changed", description: "A permission or membership role changed (workspace/team/file).", fire: "permission.change", metadata: ["change", "targetId"] }, { name: "database.created", description: "A database was created (page with a database block).", fire: "database.create", metadata: ["title"] }, { name: "record.created", description: "A record was inserted into a database.", fire: "record.create", metadata: ["databaseId"] }, { name: "record.updated", description: "A record's properties changed.", fire: "record.update", metadata: ["databaseId"] }, { name: "file.uploaded", description: "A file was uploaded (first version).", fire: "file.upload", metadata: ["name", "size"] }, { name: "automation.executed", description: "An automation run reached a terminal state (worker).", fire: "automation.run", metadata: ["automationId", "runId", "status"] }, { name: "api_key.created", description: "A workspace API key was created (the raw key is never audited).", fire: "api_key.create", metadata: ["name", "scopes"] }, { name: "ai.budget_exceeded", description: "The AI monthly budget was exceeded (call denied).", fire: "ai.budget", metadata: ["used", "limit"] }, { name: "oauth.token_issued", description: "An OAuth2 token was issued (authorization code or refresh).", fire: "oauth.token", metadata: ["clientId", "scopes"] }, { name: "oauth.token_revoked", description: "An OAuth2 token (family) was revoked.", fire: "oauth.revoke", metadata: ["clientId"] }, { name: "sso.login.saml", description: "A user signed in via SAML SSO.", fire: "sso.saml", metadata: ["orgId", "nameId"] }, { name: "sso.login.oidc", description: "A user signed in via OIDC SSO.", fire: "sso.oidc", metadata: ["orgId", "nameId"] }, { name: "scim.user.created", description: "SCIM provisioned a user into the organization.", fire: "scim.create", metadata: ["userId", "email", "role"] }, { name: "scim.user.deactivated", description: "SCIM deprovisioned (suspended) a user.", fire: "scim.deactivate", metadata: ["userId", "via"] }, { name: "webhook.created", description: "A workspace webhook was created.", fire: "webhook.create", metadata: ["url", "events"] }, { name: "webhook.revoked", description: "A workspace webhook was revoked (soft delete).", fire: "webhook.revoke", metadata: ["url"] } ] as const; /** The exact action strings (autocomplete + completeness assertions). */ export const AUDIT_CATALOG_NAMES: readonly string[] = AUDIT_CATALOG.map((e) => e.name);