@creatornader/agent-bridge 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/CHANGELOG.md +222 -1
  2. package/README.md +312 -12
  3. package/ROADMAP.md +88 -7
  4. package/SECURITY.md +78 -5
  5. package/SKILL.md +108 -1
  6. package/dist/archive.d.ts +2 -2
  7. package/dist/archive.js +2 -2
  8. package/dist/{bridge-service-hX1ZQp4W.d.ts → bridge-service-CNBKdYXh.d.ts} +2 -1
  9. package/dist/{bridge-store-DPVSCwfs.d.ts → bridge-store-ByJ6CaJk.d.ts} +3 -0
  10. package/dist/bridge.d.ts +25 -8
  11. package/dist/bridge.js +19 -9
  12. package/dist/{chunk-MBPZXTJA.js → chunk-7VJI2MXI.js} +285 -42
  13. package/dist/chunk-7VJI2MXI.js.map +1 -0
  14. package/dist/{chunk-4VB44KJU.js → chunk-AWE3DFZV.js} +3 -2
  15. package/dist/chunk-AWE3DFZV.js.map +1 -0
  16. package/dist/{chunk-GADH244D.js → chunk-E4OWHGUE.js} +16 -5
  17. package/dist/chunk-E4OWHGUE.js.map +1 -0
  18. package/dist/{chunk-NCO7RLBM.js → chunk-GTK4KOED.js} +108 -8
  19. package/dist/chunk-GTK4KOED.js.map +1 -0
  20. package/dist/{chunk-SPSMZOCJ.js → chunk-HUHG7XTB.js} +6 -5
  21. package/dist/chunk-HUHG7XTB.js.map +1 -0
  22. package/dist/{chunk-5GMB3A77.js → chunk-IVG4XWWS.js} +26 -14
  23. package/dist/chunk-IVG4XWWS.js.map +1 -0
  24. package/dist/{chunk-WAI2IYIQ.js → chunk-P635FAN4.js} +50 -7
  25. package/dist/chunk-P635FAN4.js.map +1 -0
  26. package/dist/{chunk-45BZGB77.js → chunk-QNXHDOJ5.js} +218 -24
  27. package/dist/chunk-QNXHDOJ5.js.map +1 -0
  28. package/dist/{chunk-UAHMP6GC.js → chunk-QPC6JHA3.js} +10 -7
  29. package/dist/chunk-QPC6JHA3.js.map +1 -0
  30. package/dist/{chunk-Q6PPMXJC.js → chunk-SN7PIVDF.js} +377 -21
  31. package/dist/chunk-SN7PIVDF.js.map +1 -0
  32. package/dist/{chunk-A6W4QALF.js → chunk-TC6A7GMF.js} +2 -2
  33. package/dist/{chunk-2DFNOLMO.js → chunk-XDNT6ZNZ.js} +9 -6
  34. package/dist/chunk-XDNT6ZNZ.js.map +1 -0
  35. package/dist/{chunk-CEXBMOI5.js → chunk-Z6JP6MYD.js} +2 -2
  36. package/dist/cli.js +5867 -438
  37. package/dist/cli.js.map +1 -1
  38. package/dist/client-runtime.d.ts +4 -2
  39. package/dist/client-runtime.js +7 -7
  40. package/dist/contracts/generate.js +1 -1
  41. package/dist/dr.d.ts +9 -2
  42. package/dist/dr.js +6 -4
  43. package/dist/{gateway-CggQ8vTC.d.ts → gateway-CW-61lvs.d.ts} +27 -3
  44. package/dist/gateway-main.js +71 -7
  45. package/dist/gateway-main.js.map +1 -1
  46. package/dist/gateway.d.ts +3 -3
  47. package/dist/gateway.js +3 -3
  48. package/dist/index.js +8 -8
  49. package/dist/{postgres-bridge-store-Bfd3hRId.d.ts → postgres-bridge-store-C9KG9Z69.d.ts} +1 -1
  50. package/dist/postgres-production-preflight.d.ts +32 -0
  51. package/dist/postgres-production-preflight.js +249 -0
  52. package/dist/postgres-production-preflight.js.map +1 -0
  53. package/dist/server.js +8 -8
  54. package/dist/sqlite-edge-store-CdMIOrho.d.ts +148 -0
  55. package/dist/sqlite.d.ts +19 -80
  56. package/dist/sqlite.js +11 -3
  57. package/docs/architecture-v2.md +292 -15
  58. package/docs/decisions/0004-client-lifecycle-and-endpoint-migration.md +204 -0
  59. package/docs/deployment.md +349 -0
  60. package/docs/troubleshooting.md +23 -0
  61. package/llms.txt +69 -1
  62. package/openapi/agent-bridge-v2.openapi.json +1151 -124
  63. package/package.json +10 -2
  64. package/schemas/agent-bridge-v2.capabilities.json +26 -1
  65. package/schemas/agent-bridge-v2.mcp.json +11 -1
  66. package/schemas/agent-bridge-v2.schema.json +167 -0
  67. package/sql/migrations/007_runtime_role.sql +8 -4
  68. package/sql/migrations/013_row_isolation.sql +71 -8
  69. package/sql/migrations/014_owner_control_plane.sql +124 -31
  70. package/sql/migrations/015_portable_archives.sql +143 -41
  71. package/sql/migrations/016_native_dr_fence.sql +102 -35
  72. package/sql/migrations/017_gateway_authority_binding.sql +99 -0
  73. package/sql/migrations/018_endpoint_migration_challenges.sql +1054 -0
  74. package/sql/migrations/019_endpoint_migration_same_successor.sql +348 -0
  75. package/sql/migrations/020_managed_authority_compat.sql +698 -0
  76. package/sql/migrations/021_native_backup_reader.sql +421 -0
  77. package/sql/setup.sql +1 -3
  78. package/dist/chunk-2DFNOLMO.js.map +0 -1
  79. package/dist/chunk-45BZGB77.js.map +0 -1
  80. package/dist/chunk-4VB44KJU.js.map +0 -1
  81. package/dist/chunk-5GMB3A77.js.map +0 -1
  82. package/dist/chunk-GADH244D.js.map +0 -1
  83. package/dist/chunk-MBPZXTJA.js.map +0 -1
  84. package/dist/chunk-NCO7RLBM.js.map +0 -1
  85. package/dist/chunk-Q6PPMXJC.js.map +0 -1
  86. package/dist/chunk-SPSMZOCJ.js.map +0 -1
  87. package/dist/chunk-UAHMP6GC.js.map +0 -1
  88. package/dist/chunk-WAI2IYIQ.js.map +0 -1
  89. /package/dist/{chunk-A6W4QALF.js.map → chunk-TC6A7GMF.js.map} +0 -0
  90. /package/dist/{chunk-CEXBMOI5.js.map → chunk-Z6JP6MYD.js.map} +0 -0
