-- 026_accounting_api_keys.sql -- Documentation only — applied via -- internal/storage/accounting_api_keys.go (Store.initAccountingAPIKeysSchema), -- wired into storage.New() after initSharesSchema. Idempotent -- (CREATE TABLE / CREATE INDEX IF NOT EXISTS). -- -- Per-tenant API keys for the read-only Buchhaltungs-Pull-API -- (GET /api/v1/accounting/documents[/{id}/file], siehe MEMORY.md -- project_belegdatum_und_buchhaltung). A key is a tenant-level MACHINE -- credential, not a user session: it grants read access to that tenant's -- archived documents and to nothing else, which is why creating one requires -- domain_admin. -- -- Token handling mirrors document_shares (009_shares.sql) exactly: -- * raw key = "adms_" + base64url(32 crypto/rand bytes), generated once -- * returned to the caller EXACTLY once (POST response), never retrievable again -- * only the hex SHA-256 hash is persisted (key_hash, UNIQUE) -- * authentication always looks up by key_hash, never by id -- -- Keys are never hard-deleted: revoking sets revoked_at, so the audit trail -- (audit event accounting_pull, Detail carries "key:") stays resolvable for -- GoBD-Nachvollziehbarkeit. ResolveAccountingAPIKey filters revoked_at IS NULL -- inside the same UPDATE ... RETURNING that refreshes last_used_at, so a -- revoked key can never yield a tenant id. -- -- No FK on tenant_id / created_by: consistent with the rest of the schema -- (plain BIGINT), because tenants/users are owned by other stores that -- initialise after storage.New(). CREATE TABLE IF NOT EXISTS accounting_api_keys ( id BIGSERIAL PRIMARY KEY, tenant_id BIGINT NOT NULL, key_hash TEXT NOT NULL UNIQUE, label TEXT NOT NULL DEFAULT '', created_by BIGINT, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), revoked_at TIMESTAMPTZ, last_used_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_accounting_api_keys_tenant ON accounting_api_keys(tenant_id); -- No new columns on documents: the pull query (internal/storage/accounting_pull.go) -- reads existing columns only (document_date, document_date_score from -- 025_document_date_score.sql, doc_type_id, correspondent_id, created_at) and -- paginates by the (created_at, id) keyset, which idx_documents_tenant plus the -- primary key already support.