Add durable credential registry and secret lifecycle

This commit is contained in:
ms2k 2026-07-17 00:18:02 +09:00
commit ac2289bd0a
5 changed files with 4499 additions and 4 deletions

View file

@ -0,0 +1,53 @@
-- Source credential registry metadata. Secret material lives in Vault KV v2;
-- this package only stores owner, kind, status, and opaque version coordinates.
CREATE TABLE source_credentials (
credential_id TEXT PRIMARY KEY,
owner_id TEXT NOT NULL,
label TEXT NOT NULL,
kind TEXT NOT NULL,
status TEXT NOT NULL,
active_version_id TEXT,
secret_mount TEXT NOT NULL,
secret_path TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
CONSTRAINT source_credentials_kind_check CHECK (
kind IN (
'basic_auth',
'bearer_token',
'http_headers',
'object_store',
'hugging_face'
)
),
CONSTRAINT source_credentials_status_check CHECK (status IN ('active', 'revoked')),
CONSTRAINT source_credentials_label_len CHECK (
char_length(label) BETWEEN 1 AND 200
),
CONSTRAINT source_credentials_active_status CHECK (
(status = 'active' AND active_version_id IS NOT NULL)
OR (status = 'revoked')
)
);
CREATE INDEX source_credentials_owner_created_idx
ON source_credentials (owner_id, created_at DESC, credential_id DESC);
CREATE TABLE source_credential_versions (
version_id TEXT PRIMARY KEY,
credential_id TEXT NOT NULL REFERENCES source_credentials (credential_id),
vault_kv_version BIGINT NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
destroyed_at TIMESTAMPTZ,
CONSTRAINT source_credential_versions_kv_positive CHECK (vault_kv_version >= 1)
);
CREATE INDEX source_credential_versions_credential_idx
ON source_credential_versions (credential_id, created_at DESC);
ALTER TABLE source_credentials
ADD CONSTRAINT source_credentials_active_version_fk
FOREIGN KEY (active_version_id)
REFERENCES source_credential_versions (version_id)
DEFERRABLE INITIALLY DEFERRED;

View file

@ -0,0 +1,47 @@
-- Durable intent for Vault secret writes. Secret material is never stored here.
-- Intents make create/rotate recoverable across ambiguous PostgreSQL commits
-- without best-effort Vault destruction that could erase a committed version.
CREATE TABLE secret_write_intents (
intent_id TEXT PRIMARY KEY,
operation TEXT NOT NULL,
credential_id TEXT NOT NULL,
version_id TEXT NOT NULL,
owner_id TEXT,
label TEXT,
credential_kind TEXT,
secret_mount TEXT NOT NULL,
secret_path TEXT NOT NULL,
vault_kv_version BIGINT,
status TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
CONSTRAINT secret_write_intents_operation_check CHECK (
operation IN ('create', 'rotate')
),
CONSTRAINT secret_write_intents_status_check CHECK (
status IN ('prepared', 'vault_written', 'durable_committed', 'abandoned')
),
CONSTRAINT secret_write_intents_kv_check CHECK (
vault_kv_version IS NULL OR vault_kv_version >= 1
),
CONSTRAINT secret_write_intents_create_fields_check CHECK (
(operation = 'create'
AND owner_id IS NOT NULL
AND label IS NOT NULL
AND credential_kind IS NOT NULL)
OR (operation = 'rotate'
AND owner_id IS NULL
AND label IS NULL
AND credential_kind IS NULL)
),
CONSTRAINT secret_write_intents_written_has_version CHECK (
(status = 'prepared' AND vault_kv_version IS NULL)
OR (status IN ('vault_written', 'durable_committed')
AND vault_kv_version IS NOT NULL)
OR status = 'abandoned'
)
);
CREATE INDEX secret_write_intents_status_idx
ON secret_write_intents (status, created_at);

View file

@ -0,0 +1,21 @@
-- Durable execution-grant credential pins. Resolution always re-reads this table:
-- revoked or missing grants fail closed even if a worker still holds a grant id.
CREATE TABLE execution_grant_credential_pins (
grant_id TEXT PRIMARY KEY,
credential_id TEXT NOT NULL,
version_id TEXT NOT NULL,
status TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
revoked_at TIMESTAMPTZ,
CONSTRAINT execution_grant_credential_pins_status_check CHECK (
status IN ('active', 'revoked')
),
CONSTRAINT execution_grant_credential_pins_revoked_at_check CHECK (
(status = 'active' AND revoked_at IS NULL)
OR (status = 'revoked' AND revoked_at IS NOT NULL)
)
);
CREATE INDEX execution_grant_credential_pins_credential_idx
ON execution_grant_credential_pins (credential_id, status);

View file

@ -0,0 +1,29 @@
-- Reconciler ownership claim: vault destruction runs only after an intent is
-- moved to abandoning under the credential lock. Create/rotate materialize
-- refuses non-vault_written intents, so a concurrent commit cannot land a
-- durable version that points at material the reconciler is about to destroy.
ALTER TABLE secret_write_intents
DROP CONSTRAINT secret_write_intents_status_check;
ALTER TABLE secret_write_intents
ADD CONSTRAINT secret_write_intents_status_check CHECK (
status IN (
'prepared',
'vault_written',
'durable_committed',
'abandoning',
'abandoned'
)
);
ALTER TABLE secret_write_intents
DROP CONSTRAINT secret_write_intents_written_has_version;
ALTER TABLE secret_write_intents
ADD CONSTRAINT secret_write_intents_written_has_version CHECK (
(status = 'prepared' AND vault_kv_version IS NULL)
OR (status IN ('vault_written', 'durable_committed', 'abandoning')
AND vault_kv_version IS NOT NULL)
OR status = 'abandoned'
);

View file

@ -0,0 +1,13 @@
ALTER TABLE source_credential_versions
ADD COLUMN secret_mount TEXT,
ADD COLUMN secret_path TEXT;
UPDATE source_credential_versions AS version
SET secret_mount = credential.secret_mount,
secret_path = credential.secret_path
FROM source_credentials AS credential
WHERE credential.credential_id = version.credential_id;
ALTER TABLE source_credential_versions
ALTER COLUMN secret_mount SET NOT NULL,
ALTER COLUMN secret_path SET NOT NULL;