20260802_450_agent_governance.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. """Add governed Agent identity, grants, credentials and decision evidence."""
  2. from alembic import op
  3. revision = "20260802_450"
  4. down_revision = "20260802_440"
  5. branch_labels = None
  6. depends_on = None
  7. def upgrade() -> None:
  8. op.execute(
  9. """
  10. CREATE TABLE public.governed_agents (
  11. uid UUID PRIMARY KEY,
  12. code VARCHAR(120) NOT NULL UNIQUE,
  13. name VARCHAR(300) NOT NULL,
  14. purpose VARCHAR(2000) NOT NULL,
  15. owner_uid UUID NOT NULL REFERENCES public.users(id),
  16. machine_subject VARCHAR(220) NOT NULL UNIQUE,
  17. business_domain_uids JSONB NOT NULL,
  18. environments JSONB NOT NULL,
  19. autonomy_level VARCHAR(30) NOT NULL CHECK (
  20. autonomy_level IN (
  21. 'read_only','suggestion','approval_execution',
  22. 'low_risk_automatic'
  23. )
  24. ),
  25. prompt_policy JSONB NOT NULL,
  26. status VARCHAR(20) NOT NULL CHECK (
  27. status IN ('draft','active','suspended','retired')
  28. ),
  29. current_version INTEGER NOT NULL DEFAULT 1 CHECK (current_version > 0),
  30. created_by UUID NOT NULL REFERENCES public.users(id),
  31. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  32. updated_by UUID NOT NULL REFERENCES public.users(id),
  33. updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  34. retired_at TIMESTAMPTZ,
  35. CHECK (jsonb_typeof(business_domain_uids) = 'array'),
  36. CHECK (jsonb_array_length(business_domain_uids) > 0),
  37. CHECK (jsonb_typeof(environments) = 'array'),
  38. CHECK (jsonb_array_length(environments) > 0),
  39. CHECK (jsonb_typeof(prompt_policy) = 'object')
  40. );
  41. CREATE INDEX idx_governed_agent_owner_status
  42. ON public.governed_agents(owner_uid, status, updated_at DESC);
  43. CREATE TABLE public.governed_agent_versions (
  44. uid UUID PRIMARY KEY,
  45. agent_uid UUID NOT NULL
  46. REFERENCES public.governed_agents(uid) ON DELETE RESTRICT,
  47. version INTEGER NOT NULL CHECK (version > 0),
  48. status VARCHAR(20) NOT NULL CHECK (
  49. status IN ('draft','published','superseded')
  50. ),
  51. definition JSONB NOT NULL,
  52. content_hash CHAR(64) NOT NULL,
  53. change_reason VARCHAR(1000) NOT NULL,
  54. created_by UUID NOT NULL REFERENCES public.users(id),
  55. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  56. published_by UUID REFERENCES public.users(id),
  57. published_at TIMESTAMPTZ,
  58. UNIQUE (agent_uid, version),
  59. CHECK (jsonb_typeof(definition) = 'object')
  60. );
  61. CREATE UNIQUE INDEX uq_governed_agent_published_version
  62. ON public.governed_agent_versions(agent_uid)
  63. WHERE status = 'published';
  64. CREATE TABLE public.agent_tool_grants (
  65. uid UUID PRIMARY KEY,
  66. agent_uid UUID NOT NULL
  67. REFERENCES public.governed_agents(uid) ON DELETE RESTRICT,
  68. interface_type VARCHAR(20) NOT NULL CHECK (interface_type IN ('api','mcp')),
  69. tool_name VARCHAR(200) NOT NULL,
  70. action VARCHAR(20) NOT NULL CHECK (action IN ('read','suggest','execute')),
  71. business_domain_uid UUID NOT NULL,
  72. environment VARCHAR(30) NOT NULL CHECK (
  73. environment IN ('development','test','production')
  74. ),
  75. risk_level VARCHAR(20) NOT NULL CHECK (
  76. risk_level IN ('low','medium','high','critical')
  77. ),
  78. requires_approval BOOLEAN NOT NULL DEFAULT FALSE,
  79. status VARCHAR(20) NOT NULL CHECK (status IN ('active','revoked')),
  80. created_by UUID NOT NULL REFERENCES public.users(id),
  81. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  82. revoked_by UUID REFERENCES public.users(id),
  83. revoked_at TIMESTAMPTZ
  84. );
  85. CREATE UNIQUE INDEX uq_agent_active_tool_grant
  86. ON public.agent_tool_grants(
  87. agent_uid, interface_type, tool_name, action,
  88. business_domain_uid, environment
  89. ) WHERE status = 'active';
  90. CREATE TABLE public.agent_credentials (
  91. uid UUID PRIMARY KEY,
  92. agent_uid UUID NOT NULL
  93. REFERENCES public.governed_agents(uid) ON DELETE RESTRICT,
  94. jti UUID NOT NULL UNIQUE,
  95. token_digest CHAR(64) NOT NULL UNIQUE,
  96. issued_by UUID NOT NULL REFERENCES public.users(id),
  97. issued_at TIMESTAMPTZ NOT NULL,
  98. expires_at TIMESTAMPTZ NOT NULL,
  99. status VARCHAR(20) NOT NULL CHECK (
  100. status IN ('active','revoked','expired')
  101. ),
  102. revoked_by UUID REFERENCES public.users(id),
  103. revoked_at TIMESTAMPTZ,
  104. CHECK (expires_at > issued_at),
  105. CHECK (expires_at <= issued_at + INTERVAL '15 minutes')
  106. );
  107. CREATE INDEX idx_agent_credential_active
  108. ON public.agent_credentials(agent_uid, status, expires_at);
  109. CREATE TABLE public.agent_action_requests (
  110. uid UUID PRIMARY KEY,
  111. agent_uid UUID NOT NULL
  112. REFERENCES public.governed_agents(uid) ON DELETE RESTRICT,
  113. agent_version INTEGER NOT NULL CHECK (agent_version > 0),
  114. grant_uid UUID REFERENCES public.agent_tool_grants(uid),
  115. correlation_id UUID NOT NULL,
  116. interface_type VARCHAR(20) NOT NULL CHECK (interface_type IN ('api','mcp')),
  117. tool_name VARCHAR(200) NOT NULL,
  118. action VARCHAR(20) NOT NULL CHECK (action IN ('read','suggest','execute')),
  119. business_domain_uid UUID NOT NULL,
  120. environment VARCHAR(30) NOT NULL CHECK (
  121. environment IN ('development','test','production')
  122. ),
  123. risk_level VARCHAR(20) NOT NULL CHECK (
  124. risk_level IN ('low','medium','high','critical')
  125. ),
  126. input_digest CHAR(64) NOT NULL,
  127. prompt_guard JSONB NOT NULL,
  128. evidence_refs JSONB NOT NULL DEFAULT '[]'::jsonb,
  129. decision VARCHAR(40) NOT NULL CHECK (
  130. decision IN (
  131. 'authorized','denied','pending_approval',
  132. 'approved_for_manual_execution','executed'
  133. )
  134. ),
  135. reason_code VARCHAR(80) NOT NULL,
  136. approval_task_uid UUID REFERENCES public.governance_tasks(uid),
  137. automatic_execution_allowed BOOLEAN NOT NULL DEFAULT FALSE,
  138. output_digest CHAR(64),
  139. current_version INTEGER NOT NULL DEFAULT 1 CHECK (current_version > 0),
  140. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  141. updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  142. CHECK (jsonb_typeof(prompt_guard) = 'object'),
  143. CHECK (jsonb_typeof(evidence_refs) = 'array'),
  144. CHECK (
  145. NOT automatic_execution_allowed OR
  146. (action = 'execute' AND risk_level = 'low' AND decision = 'authorized')
  147. )
  148. );
  149. CREATE INDEX idx_agent_action_decision
  150. ON public.agent_action_requests(agent_uid, decision, updated_at DESC);
  151. CREATE INDEX idx_agent_action_correlation
  152. ON public.agent_action_requests(correlation_id);
  153. CREATE TABLE public.agent_governance_events (
  154. uid UUID PRIMARY KEY,
  155. agent_uid UUID NOT NULL
  156. REFERENCES public.governed_agents(uid) ON DELETE RESTRICT,
  157. agent_version INTEGER NOT NULL CHECK (agent_version > 0),
  158. action VARCHAR(60) NOT NULL,
  159. actor_subject VARCHAR(220) NOT NULL,
  160. payload JSONB NOT NULL DEFAULT '{}'::jsonb,
  161. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  162. CHECK (jsonb_typeof(payload) = 'object')
  163. );
  164. CREATE INDEX idx_agent_governance_timeline
  165. ON public.agent_governance_events(agent_uid, created_at, uid);
  166. """
  167. )
  168. def downgrade() -> None:
  169. raise RuntimeError(
  170. "Agent identities, credentials and decision evidence are retained; "
  171. "downgrade requires an approved archival migration"
  172. )