@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
package/src/schema.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  integer,
10
10
  pgTable,
11
11
  primaryKey,
12
+ smallint,
12
13
  text,
13
14
  timestamp,
14
15
  unique,
@@ -17,7 +18,8 @@ import {
17
18
  type AnyPgColumn,
18
19
  } from 'drizzle-orm/pg-core';
19
20
 
20
- import { ACTIVE_SUBJECT, relationsOf } from './relations.ts';
21
+ import { ACTIVE_SUBJECT } from './active-subject.ts';
22
+ import { recorded } from './change-records.ts';
21
23
 
22
24
  const bytea = customType<{ data: Buffer }>({
23
25
  dataType: () => 'bytea',
@@ -74,8 +76,15 @@ export const environments = pgTable(
74
76
  // A personal config's owner, `user:<email>`: a fork whose values only they
75
77
  // read, the vault binding each to them. Set when it is made and never after.
76
78
  owner: text(),
79
+ // Whether it is protected, on 0.15's environment object (0.14 keeps it in
80
+ // the policy's entries). Null until 0.15's cutover writes it.
81
+ protected: boolean(),
82
+ // 0.15's seal (0022_log_015): the vault's MAC over the object this row is part of, HMAC-SHA256 cut to 16
83
+ // bytes, the same seal its newest change in `log_changes` records. Null until the row is sealed.
84
+ seal: bytea(),
77
85
  },
78
86
  (table) => [
87
+ check('environments_seal_check', sql`${table.seal} IS NULL OR octet_length(${table.seal}) = 16`),
79
88
  unique('environments_project_id_slug_key').on(table.projectId, table.slug),
80
89
  unique('environments_project_scoped').on(table.id, table.projectId),
81
90
  check('environments_slug_check', sql`${table.slug} ~ ${PLACE_SLUG}`),
@@ -118,8 +127,12 @@ export const secrets = pgTable(
118
127
  archivedAt: timestamp('archived_at', { withTimezone: true }),
119
128
  // Within its environment: the same key elsewhere has its own.
120
129
  folder: text(),
130
+ // 0.15's seal (0022_log_015): the vault's MAC over the object this row is part of, HMAC-SHA256 cut to 16
131
+ // bytes, the same seal its newest change in `log_changes` records. Null until the row is sealed.
132
+ seal: bytea(),
121
133
  },
122
134
  (table) => [
135
+ check('secrets_seal_check', sql`${table.seal} IS NULL OR octet_length(${table.seal}) = 16`),
123
136
  unique('secrets_project_id_environment_id_key_key').on(
124
137
  table.projectId,
125
138
  table.environmentId,
@@ -143,6 +156,8 @@ export const secrets = pgTable(
143
156
  foreignColumns: [environments.id, environments.projectId],
144
157
  }),
145
158
  index('secrets_lookup_idx').on(table.projectId, table.environmentId, table.key),
159
+ // The live keys of an environment, which a read by environment alone (the places tree's counts, `liveKeysIn`) walks without the archived ones beside them; the one above leads with the project (T152, 0032).
160
+ index('secrets_live_idx').on(table.environmentId).where(sql`${table.archivedAt} IS NULL`),
146
161
  ],
147
162
  );
148
163
 
@@ -257,8 +272,12 @@ export const vaultMembers = pgTable(
257
272
  // The vault's MAC over the row and the member's grants, lapsed ones too:
258
273
  // a grant added, edited or deleted outside the vault fails it.
259
274
  mac: bytea().notNull(),
275
+ // 0.15's seal (0022_log_015): the vault's MAC over the object this row is part of, HMAC-SHA256 cut to 16
276
+ // bytes, the same seal its newest change in `log_changes` records. Null until the row is sealed.
277
+ seal: bytea(),
260
278
  },
261
279
  (table) => [
280
+ check('vault_members_seal_check', sql`${table.seal} IS NULL OR octet_length(${table.seal}) = 16`),
262
281
  check('vault_members_mac_check', sql`octet_length(${table.mac}) = 32`),
263
282
  check('vault_members_principal_check', sql`${table.principal} ~ '^(user|token):[^[:space:]:][^[:space:]]*$'`),
264
283
  // A person is their email address, lowercased, as in sign-in.
@@ -378,6 +397,10 @@ export const auditLog = pgTable(
378
397
  prevHash: bytea('prev_hash').notNull(),
379
398
  mac: bytea().notNull(),
380
399
  hash: bytea().notNull(),
400
+ // Set on every entry a writer under 0.15's log appends, and covered by no MAC: once the cutover is there, an
401
+ // allowed write without it is 0.14's code, which migration 0047's trigger refuses (docs/design/0.15-plan.md,
402
+ // "As built: PR 9b"). Gone with 0.14's appends in 0.16.
403
+ log015: boolean('log015'),
381
404
  },
382
405
  (table) => [
383
406
  check('audit_log_author_check', sql`${table.author} IN ('app', 'vault')`),
@@ -485,6 +508,10 @@ export const secretReferences = pgTable(
485
508
  createdSeq: bigint('created_seq', { mode: 'bigint' }).notNull(),
486
509
  createdAt: createdAt(),
487
510
  createdBy: text('created_by').notNull(),
511
+ // 0.15's end of the reference (0028): when its holder stopped reading its source, broken, replaced or cut, written
512
+ // with 0.14's `reference.end` in the vault's decision. The holder's object reads its newest row's source only while
513
+ // this is null. Null on rows 0.14 ended: 0.14's log holds those ends, which the cutover (plan PR 9) writes here.
514
+ endedAt: timestamp('ended_at', { withTimezone: true }),
488
515
  },
489
516
  (table) => [
490
517
  unique('secret_references_created_seq_key').on(table.createdSeq),
@@ -598,14 +625,21 @@ export const serviceBindings = pgTable(
598
625
  scope: text(),
599
626
  // What a workload's binding says beyond its claims (`BindingPolicy`), canonical JSON: null on a binding `/api/auth/oidc` takes, never on another (docs/design/workload-access.md, section 11).
600
627
  policy: text(),
628
+ // The instance it was made on, its public URL, which its object's seal covers (0040_binding_origin; PR 3b): null on one
629
+ // made before, which is named on the instance's public URL.
630
+ origin: text(),
601
631
  label: text(),
602
632
  createdAt: createdAt(),
603
633
  createdBy: text('created_by').notNull(),
604
634
  lastUsedAt: timestamp('last_used_at', { withTimezone: true }),
605
635
  revokedAt: timestamp('revoked_at', { withTimezone: true }),
606
636
  revokedBy: text('revoked_by'),
637
+ // 0.15's seal (0022_log_015): the vault's MAC over the object this row is part of, HMAC-SHA256 cut to 16
638
+ // bytes, the same seal its newest change in `log_changes` records. Null until the row is sealed.
639
+ seal: bytea(),
607
640
  },
608
641
  (table) => [
642
+ check('service_bindings_seal_check', sql`${table.seal} IS NULL OR octet_length(${table.seal}) = 16`),
609
643
  check('service_bindings_principal_check', sql`${table.principal} LIKE 'token:%'`),
610
644
  check('service_bindings_auth_mac_check', sql`octet_length(${table.authMac}) = 32`),
611
645
  check('service_bindings_claims_check', sql`${table.claims}::jsonb IS NOT NULL`),
@@ -717,8 +751,12 @@ export const mcpConnections = pgTable(
717
751
  lastUsedIp: text('last_used_ip'),
718
752
  revokedAt: timestamp('revoked_at', { withTimezone: true }),
719
753
  revokedBy: text('revoked_by'),
754
+ // 0.15's seal (T147): the vault's MAC over the object this row is part of, HMAC-SHA256 cut to 16 bytes, the same
755
+ // seal its newest change in `log_changes` records. Null until the row is sealed.
756
+ seal: bytea(),
720
757
  },
721
758
  (table) => [
759
+ check('mcp_connections_seal_check', sql`${table.seal} IS NULL OR octet_length(${table.seal}) = 16`),
722
760
  check('mcp_connections_principal_check', sql`${table.principal} LIKE 'user:%'`),
723
761
  check('mcp_connections_registration_check', sql`${table.registration} IN ('cimd', 'dcr')`),
724
762
  check('mcp_connections_auth_mac_check', sql`octet_length(${table.authMac}) = 32`),
@@ -862,8 +900,12 @@ export const credentials = pgTable(
862
900
  enrollmentId: uuid('enrollment_id'),
863
901
  approvedAt: timestamp('approved_at', { withTimezone: true }),
864
902
  approvedBy: text('approved_by'),
903
+ // 0.15's seal (0022_log_015): the vault's MAC over the object this row is part of, HMAC-SHA256 cut to 16
904
+ // bytes, the same seal its newest change in `log_changes` records. Null until the row is sealed.
905
+ seal: bytea(),
865
906
  },
866
907
  (table) => [
908
+ check('credentials_seal_check', sql`${table.seal} IS NULL OR octet_length(${table.seal}) = 16`),
867
909
  unique('credentials_token_hash_key').on(table.tokenHash),
868
910
  check('credentials_kind_check', sql`${table.kind} IN ('browser', 'cli', 'service', 'personal', 'handle', 'workload')`),
869
911
  check('credentials_principal_check', sql`${table.principal} LIKE 'user:%' OR ${table.principal} LIKE 'token:%'`),
@@ -949,8 +991,251 @@ export const deviceAuthorizations = pgTable(
949
991
  ],
950
992
  );
951
993
 
952
- // For Drizzle's relational queries; see relations.ts.
953
- export const {
954
- environmentsRelations,
955
- secretsRelations,
956
- } = relationsOf({ projects, environments, secrets, secretVersions });
994
+ /**
995
+ * 0.15's configuration objects (0022_log_015): decision inputs no other row
996
+ * holds, one row each, sealed as every input is. `policy` holds the bulk
997
+ * cap's count and window, which a use reads in its own statement; `roles`
998
+ * the role tables. JSON, as the vault writes it. Written only through
999
+ * `change_input`.
1000
+ */
1001
+ export const logConfig = pgTable(
1002
+ 'log_config',
1003
+ {
1004
+ name: text().primaryKey(),
1005
+ value: text().notNull(),
1006
+ seal: bytea(),
1007
+ },
1008
+ (table) => [
1009
+ check('log_config_name_check', sql`${table.name} IN ('policy', 'roles')`),
1010
+ check('log_config_value_check', sql`${table.value}::jsonb IS NOT NULL`),
1011
+ check('log_config_seal_check', sql`${table.seal} IS NULL OR octet_length(${table.seal}) = 16`),
1012
+ ],
1013
+ );
1014
+
1015
+ /**
1016
+ * 0.15's log (0022_log_015, docs/design/hot-path-one.md): two tables numbered
1017
+ * from one sequence, `log_number`, each number taken under the fence's lock
1018
+ * (a credential's issue: under the issue lock, which the barrier waits for;
1019
+ * 0026), so that the number alone orders uses against changes. Nothing reads or
1020
+ * writes them before 0.15's vault; the vault login reads them and writes
1021
+ * them only through the migration's functions.
1022
+ *
1023
+ * A change: any write to a decision input, or to configuration, kept for
1024
+ * good. `object` is the input it changes (`log_object(kind, id)`), `seal`
1025
+ * that object's seal after it, `payload` what it does. The MAC is the
1026
+ * vault's, 16 bytes, its key's tag then HMAC-SHA256 cut to 12 (D162), over everything but the number,
1027
+ * which the vault does not know when it asks. The vault login reads them,
1028
+ * and the app's reads the changes too, for the audit pages: a sign-in
1029
+ * through a trust binding, and the vault's token issues, revokes and
1030
+ * sign-outs, are recorded there alone (D147, T156, T164).
1031
+ */
1032
+ export const logChanges = pgTable(
1033
+ 'log_changes',
1034
+ {
1035
+ number: bigint({ mode: 'bigint' }).primaryKey(),
1036
+ // Milliseconds since the epoch, the vault's clock when it asked.
1037
+ at: bigint({ mode: 'number' }).notNull(),
1038
+ // A request id names one change: a retry finds its entry.
1039
+ requestId: text('request_id').notNull(),
1040
+ object: uuid().notNull(),
1041
+ actor: text().notNull(),
1042
+ payload: text().notNull(),
1043
+ seal: bytea(),
1044
+ mac: bytea().notNull(),
1045
+ },
1046
+ (table) => [
1047
+ unique('log_changes_request_id_key').on(table.requestId),
1048
+ check('log_changes_seal_check', sql`${table.seal} IS NULL OR octet_length(${table.seal}) = 16`),
1049
+ check('log_changes_mac_check', sql`octet_length(${table.mac}) = 16`),
1050
+ // Each input's newest change, its seal from the index alone (INCLUDE (seal), in the migration's SQL: Drizzle
1051
+ // has no INCLUDE): one probe per input.
1052
+ index('log_changes_object_idx').on(table.object, table.number),
1053
+ // The sign-ins through trust bindings, newest first by when the vault asked (`at`; its number comes later, after
1054
+ // any wait), every service's and one's: what the audit pages list where 0.14's `token.exchange` stood (T156). Only
1055
+ // a sign-in's payload holds a `signIn` object (change-records.ts, which the reads repeat).
1056
+ index('log_changes_sign_in_idx').on(table.at, table.number).where(recorded('postgres', table.payload, 'signIn')),
1057
+ index('log_changes_sign_in_actor_idx').on(table.actor, table.at, table.number).where(recorded('postgres', table.payload, 'signIn')),
1058
+ // The vault's token issues, revokes and sign-outs, ordered as the sign-ins, every member's and one's: what the audit
1059
+ // pages list where 0.14's `token.create`, `token.revoke` and `sign_out` stood, and count while they hide them (T164).
1060
+ // Only an issue's payload, by how it starts, holds an `issue` record, and only a revoke's these reasons
1061
+ // (change-records.ts). A binding's end revokes its credentials with the reason `unbound`, of which 0.14 logged
1062
+ // none: left out.
1063
+ index('log_changes_issue_revoke_idx').on(table.at, table.number).where(recorded('postgres', table.payload, 'issue', 'revoke', 'signOut')),
1064
+ index('log_changes_issue_revoke_actor_idx').on(table.actor, table.at, table.number).where(recorded('postgres', table.payload, 'issue', 'revoke', 'signOut')),
1065
+ ],
1066
+ );
1067
+
1068
+ /**
1069
+ * A use, the slim row (R53): no principal (the credential's), no
1070
+ * environment (the secret's, or `place`), no kind (the table says it).
1071
+ * Partitioned by the day of `at` (`make_day`), which retention drops whole;
1072
+ * fixed-width columns first, and eight of them, so the row pads to nothing. One index, the
1073
+ * replay's: a retried call repeats its `at` and request id, both under its
1074
+ * MAC, and lands in the same day.
1075
+ */
1076
+ export const logUses = pgTable(
1077
+ 'log_uses',
1078
+ {
1079
+ number: bigint({ mode: 'bigint' }).notNull(),
1080
+ at: bigint({ mode: 'number' }).notNull(),
1081
+ // Random, unique within its millisecond.
1082
+ requestId: bigint('request_id', { mode: 'bigint' }).notNull(),
1083
+ credential: uuid().notNull(),
1084
+ secret: uuid().notNull(),
1085
+ // What the use was for, as @coffre/vault codes it (a use, a reveal…), plus 256 when it may keep a sensitive value:
1086
+ // one column, so that the row has eight and its null bitmap fits the header's padding (8 B a use, measured).
1087
+ purpose: smallint().notNull(),
1088
+ mac: bytea().notNull(),
1089
+ // The environment it was asked at, when not the secret's own (a fork, a reference).
1090
+ place: uuid(),
1091
+ },
1092
+ (table) => [
1093
+ check('log_uses_mac_check', sql`octet_length(${table.mac}) = 16`),
1094
+ uniqueIndex('log_uses_request_idx').on(table.at, table.requestId),
1095
+ ],
1096
+ );
1097
+
1098
+ /**
1099
+ * 0.15's reads as the audit pages list them (T166; formal/Reads.tla): a
1100
+ * projection of each use a signed pair seals, written by the vault's
1101
+ * projector apart from the checkpointer, from the sealed objects at the
1102
+ * use's number: its account, its key's place, the fork it was read in,
1103
+ * through a reference the source and its place, the version read; under the
1104
+ * vault's MAC over all of it, the number included. Partitioned by the day of
1105
+ * `at`, as the uses are (`make_reads_day`), and dropped with them.
1106
+ */
1107
+ export const logReads = pgTable(
1108
+ 'log_reads',
1109
+ {
1110
+ number: bigint({ mode: 'bigint' }).notNull(),
1111
+ at: bigint({ mode: 'number' }).notNull(),
1112
+ credential: uuid().notNull(),
1113
+ purpose: smallint().notNull(),
1114
+ principal: text().notNull(),
1115
+ secret: uuid().notNull(),
1116
+ environment: uuid().notNull(),
1117
+ project: uuid().notNull(),
1118
+ place: uuid(),
1119
+ source: uuid(),
1120
+ sourceEnvironment: uuid('source_environment'),
1121
+ sourceProject: uuid('source_project'),
1122
+ version: uuid(),
1123
+ mac: bytea().notNull(),
1124
+ },
1125
+ (table) => [
1126
+ check('log_reads_mac_check', sql`octet_length(${table.mac}) = 16`),
1127
+ uniqueIndex('log_reads_number_idx').on(table.at, table.number),
1128
+ index('log_reads_principal_idx').on(table.principal, table.at, table.number),
1129
+ ],
1130
+ );
1131
+
1132
+ /** The days each member's reads are in (`log_reads`), so that a page of theirs reads those days alone: the projector writes them with the reads. */
1133
+ export const logReadDays = pgTable(
1134
+ 'log_read_days',
1135
+ { principal: text().notNull(), day: integer().notNull() },
1136
+ (table) => [primaryKey({ name: 'log_read_days_pkey', columns: [table.principal, table.day] })],
1137
+ );
1138
+
1139
+ /** How far the projector has gone: the number every use at or below it is projected, and when. */
1140
+ export const logReadsCursor = pgTable(
1141
+ 'log_reads_cursor',
1142
+ {
1143
+ onlyRow: boolean('only_row').primaryKey().default(true),
1144
+ through: bigint({ mode: 'bigint' }).notNull(),
1145
+ // The signed pair at or below `through`, whose floor of `at` the next read of uses takes.
1146
+ pair: bigint({ mode: 'bigint' }).notNull(),
1147
+ at: bigint({ mode: 'number' }).notNull(),
1148
+ // The vault's MAC over the three (formal/Reads.tla, CursorMac): a cursor moved ahead would have the pages list reads as
1149
+ // whole that were never projected. Null at the start, before any read is projected.
1150
+ mac: bytea(),
1151
+ },
1152
+ (table) => [
1153
+ check('log_reads_cursor_only_row_check', sql`${table.onlyRow}`),
1154
+ check('log_reads_cursor_mac_check', sql`${table.mac} IS NULL OR octet_length(${table.mac}) = 16`),
1155
+ ],
1156
+ );
1157
+
1158
+ /**
1159
+ * The bulk cap (R28, R52): each principal's last logged use of each key, as
1160
+ * the start of that use's refresh period, written only by a use that holds
1161
+ * the principal's lock (exclusive to enter a key, shared to move it on:
1162
+ * 0028), at most once per refresh per key. A row older than the window, a
1163
+ * refresh, the deadline and a minute counts for nothing; `prune_bulk`
1164
+ * forgets it an hour after that.
1165
+ */
1166
+ export const logBulk = pgTable(
1167
+ 'log_bulk',
1168
+ {
1169
+ principal: text().notNull(),
1170
+ secret: uuid().notNull(),
1171
+ last: bigint({ mode: 'number' }).notNull(),
1172
+ },
1173
+ (table) => [primaryKey({ name: 'log_bulk_pkey', columns: [table.principal, table.secret] })],
1174
+ );
1175
+
1176
+ /**
1177
+ * The fence's one row: the lowest `at` a use may still be written with,
1178
+ * raised by each checkpoint's barrier (`close_one`), never lowered. Made
1179
+ * by the migration; without it every use is refused.
1180
+ */
1181
+ export const logFence = pgTable(
1182
+ 'log_fence',
1183
+ {
1184
+ onlyRow: boolean('only_row').primaryKey().default(true),
1185
+ atFloor: bigint('at_floor', { mode: 'number' }).notNull(),
1186
+ },
1187
+ (table) => [check('log_fence_only_row_check', sql`${table.onlyRow}`)],
1188
+ );
1189
+
1190
+ /**
1191
+ * A signed checkpoint: the pair of tree heads (W212), changes and uses, at
1192
+ * one `upto`, every number at or below which is final. One row, so that no
1193
+ * state holds one head without the other. `at_floor` is the floor its
1194
+ * barrier set: the next window's uses have an `at` at or above it.
1195
+ */
1196
+ export const logCheckpoints = pgTable(
1197
+ 'log_checkpoints',
1198
+ {
1199
+ upto: bigint({ mode: 'bigint' }).primaryKey(),
1200
+ at: bigint({ mode: 'number' }).notNull(),
1201
+ atFloor: bigint('at_floor', { mode: 'number' }).notNull(),
1202
+ changesSize: bigint('changes_size', { mode: 'bigint' }).notNull(),
1203
+ changesRoot: bytea('changes_root').notNull(),
1204
+ // The signed note (C2SP tlog-checkpoint), as witnesses get it.
1205
+ changesNote: text('changes_note').notNull(),
1206
+ usesSize: bigint('uses_size', { mode: 'bigint' }).notNull(),
1207
+ usesRoot: bytea('uses_root').notNull(),
1208
+ usesNote: text('uses_note').notNull(),
1209
+ },
1210
+ (table) => [
1211
+ check('log_checkpoints_changes_root_check', sql`octet_length(${table.changesRoot}) = 32`),
1212
+ check('log_checkpoints_uses_root_check', sql`octet_length(${table.usesRoot}) = 32`),
1213
+ // A day's retention cut, the first checkpoint whose floor is past the day's end (migration 0027).
1214
+ index('log_checkpoints_floor_idx').on(table.atFloor, table.upto),
1215
+ ],
1216
+ );
1217
+
1218
+ /**
1219
+ * The two trees' tiles (C2SP tlog-tiles: height 8, up to 256 hashes of one
1220
+ * level). Partitioned by tree; the uses tree's leaf tiles (level 0) by
1221
+ * `leaf`, the first leaf a tile covers (`make_tiles`), so that retention
1222
+ * drops a table rather than deleting a day's rows (W212); its levels above,
1223
+ * which span far past their first leaf, in one table (W217).
1224
+ */
1225
+ export const logTiles = pgTable(
1226
+ 'log_tiles',
1227
+ {
1228
+ tree: text().notNull(),
1229
+ level: smallint().notNull(),
1230
+ // idx · 256^(level + 1): a tile's place, in its tree's leaves.
1231
+ leaf: bigint({ mode: 'bigint' }).notNull(),
1232
+ hashes: bytea().notNull(),
1233
+ },
1234
+ (table) => [
1235
+ primaryKey({ name: 'log_tiles_pkey', columns: [table.tree, table.level, table.leaf] }),
1236
+ check('log_tiles_tree_check', sql`${table.tree} IN ('changes', 'uses')`),
1237
+ check('log_tiles_level_check', sql`${table.level} BETWEEN 0 AND 7`),
1238
+ check('log_tiles_leaf_check', sql`${table.leaf} >= 0 AND ${table.leaf} % (256::numeric ^ (${table.level} + 1)) = 0`),
1239
+ check('log_tiles_hashes_check', sql`octet_length(${table.hashes}) BETWEEN 32 AND 8192 AND octet_length(${table.hashes}) % 32 = 0`),
1240
+ ],
1241
+ );
@@ -1,7 +1,5 @@
1
1
  import assert from 'node:assert/strict';
2
- import pg from 'pg';
3
-
4
- import { postgresConnection } from './postgres.ts';
2
+ import { postgresClient } from './postgres.ts';
5
3
 
6
4
  function requiredEnvironment(name: string): string {
7
5
  const value = process.env[name]?.trim();
@@ -10,19 +8,15 @@ function requiredEnvironment(name: string): string {
10
8
  }
11
9
 
12
10
  const expectedRole = requiredEnvironment('COFFRE_RUNTIME_ROLE');
13
- const pool = new pg.Pool({
14
- application_name: 'coffre-runtime-verification',
15
- ...postgresConnection(requiredEnvironment('DATABASE_URL')),
16
- max: 1,
17
- });
11
+ const client = postgresClient(requiredEnvironment('DATABASE_URL'), { application_name: 'coffre-runtime-verification', max: 1 });
18
12
 
19
13
  try {
20
- const identity = await pool.query<{
14
+ const identity = await client.unsafe<{
21
15
  current_user: string;
22
16
  memberships: string[];
23
17
  safe_membership: boolean;
24
18
  unsafe_attributes: boolean;
25
- }>(
19
+ }[]>(
26
20
  `SELECT current_user,
27
21
  (
28
22
  role.rolsuper OR role.rolcreatedb OR role.rolcreaterole
@@ -47,14 +41,14 @@ try {
47
41
  FROM pg_roles role
48
42
  WHERE role.rolname = current_user`,
49
43
  );
50
- assert.deepEqual(identity.rows, [{
44
+ assert.deepEqual([...identity], [{
51
45
  current_user: expectedRole,
52
46
  memberships: ['coffre_app'],
53
47
  safe_membership: true,
54
48
  unsafe_attributes: false,
55
49
  }]);
56
50
 
57
- const ownership = await pool.query<{ object_count: number }>(
51
+ const ownership = await client.unsafe<{ object_count: number }[]>(
58
52
  `SELECT count(*)::int AS object_count
59
53
  FROM pg_shdepend dependency
60
54
  JOIN pg_roles role ON role.oid = dependency.refobjid
@@ -62,9 +56,9 @@ try {
62
56
  AND dependency.deptype = 'o'
63
57
  AND role.rolname = current_user`,
64
58
  );
65
- assert.equal(ownership.rows[0]?.object_count, 0, 'runtime role owns database objects');
59
+ assert.equal(ownership[0]?.object_count, 0, 'runtime role owns database objects');
66
60
 
67
- const privileges = await pool.query<{
61
+ const privileges = await client.unsafe<{
68
62
  audit_delete: boolean;
69
63
  audit_truncate: boolean;
70
64
  audit_update: boolean;
@@ -74,7 +68,7 @@ try {
74
68
  head_hash_update: boolean;
75
69
  migrations_select: boolean;
76
70
  schema_create: boolean;
77
- }>(
71
+ }[]>(
78
72
  `SELECT has_database_privilege(current_user, current_database(), 'CREATE') AS database_create,
79
73
  has_database_privilege(current_user, current_database(), 'TEMPORARY') AS database_temporary,
80
74
  has_schema_privilege(current_user, 'public', 'CREATE') AS schema_create,
@@ -91,7 +85,7 @@ try {
91
85
  current_user, 'drizzle.__drizzle_migrations', 'SELECT'
92
86
  ) AS migrations_select`,
93
87
  );
94
- assert.deepEqual(privileges.rows, [{
88
+ assert.deepEqual([...privileges], [{
95
89
  audit_delete: false,
96
90
  audit_truncate: false,
97
91
  audit_update: false,
@@ -103,20 +97,15 @@ try {
103
97
  schema_create: false,
104
98
  }]);
105
99
 
106
- const client = await pool.connect();
107
- try {
108
- await client.query('BEGIN');
109
- // What every append does to the log's head, rolled back.
110
- const head = await client.query(
111
- `UPDATE audit_chain_head SET next_seq = next_seq, head_hash = head_hash WHERE only_row`,
112
- );
113
- assert.equal(head.rowCount, 1, 'runtime head update did not affect the singleton');
114
- } finally {
115
- await client.query('ROLLBACK');
116
- client.release();
117
- }
100
+ // What every append does to the log's head, rolled back.
101
+ const rolledBack = new Error('rolled back');
102
+ await assert.rejects(client.begin(async (tx) => {
103
+ const head = await tx.unsafe(`UPDATE audit_chain_head SET next_seq = next_seq, head_hash = head_hash WHERE only_row`);
104
+ assert.equal(head.count, 1, 'runtime head update did not affect the singleton');
105
+ throw rolledBack;
106
+ }), (error) => error === rolledBack);
118
107
 
119
108
  console.log(`runtime database role ${expectedRole} passed privilege verification`);
120
109
  } finally {
121
- await pool.end();
110
+ await client.end();
122
111
  }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Erwin Kuhn
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,73 +0,0 @@
1
- import { g as schema_d_exports } from "./schema-BfQEBqFv.js";
2
- import { Query, Table } from "drizzle-orm";
3
- import { NodePgDatabase } from "drizzle-orm/node-postgres";
4
- import pg from "pg";
5
- //#region src/prepared.d.ts
6
- /** The part of a select builder `prepared` reads, once. */
7
- type Select = PromiseLike<unknown> & {
8
- toSQL(): Query;
9
- prepare(...name: never[]): unknown;
10
- };
11
- /**
12
- * A select built once per process, or once per isolate on Workers, and run
13
- * with new values each time. Drizzle rebuilds a query's SQL from its
14
- * builder at every run, and that was most of a short query's CPU; this
15
- * builds it from `build` the first time a database of its dialect asks,
16
- * then hands the SQL and its columns to that call's own session
17
- * (`session.prepareQuery`), which binds `values` to the placeholders
18
- * (`sql.placeholder`) and maps the rows as the builder would.
19
- *
20
- * `build` must not depend on anything but the database's dialect, and the
21
- * values go to the driver as they are, not through a column's encoding:
22
- * strings, numbers, bigints and bytes.
23
- *
24
- * const member = prepared((db) => db.select().from(tablesOf(db).vaultMembers).where(eq(…, sql.placeholder('principal'))));
25
- * const [row] = await member(db, { principal });
26
- */
27
- declare function prepared<Q extends Select>(build: (db: Queryable) => Q): (db: Queryable, values?: Record<string, unknown>) => Promise<Awaited<Q>>;
28
- //#endregion
29
- //#region src/database.d.ts
30
- /**
31
- * A database, typed as Postgres whichever it is; see portable.ts for why
32
- * that holds. connect.ts opens one of each from a URL.
33
- */
34
- type Database = NodePgDatabase<typeof schema_d_exports>;
35
- type Transaction = Parameters<Parameters<Database['transaction']>[0]>[0];
36
- /** Anything a read can run on: the database, or an open transaction. */
37
- type Queryable = Database | Transaction;
38
- /**
39
- * What Drizzle needs from a connection pool. A `pg.Pool` fits, and so does the
40
- * per-invocation Hyperdrive pool, whose class name ends in `Pool` for the
41
- * same reason: that is how Drizzle knows to check out a client for a
42
- * transaction rather than run BEGIN on a shared connection.
43
- */
44
- type PoolLike = pg.Pool | {
45
- query: pg.Pool['query'];
46
- connect: () => Promise<Pick<pg.PoolClient, 'query'> & {
47
- release: () => void;
48
- }>;
49
- };
50
- /**
51
- * A query made on the database from inside one of its own transactions,
52
- * rather than through the transaction. With one connection per call, as on
53
- * Workers, it would wait for the transaction, which waits for it: a call
54
- * Cloudflare cancels as hung, and nothing says why. So it is refused at
55
- * once, with this, wherever the database runs.
56
- */
57
- declare class QueryOutsideTransaction extends Error {
58
- constructor();
59
- }
60
- declare function createDatabase(pool: PoolLike): Database;
61
- /**
62
- * The tables of the database's own dialect: the Postgres ones on Postgres,
63
- * the SQLite ones on SQLite. Every query builds on these.
64
- */
65
- declare function tablesOf(db: Queryable): typeof schema_d_exports;
66
- /**
67
- * The database's own twin of `table`, which may be another dialect's: the
68
- * server names tables by importing schema.ts, and the database decides
69
- * which one that means.
70
- */
71
- declare function own<T extends Table>(db: Queryable, table: T): T;
72
- //#endregion
73
- export { Transaction as a, tablesOf as c, Queryable as i, prepared as l, PoolLike as n, createDatabase as o, QueryOutsideTransaction as r, own as s, Database as t };
@@ -1,29 +0,0 @@
1
- import { checkServerIdentity } from "node:tls";
2
- import { parseIntoClientConfig } from "pg-connection-string";
3
- //#region src/postgres.ts
4
- /** Keep libpq's system-root shortcut from being read as a certificate filename by pg. */
5
- function postgresConnection(url) {
6
- let parsed;
7
- try {
8
- parsed = new URL(url);
9
- } catch {
10
- throw new Error("invalid Postgres URL");
11
- }
12
- const parameters = parsed.searchParams;
13
- if (parameters.getAll("sslrootcert").at(-1) !== "system") return { connectionString: url };
14
- const mode = parameters.getAll("sslmode").at(-1);
15
- if (mode !== void 0 && mode !== "verify-full") throw new Error("sslrootcert=system requires sslmode=verify-full");
16
- parameters.delete("sslrootcert");
17
- parameters.set("sslmode", "verify-full");
18
- const config = parseIntoClientConfig(parsed.toString());
19
- const host = config.host;
20
- if (!host) throw new Error("sslrootcert=system requires a Postgres hostname");
21
- config.ssl = {
22
- ...config.ssl,
23
- rejectUnauthorized: true,
24
- checkServerIdentity: (_hostname, certificate) => checkServerIdentity(host, certificate)
25
- };
26
- return config;
27
- }
28
- //#endregion
29
- export { postgresConnection as t };
@@ -1,38 +0,0 @@
1
- import { relations, sql } from "drizzle-orm";
2
- //#region src/relations.ts
3
- /**
4
- * What both schemas share beyond their columns: the relations and the
5
- * one generated column.
6
- *
7
- * Relations are for Drizzle's relational queries (`db.query.secrets.findMany({ with })`), which read a row and what hangs off it in one
8
- * statement on every dialect. They add no constraints; the foreign keys in
9
- * each schema do that. Each schema calls this with its own tables, so a
10
- * relational query on SQLite joins SQLite tables.
11
- */
12
- function relationsOf(t) {
13
- return {
14
- environmentsRelations: relations(t.environments, ({ one }) => ({ project: one(t.projects, {
15
- fields: [t.environments.projectId],
16
- references: [t.projects.id]
17
- }) })),
18
- secretsRelations: relations(t.secrets, ({ one }) => ({
19
- project: one(t.projects, {
20
- fields: [t.secrets.projectId],
21
- references: [t.projects.id]
22
- }),
23
- environment: one(t.environments, {
24
- fields: [t.secrets.environmentId],
25
- references: [t.environments.id]
26
- })
27
- }))
28
- };
29
- }
30
- /**
31
- * `identities.active_subject`: the subject while the account is bound, null
32
- * once revoked. A unique index on (provider, issuer_hash, active_subject)
33
- * binds an account to one person at a time. Plain column names, so the
34
- * expression reads the same on both databases.
35
- */
36
- const ACTIVE_SUBJECT = sql`CASE WHEN revoked_at IS NULL THEN subject END`;
37
- //#endregion
38
- export { relationsOf as n, ACTIVE_SUBJECT as t };