144 lines
6.4 KiB
PL/PgSQL
144 lines
6.4 KiB
PL/PgSQL
-- Durable external MCP version lifecycle receipts.
|
|
--
|
|
-- Safety contract:
|
|
-- * catalog metadata only; no package install, tool call, credential, raw page,
|
|
-- request body, response body, or RAG content is stored;
|
|
-- * additive schema only, so application rollback does not delete evidence;
|
|
-- * every observation is bound to one run_id / trace_id / work_item_id;
|
|
-- * tenant RLS remains fail-closed through app.project_id.
|
|
|
|
BEGIN;
|
|
|
|
CREATE TABLE IF NOT EXISTS awooop_mcp_version_lifecycle_run (
|
|
run_id UUID PRIMARY KEY,
|
|
trace_id VARCHAR(128) NOT NULL,
|
|
work_item_id VARCHAR(128) NOT NULL,
|
|
project_id VARCHAR(64) NOT NULL,
|
|
trigger_kind VARCHAR(32) NOT NULL,
|
|
status VARCHAR(48) NOT NULL,
|
|
candidate_count INTEGER NOT NULL DEFAULT 0,
|
|
observed_count INTEGER NOT NULL DEFAULT 0,
|
|
baseline_count INTEGER NOT NULL DEFAULT 0,
|
|
changed_count INTEGER NOT NULL DEFAULT 0,
|
|
policy_blocked_count INTEGER NOT NULL DEFAULT 0,
|
|
error_count INTEGER NOT NULL DEFAULT 0,
|
|
receipt JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
ended_at TIMESTAMPTZ,
|
|
|
|
CONSTRAINT uix_mcp_version_lifecycle_trace
|
|
UNIQUE (project_id, trace_id),
|
|
CONSTRAINT chk_mcp_version_lifecycle_project
|
|
CHECK (project_id = 'awoooi'),
|
|
CONSTRAINT chk_mcp_version_lifecycle_trigger
|
|
CHECK (trigger_kind IN ('startup','scheduled','operator_replay','test')),
|
|
CONSTRAINT chk_mcp_version_lifecycle_status
|
|
CHECK (status IN (
|
|
'running',
|
|
'completed_no_change',
|
|
'completed_no_write_policy_blocked',
|
|
'partial_degraded',
|
|
'failed'
|
|
)),
|
|
CONSTRAINT chk_mcp_version_lifecycle_counts
|
|
CHECK (
|
|
candidate_count >= 0 AND observed_count >= 0 AND baseline_count >= 0
|
|
AND changed_count >= 0 AND policy_blocked_count >= 0
|
|
AND error_count >= 0
|
|
),
|
|
CONSTRAINT chk_mcp_version_lifecycle_terminal_time
|
|
CHECK (
|
|
(status = 'running' AND ended_at IS NULL)
|
|
OR (status <> 'running' AND ended_at IS NOT NULL)
|
|
)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS awooop_mcp_version_observation (
|
|
observation_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
run_id UUID NOT NULL
|
|
REFERENCES awooop_mcp_version_lifecycle_run(run_id) ON DELETE RESTRICT,
|
|
trace_id VARCHAR(128) NOT NULL,
|
|
work_item_id VARCHAR(128) NOT NULL,
|
|
project_id VARCHAR(64) NOT NULL,
|
|
candidate_id VARCHAR(160) NOT NULL,
|
|
catalog_source VARCHAR(32) NOT NULL,
|
|
source_url TEXT NOT NULL,
|
|
source_url_sha256 VARCHAR(64) NOT NULL,
|
|
http_status INTEGER,
|
|
content_sha256 VARCHAR(64),
|
|
observed_version VARCHAR(128),
|
|
previous_version VARCHAR(128),
|
|
discovery_status VARCHAR(32) NOT NULL,
|
|
change_state VARCHAR(32) NOT NULL,
|
|
lifecycle_terminal VARCHAR(48) NOT NULL,
|
|
policy_decision JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
stage_receipts JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
error_class VARCHAR(128),
|
|
observed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
CONSTRAINT uix_mcp_version_observation_run_candidate
|
|
UNIQUE (run_id, candidate_id),
|
|
CONSTRAINT chk_mcp_version_observation_project
|
|
CHECK (project_id = 'awoooi'),
|
|
CONSTRAINT chk_mcp_version_catalog_source
|
|
CHECK (catalog_source IN ('smithery','mcp-so')),
|
|
CONSTRAINT chk_mcp_version_source_url_hash
|
|
CHECK (source_url_sha256 ~ '^[0-9a-f]{64}$'),
|
|
CONSTRAINT chk_mcp_version_content_hash
|
|
CHECK (content_sha256 IS NULL OR content_sha256 ~ '^[0-9a-f]{64}$'),
|
|
CONSTRAINT chk_mcp_version_discovery_status
|
|
CHECK (discovery_status IN ('observed','source_error','policy_blocked')),
|
|
CONSTRAINT chk_mcp_version_change_state
|
|
CHECK (change_state IN (
|
|
'baseline_created','no_change','version_changed',
|
|
'content_changed','source_unavailable'
|
|
)),
|
|
CONSTRAINT chk_mcp_version_terminal
|
|
CHECK (lifecycle_terminal IN (
|
|
'no_write_baseline','no_write_no_change','no_write_policy_blocked',
|
|
'no_write_source_error','replay_passed_canary_pending',
|
|
'promoted','rolled_back'
|
|
))
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_mcp_version_lifecycle_recent
|
|
ON awooop_mcp_version_lifecycle_run (project_id, started_at DESC);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_mcp_version_observation_candidate_recent
|
|
ON awooop_mcp_version_observation
|
|
(project_id, candidate_id, observed_at DESC);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_mcp_version_observation_run
|
|
ON awooop_mcp_version_observation (project_id, run_id, observed_at);
|
|
|
|
ALTER TABLE awooop_mcp_version_lifecycle_run ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE awooop_mcp_version_lifecycle_run FORCE ROW LEVEL SECURITY;
|
|
ALTER TABLE awooop_mcp_version_observation ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE awooop_mcp_version_observation FORCE ROW LEVEL SECURITY;
|
|
|
|
DROP POLICY IF EXISTS mcp_version_lifecycle_run_tenant
|
|
ON awooop_mcp_version_lifecycle_run;
|
|
CREATE POLICY mcp_version_lifecycle_run_tenant
|
|
ON awooop_mcp_version_lifecycle_run
|
|
USING (project_id = current_setting('app.project_id', TRUE))
|
|
WITH CHECK (project_id = current_setting('app.project_id', TRUE));
|
|
|
|
DROP POLICY IF EXISTS mcp_version_observation_tenant
|
|
ON awooop_mcp_version_observation;
|
|
CREATE POLICY mcp_version_observation_tenant
|
|
ON awooop_mcp_version_observation
|
|
USING (project_id = current_setting('app.project_id', TRUE))
|
|
WITH CHECK (project_id = current_setting('app.project_id', TRUE));
|
|
|
|
GRANT SELECT, INSERT, UPDATE ON awooop_mcp_version_lifecycle_run TO awooop_app;
|
|
GRANT SELECT, INSERT ON awooop_mcp_version_observation TO awooop_app;
|
|
GRANT SELECT, INSERT, UPDATE ON awooop_mcp_version_lifecycle_run TO awoooi;
|
|
GRANT SELECT, INSERT ON awooop_mcp_version_observation TO awoooi;
|
|
|
|
COMMENT ON TABLE awooop_mcp_version_lifecycle_run IS
|
|
'Durable controlled-apply lifecycle run receipts; additive and rollback-safe.';
|
|
COMMENT ON TABLE awooop_mcp_version_observation IS
|
|
'Normalized external MCP catalog observations; raw external content is forbidden.';
|
|
|
|
COMMIT;
|