@@ -18,11 +18,73 @@ begin
18
18
  role_name
19
19
  );
20
20
  end if;
21
- execute format(
22
- 'alter role %I nologin nosuperuser nocreatedb nocreaterole noreplication nobypassrls',
23
- role_name
24
- );
25
- execute format('grant %I to %I with admin option',role_name,current_user);
21
+ if exists (
22
+ select 1 from pg_roles where rolname=role_name and (
23
+ rolcanlogin or not rolinherit or rolsuper or rolcreatedb or rolcreaterole
24
+ or rolreplication or rolbypassrls or rolconnlimit<>-1
25
+ )
26
+ ) then
27
+ raise exception 'Agent Bridge control role % has unsafe attributes',role_name;
28
+ end if;
29
+ if current_setting('server_version_num')::integer>=160000 then
30
+ if not exists (
31
+ select 1 from pg_catalog.pg_auth_members membership
32
+ join pg_catalog.pg_roles granted on granted.oid=membership.roleid
33
+ join pg_catalog.pg_roles member on member.oid=membership.member
34
+ where granted.rolname=role_name and member.rolname=current_user
35
+ and membership.admin_option
36
+ ) then
37
+ execute format(
38
+ 'grant %I to %I with admin true,inherit true,set true',role_name,current_user
39
+ );
40
+ elsif not exists (
41
+ select 1 from pg_catalog.pg_auth_members membership
42
+ join pg_catalog.pg_roles granted on granted.oid=membership.roleid
43
+ join pg_catalog.pg_roles member on member.oid=membership.member
44
+ where granted.rolname=role_name and member.rolname=current_user
45
+ and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
46
+ and coalesce((to_jsonb(membership)->>'set_option')::boolean,true)
47
+ ) then
48
+ execute format('grant %I to %I with inherit true,set true',role_name,current_user);
49
+ end if;
50
+ elsif not exists (
51
+ select 1 from pg_catalog.pg_auth_members membership
52
+ join pg_catalog.pg_roles granted on granted.oid=membership.roleid
53
+ join pg_catalog.pg_roles member on member.oid=membership.member
54
+ where granted.rolname=role_name and member.rolname=current_user
55
+ and membership.admin_option
56
+ ) then
57
+ execute format('grant %I to %I with admin option',role_name,current_user);
58
+ end if;
59
+ if not exists (
60
+ select 1 from pg_catalog.pg_auth_members membership
61
+ join pg_catalog.pg_roles granted on granted.oid=membership.roleid
62
+ join pg_catalog.pg_roles member on member.oid=membership.member
63
+ where granted.rolname=role_name and member.rolname=current_user
64
+ and membership.admin_option
65
+ ) or not exists (
66
+ select 1 from pg_catalog.pg_auth_members membership
67
+ join pg_catalog.pg_roles granted on granted.oid=membership.roleid
68
+ join pg_catalog.pg_roles member on member.oid=membership.member
69
+ where granted.rolname=role_name and member.rolname=current_user
70
+ and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
71
+ and coalesce((to_jsonb(membership)->>'set_option')::boolean,true)
72
+ ) or exists (
73
+ select 1 from pg_catalog.pg_auth_members membership
74
+ join pg_catalog.pg_roles granted on granted.oid=membership.roleid
75
+ join pg_catalog.pg_roles member on member.oid=membership.member
76
+ where granted.rolname=role_name and member.rolname=current_user
77
+ and not (
78
+ (membership.grantor=10 and membership.admin_option
79
+ and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
80
+ =coalesce((to_jsonb(membership)->>'set_option')::boolean,true))
81
+ or (membership.grantor=member.oid
82
+ and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
83
+ and coalesce((to_jsonb(membership)->>'set_option')::boolean,true))
84
+ )
85
+ ) then
86
+ raise exception 'Agent Bridge control role % has unsafe owner authority',role_name;
87
+ end if;
26
88
  end loop;
