@coffre/db 0.0.0-stage → 0.1.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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +14 -2
  3. package/dist/commits-BuW-NL3n.js +30 -0
  4. package/dist/connect.d.ts +21 -0
  5. package/dist/connect.js +155 -0
  6. package/dist/database-CWpo5aF0.d.ts +39 -0
  7. package/dist/dialect.d.ts +88 -0
  8. package/dist/dialect.js +128 -0
  9. package/dist/hyperdrive.d.ts +32 -0
  10. package/dist/hyperdrive.js +53 -0
  11. package/dist/index.d.ts +3 -0
  12. package/dist/index.js +34 -0
  13. package/dist/log.d.ts +72 -0
  14. package/dist/log.js +133 -0
  15. package/dist/migrate.d.ts +10 -0
  16. package/dist/migrate.js +129 -0
  17. package/dist/migrations/postgres/0000_baseline.sql +502 -0
  18. package/dist/migrations/postgres/meta/0000_snapshot.json +1939 -0
  19. package/dist/migrations/postgres/meta/_journal.json +13 -0
  20. package/dist/migrations/sqlite/0000_baseline.sql +293 -0
  21. package/dist/migrations/sqlite/meta/0000_snapshot.json +1811 -0
  22. package/dist/migrations/sqlite/meta/_journal.json +13 -0
  23. package/dist/portable.d.ts +33 -0
  24. package/dist/portable.js +12 -0
  25. package/dist/relations-DqgxtlKu.js +58 -0
  26. package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
  27. package/dist/schema-Cd4WOnB5.d.ts +2503 -0
  28. package/dist/schema-sqlite.d.ts +2587 -0
  29. package/dist/schema-sqlite.js +401 -0
  30. package/dist/schema-version.d.ts +11 -0
  31. package/dist/schema-version.js +16 -0
  32. package/dist/schema.d.ts +2 -0
  33. package/dist/schema.js +448 -0
  34. package/package.json +91 -3
  35. package/src/baseline/postgres.sql +238 -0
  36. package/src/baseline/sqlite.sql +27 -0
  37. package/src/baseline.ts +104 -0
  38. package/src/commits.ts +47 -0
  39. package/src/connect.ts +168 -0
  40. package/src/database.ts +63 -0
  41. package/src/dialect.ts +174 -0
  42. package/src/drizzle.postgres.config.ts +7 -0
  43. package/src/drizzle.sqlite.config.ts +7 -0
  44. package/src/hyperdrive.ts +62 -0
  45. package/src/log.ts +177 -0
  46. package/src/migrate-main.ts +8 -0
  47. package/src/migrate.ts +206 -0
  48. package/src/migrations/postgres/0000_baseline.sql +502 -0
  49. package/src/migrations/postgres/meta/0000_snapshot.json +1939 -0
  50. package/src/migrations/postgres/meta/_journal.json +13 -0
  51. package/src/migrations/sqlite/0000_baseline.sql +293 -0
  52. package/src/migrations/sqlite/meta/0000_snapshot.json +1811 -0
  53. package/src/migrations/sqlite/meta/_journal.json +13 -0
  54. package/src/portable.ts +54 -0
  55. package/src/relations.ts +53 -0
  56. package/src/schema-version.ts +13 -0
  57. package/src/schema.sqlite.ts +511 -0
  58. package/src/schema.ts +548 -0
  59. package/src/verify-runtime.ts +120 -0
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": "7",
3
+ "dialect": "postgresql",
4
+ "entries": [
5
+ {
6
+ "idx": 0,
7
+ "version": "7",
8
+ "when": 1790874461895,
9
+ "tag": "0000_baseline",
10
+ "breakpoints": true
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,293 @@
1
+ -- The schema on SQLite. Generated by `pnpm db:generate` from
2
+ -- baseline/sqlite.sql and src/schema.sqlite.ts; edit those, not this.
3
+
4
+ CREATE TABLE `audit_chain_head` (
5
+ `only_row` integer PRIMARY KEY DEFAULT true NOT NULL,
6
+ `next_seq` integer DEFAULT 0 NOT NULL,
7
+ `head_hash` blob NOT NULL,
8
+ CONSTRAINT "audit_chain_head_only_row_check" CHECK("audit_chain_head"."only_row"),
9
+ CONSTRAINT "audit_chain_head_next_seq_check" CHECK("audit_chain_head"."next_seq" >= 0),
10
+ CONSTRAINT "audit_chain_head_head_hash_check" CHECK(octet_length("audit_chain_head"."head_hash") = 32)
11
+ );
12
+ --> statement-breakpoint
13
+ CREATE TABLE `audit_log` (
14
+ `seq` integer PRIMARY KEY NOT NULL,
15
+ `author` text NOT NULL,
16
+ `key_id` text NOT NULL,
17
+ `occurred_at` integer NOT NULL,
18
+ `actor` text NOT NULL,
19
+ `action` text NOT NULL,
20
+ `decision` text NOT NULL,
21
+ `code` text,
22
+ `subject_principal` text,
23
+ `project_id` text,
24
+ `environment_id` text,
25
+ `secret_id` text,
26
+ `secret_version_id` text,
27
+ `operation_id` text,
28
+ `request_id` text,
29
+ `source_ip` text,
30
+ `related_seq` integer,
31
+ `metadata` text DEFAULT '{}' NOT NULL,
32
+ `prev_hash` blob NOT NULL,
33
+ `mac` blob NOT NULL,
34
+ `hash` blob NOT NULL,
35
+ FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE restrict,
36
+ FOREIGN KEY (`environment_id`) REFERENCES `environments`(`id`) ON UPDATE no action ON DELETE restrict,
37
+ FOREIGN KEY (`secret_id`) REFERENCES `secrets`(`id`) ON UPDATE no action ON DELETE restrict,
38
+ FOREIGN KEY (`secret_version_id`) REFERENCES `secret_versions`(`id`) ON UPDATE no action ON DELETE restrict,
39
+ FOREIGN KEY (`related_seq`) REFERENCES `audit_log`(`seq`) ON UPDATE no action ON DELETE restrict,
40
+ CONSTRAINT "audit_log_author_check" CHECK("audit_log"."author" IN ('app', 'vault')),
41
+ CONSTRAINT "audit_log_actor_check" CHECK("audit_log"."actor" GLOB 'user:?*' OR "audit_log"."actor" GLOB 'token:?*' OR "audit_log"."actor" GLOB 'sync:?*' OR "audit_log"."actor" GLOB 'system:?*'),
42
+ CONSTRAINT "audit_log_decision_check" CHECK("audit_log"."decision" IN ('allow', 'deny')),
43
+ CONSTRAINT "audit_log_metadata_check" CHECK(json_valid("audit_log"."metadata")),
44
+ CONSTRAINT "audit_log_prev_hash_check" CHECK(octet_length("audit_log"."prev_hash") = 32),
45
+ CONSTRAINT "audit_log_mac_check" CHECK(octet_length("audit_log"."mac") = 32),
46
+ CONSTRAINT "audit_log_hash_check" CHECK(octet_length("audit_log"."hash") = 32)
47
+ );
48
+ --> statement-breakpoint
49
+ CREATE INDEX `audit_log_project_idx` ON `audit_log` (`project_id`,`seq`);--> statement-breakpoint
50
+ CREATE INDEX `audit_log_environment_idx` ON `audit_log` (`environment_id`,`seq`);--> statement-breakpoint
51
+ CREATE INDEX `audit_log_secret_idx` ON `audit_log` (`secret_id`,`seq`);--> statement-breakpoint
52
+ CREATE INDEX `audit_log_actor_idx` ON `audit_log` (`actor`,`seq`);--> statement-breakpoint
53
+ CREATE INDEX `audit_log_operation_idx` ON `audit_log` (`operation_id`,`seq`);--> statement-breakpoint
54
+ CREATE INDEX `audit_log_action_idx` ON `audit_log` (`author`,`action`,`seq`);--> statement-breakpoint
55
+ CREATE INDEX `audit_log_releases_idx` ON `audit_log` (`author`,`actor`,`action`,`decision`,`occurred_at`);--> statement-breakpoint
56
+ CREATE INDEX `audit_log_subject_idx` ON `audit_log` (`author`,`subject_principal`,`seq`);--> statement-breakpoint
57
+ CREATE TABLE `credentials` (
58
+ `id` text PRIMARY KEY NOT NULL,
59
+ `kind` text NOT NULL,
60
+ `auth_mac` blob NOT NULL,
61
+ `token_hash` blob NOT NULL,
62
+ `token_hint` text NOT NULL,
63
+ `generation` integer NOT NULL,
64
+ `principal` text NOT NULL,
65
+ `identity_id` text,
66
+ `label` text,
67
+ `created_at` integer DEFAULT (CAST(unixepoch('subsec') * 1000 AS INTEGER)) NOT NULL,
68
+ `created_by` text NOT NULL,
69
+ `expires_at` integer NOT NULL,
70
+ `last_used_at` integer,
71
+ `last_used_ip` text,
72
+ `revoked_at` integer,
73
+ `revoked_by` text,
74
+ FOREIGN KEY (`principal`) REFERENCES `vault_members`(`principal`) ON UPDATE no action ON DELETE restrict,
75
+ FOREIGN KEY (`identity_id`,`principal`,`generation`) REFERENCES `identities`(`id`,`principal`,`generation`) ON UPDATE no action ON DELETE restrict,
76
+ CONSTRAINT "credentials_kind_check" CHECK("credentials"."kind" IN ('browser', 'cli', 'service')),
77
+ CONSTRAINT "credentials_principal_check" CHECK("credentials"."principal" LIKE 'user:%' OR "credentials"."principal" LIKE 'token:%'),
78
+ CONSTRAINT "credentials_kind_matches_principal" CHECK(("credentials"."kind" = 'service') = ("credentials"."principal" LIKE 'token:%')),
79
+ CONSTRAINT "credentials_token_hash_check" CHECK(octet_length("credentials"."token_hash") = 32),
80
+ CONSTRAINT "credentials_auth_mac_check" CHECK(octet_length("credentials"."auth_mac") = 32)
81
+ );
82
+ --> statement-breakpoint
83
+ CREATE INDEX `credentials_principal_idx` ON `credentials` (`principal`,`generation`);--> statement-breakpoint
84
+ CREATE UNIQUE INDEX `credentials_token_hash_key` ON `credentials` (`token_hash`);--> statement-breakpoint
85
+ CREATE TABLE `device_authorizations` (
86
+ `id` text PRIMARY KEY NOT NULL,
87
+ `device_code_hash` blob NOT NULL,
88
+ `auth_mac` blob NOT NULL,
89
+ `user_code` text NOT NULL,
90
+ `client_label` text,
91
+ `client_ip` text,
92
+ `created_at` integer DEFAULT (CAST(unixepoch('subsec') * 1000 AS INTEGER)) NOT NULL,
93
+ `expires_at` integer NOT NULL,
94
+ `decided_at` integer,
95
+ `decision` text,
96
+ `generation` integer DEFAULT 0 NOT NULL,
97
+ `principal` text,
98
+ `consumed_at` integer,
99
+ FOREIGN KEY (`principal`) REFERENCES `vault_members`(`principal`) ON UPDATE no action ON DELETE restrict,
100
+ CONSTRAINT "device_authorizations_auth_mac_check" CHECK(octet_length("device_authorizations"."auth_mac") = 32),
101
+ CONSTRAINT "device_authorizations_decision_check" CHECK("device_authorizations"."decision" IS NULL OR "device_authorizations"."decision" IN ('approved', 'denied')),
102
+ CONSTRAINT "device_authorizations_approval_names_principal" CHECK(("device_authorizations"."decision" IS NULL AND "device_authorizations"."decided_at" IS NULL AND "device_authorizations"."principal" IS NULL AND "device_authorizations"."generation" = 0 AND "device_authorizations"."consumed_at" IS NULL)
103
+ OR ("device_authorizations"."decision" IS NOT NULL AND "device_authorizations"."decided_at" IS NOT NULL AND (
104
+ ("device_authorizations"."decision" = 'denied' AND "device_authorizations"."principal" IS NULL AND "device_authorizations"."generation" = 0 AND "device_authorizations"."consumed_at" IS NULL)
105
+ OR ("device_authorizations"."decision" = 'approved' AND "device_authorizations"."principal" IS NOT NULL AND "device_authorizations"."principal" LIKE 'user:%')
106
+ )))
107
+ );
108
+ --> statement-breakpoint
109
+ CREATE UNIQUE INDEX `device_authorizations_device_code_hash_key` ON `device_authorizations` (`device_code_hash`);--> statement-breakpoint
110
+ CREATE UNIQUE INDEX `device_authorizations_user_code_key` ON `device_authorizations` (`user_code`);--> statement-breakpoint
111
+ CREATE TABLE `environments` (
112
+ `id` text PRIMARY KEY NOT NULL,
113
+ `project_id` text NOT NULL,
114
+ `slug` text NOT NULL,
115
+ `name` text NOT NULL,
116
+ `created_at` integer DEFAULT (CAST(unixepoch('subsec') * 1000 AS INTEGER)) NOT NULL,
117
+ `archived_at` integer,
118
+ FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE restrict,
119
+ CONSTRAINT "environments_slug_check" CHECK(length("environments"."slug") BETWEEN 1 AND 63 AND "environments"."slug" GLOB '[a-z0-9]*' AND "environments"."slug" NOT GLOB '*[^a-z0-9-]*')
120
+ );
121
+ --> statement-breakpoint
122
+ CREATE UNIQUE INDEX `environments_project_id_slug_key` ON `environments` (`project_id`,`slug`);--> statement-breakpoint
123
+ CREATE UNIQUE INDEX `environments_project_scoped` ON `environments` (`id`,`project_id`);--> statement-breakpoint
124
+ CREATE TABLE `identities` (
125
+ `id` text PRIMARY KEY NOT NULL,
126
+ `provider` text NOT NULL,
127
+ `auth_mac` blob NOT NULL,
128
+ `subject` text NOT NULL,
129
+ `issuer_hash` text NOT NULL,
130
+ `generation` integer NOT NULL,
131
+ `principal` text NOT NULL,
132
+ `email` text,
133
+ `created_at` integer DEFAULT (CAST(unixepoch('subsec') * 1000 AS INTEGER)) NOT NULL,
134
+ `created_by` text NOT NULL,
135
+ `last_sign_in_at` integer,
136
+ `revoked_at` integer,
137
+ `revoked_by` text,
138
+ `active_subject` text GENERATED ALWAYS AS (CASE WHEN revoked_at IS NULL THEN subject END) STORED,
139
+ FOREIGN KEY (`principal`) REFERENCES `vault_members`(`principal`) ON UPDATE no action ON DELETE restrict,
140
+ CONSTRAINT "identities_principal_check" CHECK("identities"."principal" LIKE 'user:%'),
141
+ CONSTRAINT "identities_provider_check" CHECK(length("identities"."provider") BETWEEN 1 AND 32 AND "identities"."provider" GLOB '[a-z0-9]*' AND "identities"."provider" NOT GLOB '*[^a-z0-9-]*'),
142
+ CONSTRAINT "identities_auth_mac_check" CHECK(octet_length("identities"."auth_mac") = 32)
143
+ );
144
+ --> statement-breakpoint
145
+ CREATE UNIQUE INDEX `identities_active_subject` ON `identities` (`provider`,`issuer_hash`,`active_subject`);--> statement-breakpoint
146
+ CREATE INDEX `identities_principal_idx` ON `identities` (`principal`,`generation`);--> statement-breakpoint
147
+ CREATE UNIQUE INDEX `identities_member_generation_key` ON `identities` (`id`,`principal`,`generation`);--> statement-breakpoint
148
+ CREATE TABLE `projects` (
149
+ `id` text PRIMARY KEY NOT NULL,
150
+ `slug` text NOT NULL,
151
+ `name` text NOT NULL,
152
+ `created_at` integer DEFAULT (CAST(unixepoch('subsec') * 1000 AS INTEGER)) NOT NULL,
153
+ `archived_at` integer,
154
+ CONSTRAINT "projects_slug_check" CHECK(length("projects"."slug") BETWEEN 1 AND 63 AND "projects"."slug" GLOB '[a-z0-9]*' AND "projects"."slug" NOT GLOB '*[^a-z0-9-]*')
155
+ );
156
+ --> statement-breakpoint
157
+ CREATE UNIQUE INDEX `projects_slug_key` ON `projects` (`slug`);--> statement-breakpoint
158
+ CREATE TABLE `secret_versions` (
159
+ `id` text PRIMARY KEY NOT NULL,
160
+ `secret_id` text NOT NULL,
161
+ `version` integer NOT NULL,
162
+ `envelope_version` integer NOT NULL,
163
+ `ciphertext` blob NOT NULL,
164
+ `iv` blob NOT NULL,
165
+ `auth_tag` blob NOT NULL,
166
+ `wrapped_dek` blob NOT NULL,
167
+ `kek_provider` text NOT NULL,
168
+ `kek_id` text NOT NULL,
169
+ `kek_version` text NOT NULL,
170
+ `created_at` integer DEFAULT (CAST(unixepoch('subsec') * 1000 AS INTEGER)) NOT NULL,
171
+ `created_by` text NOT NULL,
172
+ FOREIGN KEY (`secret_id`) REFERENCES `secrets`(`id`) ON UPDATE no action ON DELETE restrict,
173
+ CONSTRAINT "secret_versions_version_check" CHECK("secret_versions"."version" > 0),
174
+ CONSTRAINT "secret_versions_iv_check" CHECK(octet_length("secret_versions"."iv") = 12),
175
+ CONSTRAINT "secret_versions_auth_tag_check" CHECK(octet_length("secret_versions"."auth_tag") = 16)
176
+ );
177
+ --> statement-breakpoint
178
+ CREATE INDEX `secret_versions_secret_idx` ON `secret_versions` (`secret_id`,`version`);--> statement-breakpoint
179
+ CREATE UNIQUE INDEX `secret_versions_secret_id_version_key` ON `secret_versions` (`secret_id`,`version`);--> statement-breakpoint
180
+ CREATE TABLE `secrets` (
181
+ `id` text PRIMARY KEY NOT NULL,
182
+ `project_id` text NOT NULL,
183
+ `environment_id` text NOT NULL,
184
+ `key` text NOT NULL,
185
+ `created_at` integer DEFAULT (CAST(unixepoch('subsec') * 1000 AS INTEGER)) NOT NULL,
186
+ `updated_at` integer DEFAULT (CAST(unixepoch('subsec') * 1000 AS INTEGER)) NOT NULL,
187
+ `current_version_id` text,
188
+ `current_version` integer DEFAULT 0 NOT NULL,
189
+ `archived_at` integer,
190
+ FOREIGN KEY (`current_version_id`) REFERENCES `secret_versions`(`id`) ON UPDATE no action ON DELETE no action,
191
+ FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE restrict,
192
+ FOREIGN KEY (`environment_id`) REFERENCES `environments`(`id`) ON UPDATE no action ON DELETE restrict,
193
+ FOREIGN KEY (`environment_id`,`project_id`) REFERENCES `environments`(`id`,`project_id`) ON UPDATE no action ON DELETE no action,
194
+ CONSTRAINT "secrets_key_check" CHECK(length("secrets"."key") BETWEEN 1 AND 128 AND "secrets"."key" GLOB '[A-Za-z_]*' AND "secrets"."key" NOT GLOB '*[^A-Za-z0-9_]*')
195
+ );
196
+ --> statement-breakpoint
197
+ CREATE INDEX `secrets_lookup_idx` ON `secrets` (`project_id`,`environment_id`,`key`);--> statement-breakpoint
198
+ CREATE UNIQUE INDEX `secrets_project_id_environment_id_key_key` ON `secrets` (`project_id`,`environment_id`,`key`);--> statement-breakpoint
199
+ CREATE TABLE `sync_keys` (
200
+ `sync_id` text NOT NULL,
201
+ `key` text NOT NULL,
202
+ `secret_version_id` text,
203
+ `pushed_at` integer DEFAULT (CAST(unixepoch('subsec') * 1000 AS INTEGER)) NOT NULL,
204
+ `removed_at` integer,
205
+ PRIMARY KEY(`sync_id`, `key`),
206
+ FOREIGN KEY (`sync_id`) REFERENCES `syncs`(`id`) ON UPDATE no action ON DELETE restrict,
207
+ FOREIGN KEY (`secret_version_id`) REFERENCES `secret_versions`(`id`) ON UPDATE no action ON DELETE restrict
208
+ );
209
+ --> statement-breakpoint
210
+ CREATE TABLE `syncs` (
211
+ `id` text PRIMARY KEY NOT NULL,
212
+ `project_id` text NOT NULL,
213
+ `environment_id` text NOT NULL,
214
+ `provider` text NOT NULL,
215
+ `config` text NOT NULL,
216
+ `credential_secret_id` text NOT NULL,
217
+ `created_at` integer DEFAULT (CAST(unixepoch('subsec') * 1000 AS INTEGER)) NOT NULL,
218
+ `created_by` text NOT NULL,
219
+ `paused_at` integer,
220
+ `archived_at` integer,
221
+ `lease_until` integer,
222
+ `last_run_at` integer,
223
+ `last_status` text,
224
+ `last_error` text,
225
+ FOREIGN KEY (`environment_id`,`project_id`) REFERENCES `environments`(`id`,`project_id`) ON UPDATE no action ON DELETE restrict,
226
+ FOREIGN KEY (`credential_secret_id`) REFERENCES `secrets`(`id`) ON UPDATE no action ON DELETE restrict,
227
+ CONSTRAINT "syncs_config_check" CHECK(json_valid("syncs"."config")),
228
+ CONSTRAINT "syncs_last_status_check" CHECK("syncs"."last_status" IS NULL OR "syncs"."last_status" IN ('ok', 'partial', 'failed'))
229
+ );
230
+ --> statement-breakpoint
231
+ CREATE INDEX `syncs_environment_idx` ON `syncs` (`environment_id`);--> statement-breakpoint
232
+ CREATE TABLE `vault_grants` (
233
+ `principal` text NOT NULL,
234
+ `project_id` text,
235
+ `environment_id` text,
236
+ `role` text NOT NULL,
237
+ `expires_at` integer,
238
+ `granted_at` integer NOT NULL,
239
+ `granted_by` text NOT NULL,
240
+ FOREIGN KEY (`principal`) REFERENCES `vault_members`(`principal`) ON UPDATE no action ON DELETE restrict,
241
+ FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE restrict,
242
+ FOREIGN KEY (`environment_id`) REFERENCES `environments`(`id`) ON UPDATE no action ON DELETE restrict,
243
+ CONSTRAINT "vault_grants_one_place" CHECK(("vault_grants"."project_id" IS NULL) <> ("vault_grants"."environment_id" IS NULL)),
244
+ CONSTRAINT "vault_grants_role_check" CHECK("vault_grants"."role" IN ('viewer', 'developer', 'maintainer', 'access-manager', 'auditor', 'owner')),
245
+ CONSTRAINT "vault_grants_environment_role_check" CHECK("vault_grants"."environment_id" IS NULL OR "vault_grants"."role" IN ('viewer', 'developer', 'auditor'))
246
+ );
247
+ --> statement-breakpoint
248
+ CREATE UNIQUE INDEX `vault_grants_on_project` ON `vault_grants` (`principal`,`project_id`);--> statement-breakpoint
249
+ CREATE UNIQUE INDEX `vault_grants_on_environment` ON `vault_grants` (`principal`,`environment_id`);--> statement-breakpoint
250
+ CREATE TABLE `vault_members` (
251
+ `principal` text PRIMARY KEY NOT NULL,
252
+ `status` text NOT NULL,
253
+ `owner` integer DEFAULT false NOT NULL,
254
+ `generation` integer DEFAULT 0 NOT NULL,
255
+ `created_at` integer NOT NULL,
256
+ `created_by` text NOT NULL,
257
+ `status_changed_at` integer NOT NULL,
258
+ `status_changed_by` text NOT NULL,
259
+ `access_seq` integer NOT NULL,
260
+ `mac` blob NOT NULL,
261
+ CONSTRAINT "vault_members_mac_check" CHECK(octet_length("vault_members"."mac") = 32),
262
+ CONSTRAINT "vault_members_principal_check" CHECK(("vault_members"."principal" GLOB 'user:?*' OR "vault_members"."principal" GLOB 'token:?*' OR "vault_members"."principal" GLOB 'sync:?*')
263
+ AND substr("vault_members"."principal", instr("vault_members"."principal", ':') + 1, 1) <> ':'
264
+ AND instr("vault_members"."principal", ' ') = 0 AND instr("vault_members"."principal", char(9)) = 0
265
+ AND instr("vault_members"."principal", char(10)) = 0 AND instr("vault_members"."principal", char(13)) = 0),
266
+ CONSTRAINT "vault_members_user_lowercase" CHECK("vault_members"."principal" NOT LIKE 'user:%' OR "vault_members"."principal" = lower("vault_members"."principal")),
267
+ CONSTRAINT "vault_members_status_check" CHECK("vault_members"."status" IN ('active', 'removed')),
268
+ CONSTRAINT "vault_members_owner_check" CHECK(NOT "vault_members"."owner" OR ("vault_members"."status" = 'active' AND "vault_members"."principal" LIKE 'user:%')),
269
+ CONSTRAINT "vault_members_generation_check" CHECK("vault_members"."generation" >= 0)
270
+ );--> statement-breakpoint
271
+
272
+ -- The chain starts at sequence 0 from 32 zero bytes.
273
+ INSERT INTO `audit_chain_head` (`only_row`, `next_seq`, `head_hash`) VALUES (true, 0, zeroblob(32));
274
+ --> statement-breakpoint
275
+
276
+ -- The audit log only grows: no statement may change or delete an entry.
277
+ -- What gets past this anyway, the entries' MACs and chain show.
278
+ -- REPLACE can delete without firing DELETE triggers, so refuse it before insert.
279
+ CREATE TRIGGER `audit_log_no_replace` BEFORE INSERT ON `audit_log`
280
+ WHEN EXISTS (SELECT 1 FROM `audit_log` WHERE `seq` = NEW.`seq`)
281
+ BEGIN
282
+ SELECT RAISE(ABORT, 'audit_log is append-only');
283
+ END;
284
+ --> statement-breakpoint
285
+ CREATE TRIGGER `audit_log_no_update` BEFORE UPDATE ON `audit_log`
286
+ BEGIN
287
+ SELECT RAISE(ABORT, 'audit_log is append-only');
288
+ END;
289
+ --> statement-breakpoint
290
+ CREATE TRIGGER `audit_log_no_delete` BEFORE DELETE ON `audit_log`
291
+ BEGIN
292
+ SELECT RAISE(ABORT, 'audit_log is append-only');
293
+ END;