@coffre/db 0.14.5 → 0.15.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 (214) hide show
  1. package/README.md +2 -1
  2. package/dist/change-records-_fKvahvs.js +55 -0
  3. package/dist/connect.d.ts +22 -10
  4. package/dist/connect.js +74 -12
  5. package/dist/database-CVteLhp1.d.ts +410 -0
  6. package/dist/{database-BadNeeN-.js → database-D55ENPX-.js} +46 -25
  7. package/dist/dialect.d.ts +4 -3
  8. package/dist/dialect.js +14 -5
  9. package/dist/grants.d.ts +4 -4
  10. package/dist/grants.js +4 -4
  11. package/dist/hyperdrive.d.ts +48 -23
  12. package/dist/hyperdrive.js +168 -91
  13. package/dist/index.d.ts +3 -3
  14. package/dist/index.js +1 -1
  15. package/dist/log-DaD7fyUq.d.ts +722 -0
  16. package/dist/log.d.ts +2 -622
  17. package/dist/log.js +178 -13
  18. package/dist/migrate.d.ts +43 -3
  19. package/dist/migrate.js +149 -29
  20. package/dist/migrations/postgres/0022_log_015.sql +672 -0
  21. package/dist/migrations/postgres/0023_change_path.sql +153 -0
  22. package/dist/migrations/postgres/0024_use_015.sql +556 -0
  23. package/dist/migrations/postgres/0025_app_change.sql +221 -0
  24. package/dist/migrations/postgres/0026_issue_lock.sql +106 -0
  25. package/dist/migrations/postgres/0027_retention.sql +118 -0
  26. package/dist/migrations/postgres/0028_use_stalls.sql +594 -0
  27. package/dist/migrations/postgres/0029_use_objects.sql +887 -0
  28. package/dist/migrations/postgres/0030_unarchive_restores.sql +259 -0
  29. package/dist/migrations/postgres/0031_app_read.sql +65 -0
  30. package/dist/migrations/postgres/0032_secrets_live_index.sql +4 -0
  31. package/dist/migrations/postgres/0033_use_generations.sql +582 -0
  32. package/dist/migrations/postgres/0034_vault_credentials.sql +153 -0
  33. package/dist/migrations/postgres/0035_first_days.sql +3 -0
  34. package/dist/migrations/postgres/0036_vault_issues.sql +91 -0
  35. package/dist/migrations/postgres/0037_vault_bindings.sql +271 -0
  36. package/dist/migrations/postgres/0040_binding_origin.sql +159 -0
  37. package/dist/migrations/postgres/0041_cutover.sql +132 -0
  38. package/dist/migrations/postgres/0042_sign_in_index.sql +7 -0
  39. package/dist/migrations/postgres/0043_issue_revoke_index.sql +4 -0
  40. package/dist/migrations/postgres/0044_use_by_name.sql +705 -0
  41. package/dist/migrations/postgres/0045_decide_first.sql +878 -0
  42. package/dist/migrations/postgres/0046_way_in_uses.sql +1149 -0
  43. package/dist/migrations/postgres/0047_cutover_finish.sql +268 -0
  44. package/dist/migrations/postgres/0048_use_batch.sql +59 -0
  45. package/dist/migrations/postgres/0049_reads.sql +171 -0
  46. package/dist/migrations/postgres/meta/0022_snapshot.json +3831 -0
  47. package/dist/migrations/postgres/meta/0023_snapshot.json +3831 -0
  48. package/dist/migrations/postgres/meta/0024_snapshot.json +3831 -0
  49. package/dist/migrations/postgres/meta/0025_snapshot.json +3831 -0
  50. package/dist/migrations/postgres/meta/0026_snapshot.json +3831 -0
  51. package/dist/migrations/postgres/meta/0027_snapshot.json +3853 -0
  52. package/dist/migrations/postgres/meta/0028_snapshot.json +3853 -0
  53. package/dist/migrations/postgres/meta/0029_snapshot.json +3859 -0
  54. package/dist/migrations/postgres/meta/0030_snapshot.json +3859 -0
  55. package/dist/migrations/postgres/meta/0031_snapshot.json +3859 -0
  56. package/dist/migrations/postgres/meta/0032_snapshot.json +3875 -0
  57. package/dist/migrations/postgres/meta/0033_snapshot.json +3875 -0
  58. package/dist/migrations/postgres/meta/0034_snapshot.json +3875 -0
  59. package/dist/migrations/postgres/meta/0035_snapshot.json +3875 -0
  60. package/dist/migrations/postgres/meta/0036_snapshot.json +3875 -0
  61. package/dist/migrations/postgres/meta/0037_snapshot.json +3875 -0
  62. package/dist/migrations/postgres/meta/0038_snapshot.json +3881 -0
  63. package/dist/migrations/postgres/meta/0039_snapshot.json +3881 -0
  64. package/dist/migrations/postgres/meta/0040_snapshot.json +3931 -0
  65. package/dist/migrations/postgres/meta/0041_snapshot.json +3981 -0
  66. package/dist/migrations/postgres/meta/0042_snapshot.json +3981 -0
  67. package/dist/migrations/postgres/meta/0043_snapshot.json +3981 -0
  68. package/dist/migrations/postgres/meta/0044_snapshot.json +3991 -0
  69. package/dist/migrations/postgres/meta/0045_snapshot.json +3997 -0
  70. package/dist/migrations/postgres/meta/0046_snapshot.json +3997 -0
  71. package/dist/migrations/postgres/meta/0047_snapshot.json +4234 -0
  72. package/dist/migrations/postgres/meta/_journal.json +182 -0
  73. package/dist/migrations/sqlite/0018_log_015.sql +84 -0
  74. package/dist/migrations/sqlite/0019_retention.sql +2 -0
  75. package/dist/migrations/sqlite/0020_ended_references.sql +1 -0
  76. package/dist/migrations/sqlite/0021_secrets_live_index.sql +1 -0
  77. package/dist/migrations/sqlite/0022_binding_origin.sql +1 -0
  78. package/dist/migrations/sqlite/0023_sign_in_index.sql +2 -0
  79. package/dist/migrations/sqlite/0024_issue_revoke_index.sql +2 -0
  80. package/dist/migrations/sqlite/0025_way_in_uses.sql +42 -0
  81. package/dist/migrations/sqlite/0026_cutover_finish.sql +11 -0
  82. package/dist/migrations/sqlite/0027_reads.sql +38 -0
  83. package/dist/migrations/sqlite/meta/0018_snapshot.json +3529 -0
  84. package/dist/migrations/sqlite/meta/0019_snapshot.json +3538 -0
  85. package/dist/migrations/sqlite/meta/0020_snapshot.json +3545 -0
  86. package/dist/migrations/sqlite/meta/0021_snapshot.json +3553 -0
  87. package/dist/migrations/sqlite/meta/0022_snapshot.json +3560 -0
  88. package/dist/migrations/sqlite/meta/0023_snapshot.json +3579 -0
  89. package/dist/migrations/sqlite/meta/0024_snapshot.json +3598 -0
  90. package/dist/migrations/sqlite/meta/0025_snapshot.json +3609 -0
  91. package/dist/migrations/sqlite/meta/0026_snapshot.json +3616 -0
  92. package/dist/migrations/sqlite/meta/0027_snapshot.json +3834 -0
  93. package/dist/migrations/sqlite/meta/_journal.json +70 -0
  94. package/dist/platform-Y3gICK6n.d.ts +58 -0
  95. package/dist/portable.d.ts +2 -2
  96. package/dist/postgres-BI3xTnyw.js +1718 -0
  97. package/dist/postgres-platform-node.d.ts +14 -0
  98. package/dist/postgres-platform-node.js +21 -0
  99. package/dist/postgres-platform-workerd.d.ts +61 -0
  100. package/dist/postgres-platform-workerd.js +163 -0
  101. package/dist/{schema-BfQEBqFv.d.ts → schema-DgoPqqsI.d.ts} +1326 -7
  102. package/dist/schema-sqlite.d.ts +1306 -6
  103. package/dist/schema-sqlite.js +154 -18
  104. package/dist/schema-version.d.ts +1 -1
  105. package/dist/schema-version.js +254 -2
  106. package/dist/schema.d.ts +2 -2
  107. package/dist/schema.js +226 -20
  108. package/package.json +13 -5
  109. package/src/active-subject.ts +9 -0
  110. package/src/change-records.ts +38 -0
  111. package/src/connect.ts +67 -13
  112. package/src/database.ts +67 -51
  113. package/src/dialect.ts +17 -8
  114. package/src/grants.ts +3 -3
  115. package/src/hyperdrive.ts +181 -104
  116. package/src/log.ts +196 -15
  117. package/src/migrate.ts +157 -30
  118. package/src/migrations/postgres/0022_log_015.sql +672 -0
  119. package/src/migrations/postgres/0023_change_path.sql +153 -0
  120. package/src/migrations/postgres/0024_use_015.sql +556 -0
  121. package/src/migrations/postgres/0025_app_change.sql +221 -0
  122. package/src/migrations/postgres/0026_issue_lock.sql +106 -0
  123. package/src/migrations/postgres/0027_retention.sql +118 -0
  124. package/src/migrations/postgres/0028_use_stalls.sql +594 -0
  125. package/src/migrations/postgres/0029_use_objects.sql +887 -0
  126. package/src/migrations/postgres/0030_unarchive_restores.sql +259 -0
  127. package/src/migrations/postgres/0031_app_read.sql +65 -0
  128. package/src/migrations/postgres/0032_secrets_live_index.sql +4 -0
  129. package/src/migrations/postgres/0033_use_generations.sql +582 -0
  130. package/src/migrations/postgres/0034_vault_credentials.sql +153 -0
  131. package/src/migrations/postgres/0035_first_days.sql +3 -0
  132. package/src/migrations/postgres/0036_vault_issues.sql +91 -0
  133. package/src/migrations/postgres/0037_vault_bindings.sql +271 -0
  134. package/src/migrations/postgres/0040_binding_origin.sql +159 -0
  135. package/src/migrations/postgres/0041_cutover.sql +132 -0
  136. package/src/migrations/postgres/0042_sign_in_index.sql +7 -0
  137. package/src/migrations/postgres/0043_issue_revoke_index.sql +4 -0
  138. package/src/migrations/postgres/0044_use_by_name.sql +705 -0
  139. package/src/migrations/postgres/0045_decide_first.sql +878 -0
  140. package/src/migrations/postgres/0046_way_in_uses.sql +1149 -0
  141. package/src/migrations/postgres/0047_cutover_finish.sql +268 -0
  142. package/src/migrations/postgres/0048_use_batch.sql +59 -0
  143. package/src/migrations/postgres/0049_reads.sql +171 -0
  144. package/src/migrations/postgres/meta/0022_snapshot.json +3831 -0
  145. package/src/migrations/postgres/meta/0023_snapshot.json +3831 -0
  146. package/src/migrations/postgres/meta/0024_snapshot.json +3831 -0
  147. package/src/migrations/postgres/meta/0025_snapshot.json +3831 -0
  148. package/src/migrations/postgres/meta/0026_snapshot.json +3831 -0
  149. package/src/migrations/postgres/meta/0027_snapshot.json +3853 -0
  150. package/src/migrations/postgres/meta/0028_snapshot.json +3853 -0
  151. package/src/migrations/postgres/meta/0029_snapshot.json +3859 -0
  152. package/src/migrations/postgres/meta/0030_snapshot.json +3859 -0
  153. package/src/migrations/postgres/meta/0031_snapshot.json +3859 -0
  154. package/src/migrations/postgres/meta/0032_snapshot.json +3875 -0
  155. package/src/migrations/postgres/meta/0033_snapshot.json +3875 -0
  156. package/src/migrations/postgres/meta/0034_snapshot.json +3875 -0
  157. package/src/migrations/postgres/meta/0035_snapshot.json +3875 -0
  158. package/src/migrations/postgres/meta/0036_snapshot.json +3875 -0
  159. package/src/migrations/postgres/meta/0037_snapshot.json +3875 -0
  160. package/src/migrations/postgres/meta/0038_snapshot.json +3881 -0
  161. package/src/migrations/postgres/meta/0039_snapshot.json +3881 -0
  162. package/src/migrations/postgres/meta/0040_snapshot.json +3931 -0
  163. package/src/migrations/postgres/meta/0041_snapshot.json +3981 -0
  164. package/src/migrations/postgres/meta/0042_snapshot.json +3981 -0
  165. package/src/migrations/postgres/meta/0043_snapshot.json +3981 -0
  166. package/src/migrations/postgres/meta/0044_snapshot.json +3991 -0
  167. package/src/migrations/postgres/meta/0045_snapshot.json +3997 -0
  168. package/src/migrations/postgres/meta/0046_snapshot.json +3997 -0
  169. package/src/migrations/postgres/meta/0047_snapshot.json +4234 -0
  170. package/src/migrations/postgres/meta/_journal.json +182 -0
  171. package/src/migrations/sqlite/0018_log_015.sql +84 -0
  172. package/src/migrations/sqlite/0019_retention.sql +2 -0
  173. package/src/migrations/sqlite/0020_ended_references.sql +1 -0
  174. package/src/migrations/sqlite/0021_secrets_live_index.sql +1 -0
  175. package/src/migrations/sqlite/0022_binding_origin.sql +1 -0
  176. package/src/migrations/sqlite/0023_sign_in_index.sql +2 -0
  177. package/src/migrations/sqlite/0024_issue_revoke_index.sql +2 -0
  178. package/src/migrations/sqlite/0025_way_in_uses.sql +42 -0
  179. package/src/migrations/sqlite/0026_cutover_finish.sql +11 -0
  180. package/src/migrations/sqlite/0027_reads.sql +38 -0
  181. package/src/migrations/sqlite/meta/0018_snapshot.json +3529 -0
  182. package/src/migrations/sqlite/meta/0019_snapshot.json +3538 -0
  183. package/src/migrations/sqlite/meta/0020_snapshot.json +3545 -0
  184. package/src/migrations/sqlite/meta/0021_snapshot.json +3553 -0
  185. package/src/migrations/sqlite/meta/0022_snapshot.json +3560 -0
  186. package/src/migrations/sqlite/meta/0023_snapshot.json +3579 -0
  187. package/src/migrations/sqlite/meta/0024_snapshot.json +3598 -0
  188. package/src/migrations/sqlite/meta/0025_snapshot.json +3609 -0
  189. package/src/migrations/sqlite/meta/0026_snapshot.json +3616 -0
  190. package/src/migrations/sqlite/meta/0027_snapshot.json +3834 -0
  191. package/src/migrations/sqlite/meta/_journal.json +70 -0
  192. package/src/postgres-js/README.md +183 -0
  193. package/src/postgres-js/UNLICENSE +24 -0
  194. package/src/postgres-js/bytes.ts +89 -0
  195. package/src/postgres-js/connection.ts +882 -0
  196. package/src/postgres-js/errors.ts +84 -0
  197. package/src/postgres-js/index.ts +516 -0
  198. package/src/postgres-js/platform.node.ts +29 -0
  199. package/src/postgres-js/platform.ts +60 -0
  200. package/src/postgres-js/platform.workerd.ts +225 -0
  201. package/src/postgres-js/query.ts +123 -0
  202. package/src/postgres-js/queue.ts +42 -0
  203. package/src/postgres-js/result.ts +46 -0
  204. package/src/postgres-js/types.ts +52 -0
  205. package/src/postgres.ts +375 -26
  206. package/src/schema-version.ts +2 -2
  207. package/src/schema.sqlite.ts +198 -7
  208. package/src/schema.ts +291 -6
  209. package/src/verify-runtime.ts +18 -29
  210. package/LICENSE +0 -21
  211. package/dist/database-wq1AVRAP.d.ts +0 -73
  212. package/dist/postgres-Bmar8y0q.js +0 -29
  213. package/dist/relations-BfR3JjT1.js +0 -38
  214. package/src/relations.ts +0 -40