27
89
  end
28
90
  $roles$;
@@ -144,27 +206,38 @@ begin
144
206
  where event.member_role=session_user and event.control_role in ('operator','auditor')
145
207
  order by event.control_role,event.sequence desc
146
208
  ), direct_membership as (
147
- select granted.rolname control_role,membership.admin_option,
148
- coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true) inherit_option,
149
- coalesce((to_jsonb(membership)->>'set_option')::boolean,true) set_option,
150
- grantor.rolname grantor_role
209
+ select granted.rolname control_role,
210
+ bool_or(membership.admin_option) admin_option,
211
+ bool_or(coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)) inherit_option,
212
+ bool_or(coalesce((to_jsonb(membership)->>'set_option')::boolean,true)) set_option,
213
+ bool_and(case when member.rolname=schema_owner then
214
+ (membership.grantor=10 and membership.admin_option
215
+ and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
216
+ =coalesce((to_jsonb(membership)->>'set_option')::boolean,true))
217
+ or (membership.grantor=member.oid
218
+ and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
219
+ and coalesce((to_jsonb(membership)->>'set_option')::boolean,true))
220
+ else membership.grantor=(select oid from pg_catalog.pg_roles where rolname=schema_owner)
221
+ and not membership.admin_option
222
+ and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
223
+ and coalesce((to_jsonb(membership)->>'set_option')::boolean,true) end) grants_valid
151
224
  from pg_catalog.pg_auth_members membership
152
225
  join pg_catalog.pg_roles granted on granted.oid=membership.roleid
153
226
  join pg_catalog.pg_roles member on member.oid=membership.member
154
- join pg_catalog.pg_roles grantor on grantor.oid=membership.grantor
155
227
  where member.rolname=session_user and granted.rolname in (operator_role,auditor_role)
228
+ group by granted.rolname
156
229
  )
157
230
  select
