oficina / home Public

Clone
git clone https://amadan.net/oficina/home

Plain git — no account needed to clone.

Download

Download this file

1{
2 "version": 1,
3 "id": "directory-historical-empty-v1",
4 "role": "directory",
5 "validation_policy": 1,
6 "migrations": [
7 {
8 "id": "home/0001_people",
9 "checksum": "5e65372f0796be99d6fcbba09c97b92ed36a5faf1bf36a53be6f5caf0771eb9b"
10 },
11 {
12 "id": "home/0002_signin_links",
13 "checksum": "11429f3a12f605f0e24f890952a2831a44d8e5030d10471c7a5c1c3672e630a7"
14 },
15 {
16 "id": "oficina_directory/0001_directory",
17 "checksum": "d21fb3a2c8021c25110293d1fb7d3cad7391f020e33d3baba5b4bb2a4286d122"
18 },
19 {
20 "id": "oficina_directory_operator/0001_operator",
21 "checksum": "c2b659069da4459aa79cdd89160656d3b850474b59bfb01670ad477165055956"
22 },
23 {
24 "id": "oficina_directory_operator/0002_owner_receipts",
25 "checksum": "b916fd5db6c9a69cff4904f6d66fad8aedba12163520c6e42351561d8c1b2a5a"
26 },
27 {
28 "id": "oficina_directory_operator/0003_operator_key_identity",
29 "checksum": "56f2a3966f9752f46773c2eba0cc8fb74114d87b169728c4ef68b63a665eb8b9"
30 },
31 {
32 "id": "oficina_directory_operator_bindings/0001_operator_bindings",
33 "checksum": "0cfda439adfbb909a9b2bc88e27fb4d148ae1e60997faaf3ef130d42610f8136"
34 },
35 {
36 "id": "oficina_directory_roster/0001_roster",
37 "checksum": "cae647391de9529a39488706e123cab70392ab104b4698b5092d08ec6d7ced6c"
38 },
39 {
40 "id": "oficina_handoff/0001_exchange",
41 "checksum": "d2b6b9c416a34c3711e4959b0a05fdd13b80bc2f68b20146210c14d67fa1564e"
42 },
43 {
44 "id": "passkey/0001_init",
45 "checksum": "56d5073a880c3b1b8330654873b13cbd42a72596138499380b5a013e4d43196e"
46 },
47 {
48 "id": "sessions/0001_init",
49 "checksum": "50bbfc92cfcc708b09b672b71f24da789ae6065f426c9db7af61befa90098bbb"
50 }
51 ],
52 "objects": [
53 {
54 "Type": "index",
55 "Name": "idx_signin_links_expires_at",
56 "Table": "signin_links",
57 "SQL": "CREATE INDEX idx_signin_links_expires_at ON signin_links(expires_at)"
58 },
59 {
60 "Type": "index",
61 "Name": "memberships_person",
62 "Table": "memberships",
63 "SQL": "CREATE INDEX memberships_person ON memberships(person_ref, team_id)"
64 },
65 {
66 "Type": "index",
67 "Name": "oficina_handoff_inbox_expiry",
68 "Table": "oficina_handoff_inbox",
69 "SQL": "CREATE INDEX oficina_handoff_inbox_expiry ON oficina_handoff_inbox(expires_at)"
70 },
71 {
72 "Type": "index",
73 "Name": "oficina_handoff_pending_expiry",
74 "Table": "oficina_handoff_pending",
75 "SQL": "CREATE INDEX oficina_handoff_pending_expiry ON oficina_handoff_pending(expires_at)"
76 },
77 {
78 "Type": "index",
79 "Name": "oficina_handoff_replay_expiry",
80 "Table": "oficina_handoff_replay",
81 "SQL": "CREATE INDEX oficina_handoff_replay_expiry ON oficina_handoff_replay(expires_at)"
82 },
83 {
84 "Type": "index",
85 "Name": "passkey_credentials_subject",
86 "Table": "passkey_credentials",
87 "SQL": "CREATE INDEX passkey_credentials_subject\n ON passkey_credentials (subject)"
88 },
89 {
90 "Type": "index",
91 "Name": "passkey_recovery_codes_subject",
92 "Table": "passkey_recovery_codes",
93 "SQL": "CREATE INDEX passkey_recovery_codes_subject\n ON passkey_recovery_codes (subject)"
94 },
95 {
96 "Type": "index",
97 "Name": "sqlite_autoindex_directory_operator_consumer_records_1",
98 "Table": "directory_operator_consumer_records",
99 "SQL": ""
100 },
101 {
102 "Type": "index",
103 "Name": "sqlite_autoindex_directory_operator_consumer_records_2",
104 "Table": "directory_operator_consumer_records",
105 "SQL": ""
106 },
107 {
108 "Type": "index",
109 "Name": "sqlite_autoindex_directory_operator_owner_receipts_1",
110 "Table": "directory_operator_owner_receipts",
111 "SQL": ""
112 },
113 {
114 "Type": "index",
115 "Name": "sqlite_autoindex_directory_operator_owner_receipts_2",
116 "Table": "directory_operator_owner_receipts",
117 "SQL": ""
118 },
119 {
120 "Type": "index",
121 "Name": "sqlite_autoindex_directory_operator_owner_receipts_3",
122 "Table": "directory_operator_owner_receipts",
123 "SQL": ""
124 },
125 {
126 "Type": "index",
127 "Name": "sqlite_autoindex_directory_operator_results_1",
128 "Table": "directory_operator_results",
129 "SQL": ""
130 },
131 {
132 "Type": "index",
133 "Name": "sqlite_autoindex_directory_operator_results_2",
134 "Table": "directory_operator_results",
135 "SQL": ""
136 },
137 {
138 "Type": "index",
139 "Name": "sqlite_autoindex_memberships_1",
140 "Table": "memberships",
141 "SQL": ""
142 },
143 {
144 "Type": "index",
145 "Name": "sqlite_autoindex_oficina_handoff_inbox_1",
146 "Table": "oficina_handoff_inbox",
147 "SQL": ""
148 },
149 {
150 "Type": "index",
151 "Name": "sqlite_autoindex_oficina_handoff_inbox_2",
152 "Table": "oficina_handoff_inbox",
153 "SQL": ""
154 },
155 {
156 "Type": "index",
157 "Name": "sqlite_autoindex_oficina_handoff_pending_1",
158 "Table": "oficina_handoff_pending",
159 "SQL": ""
160 },
161 {
162 "Type": "index",
163 "Name": "sqlite_autoindex_oficina_handoff_pending_2",
164 "Table": "oficina_handoff_pending",
165 "SQL": ""
166 },
167 {
168 "Type": "index",
169 "Name": "sqlite_autoindex_oficina_handoff_replay_1",
170 "Table": "oficina_handoff_replay",
171 "SQL": ""
172 },
173 {
174 "Type": "index",
175 "Name": "sqlite_autoindex_passkey_challenges_1",
176 "Table": "passkey_challenges",
177 "SQL": ""
178 },
179 {
180 "Type": "index",
181 "Name": "sqlite_autoindex_passkey_credentials_1",
182 "Table": "passkey_credentials",
183 "SQL": ""
184 },
185 {
186 "Type": "index",
187 "Name": "sqlite_autoindex_passkey_pending_1",
188 "Table": "passkey_pending",
189 "SQL": ""
190 },
191 {
192 "Type": "index",
193 "Name": "sqlite_autoindex_passkey_recovery_codes_1",
194 "Table": "passkey_recovery_codes",
195 "SQL": ""
196 },
197 {
198 "Type": "index",
199 "Name": "sqlite_autoindex_people_1",
200 "Table": "people",
201 "SQL": ""
202 },
203 {
204 "Type": "index",
205 "Name": "sqlite_autoindex_people_2",
206 "Table": "people",
207 "SQL": ""
208 },
209 {
210 "Type": "index",
211 "Name": "sqlite_autoindex_rastrillo_migrations_1",
212 "Table": "rastrillo_migrations",
213 "SQL": ""
214 },
215 {
216 "Type": "index",
217 "Name": "sqlite_autoindex_roster_consumers_1",
218 "Table": "roster_consumers",
219 "SQL": ""
220 },
221 {
222 "Type": "index",
223 "Name": "sqlite_autoindex_roster_operator_receipts_1",
224 "Table": "roster_operator_receipts",
225 "SQL": ""
226 },
227 {
228 "Type": "index",
229 "Name": "sqlite_autoindex_roster_tokens_1",
230 "Table": "roster_tokens",
231 "SQL": ""
232 },
233 {
234 "Type": "index",
235 "Name": "sqlite_autoindex_roster_tokens_2",
236 "Table": "roster_tokens",
237 "SQL": ""
238 },
239 {
240 "Type": "index",
241 "Name": "sqlite_autoindex_sessions_1",
242 "Table": "sessions",
243 "SQL": ""
244 },
245 {
246 "Type": "index",
247 "Name": "sqlite_autoindex_signin_links_1",
248 "Table": "signin_links",
249 "SQL": ""
250 },
251 {
252 "Type": "index",
253 "Name": "sqlite_autoindex_team_instances_1",
254 "Table": "team_instances",
255 "SQL": ""
256 },
257 {
258 "Type": "index",
259 "Name": "sqlite_autoindex_team_instances_2",
260 "Table": "team_instances",
261 "SQL": ""
262 },
263 {
264 "Type": "index",
265 "Name": "sqlite_autoindex_team_instances_3",
266 "Table": "team_instances",
267 "SQL": ""
268 },
269 {
270 "Type": "index",
271 "Name": "sqlite_autoindex_teams_1",
272 "Table": "teams",
273 "SQL": ""
274 },
275 {
276 "Type": "index",
277 "Name": "sqlite_autoindex_teams_2",
278 "Table": "teams",
279 "SQL": ""
280 },
281 {
282 "Type": "index",
283 "Name": "sqlite_autoindex_verification_active_1",
284 "Table": "verification_active",
285 "SQL": ""
286 },
287 {
288 "Type": "index",
289 "Name": "sqlite_autoindex_verification_active_2",
290 "Table": "verification_active",
291 "SQL": ""
292 },
293 {
294 "Type": "index",
295 "Name": "sqlite_autoindex_verification_attempts_1",
296 "Table": "verification_attempts",
297 "SQL": ""
298 },
299 {
300 "Type": "index",
301 "Name": "sqlite_autoindex_verification_outcomes_1",
302 "Table": "verification_outcomes",
303 "SQL": ""
304 },
305 {
306 "Type": "table",
307 "Name": "_litestream_lock",
308 "Table": "_litestream_lock",
309 "SQL": "CREATE TABLE _litestream_lock (id INTEGER)"
310 },
311 {
312 "Type": "table",
313 "Name": "_litestream_seq",
314 "Table": "_litestream_seq",
315 "SQL": "CREATE TABLE _litestream_seq (id INTEGER PRIMARY KEY, seq INTEGER)"
316 },
317 {
318 "Type": "table",
319 "Name": "directory_operator_consumer_records",
320 "Table": "directory_operator_consumer_records",
321 "SQL": "CREATE TABLE directory_operator_consumer_records (\n consumer_id TEXT PRIMARY KEY REFERENCES roster_consumers(consumer_id),\n operation_id TEXT NOT NULL UNIQUE REFERENCES roster_operator_receipts(operation_id),\n sequence INTEGER NOT NULL CHECK(typeof(sequence)='integer' AND sequence\u003e0),\n generation TEXT NOT NULL CHECK(length(generation)=43),\n record_digest BLOB NOT NULL CHECK(typeof(record_digest)='blob' AND length(record_digest)=32)\n)"
322 },
323 {
324 "Type": "table",
325 "Name": "directory_operator_owner_receipts",
326 "Table": "directory_operator_owner_receipts",
327 "SQL": "CREATE TABLE directory_operator_owner_receipts (\n issuer TEXT NOT NULL,\n owner_operation_id TEXT NOT NULL CHECK(length(owner_operation_id)=43),\n directory_operation_id TEXT NOT NULL UNIQUE REFERENCES directory_operator_results(operation_id) DEFERRABLE INITIALLY DEFERRED,\n team_id INTEGER NOT NULL UNIQUE REFERENCES teams(id),\n PRIMARY KEY(issuer,owner_operation_id)\n)"
328 },
329 {
330 "Type": "table",
331 "Name": "directory_operator_results",
332 "Table": "directory_operator_results",
333 "SQL": "CREATE TABLE directory_operator_results (\n operation_id TEXT PRIMARY KEY CHECK(length(operation_id)=43),\n action TEXT NOT NULL,\n sequence INTEGER NOT NULL UNIQUE CHECK(typeof(sequence)='integer' AND sequence\u003e0),\n authorization_head TEXT NOT NULL CHECK(length(authorization_head)=43),\n request_mac BLOB NOT NULL CHECK(typeof(request_mac)='blob' AND length(request_mac)=32),\n sealed_result BLOB NOT NULL CHECK(typeof(sealed_result)='blob' AND length(sealed_result) BETWEEN 29 AND 49180),\n completed_at INTEGER NOT NULL CHECK(typeof(completed_at)='integer' AND completed_at\u003e0)\n, operator_ref TEXT NOT NULL DEFAULT '')"
334 },
335 {
336 "Type": "table",
337 "Name": "directory_operator_state",
338 "Table": "directory_operator_state",
339 "SQL": "CREATE TABLE directory_operator_state (\n singleton INTEGER PRIMARY KEY CHECK(singleton=1),\n origin TEXT NOT NULL,\n installation TEXT NOT NULL,\n registry TEXT NOT NULL,\n device TEXT NOT NULL,\n sequence INTEGER NOT NULL CHECK(typeof(sequence)='integer' AND sequence\u003e0),\n head TEXT NOT NULL CHECK(length(head)=43),\n completed_at INTEGER NOT NULL CHECK(typeof(completed_at)='integer' AND completed_at\u003e0)\n, key_id TEXT NOT NULL DEFAULT '')"
340 },
341 {
342 "Type": "table",
343 "Name": "memberships",
344 "Table": "memberships",
345 "SQL": "CREATE TABLE memberships (\n team_id INTEGER NOT NULL REFERENCES teams(id) ON DELETE RESTRICT,\n person_ref TEXT NOT NULL CHECK(length(person_ref) = 29 AND substr(person_ref,1,3) = 'pr_' AND substr(person_ref,4) NOT GLOB '*[^a-z2-7]*'),\n role TEXT NOT NULL CHECK(role IN ('owner', 'member')),\n PRIMARY KEY(team_id, person_ref)\n)"
346 },
347 {
348 "Type": "table",
349 "Name": "oficina_handoff_clock",
350 "Table": "oficina_handoff_clock",
351 "SQL": "CREATE TABLE oficina_handoff_clock (\n id INTEGER PRIMARY KEY CHECK (id = 1),\n observed INTEGER NOT NULL\n)"
352 },
353 {
354 "Type": "table",
355 "Name": "oficina_handoff_inbox",
356 "Table": "oficina_handoff_inbox",
357 "SQL": "CREATE TABLE oficina_handoff_inbox (\n handle_hash TEXT PRIMARY KEY,\n jti_hash TEXT NOT NULL UNIQUE,\n sealed_token BLOB NOT NULL,\n created_at INTEGER NOT NULL,\n expires_at INTEGER NOT NULL\n)"
358 },
359 {
360 "Type": "table",
361 "Name": "oficina_handoff_pending",
362 "Table": "oficina_handoff_pending",
363 "SQL": "CREATE TABLE oficina_handoff_pending (\n secret_hash TEXT PRIMARY KEY,\n nonce_hash TEXT NOT NULL UNIQUE,\n sealed_path BLOB NOT NULL,\n created_at INTEGER NOT NULL,\n expires_at INTEGER NOT NULL\n)"
364 },
365 {
366 "Type": "table",
367 "Name": "oficina_handoff_replay",
368 "Table": "oficina_handoff_replay",
369 "SQL": "CREATE TABLE oficina_handoff_replay (\n jti_hash TEXT PRIMARY KEY,\n expires_at INTEGER NOT NULL\n)"
370 },
371 {
372 "Type": "table",
373 "Name": "passkey_challenges",
374 "Table": "passkey_challenges",
375 "SQL": "CREATE TABLE passkey_challenges (\n challenge TEXT PRIMARY KEY,\n subject TEXT NOT NULL,\n purpose TEXT NOT NULL,\n expires_at TEXT NOT NULL\n)"
376 },
377 {
378 "Type": "table",
379 "Name": "passkey_credentials",
380 "Table": "passkey_credentials",
381 "SQL": "CREATE TABLE passkey_credentials (\n id TEXT PRIMARY KEY,\n subject TEXT NOT NULL,\n public_key BLOB NOT NULL,\n sign_count INTEGER NOT NULL DEFAULT 0,\n created_at TEXT NOT NULL\n)"
382 },
383 {
384 "Type": "table",
385 "Name": "passkey_pending",
386 "Table": "passkey_pending",
387 "SQL": "CREATE TABLE passkey_pending (\n token_hash TEXT PRIMARY KEY,\n subject TEXT NOT NULL,\n method TEXT NOT NULL DEFAULT '',\n return_to TEXT NOT NULL DEFAULT '',\n expires_at TEXT NOT NULL\n)"
388 },
389 {
390 "Type": "table",
391 "Name": "passkey_recovery_codes",
392 "Table": "passkey_recovery_codes",
393 "SQL": "CREATE TABLE passkey_recovery_codes (\n code_hash TEXT PRIMARY KEY,\n subject TEXT NOT NULL,\n created_at TEXT NOT NULL\n)"
394 },
395 {
396 "Type": "table",
397 "Name": "people",
398 "Table": "people",
399 "SQL": "CREATE TABLE people (\n person_ref TEXT PRIMARY KEY,\n blind_index TEXT NOT NULL UNIQUE, -- UNIQUE already indexes it; no second index\n created_at TEXT NOT NULL\n)"
400 },
401 {
402 "Type": "table",
403 "Name": "rastrillo_migrations",
404 "Table": "rastrillo_migrations",
405 "SQL": "CREATE TABLE rastrillo_migrations (\n id TEXT PRIMARY KEY,\n applied_at TEXT NOT NULL,\n checksum TEXT NOT NULL\n)"
406 },
407 {
408 "Type": "table",
409 "Name": "roster_consumers",
410 "Table": "roster_consumers",
411 "SQL": "CREATE TABLE roster_consumers (\n consumer_id TEXT PRIMARY KEY REFERENCES team_instances(consumer_id) ON DELETE RESTRICT,\n team_id INTEGER NOT NULL REFERENCES teams(id) ON DELETE RESTRICT,\n app TEXT NOT NULL,\n origin TEXT NOT NULL,\n enabled INTEGER NOT NULL CHECK(enabled IN (0,1)),\n FOREIGN KEY(team_id,app) REFERENCES team_instances(team_id,app) ON DELETE RESTRICT\n)"
412 },
413 {
414 "Type": "table",
415 "Name": "roster_generation",
416 "Table": "roster_generation",
417 "SQL": "CREATE TABLE roster_generation (\n singleton INTEGER PRIMARY KEY CHECK(singleton = 1),\n generation TEXT NOT NULL CHECK(length(generation) = 43 AND generation NOT GLOB '*[^a-zA-Z0-9_-]*')\n)"
418 },
419 {
420 "Type": "table",
421 "Name": "roster_operator_receipts",
422 "Table": "roster_operator_receipts",
423 "SQL": "CREATE TABLE roster_operator_receipts (\n operation_id TEXT PRIMARY KEY CHECK(length(operation_id) BETWEEN 1 AND 128 AND operation_id NOT GLOB '*[^a-zA-Z0-9_-]*'),\n sequence INTEGER NOT NULL CHECK(typeof(sequence) = 'integer' AND sequence \u003e 0),\n operation TEXT NOT NULL CHECK(operation IN ('bootstrap','provision','rotate','disable','restore')),\n consumer_id TEXT CHECK(consumer_id IS NULL OR (length(consumer_id) BETWEEN 1 AND 128 AND consumer_id NOT GLOB '*[^a-zA-Z0-9_-]*')),\n generation TEXT NOT NULL CHECK(length(generation) = 43 AND generation NOT GLOB '*[^a-zA-Z0-9_-]*'),\n record_digest BLOB NOT NULL CHECK(typeof(record_digest) = 'blob' AND length(record_digest) = 32),\n completed_at INTEGER NOT NULL CHECK(typeof(completed_at) = 'integer' AND completed_at \u003e 0)\n)"
424 },
425 {
426 "Type": "table",
427 "Name": "roster_tokens",
428 "Table": "roster_tokens",
429 "SQL": "CREATE TABLE roster_tokens (\n token_hash BLOB PRIMARY KEY CHECK(typeof(token_hash) = 'blob' AND length(token_hash) = 32),\n consumer_id TEXT NOT NULL REFERENCES roster_consumers(consumer_id) ON DELETE RESTRICT,\n slot TEXT NOT NULL CHECK(slot IN ('current','previous')),\n issued_at INTEGER NOT NULL CHECK(typeof(issued_at) = 'integer' AND issued_at \u003e 0),\n expires_at INTEGER,\n UNIQUE(consumer_id,slot),\n CHECK((slot = 'current' AND expires_at IS NULL) OR\n (slot = 'previous' AND typeof(expires_at) = 'integer' AND expires_at \u003e issued_at AND expires_at - issued_at \u003c= 300))\n)"
430 },
431 {
432 "Type": "table",
433 "Name": "sessions",
434 "Table": "sessions",
435 "SQL": "CREATE TABLE sessions (\n token_hash TEXT PRIMARY KEY,\n subject TEXT NOT NULL,\n method TEXT NOT NULL DEFAULT '',\n auth_time TEXT NOT NULL DEFAULT '',\n created_at TEXT NOT NULL,\n expires_at TEXT NOT NULL\n)"
436 },
437 {
438 "Type": "table",
439 "Name": "signin_links",
440 "Table": "signin_links",
441 "SQL": "CREATE TABLE signin_links (\n token_hash TEXT PRIMARY KEY,\n sealed_address BLOB NOT NULL,\n purpose TEXT NOT NULL,\n expires_at TEXT NOT NULL\n)"
442 },
443 {
444 "Type": "table",
445 "Name": "sqlite_sequence",
446 "Table": "sqlite_sequence",
447 "SQL": "CREATE TABLE sqlite_sequence(name,seq)"
448 },
449 {
450 "Type": "table",
451 "Name": "team_instances",
452 "Table": "team_instances",
453 "SQL": "CREATE TABLE team_instances (\n team_id INTEGER NOT NULL REFERENCES teams(id) ON DELETE RESTRICT,\n app TEXT NOT NULL,\n origin TEXT NOT NULL UNIQUE,\n consumer_id TEXT NOT NULL UNIQUE,\n state TEXT NOT NULL CHECK(state IN ('pending', 'live', 'disabled')),\n PRIMARY KEY(team_id, app)\n)"
454 },
455 {
456 "Type": "table",
457 "Name": "teams",
458 "Table": "teams",
459 "SQL": "CREATE TABLE teams (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n sqid TEXT NOT NULL UNIQUE CHECK(length(sqid) BETWEEN 1 AND 64 AND sqid NOT GLOB '*[^a-zA-Z0-9]*'),\n label TEXT NOT NULL UNIQUE CHECK(length(label) BETWEEN 1 AND 63 AND label NOT GLOB '*[^a-z0-9-]*' AND substr(label,1,1) != '-' AND substr(label,-1,1) != '-'),\n sealed_name BLOB NOT NULL CHECK(typeof(sealed_name) = 'blob' AND length(sealed_name) BETWEEN 29 AND 284),\n created_at INTEGER NOT NULL CHECK(created_at \u003e 0),\n roster_epoch INTEGER NOT NULL DEFAULT 1 CHECK(typeof(roster_epoch) = 'integer' AND roster_epoch \u003e 0)\n)"
460 },
461 {
462 "Type": "table",
463 "Name": "verification_active",
464 "Table": "verification_active",
465 "SQL": "CREATE TABLE verification_active (\n consumer_id TEXT PRIMARY KEY REFERENCES roster_consumers(consumer_id),\n operation_id TEXT NOT NULL UNIQUE REFERENCES verification_attempts(operation_id)\n)"
466 },
467 {
468 "Type": "table",
469 "Name": "verification_attempts",
470 "Table": "verification_attempts",
471 "SQL": "CREATE TABLE verification_attempts (\n operation_id TEXT PRIMARY KEY CHECK(length(operation_id)=43),\n consumer_id TEXT NOT NULL REFERENCES roster_consumers(consumer_id),\n team_id INTEGER NOT NULL REFERENCES teams(id),\n app TEXT NOT NULL,\n origin TEXT NOT NULL,\n generation TEXT NOT NULL CHECK(length(generation)=43),\n record_sequence INTEGER NOT NULL CHECK(typeof(record_sequence)='integer' AND record_sequence\u003e0),\n record_digest BLOB NOT NULL CHECK(typeof(record_digest)='blob' AND length(record_digest)=32),\n expected_epoch INTEGER NOT NULL CHECK(typeof(expected_epoch)='integer' AND expected_epoch\u003e0),\n owner_ref TEXT NOT NULL CHECK(length(owner_ref)=29),\n issued_at INTEGER NOT NULL CHECK(typeof(issued_at)='integer' AND issued_at\u003e0),\n expires_at INTEGER NOT NULL CHECK(typeof(expires_at)='integer' AND expires_at\u003eissued_at AND expires_at-issued_at\u003c=600),\n authorization_digest BLOB NOT NULL CHECK(typeof(authorization_digest)='blob' AND length(authorization_digest)=32)\n)"
472 },
473 {
474 "Type": "table",
475 "Name": "verification_outcomes",
476 "Table": "verification_outcomes",
477 "SQL": "CREATE TABLE verification_outcomes (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n attempt_id TEXT NOT NULL REFERENCES verification_attempts(operation_id),\n operator_operation_id TEXT NOT NULL CHECK(length(operator_operation_id)=43),\n kind TEXT NOT NULL CHECK(kind IN ('opened','expired','activated')),\n completed_at INTEGER NOT NULL CHECK(typeof(completed_at)='integer' AND completed_at\u003e0),\n UNIQUE(attempt_id,operator_operation_id,kind)\n)"
478 },
479 {
480 "Type": "trigger",
481 "Name": "memberships_epoch_delete",
482 "Table": "memberships",
483 "SQL": "CREATE TRIGGER memberships_epoch_delete AFTER DELETE ON memberships BEGIN\n SELECT CASE WHEN (SELECT roster_epoch FROM teams WHERE id = OLD.team_id) = 9223372036854775807 THEN RAISE(ABORT,'roster epoch exhausted') END;\n UPDATE teams SET roster_epoch = roster_epoch + 1 WHERE id = OLD.team_id;\nEND"
484 },
485 {
486 "Type": "trigger",
487 "Name": "memberships_epoch_insert",
488 "Table": "memberships",
489 "SQL": "CREATE TRIGGER memberships_epoch_insert AFTER INSERT ON memberships BEGIN\n SELECT CASE WHEN (SELECT roster_epoch FROM teams WHERE id = NEW.team_id) = 9223372036854775807 THEN RAISE(ABORT,'roster epoch exhausted') END;\n UPDATE teams SET roster_epoch = roster_epoch + 1 WHERE id = NEW.team_id;\nEND"
490 },
491 {
492 "Type": "trigger",
493 "Name": "memberships_epoch_update",
494 "Table": "memberships",
495 "SQL": "CREATE TRIGGER memberships_epoch_update AFTER UPDATE ON memberships BEGIN\n SELECT CASE WHEN EXISTS(SELECT 1 FROM teams WHERE id IN (OLD.team_id,NEW.team_id) AND roster_epoch = 9223372036854775807) THEN RAISE(ABORT,'roster epoch exhausted') END;\n UPDATE teams SET roster_epoch = roster_epoch + 1 WHERE id IN (OLD.team_id,NEW.team_id);\nEND"
496 }
497 ],
498 "empty_tables": [
499 "passkey_challenges",
500 "passkey_credentials",
501 "passkey_pending",
502 "passkey_recovery_codes",
503 "people",
504 "signin_links"
505 ]
506}
507