@@ -0,0 +1,556 @@
1
+ -- 0.15's use, whole (docs/design/0.15-plan.md, PR 5; hot-path-one.md, "The use path on main"): use_credential again, reading every input PR 3's `readUse` reads (packages/vault/src/log-replay.ts), in one of two shapes, and answering its row as one JSON value. 0022's version read five inputs; nothing ever called it, so it is replaced, not kept beside. It only adds otherwise: 0.14's code calls none of it.
2
+ DROP FUNCTION use_credential(uuid, uuid, bigint, bigint, smallint, uuid, bytea, bigint, bigint, bigint, bigint, bigint, bytea);
3
+ --> statement-breakpoint
4
+ DROP TYPE use_view;
5
+ --> statement-breakpoint
6
+ -- What a use reads, as use_credential returns it, as JSON (`to_json` of this row): one value, where the row itself, about a hundred typed columns, costs a row description and a field each per call, about 0.2 ms more of Postgres CPU a use (measured: hot-path-one.md, "The use path on main"). Its whole numbers come as JSON numbers, which the vault reads exactly (use.postgres.ts). `state` is null when the call did its work; otherwise it did nothing, and the rest is to be ignored: 'late' (past its deadline, or below the floor), 'ahead' (stamped more than a minute past the database's clock), 'unconfigured' (no policy); 'reprobe' and 'lock' never leave the function.
7
+ --
8
+ -- Each input is its row's columns, its seal (`*_seal`), and, in the probing form, the seal of its object's newest change (`*_last`; null in the cached form, where the isolate holds them). `inputs` is the newest change of any input, what a cover is held to; `probed`, whether the statement probed them (or held to the isolate's). The environments are the four `readUse` may weigh, each read only where the rows name it: e1 the place read through, e2 the key's own, e3 a reference's source's, e4 a personal config's parent. `m_old`: the newest 0.14 access entry the principal's newest change accounts for; `m_access`: the newest one 0.14's log holds (UsesSeeOldWrites, Migration015.tla).
9
+ CREATE TYPE use_row AS (
10
+ state text, number bigint, covered bigint, newest bigint, newest_mac bytea, inputs bigint, probed boolean,
11
+ c_principal text, c_kind text, c_scope text, c_revoked boolean, c_expires bigint, c_seal bytea, c_last bytea,
12
+ m_status text, m_owner boolean, m_role text, m_scope text, m_seal bytea, m_last bytea, m_old bigint, m_access bigint,
13
+ g_project uuid[], g_environment uuid[], g_slug text[], g_role text[], g_expires bigint[],
14
+ r_value text, r_seal bytea, r_last bytea,
15
+ p_value text, p_seal bytea, p_last bytea, cap_count integer, cap_window bigint,
16
+ s_project uuid, s_environment uuid, s_version uuid, s_source uuid, s_seal bytea, s_last bytea,
17
+ x_project uuid, x_environment uuid, x_version uuid, x_source uuid, x_seal bytea, x_last bytea,
18
+ v_envelope integer, v_ciphertext bytea, v_iv bytea, v_auth_tag bytea, v_wrapped_dek bytea, v_kek_provider text, v_kek_id text, v_kek_version text, v_sensitive boolean, v_plain boolean,
19
+ e1_id uuid, e1_project uuid, e1_slug text, e1_owner text, e1_parent uuid, e1_protected boolean, e1_archived boolean, e1_seal bytea, e1_last bytea,
20
+ e2_id uuid, e2_project uuid, e2_slug text, e2_owner text, e2_parent uuid, e2_protected boolean, e2_archived boolean, e2_seal bytea, e2_last bytea,
21
+ e3_id uuid, e3_project uuid, e3_slug text, e3_owner text, e3_parent uuid, e3_protected boolean, e3_archived boolean, e3_seal bytea, e3_last bytea,
22
+ e4_id uuid, e4_project uuid, e4_slug text, e4_owner text, e4_parent uuid, e4_protected boolean, e4_archived boolean, e4_seal bytea, e4_last bytea,
23
+ in_set boolean, others integer
24
+ );
25
+ --> statement-breakpoint
26
+ -- A use (hot-path-one.md, "A use"; formal/HotPathOne.tla): the fence shared (UseLocks), then one statement, with a snapshot of its own taken after the lock (StatementSnapshot: each of a volatile function's statements takes a new one under READ COMMITTED; a bare CTE's one snapshot would predate the wait), that reads every input the decision needs and inserts the entry, numbered now (UseNumberUnderLock). The lock goes with the commit (LockToCommit: an xact lock). The Worker decides on what it returns, and releases a value only once the call has returned, after the commit (ReleaseAfterCommit, SyncCommit); the log records the request, not the outcome.
27
+ --
28
+ -- The deadline (DeadlineChecked, R53, R54): a call that comes after it, or with a deadline more than deadline_max() after its `at`, or an `at` below the floor (FloorChecked; or no floor row), writes nothing and answers 'late', each by clock_timestamp(), never now(), which is the transaction's start, before any lock wait; one that runs past it, waiting for its principal's lock say, rolls back (57014). A replay of a call that wrote fails on (at, request_id), 23505: deciding again on today's state could allow, after a revocation, a use the log has before it.
29
+ --
30
+ -- The bulk cap (R28, R52), its count and window the policy object's newest change's (CapFromPolicy, CapSealed): probed, or, in the cached form, the isolate's, which it read from that change and passes only with the change it read it at, held here to be still the log's newest. Never the policy's row, nor the deployment's config. A use before the policy is logged, or under one that names no count or window, answers 'unconfigured'. The cap's row moves for every request it admits, whatever the seals say (HotPathOneStaleBulk), under the principal's own lock (PrincipalLocks): a use that may write it takes it and reads again. As 0022's, which says the rule.
31
+ --
32
+ -- Covers (p_cover, with its entry's at and request id; CoverChecksChanges, CoverChecksAllInputs, CoverExists, CoverFresh): a repeat of an allowed use this isolate wrote, of the same credential and key, with no change to any input numbered after it, its entry still in the log and the key's bulk row fresh, writes nothing; the Worker decides on what was read.
33
+ --
34
+ -- The seal cache (p_known, p_known_mac; CacheChecked, CacheSameChange): if the log's newest change is still exactly the one the isolate read its seals at, by number and MAC, the statement probes no input's newest change. Otherwise it writes nothing, answers 'reprobe' inside the function, and the probing statement runs. Two statements, not one with its probes skipped: a probe's plan nodes cost most of it even when it does not run (measured, W211).
35
+ --
36
+ -- The inputs, each one object of W216's (sealed-inputs.md), in `readUse`'s order: the credential, its principal (the member, its grants, the access entries of 0.14 about it), the roles, the policy, the key (a reference's when it has no version of its own: its newest secret_references row names the source), the source, and the environments the decision weighs.
37
+ CREATE FUNCTION use_credential(
38
+ p_credential uuid, p_secret uuid, p_request bigint, p_at bigint, p_purpose smallint, p_place uuid, p_mac bytea, p_deadline bigint,
39
+ p_cover bigint DEFAULT NULL, p_cover_at bigint DEFAULT NULL, p_cover_request bigint DEFAULT NULL, p_known bigint DEFAULT NULL, p_known_mac bytea DEFAULT NULL,
40
+ p_cap_count integer DEFAULT NULL, p_cap_window bigint DEFAULT NULL
41
+ ) RETURNS json
42
+ LANGUAGE plpgsql SECURITY DEFINER SET search_path = pg_catalog, pg_temp SET bytea_output = 'hex' AS $$
43
+ DECLARE
44
+ r public.use_row;
45
+ locked boolean := false;
46
+ probe boolean := p_known IS NULL OR p_cap_count IS NULL OR p_cap_window IS NULL;
47
+ -- A use through a place, or of a key with no value of its own (a reference), reads every input; the others, most
48
+ -- uses, the plain shape: no place, no source, none of their joins.
49
+ whole boolean := p_place IS NOT NULL;
50
+ BEGIN
51
+ IF p_deadline IS NULL OR p_deadline > p_at + public.deadline_max() THEN r.state := 'late'; RETURN to_json(r); END IF;
52
+ -- Its Worker's clock ahead by over a minute (0022 says why): checked here at no cost, and again in the statement.
53
+ IF p_at > (extract(epoch FROM clock_timestamp()) * 1000)::bigint + 60000 THEN r.state := 'ahead'; RETURN to_json(r); END IF;
54
+ PERFORM pg_advisory_xact_lock_shared(1668244070, 0);
55
+ LOOP
56
+ IF probe AND NOT whole THEN
57
+ WITH i AS MATERIALIZED (
58
+ SELECT n.number AS newest, n.mac AS newest_mac, coalesce(n.number = p_known AND n.mac = p_known_mac, false) AS cached,
59
+ f.at_floor, (lpol.payload::jsonb #>> '{value,bulk,count}')::integer AS cap_count, (lpol.payload::jsonb #>> '{value,bulk,windowMs}')::bigint AS cap_window,
60
+ lpol.number AS lpol_number, lpol.seal AS p_last,
61
+ c.principal AS p, c.kind AS c_kind, c.scope AS c_scope, c.revoked_at IS NOT NULL AS c_revoked,
62
+ floor(extract(epoch FROM least(c.expires_at, c.not_after)) * 1000)::bigint AS c_expires, c.seal AS c_seal,
63
+ m.status AS m_status, m.owner AS m_owner, m.role AS m_role, m.scope AS m_scope, m.seal AS m_seal,
64
+ cfg.r_value, cfg.r_seal, cfg.p_value, cfg.p_seal,
65
+ s.project_id AS s_project, s.environment_id AS s_environment, s.current_version_id AS s_version, NULL::uuid AS s_source, s.seal AS s_seal,
66
+ NULL::uuid AS x_project, NULL::uuid AS x_environment, NULL::uuid AS x_version, NULL::uuid AS x_source, NULL::bytea AS x_seal,
67
+ sv.envelope_version, sv.ciphertext, sv.iv, sv.auth_tag, sv.wrapped_dek, sv.kek_provider, sv.kek_id, sv.kek_version, sv.sensitive, sv.plaintext IS NOT NULL AS v_plain,
68
+ NULL::uuid AS e1_id, NULL::uuid AS e1_project, NULL::text AS e1_slug, NULL::text AS e1_owner, NULL::uuid AS e1_parent, NULL::boolean AS e1_protected, NULL::boolean AS e1_archived, NULL::bytea AS e1_seal,
69
+ s.environment_id AS e2_id, e2.project_id AS e2_project, e2.slug AS e2_slug, e2.owner AS e2_owner, e2.parent_id AS e2_parent, e2.protected AS e2_protected, e2.archived_at IS NOT NULL AS e2_archived, e2.seal AS e2_seal,
70
+ NULL::uuid AS e3_id, NULL::uuid AS e3_project, NULL::text AS e3_slug, NULL::text AS e3_owner, NULL::uuid AS e3_parent, NULL::boolean AS e3_protected, NULL::boolean AS e3_archived, NULL::bytea AS e3_seal,
71
+ CASE WHEN e2.owner IS NOT NULL THEN e2.parent_id END AS e4_id, e4.project_id AS e4_project, e4.slug AS e4_slug, e4.owner AS e4_owner, e4.parent_id AS e4_parent, e4.protected AS e4_protected, e4.archived_at IS NOT NULL AS e4_archived, e4.seal AS e4_seal,
72
+ b.last AS bulk_last,
73
+ -- Each input's object key, computed once: a key in a probe's own condition is computed again for every row it reads (0022, decision 3).
74
+ public.log_object('credential', p_credential::text) AS k_credential,
75
+ public.log_object('principal', c.principal) AS k_member,
76
+ public.log_object('secret', p_secret::text) AS k_secret,
77
+ public.log_object('environment', s.environment_id::text) AS k_e2,
78
+ public.log_object('environment', (CASE WHEN e2.owner IS NOT NULL THEN e2.parent_id END)::text) AS k_e4
79
+ FROM (SELECT 1) one
80
+ LEFT JOIN LATERAL (SELECT x.number, x.mac FROM public.log_changes x ORDER BY x.number DESC LIMIT 1) n ON true
81
+ LEFT JOIN public.log_fence f ON f.only_row
82
+ -- The roles and the policy, both rows in one read of their one-page table.
83
+ LEFT JOIN LATERAL (
84
+ SELECT max(g.value) FILTER (WHERE g.name = 'roles') AS r_value, (array_agg(g.seal) FILTER (WHERE g.name = 'roles'))[1] AS r_seal,
85
+ max(g.value) FILTER (WHERE g.name = 'policy') AS p_value, (array_agg(g.seal) FILTER (WHERE g.name = 'policy'))[1] AS p_seal
86
+ FROM public.log_config g
87
+ ) cfg ON true
88
+ -- The cap as the policy's newest change says it (CapSealed): every policy change logs the whole policy as its `value`.
89
+ LEFT JOIN LATERAL (
90
+ SELECT x.number, x.seal, x.payload FROM public.log_changes x WHERE x.object = public.log_object('policy', 'instance') ORDER BY x.number DESC LIMIT 1
91
+ ) lpol ON true
92
+ LEFT JOIN public.credentials c ON c.id = p_credential
93
+ LEFT JOIN public.vault_members m ON m.principal = c.principal
94
+ -- An archived key is gone (`secret.delete`): no object.
95
+ LEFT JOIN public.secrets s ON s.id = p_secret AND s.archived_at IS NULL
96
+ -- The key's own version, its own environment, and that environment's parent when it is a personal config.
97
+ LEFT JOIN public.secret_versions sv ON sv.id = s.current_version_id
98
+ LEFT JOIN public.environments e2 ON e2.id = s.environment_id
99
+ LEFT JOIN public.environments e4 ON e4.id = e2.parent_id AND e2.owner IS NOT NULL
100
+ LEFT JOIN public.log_bulk b ON b.principal = c.principal AND b.secret = p_secret
101
+ ),
102
+ v AS MATERIALIZED (
103
+ SELECT i.*, cap.refresh,
104
+ CASE WHEN i.s_project IS NOT NULL AND i.s_version IS NULL THEN 'whole'
105
+ WHEN i.at_floor IS NULL OR p_at < i.at_floor OR (extract(epoch FROM clock_timestamp()) * 1000)::bigint > p_deadline THEN 'late'
106
+ WHEN p_at > (extract(epoch FROM clock_timestamp()) * 1000)::bigint + 60000 THEN 'ahead'
107
+ WHEN i.cap_count IS NULL OR i.cap_window IS NULL THEN 'unconfigured'
108
+ WHEN i.p IS NOT NULL AND NOT locked AND coalesce(i.bulk_last <= p_at - cap.refresh, true) THEN 'lock' END AS state,
109
+ greatest(lc.number, lm.number, lr.number, i.lpol_number, ls.number, l2.number, l4.number) AS inputs,
110
+ lc.seal AS c_last, lm.seal AS m_last, (lm.payload::jsonb ->> 'old')::bigint AS m_old, lr.seal AS r_last, ls.seal AS s_last, NULL::bytea AS x_last,
111
+ NULL::bytea AS e1_last, l2.seal AS e2_last, NULL::bytea AS e3_last, l4.seal AS e4_last,
112
+ coalesce(i.bulk_last > p_at - i.cap_window - cap.refresh, false) AS is_in,
113
+ coalesce(i.bulk_last <= p_at - cap.refresh, true) AS stale,
114
+ -- The other keys in the set, up to the cap: read only under the principal's lock, for a key not in it.
115
+ CASE WHEN locked AND i.p IS NOT NULL AND NOT coalesce(i.bulk_last > p_at - i.cap_window - cap.refresh, false) THEN (
116
+ SELECT count(*)::integer FROM (
117
+ SELECT 1 FROM public.log_bulk o
118
+ WHERE o.principal = i.p AND o.secret <> p_secret AND o.last > p_at - i.cap_window - cap.refresh - public.deadline_max()
119
+ LIMIT i.cap_count
120
+ ) z
121
+ ) ELSE 0 END AS n_others,
122
+ -- A repeat rides its cover only while every part of this holds: never on a key whose bulk row is missing (CoverFresh).
123
+ coalesce(p_cover IS NOT NULL AND i.bulk_last > p_at - cap.refresh AND p_cover > coalesce(greatest(lc.number, lm.number, lr.number, i.lpol_number, ls.number, l2.number, l4.number), 0)
124
+ AND EXISTS (SELECT 1 FROM public.log_uses u WHERE u.at = p_cover_at AND u.request_id = p_cover_request AND u.number = p_cover
125
+ AND u.credential = p_credential AND u.secret = p_secret AND u.purpose = p_purpose AND u.place IS NOT DISTINCT FROM p_place), false) AS is_covered
126
+ FROM i
127
+ CROSS JOIN LATERAL (SELECT least(60000, i.cap_window / 10) AS refresh) cap
128
+ -- Each input's newest change: one index-only probe each (log_changes_object_idx holds the seal), the principal's
129
+ -- with its payload, which names the 0.14 entry it accounts for.
130
+ LEFT JOIN LATERAL (SELECT x.number, x.seal FROM public.log_changes x WHERE x.object = i.k_credential ORDER BY x.number DESC LIMIT 1) lc ON true
131
+ LEFT JOIN LATERAL (SELECT x.number, x.seal, x.payload FROM public.log_changes x WHERE x.object = i.k_member ORDER BY x.number DESC LIMIT 1) lm ON i.k_member IS NOT NULL
132
+ LEFT JOIN LATERAL (SELECT x.number, x.seal FROM public.log_changes x WHERE x.object = public.log_object('roles', 'instance') ORDER BY x.number DESC LIMIT 1) lr ON true
133
+ LEFT JOIN LATERAL (SELECT x.number, x.seal FROM public.log_changes x WHERE x.object = i.k_secret ORDER BY x.number DESC LIMIT 1) ls ON true
134
+ LEFT JOIN LATERAL (SELECT x.number, x.seal FROM public.log_changes x WHERE x.object = i.k_e2 ORDER BY x.number DESC LIMIT 1) l2 ON i.k_e2 IS NOT NULL
135
+ LEFT JOIN LATERAL (SELECT x.number, x.seal FROM public.log_changes x WHERE x.object = i.k_e4 ORDER BY x.number DESC LIMIT 1) l4 ON i.k_e4 IS NOT NULL
136
+ ),
137
+ w AS (
138
+ INSERT INTO public.log_uses (number, at, request_id, credential, secret, purpose, mac, place)
139
+ SELECT nextval('public.log_number'), p_at, p_request, p_credential, p_secret, p_purpose, p_mac, p_place
140
+ FROM v WHERE v.state IS NULL AND NOT v.is_covered
141
+ RETURNING log_uses.number
142
+ ),
143
+ k AS (
144
+ INSERT INTO public.log_bulk (principal, secret, last)
145
+ SELECT v.p, p_secret, p_at FROM v
146
+ WHERE v.state IS NULL AND v.p IS NOT NULL AND v.stale AND (v.is_in OR v.n_others < v.cap_count)
147
+ ON CONFLICT ON CONSTRAINT log_bulk_pkey DO UPDATE SET last = EXCLUDED.last
148
+ )
149
+ SELECT v.state, (SELECT w.number FROM w), CASE WHEN v.state IS NULL AND v.is_covered THEN p_cover END, v.newest, v.newest_mac, v.inputs, true,
150
+ v.p, v.c_kind, v.c_scope, v.c_revoked, v.c_expires, v.c_seal, v.c_last,
151
+ v.m_status, v.m_owner, v.m_role, v.m_scope, v.m_seal, v.m_last, v.m_old,
152
+ (SELECT a.seq FROM public.audit_log a WHERE a.author = 'vault' AND a.subject_principal = v.p AND a.decision = 'allow'
153
+ AND a.action IN ('member.add', 'member.restore', 'member.owner', 'member.role', 'member.remove', 'access.grant', 'access.revoke') ORDER BY a.seq DESC LIMIT 1),
154
+ gr.g_project, gr.g_environment, gr.g_slug, gr.g_role, gr.g_expires,
155
+ v.r_value, v.r_seal, v.r_last,
156
+ v.p_value, v.p_seal, v.p_last, v.cap_count, v.cap_window,
157
+ v.s_project, v.s_environment, v.s_version, v.s_source, v.s_seal, v.s_last,
158
+ v.x_project, v.x_environment, v.x_version, v.x_source, v.x_seal, v.x_last,
159
+ v.envelope_version, v.ciphertext, v.iv, v.auth_tag, v.wrapped_dek, v.kek_provider, v.kek_id, v.kek_version, v.sensitive, v.v_plain,
160
+ v.e1_id, v.e1_project, v.e1_slug, v.e1_owner, v.e1_parent, v.e1_protected, v.e1_archived, v.e1_seal, v.e1_last,
161
+ v.e2_id, v.e2_project, v.e2_slug, v.e2_owner, v.e2_parent, v.e2_protected, v.e2_archived, v.e2_seal, v.e2_last,
162
+ v.e3_id, v.e3_project, v.e3_slug, v.e3_owner, v.e3_parent, v.e3_protected, v.e3_archived, v.e3_seal, v.e3_last,
163
+ v.e4_id, v.e4_project, v.e4_slug, v.e4_owner, v.e4_parent, v.e4_protected, v.e4_archived, v.e4_seal, v.e4_last,
164
+ v.is_in, v.n_others
165
+ INTO r
166
+ FROM v
167
+ -- The principal's grants as five lists, one entry per grant, lapsed ones included; an environment's grant with its project, which its row finds through `environments`.
168
+ LEFT JOIN LATERAL (
169
+ SELECT array_agg(coalesce(g.project_id, ge.project_id)) AS g_project, array_agg(g.environment_id) AS g_environment,
170
+ array_agg(g.environment_slug) AS g_slug, array_agg(g.role) AS g_role, array_agg(g.expires_at) AS g_expires
171
+ FROM public.vault_grants g LEFT JOIN public.environments ge ON ge.id = g.environment_id
172
+ WHERE g.principal = v.p
173
+ ) gr ON v.m_status IS NOT NULL;
174
+ ELSIF probe THEN
175
+ WITH i AS MATERIALIZED (
176
+ SELECT n.number AS newest, n.mac AS newest_mac, coalesce(n.number = p_known AND n.mac = p_known_mac, false) AS cached,
177
+ f.at_floor, (lpol.payload::jsonb #>> '{value,bulk,count}')::integer AS cap_count, (lpol.payload::jsonb #>> '{value,bulk,windowMs}')::bigint AS cap_window,
178
+ lpol.number AS lpol_number, lpol.seal AS p_last,
179
+ c.principal AS p, c.kind AS c_kind, c.scope AS c_scope, c.revoked_at IS NOT NULL AS c_revoked,
180
+ floor(extract(epoch FROM least(c.expires_at, c.not_after)) * 1000)::bigint AS c_expires, c.seal AS c_seal,
181
+ m.status AS m_status, m.owner AS m_owner, m.role AS m_role, m.scope AS m_scope, m.seal AS m_seal,
182
+ cfg.r_value, cfg.r_seal, cfg.p_value, cfg.p_seal,
183
+ s.project_id AS s_project, s.environment_id AS s_environment, s.current_version_id AS s_version, ref.source_secret_id AS s_source, s.seal AS s_seal,
184
+ src.project_id AS x_project, src.environment_id AS x_environment, src.current_version_id AS x_version, xref.source_secret_id AS x_source, src.seal AS x_seal,
185
+ sv.envelope_version, sv.ciphertext, sv.iv, sv.auth_tag, sv.wrapped_dek, sv.kek_provider, sv.kek_id, sv.kek_version, sv.sensitive, sv.plaintext IS NOT NULL AS v_plain,
186
+ ids.e1 AS e1_id, e1.project_id AS e1_project, e1.slug AS e1_slug, e1.owner AS e1_owner, e1.parent_id AS e1_parent, e1.protected AS e1_protected, e1.archived_at IS NOT NULL AS e1_archived, e1.seal AS e1_seal,
187
+ s.environment_id AS e2_id, e2.project_id AS e2_project, e2.slug AS e2_slug, e2.owner AS e2_owner, e2.parent_id AS e2_parent, e2.protected AS e2_protected, e2.archived_at IS NOT NULL AS e2_archived, e2.seal AS e2_seal,
188
+ ids.e3 AS e3_id, e3.project_id AS e3_project, e3.slug AS e3_slug, e3.owner AS e3_owner, e3.parent_id AS e3_parent, e3.protected AS e3_protected, e3.archived_at IS NOT NULL AS e3_archived, e3.seal AS e3_seal,
189
+ CASE WHEN ve.owner IS NOT NULL THEN ve.parent END AS e4_id, e4.project_id AS e4_project, e4.slug AS e4_slug, e4.owner AS e4_owner, e4.parent_id AS e4_parent, e4.protected AS e4_protected, e4.archived_at IS NOT NULL AS e4_archived, e4.seal AS e4_seal,
190
+ b.last AS bulk_last,
191
+ -- Each input's object key, computed once: a key in a probe's own condition is computed again for every row it reads (0022, decision 3).
192
+ public.log_object('credential', p_credential::text) AS k_credential,
193
+ public.log_object('principal', c.principal) AS k_member,
194
+ public.log_object('secret', p_secret::text) AS k_secret,
195
+ public.log_object('secret', ref.source_secret_id::text) AS k_source,
196
+ public.log_object('environment', ids.e1::text) AS k_e1,
197
+ public.log_object('environment', s.environment_id::text) AS k_e2,
198
+ public.log_object('environment', ids.e3::text) AS k_e3,
199
+ public.log_object('environment', (CASE WHEN ve.owner IS NOT NULL THEN ve.parent END)::text) AS k_e4
200
+ FROM (SELECT 1) one
201
+ LEFT JOIN LATERAL (SELECT x.number, x.mac FROM public.log_changes x ORDER BY x.number DESC LIMIT 1) n ON true
202
+ LEFT JOIN public.log_fence f ON f.only_row
203
+ -- The roles and the policy, both rows in one read of their one-page table.
204
+ LEFT JOIN LATERAL (
205
+ SELECT max(g.value) FILTER (WHERE g.name = 'roles') AS r_value, (array_agg(g.seal) FILTER (WHERE g.name = 'roles'))[1] AS r_seal,
206
+ max(g.value) FILTER (WHERE g.name = 'policy') AS p_value, (array_agg(g.seal) FILTER (WHERE g.name = 'policy'))[1] AS p_seal
207
+ FROM public.log_config g
208
+ ) cfg ON true
209
+ -- The cap as the policy's newest change says it (CapSealed): every policy change logs the whole policy as its `value`.
210
+ LEFT JOIN LATERAL (
211
+ SELECT x.number, x.seal, x.payload FROM public.log_changes x WHERE x.object = public.log_object('policy', 'instance') ORDER BY x.number DESC LIMIT 1
212
+ ) lpol ON true
213
+ LEFT JOIN public.credentials c ON c.id = p_credential
214
+ LEFT JOIN public.vault_members m ON m.principal = c.principal
215
+ -- An archived key is gone (`secret.delete`): no object.
216
+ LEFT JOIN public.secrets s ON s.id = p_secret AND s.archived_at IS NULL
217
+ -- A key with no version of its own is a reference while its newest reference row names a source.
218
+ LEFT JOIN LATERAL (
219
+ SELECT y.source_secret_id FROM public.secret_references y WHERE y.secret_id = s.id ORDER BY y.created_seq DESC LIMIT 1
220
+ ) ref ON s.current_version_id IS NULL
221
+ LEFT JOIN public.secrets src ON src.id = ref.source_secret_id AND src.archived_at IS NULL
222
+ LEFT JOIN LATERAL (
223
+ SELECT y.source_secret_id FROM public.secret_references y WHERE y.secret_id = src.id ORDER BY y.created_seq DESC LIMIT 1
224
+ ) xref ON src.current_version_id IS NULL
225
+ -- The value's version: the key's own, or its source's.
226
+ LEFT JOIN public.secret_versions sv ON sv.id = CASE WHEN ref.source_secret_id IS NULL THEN s.current_version_id ELSE src.current_version_id END
227
+ -- The environments `useEnvironments` names, each by the id the rows name, whether or not its row is there: e1 the
228
+ -- place read through, e2 the key's own, e3 its value's (a reference's source's) where that is another, and e4
229
+ -- the parent of its value's when that is a personal config.
230
+ LEFT JOIN LATERAL (
231
+ SELECT CASE WHEN p_place IS DISTINCT FROM s.environment_id THEN p_place END AS e1,
232
+ CASE WHEN src.environment_id IS DISTINCT FROM s.environment_id THEN src.environment_id END AS e3
233
+ ) ids ON true
234
+ LEFT JOIN public.environments e1 ON e1.id = ids.e1
235
+ LEFT JOIN public.environments e2 ON e2.id = s.environment_id
236
+ LEFT JOIN public.environments e3 ON e3.id = ids.e3
237
+ -- The value's environment's row: the key's own, unless it is a reference, whose source is elsewhere or gone.
238
+ LEFT JOIN LATERAL (
239
+ SELECT CASE WHEN ref.source_secret_id IS NULL OR (src.id IS NOT NULL AND ids.e3 IS NULL) THEN e2.owner ELSE e3.owner END AS owner,
240
+ CASE WHEN ref.source_secret_id IS NULL OR (src.id IS NOT NULL AND ids.e3 IS NULL) THEN e2.parent_id ELSE e3.parent_id END AS parent
241
+ ) ve ON true
242
+ LEFT JOIN public.environments e4 ON e4.id = ve.parent AND ve.owner IS NOT NULL
243
+ LEFT JOIN public.log_bulk b ON b.principal = c.principal AND b.secret = p_secret
244
+ ),
245
+ v AS MATERIALIZED (
246
+ SELECT i.*, cap.refresh,
247
+ CASE WHEN i.at_floor IS NULL OR p_at < i.at_floor OR (extract(epoch FROM clock_timestamp()) * 1000)::bigint > p_deadline THEN 'late'
248
+ WHEN p_at > (extract(epoch FROM clock_timestamp()) * 1000)::bigint + 60000 THEN 'ahead'
249
+ WHEN i.cap_count IS NULL OR i.cap_window IS NULL THEN 'unconfigured'
250
+ WHEN i.p IS NOT NULL AND NOT locked AND coalesce(i.bulk_last <= p_at - cap.refresh, true) THEN 'lock' END AS state,
251
+ greatest(lc.number, lm.number, lr.number, i.lpol_number, ls.number, lx.number, l1.number, l2.number, l3.number, l4.number) AS inputs,
252
+ lc.seal AS c_last, lm.seal AS m_last, (lm.payload::jsonb ->> 'old')::bigint AS m_old, lr.seal AS r_last, ls.seal AS s_last, lx.seal AS x_last,
253
+ l1.seal AS e1_last, l2.seal AS e2_last, l3.seal AS e3_last, l4.seal AS e4_last,
254
+ coalesce(i.bulk_last > p_at - i.cap_window - cap.refresh, false) AS is_in,
255
+ coalesce(i.bulk_last <= p_at - cap.refresh, true) AS stale,
256
+ -- The other keys in the set, up to the cap: read only under the principal's lock, for a key not in it.
257
+ CASE WHEN locked AND i.p IS NOT NULL AND NOT coalesce(i.bulk_last > p_at - i.cap_window - cap.refresh, false) THEN (
258
+ SELECT count(*)::integer FROM (
259
+ SELECT 1 FROM public.log_bulk o
260
+ WHERE o.principal = i.p AND o.secret <> p_secret AND o.last > p_at - i.cap_window - cap.refresh - public.deadline_max()
261
+ LIMIT i.cap_count
262
+ ) z
263
+ ) ELSE 0 END AS n_others,
264
+ -- A repeat rides its cover only while every part of this holds: never on a key whose bulk row is missing (CoverFresh).
265
+ coalesce(p_cover IS NOT NULL AND i.bulk_last > p_at - cap.refresh AND p_cover > coalesce(greatest(lc.number, lm.number, lr.number, i.lpol_number, ls.number, lx.number, l1.number, l2.number, l3.number, l4.number), 0)
266
+ AND EXISTS (SELECT 1 FROM public.log_uses u WHERE u.at = p_cover_at AND u.request_id = p_cover_request AND u.number = p_cover
267
+ AND u.credential = p_credential AND u.secret = p_secret AND u.purpose = p_purpose AND u.place IS NOT DISTINCT FROM p_place), false) AS is_covered
268
+ FROM i
269
+ CROSS JOIN LATERAL (SELECT least(60000, i.cap_window / 10) AS refresh) cap
270
+ -- Each input's newest change: one index-only probe each (log_changes_object_idx holds the seal), the principal's
271
+ -- with its payload, which names the 0.14 entry it accounts for.
272
+ LEFT JOIN LATERAL (SELECT x.number, x.seal FROM public.log_changes x WHERE x.object = i.k_credential ORDER BY x.number DESC LIMIT 1) lc ON true
273
+ LEFT JOIN LATERAL (SELECT x.number, x.seal, x.payload FROM public.log_changes x WHERE x.object = i.k_member ORDER BY x.number DESC LIMIT 1) lm ON i.k_member IS NOT NULL
274
+ LEFT JOIN LATERAL (SELECT x.number, x.seal FROM public.log_changes x WHERE x.object = public.log_object('roles', 'instance') ORDER BY x.number DESC LIMIT 1) lr ON true
275
+ LEFT JOIN LATERAL (SELECT x.number, x.seal FROM public.log_changes x WHERE x.object = i.k_secret ORDER BY x.number DESC LIMIT 1) ls ON true
276
+ LEFT JOIN LATERAL (SELECT x.number, x.seal FROM public.log_changes x WHERE x.object = i.k_source ORDER BY x.number DESC LIMIT 1) lx ON i.k_source IS NOT NULL
277
+ LEFT JOIN LATERAL (SELECT x.number, x.seal FROM public.log_changes x WHERE x.object = i.k_e1 ORDER BY x.number DESC LIMIT 1) l1 ON i.k_e1 IS NOT NULL
278
+ LEFT JOIN LATERAL (SELECT x.number, x.seal FROM public.log_changes x WHERE x.object = i.k_e2 ORDER BY x.number DESC LIMIT 1) l2 ON i.k_e2 IS NOT NULL
279
+ LEFT JOIN LATERAL (SELECT x.number, x.seal FROM public.log_changes x WHERE x.object = i.k_e3 ORDER BY x.number DESC LIMIT 1) l3 ON i.k_e3 IS NOT NULL
280
+ LEFT JOIN LATERAL (SELECT x.number, x.seal FROM public.log_changes x WHERE x.object = i.k_e4 ORDER BY x.number DESC LIMIT 1) l4 ON i.k_e4 IS NOT NULL
281
+ ),
282
+ w AS (
283
+ INSERT INTO public.log_uses (number, at, request_id, credential, secret, purpose, mac, place)
284
+ SELECT nextval('public.log_number'), p_at, p_request, p_credential, p_secret, p_purpose, p_mac, p_place
285
+ FROM v WHERE v.state IS NULL AND NOT v.is_covered
286
+ RETURNING log_uses.number
287
+ ),
288
+ k AS (
289
+ INSERT INTO public.log_bulk (principal, secret, last)
290
+ SELECT v.p, p_secret, p_at FROM v
291
+ WHERE v.state IS NULL AND v.p IS NOT NULL AND v.stale AND (v.is_in OR v.n_others < v.cap_count)
292
+ ON CONFLICT ON CONSTRAINT log_bulk_pkey DO UPDATE SET last = EXCLUDED.last
293
+ )
294
+ SELECT v.state, (SELECT w.number FROM w), CASE WHEN v.state IS NULL AND v.is_covered THEN p_cover END, v.newest, v.newest_mac, v.inputs, true,
295
+ v.p, v.c_kind, v.c_scope, v.c_revoked, v.c_expires, v.c_seal, v.c_last,
296
+ v.m_status, v.m_owner, v.m_role, v.m_scope, v.m_seal, v.m_last, v.m_old,
297
+ (SELECT a.seq FROM public.audit_log a WHERE a.author = 'vault' AND a.subject_principal = v.p AND a.decision = 'allow'
298
+ AND a.action IN ('member.add', 'member.restore', 'member.owner', 'member.role', 'member.remove', 'access.grant', 'access.revoke') ORDER BY a.seq DESC LIMIT 1),
299
+ gr.g_project, gr.g_environment, gr.g_slug, gr.g_role, gr.g_expires,
300
+ v.r_value, v.r_seal, v.r_last,
301
+ v.p_value, v.p_seal, v.p_last, v.cap_count, v.cap_window,
302
+ v.s_project, v.s_environment, v.s_version, v.s_source, v.s_seal, v.s_last,
303
+ v.x_project, v.x_environment, v.x_version, v.x_source, v.x_seal, v.x_last,
304
+ v.envelope_version, v.ciphertext, v.iv, v.auth_tag, v.wrapped_dek, v.kek_provider, v.kek_id, v.kek_version, v.sensitive, v.v_plain,
305
+ v.e1_id, v.e1_project, v.e1_slug, v.e1_owner, v.e1_parent, v.e1_protected, v.e1_archived, v.e1_seal, v.e1_last,
306
+ v.e2_id, v.e2_project, v.e2_slug, v.e2_owner, v.e2_parent, v.e2_protected, v.e2_archived, v.e2_seal, v.e2_last,
307
+ v.e3_id, v.e3_project, v.e3_slug, v.e3_owner, v.e3_parent, v.e3_protected, v.e3_archived, v.e3_seal, v.e3_last,
308
+ v.e4_id, v.e4_project, v.e4_slug, v.e4_owner, v.e4_parent, v.e4_protected, v.e4_archived, v.e4_seal, v.e4_last,
309
+ v.is_in, v.n_others
310
+ INTO r
311
+ FROM v
312
+ -- The principal's grants as five lists, one entry per grant, lapsed ones included; an environment's grant with its project, which its row finds through `environments`.
313
+ LEFT JOIN LATERAL (
314
+ SELECT array_agg(coalesce(g.project_id, ge.project_id)) AS g_project, array_agg(g.environment_id) AS g_environment,
315
+ array_agg(g.environment_slug) AS g_slug, array_agg(g.role) AS g_role, array_agg(g.expires_at) AS g_expires
316
+ FROM public.vault_grants g LEFT JOIN public.environments ge ON ge.id = g.environment_id
317
+ WHERE g.principal = v.p
318
+ ) gr ON v.m_status IS NOT NULL;
319
+ ELSIF NOT whole THEN
320
+ -- The isolate's seals, and its cap, read at the change it names: no probe, only whether a change came since
321
+ -- (then nothing is written: 'reprobe').
322
+ WITH i AS MATERIALIZED (
323
+ SELECT n.number AS newest, n.mac AS newest_mac, coalesce(n.number = p_known AND n.mac = p_known_mac, false) AS cached,
324
+ f.at_floor,
325
+ c.principal AS p, c.kind AS c_kind, c.scope AS c_scope, c.revoked_at IS NOT NULL AS c_revoked,
326
+ floor(extract(epoch FROM least(c.expires_at, c.not_after)) * 1000)::bigint AS c_expires, c.seal AS c_seal,
327
+ m.status AS m_status, m.owner AS m_owner, m.role AS m_role, m.scope AS m_scope, m.seal AS m_seal,
328
+ cfg.r_value, cfg.r_seal, cfg.p_value, cfg.p_seal,
329
+ s.project_id AS s_project, s.environment_id AS s_environment, s.current_version_id AS s_version, NULL::uuid AS s_source, s.seal AS s_seal,
330
+ NULL::uuid AS x_project, NULL::uuid AS x_environment, NULL::uuid AS x_version, NULL::uuid AS x_source, NULL::bytea AS x_seal,
331
+ sv.envelope_version, sv.ciphertext, sv.iv, sv.auth_tag, sv.wrapped_dek, sv.kek_provider, sv.kek_id, sv.kek_version, sv.sensitive, sv.plaintext IS NOT NULL AS v_plain,
332
+ NULL::uuid AS e1_id, NULL::uuid AS e1_project, NULL::text AS e1_slug, NULL::text AS e1_owner, NULL::uuid AS e1_parent, NULL::boolean AS e1_protected, NULL::boolean AS e1_archived, NULL::bytea AS e1_seal,
333
+ s.environment_id AS e2_id, e2.project_id AS e2_project, e2.slug AS e2_slug, e2.owner AS e2_owner, e2.parent_id AS e2_parent, e2.protected AS e2_protected, e2.archived_at IS NOT NULL AS e2_archived, e2.seal AS e2_seal,
334
+ NULL::uuid AS e3_id, NULL::uuid AS e3_project, NULL::text AS e3_slug, NULL::text AS e3_owner, NULL::uuid AS e3_parent, NULL::boolean AS e3_protected, NULL::boolean AS e3_archived, NULL::bytea AS e3_seal,
335
+ CASE WHEN e2.owner IS NOT NULL THEN e2.parent_id END AS e4_id, e4.project_id AS e4_project, e4.slug AS e4_slug, e4.owner AS e4_owner, e4.parent_id AS e4_parent, e4.protected AS e4_protected, e4.archived_at IS NOT NULL AS e4_archived, e4.seal AS e4_seal,
336
+ b.last AS bulk_last
337
+ FROM (SELECT 1) one
338
+ LEFT JOIN LATERAL (SELECT x.number, x.mac FROM public.log_changes x ORDER BY x.number DESC LIMIT 1) n ON true
339
+ LEFT JOIN public.log_fence f ON f.only_row
340
+ -- The roles and the policy, both rows in one read of their one-page table.
341
+ LEFT JOIN LATERAL (
342
+ SELECT max(g.value) FILTER (WHERE g.name = 'roles') AS r_value, (array_agg(g.seal) FILTER (WHERE g.name = 'roles'))[1] AS r_seal,
343
+ max(g.value) FILTER (WHERE g.name = 'policy') AS p_value, (array_agg(g.seal) FILTER (WHERE g.name = 'policy'))[1] AS p_seal
344
+ FROM public.log_config g
345
+ ) cfg ON true
346
+ LEFT JOIN public.credentials c ON c.id = p_credential
347
+ LEFT JOIN public.vault_members m ON m.principal = c.principal
348
+ -- An archived key is gone (`secret.delete`): no object.
349
+ LEFT JOIN public.secrets s ON s.id = p_secret AND s.archived_at IS NULL
350
+ -- The key's own version, its own environment, and that environment's parent when it is a personal config.
351
+ LEFT JOIN public.secret_versions sv ON sv.id = s.current_version_id
352
+ LEFT JOIN public.environments e2 ON e2.id = s.environment_id
353
+ LEFT JOIN public.environments e4 ON e4.id = e2.parent_id AND e2.owner IS NOT NULL
354
+ LEFT JOIN public.log_bulk b ON b.principal = c.principal AND b.secret = p_secret
355
+ ),
356
+ v AS MATERIALIZED (
357
+ SELECT i.*, least(60000, p_cap_window / 10) AS refresh,
358
+ CASE WHEN NOT i.cached THEN 'reprobe'
359
+ WHEN i.s_project IS NOT NULL AND i.s_version IS NULL THEN 'whole'
360
+ WHEN i.at_floor IS NULL OR p_at < i.at_floor OR (extract(epoch FROM clock_timestamp()) * 1000)::bigint > p_deadline THEN 'late'
361
+ WHEN p_at > (extract(epoch FROM clock_timestamp()) * 1000)::bigint + 60000 THEN 'ahead'
362
+ WHEN i.p IS NOT NULL AND NOT locked AND coalesce(i.bulk_last <= p_at - least(60000, p_cap_window / 10), true) THEN 'lock' END AS state,
363
+ coalesce(i.bulk_last > p_at - p_cap_window - least(60000, p_cap_window / 10), false) AS is_in,
364
+ coalesce(i.bulk_last <= p_at - least(60000, p_cap_window / 10), true) AS stale,
365
+ -- The other keys in the set, up to the cap: read only under the principal's lock, for a key not in it.
366
+ CASE WHEN locked AND i.p IS NOT NULL AND NOT coalesce(i.bulk_last > p_at - p_cap_window - least(60000, p_cap_window / 10), false) THEN (
367
+ SELECT count(*)::integer FROM (
368
+ SELECT 1 FROM public.log_bulk o
369
+ WHERE o.principal = i.p AND o.secret <> p_secret AND o.last > p_at - p_cap_window - least(60000, p_cap_window / 10) - public.deadline_max()
370
+ LIMIT p_cap_count
371
+ ) z
372
+ ) ELSE 0 END AS n_others,
373
+ -- A repeat rides its cover only while every part of this holds: never on a key whose bulk row is missing (CoverFresh).
374
+ coalesce(p_cover IS NOT NULL AND i.bulk_last > p_at - least(60000, p_cap_window / 10)
375
+ AND EXISTS (SELECT 1 FROM public.log_uses u WHERE u.at = p_cover_at AND u.request_id = p_cover_request AND u.number = p_cover
376
+ AND u.credential = p_credential AND u.secret = p_secret AND u.purpose = p_purpose AND u.place IS NOT DISTINCT FROM p_place), false) AS is_covered
377
+ FROM i
378
+ ),
379
+ w AS (
380
+ INSERT INTO public.log_uses (number, at, request_id, credential, secret, purpose, mac, place)
381
+ SELECT nextval('public.log_number'), p_at, p_request, p_credential, p_secret, p_purpose, p_mac, p_place
382
+ -- The isolate held its cover to the cached seals' numbers when it read them.
383
+ FROM v WHERE v.state IS NULL AND NOT v.is_covered
384
+ RETURNING log_uses.number
385
+ ),
386
+ k AS (
387
+ INSERT INTO public.log_bulk (principal, secret, last)
388
+ SELECT v.p, p_secret, p_at FROM v
389
+ WHERE v.state IS NULL AND v.p IS NOT NULL AND v.stale AND (v.is_in OR v.n_others < p_cap_count)
390
+ ON CONFLICT ON CONSTRAINT log_bulk_pkey DO UPDATE SET last = EXCLUDED.last
391
+ )
392
+ SELECT v.state, (SELECT w.number FROM w), CASE WHEN v.state IS NULL AND v.is_covered THEN p_cover END, v.newest, v.newest_mac, NULL::bigint, false,
393
+ v.p, v.c_kind, v.c_scope, v.c_revoked, v.c_expires, v.c_seal, NULL::bytea,
394
+ v.m_status, v.m_owner, v.m_role, v.m_scope, v.m_seal, NULL::bytea, NULL::bigint,
395
+ (SELECT a.seq FROM public.audit_log a WHERE a.author = 'vault' AND a.subject_principal = v.p AND a.decision = 'allow'
396
+ AND a.action IN ('member.add', 'member.restore', 'member.owner', 'member.role', 'member.remove', 'access.grant', 'access.revoke') ORDER BY a.seq DESC LIMIT 1),
397
+ gr.g_project, gr.g_environment, gr.g_slug, gr.g_role, gr.g_expires,
398
+ v.r_value, v.r_seal, NULL::bytea,
399
+ v.p_value, v.p_seal, NULL::bytea, p_cap_count, p_cap_window,
400
+ v.s_project, v.s_environment, v.s_version, v.s_source, v.s_seal, NULL::bytea,
401
+ v.x_project, v.x_environment, v.x_version, v.x_source, v.x_seal, NULL::bytea,
402
+ v.envelope_version, v.ciphertext, v.iv, v.auth_tag, v.wrapped_dek, v.kek_provider, v.kek_id, v.kek_version, v.sensitive, v.v_plain,
403
+ v.e1_id, v.e1_project, v.e1_slug, v.e1_owner, v.e1_parent, v.e1_protected, v.e1_archived, v.e1_seal, NULL::bytea,
404
+ v.e2_id, v.e2_project, v.e2_slug, v.e2_owner, v.e2_parent, v.e2_protected, v.e2_archived, v.e2_seal, NULL::bytea,
405
+ v.e3_id, v.e3_project, v.e3_slug, v.e3_owner, v.e3_parent, v.e3_protected, v.e3_archived, v.e3_seal, NULL::bytea,
406
+ v.e4_id, v.e4_project, v.e4_slug, v.e4_owner, v.e4_parent, v.e4_protected, v.e4_archived, v.e4_seal, NULL::bytea,
407
+ v.is_in, v.n_others
408
+ INTO r
409
+ FROM v
410
+ -- The principal's grants as five lists, one entry per grant, lapsed ones included; an environment's grant with its project, which its row finds through `environments`.
411
+ LEFT JOIN LATERAL (
412
+ SELECT array_agg(coalesce(g.project_id, ge.project_id)) AS g_project, array_agg(g.environment_id) AS g_environment,
413
+ array_agg(g.environment_slug) AS g_slug, array_agg(g.role) AS g_role, array_agg(g.expires_at) AS g_expires
414
+ FROM public.vault_grants g LEFT JOIN public.environments ge ON ge.id = g.environment_id
415
+ WHERE g.principal = v.p
416
+ ) gr ON v.m_status IS NOT NULL;
417
+ ELSE
418
+ WITH i AS MATERIALIZED (
419
+ SELECT n.number AS newest, n.mac AS newest_mac, coalesce(n.number = p_known AND n.mac = p_known_mac, false) AS cached,
420
+ f.at_floor,
421
+ c.principal AS p, c.kind AS c_kind, c.scope AS c_scope, c.revoked_at IS NOT NULL AS c_revoked,
422
+ floor(extract(epoch FROM least(c.expires_at, c.not_after)) * 1000)::bigint AS c_expires, c.seal AS c_seal,
423
+ m.status AS m_status, m.owner AS m_owner, m.role AS m_role, m.scope AS m_scope, m.seal AS m_seal,
424
+ cfg.r_value, cfg.r_seal, cfg.p_value, cfg.p_seal,
425
+ s.project_id AS s_project, s.environment_id AS s_environment, s.current_version_id AS s_version, ref.source_secret_id AS s_source, s.seal AS s_seal,
426
+ src.project_id AS x_project, src.environment_id AS x_environment, src.current_version_id AS x_version, xref.source_secret_id AS x_source, src.seal AS x_seal,
427
+ sv.envelope_version, sv.ciphertext, sv.iv, sv.auth_tag, sv.wrapped_dek, sv.kek_provider, sv.kek_id, sv.kek_version, sv.sensitive, sv.plaintext IS NOT NULL AS v_plain,
428
+ ids.e1 AS e1_id, e1.project_id AS e1_project, e1.slug AS e1_slug, e1.owner AS e1_owner, e1.parent_id AS e1_parent, e1.protected AS e1_protected, e1.archived_at IS NOT NULL AS e1_archived, e1.seal AS e1_seal,
429
+ s.environment_id AS e2_id, e2.project_id AS e2_project, e2.slug AS e2_slug, e2.owner AS e2_owner, e2.parent_id AS e2_parent, e2.protected AS e2_protected, e2.archived_at IS NOT NULL AS e2_archived, e2.seal AS e2_seal,
430
+ ids.e3 AS e3_id, e3.project_id AS e3_project, e3.slug AS e3_slug, e3.owner AS e3_owner, e3.parent_id AS e3_parent, e3.protected AS e3_protected, e3.archived_at IS NOT NULL AS e3_archived, e3.seal AS e3_seal,
431
+ CASE WHEN ve.owner IS NOT NULL THEN ve.parent END AS e4_id, e4.project_id AS e4_project, e4.slug AS e4_slug, e4.owner AS e4_owner, e4.parent_id AS e4_parent, e4.protected AS e4_protected, e4.archived_at IS NOT NULL AS e4_archived, e4.seal AS e4_seal,
432
+ b.last AS bulk_last
433
+ FROM (SELECT 1) one
434
+ LEFT JOIN LATERAL (SELECT x.number, x.mac FROM public.log_changes x ORDER BY x.number DESC LIMIT 1) n ON true
435
+ LEFT JOIN public.log_fence f ON f.only_row
436
+ -- The roles and the policy, both rows in one read of their one-page table.
437
+ LEFT JOIN LATERAL (
438
+ SELECT max(g.value) FILTER (WHERE g.name = 'roles') AS r_value, (array_agg(g.seal) FILTER (WHERE g.name = 'roles'))[1] AS r_seal,
439
+ max(g.value) FILTER (WHERE g.name = 'policy') AS p_value, (array_agg(g.seal) FILTER (WHERE g.name = 'policy'))[1] AS p_seal
440
+ FROM public.log_config g
441
+ ) cfg ON true
442
+ LEFT JOIN public.credentials c ON c.id = p_credential
443
+ LEFT JOIN public.vault_members m ON m.principal = c.principal
444
+ -- An archived key is gone (`secret.delete`): no object.
445
+ LEFT JOIN public.secrets s ON s.id = p_secret AND s.archived_at IS NULL
446
+ -- A key with no version of its own is a reference while its newest reference row names a source.
447
+ LEFT JOIN LATERAL (
448
+ SELECT y.source_secret_id FROM public.secret_references y WHERE y.secret_id = s.id ORDER BY y.created_seq DESC LIMIT 1
449
+ ) ref ON s.current_version_id IS NULL
450
+ LEFT JOIN public.secrets src ON src.id = ref.source_secret_id AND src.archived_at IS NULL
451
+ LEFT JOIN LATERAL (
452
+ SELECT y.source_secret_id FROM public.secret_references y WHERE y.secret_id = src.id ORDER BY y.created_seq DESC LIMIT 1
453
+ ) xref ON src.current_version_id IS NULL
454
+ -- The value's version: the key's own, or its source's.
455
+ LEFT JOIN public.secret_versions sv ON sv.id = CASE WHEN ref.source_secret_id IS NULL THEN s.current_version_id ELSE src.current_version_id END
456
+ -- The environments `useEnvironments` names, each by the id the rows name, whether or not its row is there: e1 the
457
+ -- place read through, e2 the key's own, e3 its value's (a reference's source's) where that is another, and e4
458
+ -- the parent of its value's when that is a personal config.
459
+ LEFT JOIN LATERAL (
460
+ SELECT CASE WHEN p_place IS DISTINCT FROM s.environment_id THEN p_place END AS e1,
461
+ CASE WHEN src.environment_id IS DISTINCT FROM s.environment_id THEN src.environment_id END AS e3
462
+ ) ids ON true
463
+ LEFT JOIN public.environments e1 ON e1.id = ids.e1
464
+ LEFT JOIN public.environments e2 ON e2.id = s.environment_id
465
+ LEFT JOIN public.environments e3 ON e3.id = ids.e3
466
+ -- The value's environment's row: the key's own, unless it is a reference, whose source is elsewhere or gone.
467
+ LEFT JOIN LATERAL (
468
+ SELECT CASE WHEN ref.source_secret_id IS NULL OR (src.id IS NOT NULL AND ids.e3 IS NULL) THEN e2.owner ELSE e3.owner END AS owner,
469
+ CASE WHEN ref.source_secret_id IS NULL OR (src.id IS NOT NULL AND ids.e3 IS NULL) THEN e2.parent_id ELSE e3.parent_id END AS parent
470
+ ) ve ON true
471
+ LEFT JOIN public.environments e4 ON e4.id = ve.parent AND ve.owner IS NOT NULL
472
+ LEFT JOIN public.log_bulk b ON b.principal = c.principal AND b.secret = p_secret
473
+ ),
474
+ v AS MATERIALIZED (
475
+ SELECT i.*, least(60000, p_cap_window / 10) AS refresh,
476
+ CASE WHEN NOT i.cached THEN 'reprobe'
477
+ WHEN i.at_floor IS NULL OR p_at < i.at_floor OR (extract(epoch FROM clock_timestamp()) * 1000)::bigint > p_deadline THEN 'late'
478
+ WHEN p_at > (extract(epoch FROM clock_timestamp()) * 1000)::bigint + 60000 THEN 'ahead'
479
+ WHEN i.p IS NOT NULL AND NOT locked AND coalesce(i.bulk_last <= p_at - least(60000, p_cap_window / 10), true) THEN 'lock' END AS state,
480
+ coalesce(i.bulk_last > p_at - p_cap_window - least(60000, p_cap_window / 10), false) AS is_in,
481
+ coalesce(i.bulk_last <= p_at - least(60000, p_cap_window / 10), true) AS stale,
482
+ -- The other keys in the set, up to the cap: read only under the principal's lock, for a key not in it.
483
+ CASE WHEN locked AND i.p IS NOT NULL AND NOT coalesce(i.bulk_last > p_at - p_cap_window - least(60000, p_cap_window / 10), false) THEN (
484
+ SELECT count(*)::integer FROM (
485
+ SELECT 1 FROM public.log_bulk o
486
+ WHERE o.principal = i.p AND o.secret <> p_secret AND o.last > p_at - p_cap_window - least(60000, p_cap_window / 10) - public.deadline_max()
487
+ LIMIT p_cap_count
488
+ ) z
489
+ ) ELSE 0 END AS n_others,
490
+ -- A repeat rides its cover only while every part of this holds: never on a key whose bulk row is missing (CoverFresh).
491
+ coalesce(p_cover IS NOT NULL AND i.bulk_last > p_at - least(60000, p_cap_window / 10)
492
+ AND EXISTS (SELECT 1 FROM public.log_uses u WHERE u.at = p_cover_at AND u.request_id = p_cover_request AND u.number = p_cover
493
+ AND u.credential = p_credential AND u.secret = p_secret AND u.purpose = p_purpose AND u.place IS NOT DISTINCT FROM p_place), false) AS is_covered
494
+ FROM i
495
+ ),
496
+ w AS (
497
+ INSERT INTO public.log_uses (number, at, request_id, credential, secret, purpose, mac, place)
498
+ SELECT nextval('public.log_number'), p_at, p_request, p_credential, p_secret, p_purpose, p_mac, p_place
499
+ -- The isolate held its cover to the cached seals' numbers when it read them.
500
+ FROM v WHERE v.state IS NULL AND NOT v.is_covered
501
+ RETURNING log_uses.number
502
+ ),
503
+ k AS (
504
+ INSERT INTO public.log_bulk (principal, secret, last)
505
+ SELECT v.p, p_secret, p_at FROM v
506
+ WHERE v.state IS NULL AND v.p IS NOT NULL AND v.stale AND (v.is_in OR v.n_others < p_cap_count)
507
+ ON CONFLICT ON CONSTRAINT log_bulk_pkey DO UPDATE SET last = EXCLUDED.last
508
+ )
509
+ SELECT v.state, (SELECT w.number FROM w), CASE WHEN v.state IS NULL AND v.is_covered THEN p_cover END, v.newest, v.newest_mac, NULL::bigint, false,
510
+ v.p, v.c_kind, v.c_scope, v.c_revoked, v.c_expires, v.c_seal, NULL::bytea,
511
+ v.m_status, v.m_owner, v.m_role, v.m_scope, v.m_seal, NULL::bytea, NULL::bigint,
512
+ (SELECT a.seq FROM public.audit_log a WHERE a.author = 'vault' AND a.subject_principal = v.p AND a.decision = 'allow'
513
+ AND a.action IN ('member.add', 'member.restore', 'member.owner', 'member.role', 'member.remove', 'access.grant', 'access.revoke') ORDER BY a.seq DESC LIMIT 1),
514
+ gr.g_project, gr.g_environment, gr.g_slug, gr.g_role, gr.g_expires,
515
+ v.r_value, v.r_seal, NULL::bytea,
516
+ v.p_value, v.p_seal, NULL::bytea, p_cap_count, p_cap_window,
517
+ v.s_project, v.s_environment, v.s_version, v.s_source, v.s_seal, NULL::bytea,
518
+ v.x_project, v.x_environment, v.x_version, v.x_source, v.x_seal, NULL::bytea,
519
+ v.envelope_version, v.ciphertext, v.iv, v.auth_tag, v.wrapped_dek, v.kek_provider, v.kek_id, v.kek_version, v.sensitive, v.v_plain,
520
+ v.e1_id, v.e1_project, v.e1_slug, v.e1_owner, v.e1_parent, v.e1_protected, v.e1_archived, v.e1_seal, NULL::bytea,
521
+ v.e2_id, v.e2_project, v.e2_slug, v.e2_owner, v.e2_parent, v.e2_protected, v.e2_archived, v.e2_seal, NULL::bytea,
522
+ v.e3_id, v.e3_project, v.e3_slug, v.e3_owner, v.e3_parent, v.e3_protected, v.e3_archived, v.e3_seal, NULL::bytea,
523
+ v.e4_id, v.e4_project, v.e4_slug, v.e4_owner, v.e4_parent, v.e4_protected, v.e4_archived, v.e4_seal, NULL::bytea,
524
+ v.is_in, v.n_others
525
+ INTO r
526
+ FROM v
527
+ -- The principal's grants as five lists, one entry per grant, lapsed ones included; an environment's grant with its project, which its row finds through `environments`.
528
+ LEFT JOIN LATERAL (
529
+ SELECT array_agg(coalesce(g.project_id, ge.project_id)) AS g_project, array_agg(g.environment_id) AS g_environment,
530
+ array_agg(g.environment_slug) AS g_slug, array_agg(g.role) AS g_role, array_agg(g.expires_at) AS g_expires
531
+ FROM public.vault_grants g LEFT JOIN public.environments ge ON ge.id = g.environment_id
532
+ WHERE g.principal = v.p
533
+ ) gr ON v.m_status IS NOT NULL;
534
+ END IF;
535
+ -- A use the statement took writes its entry or rides its cover, never neither.
536
+ IF r.state IS NULL AND r.number IS NULL AND r.covered IS NULL THEN
537
+ RAISE EXCEPTION 'a use wrote no entry and rode no cover' USING ERRCODE = 'XX000';
538
+ END IF;
539
+ IF r.state = 'reprobe' THEN probe := true; CONTINUE; END IF;
540
+ IF r.state = 'whole' THEN whole := true; CONTINUE; END IF;
541
+ EXIT WHEN r.state IS DISTINCT FROM 'lock';
542
+ -- Its key's bulk row is stale or missing: this use may write it, so it takes its principal's lock and reads again.
543
+ PERFORM pg_advisory_xact_lock(1668244066, hashtext(r.c_principal));
544
+ locked := true;
545
+ END LOOP;
546
+ -- The deadline holds in Postgres to the end: a call that ran past it rolls back and writes nothing. The hot login's lock_timeout and statement_timeout bound the waits.
547
+ IF r.state IS NULL AND (extract(epoch FROM clock_timestamp()) * 1000)::bigint > p_deadline THEN
548
+ RAISE EXCEPTION 'past its deadline' USING ERRCODE = '57014';
549
+ END IF;
550
+ RETURN to_json(r);
551
+ END
552
+ $$;
553
+ --> statement-breakpoint
554
+ REVOKE ALL ON FUNCTION use_credential(uuid, uuid, bigint, bigint, smallint, uuid, bytea, bigint, bigint, bigint, bigint, bigint, bytea, integer, bigint) FROM PUBLIC;
555
+ --> statement-breakpoint
556
+ GRANT EXECUTE ON FUNCTION use_credential(uuid, uuid, bigint, bigint, smallint, uuid, bytea, bigint, bigint, bigint, bigint, bigint, bytea, integer, bigint) TO coffre_vault;