158
231
  coalesce(bool_or(membership.control_role=operator_role
159
232
  and membership.inherit_option and membership.set_option
160
- and membership.grantor_role=schema_owner and (
233
+ and membership.grants_valid and (
161
234
  (session_user=schema_owner and membership.admin_option)
162
235
  or (latest.control_role='operator' and latest.action='register'
163
236
  and not membership.admin_option)
164
237
  )),false),
165
238
  coalesce(bool_or(membership.control_role=auditor_role
166
239
  and membership.inherit_option and membership.set_option
167
- and membership.grantor_role=schema_owner and (
240
+ and membership.grants_valid and (
168
241
  (session_user=schema_owner and membership.admin_option)
169
242
  or (latest.control_role='auditor' and latest.action='register'
170
243
  and not membership.admin_option)
@@ -937,15 +1010,15 @@ language sql stable set search_path = '' set timezone = 'UTC' as $$
937
1010
  from pg_catalog.pg_roles role where role.rolname in (select role_name from control_roles)
938
1011
  union all
939
1012
  select 'membership',granted.rolname||'->'||member.rolname,
940
- grantor.rolname||':'||membership.admin_option::text||':'||
941
- coalesce(to_jsonb(membership)->>'inherit_option','')||':'||
942
- coalesce(to_jsonb(membership)->>'set_option','')
1013
+ bool_or(membership.admin_option)::text||':'||
1014
+ bool_or(coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true))::text||':'||
1015
+ bool_or(coalesce((to_jsonb(membership)->>'set_option')::boolean,true))::text
943
1016
  from pg_catalog.pg_auth_members membership
944
1017
  join pg_catalog.pg_roles granted on granted.oid=membership.roleid
945
1018
  join pg_catalog.pg_roles member on member.oid=membership.member
946
- join pg_catalog.pg_roles grantor on grantor.oid=membership.grantor
947
1019
  where member.rolname in (select role_name from control_roles)
948
1020
  or granted.rolname=(select owner_role from names)
1021
+ group by granted.rolname,member.rolname
949
1022
  union all
950
1023
  select 'schema',namespace.nspname,pg_catalog.pg_get_userbyid(namespace.nspowner)
951
1024
  from pg_catalog.pg_namespace namespace where namespace.nspname='agent_bridge'
@@ -1106,27 +1179,42 @@ returns boolean language sql stable security definer set search_path = '' as $$
1106
1179
  ), active_registry as (
1107
1180
  select member_role,control_role from latest_registry where action='register'
1108
1181
  ), expected_memberships as (
1109
- select names.owner_role granted_role,names.schema_owner member_role,true admin_option
1182
+ select names.owner_role granted_role,names.schema_owner member_role,
1183
+ true admin_option,true inherit_option,true set_option
1110
1184
  from names
1111
- union all select names.operator_role,names.schema_owner,true from names
1112
- union all select names.auditor_role,names.schema_owner,true from names
1185
+ union all select names.operator_role,names.schema_owner,true,true,true from names
1186
+ union all select names.auditor_role,names.schema_owner,true,true,true from names
1113
1187
  union all
1114
1188
  select case registry.control_role when 'operator' then names.operator_role
1115
- else names.auditor_role end,registry.member_role,false
1189
+ else names.auditor_role end,registry.member_role,false,true,true
1116
1190
  from active_registry registry cross join names
1117
- ), actual_memberships as (
1191
+ ), raw_memberships as (
1118
1192
  select granted.rolname::name granted_role,member.rolname::name member_role,
1119
1193
  membership.admin_option,
1120
1194
  coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true) inherit_option,
1121
1195
  coalesce((to_jsonb(membership)->>'set_option')::boolean,true) set_option,
1122
- grantor.rolname::name grantor_role
1196
+ membership.grantor,member.oid member_oid
1123
1197
  from pg_catalog.pg_auth_members membership
1124
1198
  join pg_catalog.pg_roles granted on granted.oid=membership.roleid
1125
1199
  join pg_catalog.pg_roles member on member.oid=membership.member
1126
- join pg_catalog.pg_roles grantor on grantor.oid=membership.grantor
1127
1200
  where granted.rolname in (
1128
1201
  (select owner_role from names),(select operator_role from names),(select auditor_role from names)
1129
1202
  )
1203
+ ), actual_memberships as (
1204
+ select membership.granted_role,membership.member_role,
1205
+ bool_or(membership.admin_option) admin_option,
1206
+ bool_or(membership.inherit_option) inherit_option,
1207
+ bool_or(membership.set_option) set_option,
1208
+ bool_and(case when membership.member_role=names.schema_owner then
1209
+ (membership.grantor=10 and membership.admin_option
1210
+ and membership.inherit_option=membership.set_option)
1211
+ or (membership.grantor=membership.member_oid
1212
+ and membership.inherit_option and membership.set_option)
1213
+ else membership.grantor=(select oid from pg_catalog.pg_roles where rolname=names.schema_owner)
1214
+ and not membership.admin_option
1215
+ and membership.inherit_option and membership.set_option end) grants_valid
1216
+ from raw_memberships membership cross join names
1217
+ group by membership.granted_role,membership.member_role
1130
1218
  ), authority_closure as (
1131
1219
  select control_role.granted_role,candidate.rolname::name member_role
1132
1220
  from names
@@ -1145,16 +1233,14 @@ returns boolean language sql stable security definer set search_path = '' as $$
1145
1233
  ) from agent_bridge.owner_control_attestations attestation
1146
1234
  where attestation.name='owner-control-v1')
1147
1235
  and not exists (
1148
- (select granted_role,member_role,admin_option from actual_memberships
1149
- except select granted_role,member_role,admin_option from expected_memberships)
1236
+ (select granted_role,member_role,admin_option,inherit_option,set_option from actual_memberships
1237
+ except select granted_role,member_role,admin_option,inherit_option,set_option from expected_memberships)
1150
1238
  union all
1151
- (select granted_role,member_role,admin_option from expected_memberships
1152
- except select granted_role,member_role,admin_option from actual_memberships)
1239
+ (select granted_role,member_role,admin_option,inherit_option,set_option from expected_memberships
1240
+ except select granted_role,member_role,admin_option,inherit_option,set_option from actual_memberships)
1153
1241
  )
1154
1242
  and not exists (
1155
- select 1 from actual_memberships membership,names
1156
- where not membership.inherit_option or not membership.set_option
1157
- or membership.grantor_role<>names.schema_owner
1243
+ select 1 from actual_memberships membership where not membership.grants_valid
1158
1244
  )
1159
1245
  and not exists (
1160
1246
  select 1 from active_registry registry
@@ -1216,6 +1302,7 @@ declare
1216
1302
  membership_record record;
1217
1303
  relation_record record;
1218
1304
  begin
1305
+ execute format('grant usage,create on schema agent_bridge to %I',owner_role);
1219
1306
  execute format('alter table agent_bridge.control_requests owner to %I',owner_role);
1220
1307
  execute format('alter table agent_bridge.control_events owner to %I',owner_role);
1221
1308
  execute format('alter table agent_bridge.owner_control_attestations owner to %I',owner_role);
@@ -1230,6 +1317,7 @@ begin
1230
1317
  'control_revoke_credential(uuid,uuid,text)','owner_control_catalog_definition()',
1231
1318
  'owner_control_plane_ready()'
1232
1319
  ] loop execute format('alter function agent_bridge.%s owner to %I',role_name,owner_role); end loop;
1320
+ execute format('revoke create on schema agent_bridge from %I',owner_role);
1233
1321
 
1234
1322
  for membership_record in
1235
1323
  select granted.rolname granted_name,member.rolname member_name
@@ -1287,7 +1375,7 @@ begin
1287
1375
  execute format('grant usage on schema agent_bridge to %I,%I,%I',owner_role,operator_role,auditor_role);
1288
1376
  execute format('grant select,insert,update(request_id) on agent_bridge.control_requests to %I',owner_role);
1289
1377
  execute format('grant insert on agent_bridge.control_events to %I',owner_role);
1290
- execute format('grant select on agent_bridge.owner_control_attestations to %I',owner_role);
1378
+ execute format('grant select,insert on agent_bridge.owner_control_attestations to %I',owner_role);
1291
1379
  execute format('grant select,insert on agent_bridge.control_membership_events to %I',owner_role);
1292
1380
  execute format('grant usage on sequence agent_bridge.control_events_sequence_seq to %I',owner_role);
1293
1381
  execute format('grant usage on sequence agent_bridge.control_membership_events_sequence_seq to %I',owner_role);
@@ -1306,6 +1394,7 @@ begin
1306
1394
  execute format('grant execute on function agent_bridge.control_revoke_credential(uuid,uuid,text) to %I',operator_role);
1307
1395
  execute format('grant execute on function agent_bridge.control_credential_inventory(text,timestamp with time zone,uuid,integer) to %I,%I',operator_role,auditor_role);
1308
1396
  execute format('grant execute on function agent_bridge.owner_control_plane_ready() to %I',runtime_role);
1397
+ execute format('grant execute on function agent_bridge.owner_control_plane_ready() to %I',current_user);
1309
1398
  execute format('revoke execute on function agent_bridge.replace_credential(uuid,character,text[],text,text,timestamp with time zone,timestamp with time zone,text,uuid) from %I,%I,%I',operator_role,auditor_role,runtime_role);
1310
1399
  execute format('revoke execute on function agent_bridge.revoke_credential(uuid,text,text,uuid) from %I,%I,%I',operator_role,auditor_role,runtime_role);
1311
1400
  foreach role_name in array array['anon','authenticated'] loop
@@ -1632,8 +1721,12 @@ begin
1632
1721
  end
1633
1722
  $preflight$;
1634
1723
 
1724
+ select set_config(
1725
+ 'role','agent_bridge_control_owner_'||substr(md5(current_database()),1,16),false
1726
+ );
1635
1727
  insert into agent_bridge.owner_control_attestations(name,catalog_definition)
1636
1728
  values('owner-control-v1',agent_bridge.owner_control_catalog_definition());
1729
+ reset role;
1637
1730
 
1638
1731
  insert into agent_bridge.schema_migrations(version,name,checksum)
1639
1732
  values(14,'owner_control_plane','__AGENT_BRIDGE_MIGRATION_CHECKSUM__');
@@ -26,11 +26,73 @@ begin
26
26
  role_name
27
27
  );
