-- Service accounts: a scoped, non-human credential for automation (e.g. -- terdut-operator) that needs to manage teams, escalation policies, dead -- man's switches, integrations and OIDC group bindings without impersonating -- a human user. See SERVICE-ACCOUNTS.md for the design this implements. -- -- Deliberately not a users row: no password_hash, no is_admin, no -- user_identities linkage, so a service account can never be pulled into -- OIDC group sync or password login, and is never mistaken for a human in an -- audit trail. -- -- scope is 'instance' (acts with the same reach system administration has -- over teams: create one, list them, mint a 'team'-scoped account against -- any of them) or 'team' (acts as that one team's owner, and nothing else). -- The CHECK ties team_id's presence to scope directly, rather than leaving it -- to application code to keep the two consistent. CREATE TABLE service_accounts ( id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, name TEXT NOT NULL UNIQUE, scope TEXT NOT NULL CHECK (scope IN ('instance', 'team')), team_id BIGINT REFERENCES teams(id) ON DELETE CASCADE, created_by BIGINT REFERENCES users(id) ON DELETE SET NULL, created_at BIGINT NOT NULL DEFAULT FLOOR(EXTRACT(EPOCH FROM now()))::bigint, CONSTRAINT service_accounts_scope_team_id_chk CHECK ( (scope = 'team' AND team_id IS NOT NULL) OR (scope = 'instance' AND team_id IS NULL) ) ); CREATE INDEX service_accounts_team_id_idx ON service_accounts(team_id); -- One account, many keys: rotation is minting a new one and revoking the -- old, the same shape api_keys already has, so an account's identity and -- audit history survive a rotation instead of being recreated by it. CREATE TABLE service_account_keys ( id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, service_account_id BIGINT NOT NULL REFERENCES service_accounts(id) ON DELETE CASCADE, key_hash TEXT NOT NULL UNIQUE, name TEXT NOT NULL, created_at BIGINT NOT NULL DEFAULT FLOOR(EXTRACT(EPOCH FROM now()))::bigint, last_used_at BIGINT ); CREATE INDEX service_account_keys_service_account_id_idx ON service_account_keys(service_account_id);