28
28
  end if;
29
- execute format(
30
- 'alter role %I nologin nosuperuser nocreatedb nocreaterole noreplication nobypassrls',
31
- role_name
32
- );
33
- execute format('grant %I to %I with admin option',role_name,current_user);
29
+ if exists (
30
+ select 1 from pg_catalog.pg_roles where rolname=role_name and (
31
+ rolcanlogin or not rolinherit or rolsuper or rolcreatedb or rolcreaterole
32
+ or rolreplication or rolbypassrls or rolconnlimit<>-1
33
+ )
34
+ ) then
35
+ raise exception 'Agent Bridge archive role has unsafe attributes';
36
+ end if;
37
+ if current_setting('server_version_num')::integer>=160000 then
38
+ if not exists (
39
+ select 1 from pg_catalog.pg_auth_members membership
40
+ join pg_catalog.pg_roles granted on granted.oid=membership.roleid
41
+ join pg_catalog.pg_roles member on member.oid=membership.member
42
+ where granted.rolname=role_name and member.rolname=current_user
43
+ and membership.admin_option
44
+ ) then
45
+ execute format(
46
+ 'grant %I to %I with admin true,inherit true,set true',role_name,current_user
47
+ );
48
+ elsif not exists (
49
+ select 1 from pg_catalog.pg_auth_members membership
50
+ join pg_catalog.pg_roles granted on granted.oid=membership.roleid
51
+ join pg_catalog.pg_roles member on member.oid=membership.member
52
+ where granted.rolname=role_name and member.rolname=current_user
53
+ and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
54
+ and coalesce((to_jsonb(membership)->>'set_option')::boolean,true)
55
+ ) then
56
+ execute format('grant %I to %I with inherit true,set true',role_name,current_user);
57
+ end if;
58
+ elsif not exists (
59
+ select 1 from pg_catalog.pg_auth_members membership
60
+ join pg_catalog.pg_roles granted on granted.oid=membership.roleid
61
+ join pg_catalog.pg_roles member on member.oid=membership.member
62
+ where granted.rolname=role_name and member.rolname=current_user
63
+ and membership.admin_option
64
+ ) then
65
+ execute format('grant %I to %I with admin option',role_name,current_user);
66
+ end if;
67
+ if not exists (
68
+ select 1 from pg_catalog.pg_auth_members membership
69
+ join pg_catalog.pg_roles granted on granted.oid=membership.roleid
70
+ join pg_catalog.pg_roles member on member.oid=membership.member
71
+ where granted.rolname=role_name and member.rolname=current_user
72
+ and membership.admin_option
73
+ ) or not exists (
74
+ select 1 from pg_catalog.pg_auth_members membership
75
+ join pg_catalog.pg_roles granted on granted.oid=membership.roleid
76
+ join pg_catalog.pg_roles member on member.oid=membership.member
77
+ where granted.rolname=role_name and member.rolname=current_user
78
+ and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
79
+ and coalesce((to_jsonb(membership)->>'set_option')::boolean,true)
80
+ ) or exists (
81
+ select 1 from pg_catalog.pg_auth_members membership
82
+ join pg_catalog.pg_roles granted on granted.oid=membership.roleid
83
+ join pg_catalog.pg_roles member on member.oid=membership.member
84
+ where granted.rolname=role_name and member.rolname=current_user
85
+ and not (
86
+ (membership.grantor=10 and membership.admin_option
87
+ and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
88
+ =coalesce((to_jsonb(membership)->>'set_option')::boolean,true))
89
+ or (membership.grantor=member.oid
90
+ and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
91
+ and coalesce((to_jsonb(membership)->>'set_option')::boolean,true))
92
+ )
93
+ ) then
94
+ raise exception 'Agent Bridge archive role has unsafe owner authority';
95
+ end if;
34
96
  end
35
97
  $roles$;
36
98
 
@@ -215,19 +277,28 @@ begin
215
277
  select event.action from agent_bridge.archive_membership_events event
216
278
  where event.member_role=session_user order by event.sequence desc limit 1
217
279
  ), direct_membership as (
218
- select membership.admin_option,
219
- coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true) inherit_option,
220
- coalesce((to_jsonb(membership)->>'set_option')::boolean,true) set_option,
221
- grantor.rolname grantor_role
280
+ select bool_or(membership.admin_option) admin_option,
281
+ bool_or(coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)) inherit_option,
282
+ bool_or(coalesce((to_jsonb(membership)->>'set_option')::boolean,true)) set_option,
283
+ bool_and(case when member.rolname=schema_owner then
284
+ (membership.grantor=10 and membership.admin_option
285
+ and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
286
+ =coalesce((to_jsonb(membership)->>'set_option')::boolean,true))
287
+ or (membership.grantor=member.oid
288
+ and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
289
+ and coalesce((to_jsonb(membership)->>'set_option')::boolean,true))
290
+ else membership.grantor=(select oid from pg_catalog.pg_roles where rolname=schema_owner)
291
+ and not membership.admin_option
292
+ and coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true)
293
+ and coalesce((to_jsonb(membership)->>'set_option')::boolean,true) end) grants_valid
222
294
  from pg_catalog.pg_auth_members membership
223
295
  join pg_catalog.pg_roles granted on granted.oid=membership.roleid
224
296
  join pg_catalog.pg_roles member on member.oid=membership.member
225
- join pg_catalog.pg_roles grantor on grantor.oid=membership.grantor
226
297
  where member.rolname=session_user and granted.rolname=archive_role
227
298
  )
228
299
  select coalesce(bool_or(
229
300
  membership.inherit_option and membership.set_option
230
- and membership.grantor_role=schema_owner and (
301
+ and membership.grants_valid and (
231
302
  (session_user=schema_owner and membership.admin_option)
232
303
  or (latest.action='register' and not membership.admin_option)
233
304
  )
@@ -1396,15 +1467,15 @@ returns text language sql stable set search_path = '' set timezone = 'UTC' as $$
1396
1467
  from pg_catalog.pg_roles role where role.rolname=(select archive_role from names)
1397
1468
  union all
1398
1469
  select 'owner_membership',granted.rolname||'->'||member.rolname,
1399
- grantor.rolname||':'||membership.admin_option::text||':'||
1400
- coalesce(to_jsonb(membership)->>'inherit_option','')||':'||
1401
- coalesce(to_jsonb(membership)->>'set_option','')
1470
+ bool_or(membership.admin_option)::text||':'||
1471
+ bool_or(coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true))::text||':'||
1472
+ bool_or(coalesce((to_jsonb(membership)->>'set_option')::boolean,true))::text
1402
1473
  from pg_catalog.pg_auth_members membership
1403
1474
  join pg_catalog.pg_roles granted on granted.oid=membership.roleid
1404
1475
  join pg_catalog.pg_roles member on member.oid=membership.member
1405
- join pg_catalog.pg_roles grantor on grantor.oid=membership.grantor
1406
1476
  where granted.rolname=(select archive_role from names)
1407
1477
  and member.oid=(select schema_owner from names)
1478
+ group by granted.rolname,member.rolname
1408
1479
  union all
1409
1480
  select 'schema','agent_bridge',pg_catalog.pg_get_userbyid(namespace.nspowner)
1410
1481
  from pg_catalog.pg_namespace namespace where namespace.oid=(select schema_oid from names)
@@ -1542,19 +1613,35 @@ returns boolean language sql stable security definer set search_path = '' as $$
1542
1613
  ), active_registry as (
1543
1614
  select member_role from latest_registry where action='register'
1544
1615
  ), expected_memberships as (
1545
- select names.archive_role granted_role,names.schema_owner member_role,true admin_option from names
1546
- union all select names.archive_role,registry.member_role,false from active_registry registry cross join names
1547
- ), actual_memberships as (
1616
+ select names.archive_role granted_role,names.schema_owner member_role,
1617
+ true admin_option,true inherit_option,true set_option from names
1618
+ union all select names.archive_role,registry.member_role,false,true,true
1619
+ from active_registry registry cross join names
1620
+ ), raw_memberships as (
1548
1621
  select granted.rolname::name granted_role,member.rolname::name member_role,
1549
1622
  membership.admin_option,
1550
1623
  coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true) inherit_option,
1551
1624
  coalesce((to_jsonb(membership)->>'set_option')::boolean,true) set_option,
1552
- grantor.rolname::name grantor_role
1625
+ membership.grantor,member.oid member_oid
1553
1626
  from pg_catalog.pg_auth_members membership
1554
1627
  join pg_catalog.pg_roles granted on granted.oid=membership.roleid
1555
1628
  join pg_catalog.pg_roles member on member.oid=membership.member
1556
- join pg_catalog.pg_roles grantor on grantor.oid=membership.grantor
1557
1629
  where granted.rolname=(select archive_role from names)
1630
+ ), actual_memberships as (
1631
+ select membership.granted_role,membership.member_role,
1632
+ bool_or(membership.admin_option) admin_option,
1633
+ bool_or(membership.inherit_option) inherit_option,
1634
+ bool_or(membership.set_option) set_option,
1635
+ bool_and(case when membership.member_role=names.schema_owner then
1636
+ (membership.grantor=10 and membership.admin_option
1637
+ and membership.inherit_option=membership.set_option)
1638
+ or (membership.grantor=membership.member_oid
1639
+ and membership.inherit_option and membership.set_option)
1640
+ else membership.grantor=(select oid from pg_catalog.pg_roles where rolname=names.schema_owner)
1641
+ and not membership.admin_option
1642
+ and membership.inherit_option and membership.set_option end) grants_valid
1643
+ from raw_memberships membership cross join names
1644
+ group by membership.granted_role,membership.member_role
1558
1645
  )
1559
1646
  select current_setting('server_version_num')::integer/10000=any(array[15,16,17,18])
1560
1647
  and exists(select 1 from pg_catalog.pg_roles role,names where role.rolname=names.archive_role
@@ -1565,15 +1652,13 @@ returns boolean language sql stable security definer set search_path = '' as $$
1565
1652
  ) from agent_bridge.portable_archive_attestations attestation
1566
1653
  where attestation.name='portable-archive-v1')
1567
1654
  and not exists(
1568
- (select granted_role,member_role,admin_option from actual_memberships
1569
- except select granted_role,member_role,admin_option from expected_memberships)
1655
+ (select granted_role,member_role,admin_option,inherit_option,set_option from actual_memberships
1656
+ except select granted_role,member_role,admin_option,inherit_option,set_option from expected_memberships)
1570
1657
  union all
1571
- (select granted_role,member_role,admin_option from expected_memberships
1572
- except select granted_role,member_role,admin_option from actual_memberships)
1658
+ (select granted_role,member_role,admin_option,inherit_option,set_option from expected_memberships
1659
+ except select granted_role,member_role,admin_option,inherit_option,set_option from actual_memberships)
1573
1660
  )
1574
- and not exists(select 1 from actual_memberships membership,names
1575
- where not membership.inherit_option or not membership.set_option
1576
- or membership.grantor_role<>names.schema_owner)
1661
+ and not exists(select 1 from actual_memberships membership where not membership.grants_valid)
1577
1662
  and not exists(select 1 from active_registry registry
1578
1663
  left join pg_catalog.pg_roles role on role.rolname=registry.member_role
1579
1664
  where role.oid is null or not role.rolcanlogin or role.rolsuper or role.rolcreaterole
@@ -1706,27 +1791,42 @@ returns boolean language sql stable security definer set search_path = '' as $$
1706
1791
  ), active_registry as (
1707
1792
  select member_role,control_role from latest_registry where action='register'
1708
1793
  ), expected_memberships as (
1709
- select names.owner_role granted_role,names.schema_owner member_role,true admin_option
1794
+ select names.owner_role granted_role,names.schema_owner member_role,
1795
+ true admin_option,true inherit_option,true set_option
1710
1796
  from names
1711
- union all select names.operator_role,names.schema_owner,true from names
1712
- union all select names.auditor_role,names.schema_owner,true from names
1797
+ union all select names.operator_role,names.schema_owner,true,true,true from names
1798
+ union all select names.auditor_role,names.schema_owner,true,true,true from names
1713
1799
  union all
1714
1800
  select case registry.control_role when 'operator' then names.operator_role
1715
- else names.auditor_role end,registry.member_role,false
1801
+ else names.auditor_role end,registry.member_role,false,true,true
1716
1802
  from active_registry registry cross join names
1717
- ), actual_memberships as (
1803
+ ), raw_memberships as (
1718
1804
  select granted.rolname::name granted_role,member.rolname::name member_role,
1719
1805
  membership.admin_option,
1720
1806
  coalesce((to_jsonb(membership)->>'inherit_option')::boolean,true) inherit_option,
1721
1807
  coalesce((to_jsonb(membership)->>'set_option')::boolean,true) set_option,
1722
- grantor.rolname::name grantor_role
1808
+ membership.grantor,member.oid member_oid
1723
1809
  from pg_catalog.pg_auth_members membership
1724
1810
  join pg_catalog.pg_roles granted on granted.oid=membership.roleid
1725
1811
  join pg_catalog.pg_roles member on member.oid=membership.member
1726
- join pg_catalog.pg_roles grantor on grantor.oid=membership.grantor
1727
1812
  where granted.rolname in (
1728
1813
  (select owner_role from names),(select operator_role from names),(select auditor_role from names)
1729
1814
  )
1815
+ ), actual_memberships as (
1816
+ select membership.granted_role,membership.member_role,
1817
+ bool_or(membership.admin_option) admin_option,
1818
+ bool_or(membership.inherit_option) inherit_option,
1819
+ bool_or(membership.set_option) set_option,
1820
+ bool_and(case when membership.member_role=names.schema_owner then
1821
+ (membership.grantor=10 and membership.admin_option
1822
+ and membership.inherit_option=membership.set_option)
1823
+ or (membership.grantor=membership.member_oid
1824
+ and membership.inherit_option and membership.set_option)
1825
+ else membership.grantor=(select oid from pg_catalog.pg_roles where rolname=names.schema_owner)
1826
+ and not membership.admin_option
1827
+ and membership.inherit_option and membership.set_option end) grants_valid
1828
+ from raw_memberships membership cross join names
1829
+ group by membership.granted_role,membership.member_role
1730
1830
  ), authority_closure as (
1731
1831
  select control_role.granted_role,candidate.rolname::name member_role
1732
1832
  from names
@@ -1745,16 +1845,14 @@ returns boolean language sql stable security definer set search_path = '' as $$
1745
1845
  ) from agent_bridge.owner_control_attestations attestation
1746
1846
  where attestation.name='owner-control-v2')
1747
1847
  and not exists (
1748
- (select granted_role,member_role,admin_option from actual_memberships
1749
- except select granted_role,member_role,admin_option from expected_memberships)
1848
+ (select granted_role,member_role,admin_option,inherit_option,set_option from actual_memberships
1849
+ except select granted_role,member_role,admin_option,inherit_option,set_option from expected_memberships)
1750
1850
  union all
1751
- (select granted_role,member_role,admin_option from expected_memberships
1752
- except select granted_role,member_role,admin_option from actual_memberships)
1851
+ (select granted_role,member_role,admin_option,inherit_option,set_option from expected_memberships
1852
+ except select granted_role,member_role,admin_option,inherit_option,set_option from actual_memberships)
1753
1853
  )
1754
1854
  and not exists (
1755
- select 1 from actual_memberships membership,names
1756
- where not membership.inherit_option or not membership.set_option
1757
- or membership.grantor_role<>names.schema_owner
1855
+ select 1 from actual_memberships membership where not membership.grants_valid
1758
1856
  )
1759
1857
  and not exists (
1760
1858
  select 1 from active_registry registry
@@ -1792,8 +1890,12 @@ returns boolean language sql stable security definer set search_path = '' as $$
1792
1890
  )
1793
1891
  $$;
1794
1892
 
1893
+ select set_config(
1894
+ 'role','agent_bridge_control_owner_'||substr(md5(current_database()),1,16),false
1895
+ );
1795
1896
  insert into agent_bridge.owner_control_attestations(name,catalog_definition)
1796
1897
  values('owner-control-v2',agent_bridge.owner_control_catalog_definition());
1898
+ reset role;
1797
1899
 
1798
1900
  insert into agent_bridge.portable_archive_attestations(name,catalog_definition)
1799
1901
  values('portable-archive-v1',agent_bridge.portable_archive_catalog_definition());