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-current-v1",
4 "role": "directory",
5 "validation_policy": 1,
6 "migrations": [
7 {
8 "id": "oficina_directory/0001_directory",
9 "checksum": "d21fb3a2c8021c25110293d1fb7d3cad7391f020e33d3baba5b4bb2a4286d122"
10 },
11 {
12 "id": "oficina_directory_operator/0001_operator",
13 "checksum": "c2b659069da4459aa79cdd89160656d3b850474b59bfb01670ad477165055956"
14 },
15 {
16 "id": "oficina_directory_operator/0002_owner_receipts",
17 "checksum": "b916fd5db6c9a69cff4904f6d66fad8aedba12163520c6e42351561d8c1b2a5a"
18 },
19 {
20 "id": "oficina_directory_operator/0003_operator_key_identity",
21 "checksum": "56f2a3966f9752f46773c2eba0cc8fb74114d87b169728c4ef68b63a665eb8b9"
22 },
23 {
24 "id": "oficina_directory_operator_bindings/0001_operator_bindings",
25 "checksum": "0cfda439adfbb909a9b2bc88e27fb4d148ae1e60997faaf3ef130d42610f8136"
26 },
27 {
28 "id": "oficina_directory_roster/0001_roster",
29 "checksum": "cae647391de9529a39488706e123cab70392ab104b4698b5092d08ec6d7ced6c"
30 },
31 {
32 "id": "oficina_handoff/0001_exchange",
33 "checksum": "d2b6b9c416a34c3711e4959b0a05fdd13b80bc2f68b20146210c14d67fa1564e"
34 },
35 {
36 "id": "sessions/0001_init",
37 "checksum": "50bbfc92cfcc708b09b672b71f24da789ae6065f426c9db7af61befa90098bbb"
38 }
39 ],
40 "objects": [
41 {
42 "Type": "index",
43 "Name": "memberships_person",
44 "Table": "memberships",
45 "SQL": "CREATE INDEX memberships_person ON memberships(person_ref, team_id)"
46 },
47 {
48 "Type": "index",
49 "Name": "oficina_handoff_inbox_expiry",
50 "Table": "oficina_handoff_inbox",
51 "SQL": "CREATE INDEX oficina_handoff_inbox_expiry ON oficina_handoff_inbox(expires_at)"
52 },
53 {
54 "Type": "index",
55 "Name": "oficina_handoff_pending_expiry",
56 "Table": "oficina_handoff_pending",
57 "SQL": "CREATE INDEX oficina_handoff_pending_expiry ON oficina_handoff_pending(expires_at)"
58 },
59 {
60 "Type": "index",
61 "Name": "oficina_handoff_replay_expiry",
62 "Table": "oficina_handoff_replay",
63 "SQL": "CREATE INDEX oficina_handoff_replay_expiry ON oficina_handoff_replay(expires_at)"
64 },
65 {
66 "Type": "index",
67 "Name": "sqlite_autoindex_directory_operator_consumer_records_1",
68 "Table": "directory_operator_consumer_records",
69 "SQL": ""
70 },
71 {
72 "Type": "index",
73 "Name": "sqlite_autoindex_directory_operator_consumer_records_2",
74 "Table": "directory_operator_consumer_records",
75 "SQL": ""
76 },
77 {
78 "Type": "index",
79 "Name": "sqlite_autoindex_directory_operator_owner_receipts_1",
80 "Table": "directory_operator_owner_receipts",
81 "SQL": ""
82 },
83 {
84 "Type": "index",
85 "Name": "sqlite_autoindex_directory_operator_owner_receipts_2",
86 "Table": "directory_operator_owner_receipts",
87 "SQL": ""
88 },
89 {
90 "Type": "index",
91 "Name": "sqlite_autoindex_directory_operator_owner_receipts_3",
92 "Table": "directory_operator_owner_receipts",
93 "SQL": ""
94 },
95 {
96 "Type": "index",
97 "Name": "sqlite_autoindex_directory_operator_results_1",
98 "Table": "directory_operator_results",
99 "SQL": ""
100 },
101 {
102 "Type": "index",
103 "Name": "sqlite_autoindex_directory_operator_results_2",
104 "Table": "directory_operator_results",
105 "SQL": ""
106 },
107 {
108 "Type": "index",
109 "Name": "sqlite_autoindex_memberships_1",
110 "Table": "memberships",
111 "SQL": ""
112 },
113 {
114 "Type": "index",
115 "Name": "sqlite_autoindex_oficina_handoff_inbox_1",
116 "Table": "oficina_handoff_inbox",
117 "SQL": ""
118 },
119 {
120 "Type": "index",
121 "Name": "sqlite_autoindex_oficina_handoff_inbox_2",
122 "Table": "oficina_handoff_inbox",
123 "SQL": ""
124 },
125 {
126 "Type": "index",
127 "Name": "sqlite_autoindex_oficina_handoff_pending_1",
128 "Table": "oficina_handoff_pending",
129 "SQL": ""
130 },
131 {
132 "Type": "index",
133 "Name": "sqlite_autoindex_oficina_handoff_pending_2",
134 "Table": "oficina_handoff_pending",
135 "SQL": ""
136 },
137 {
138 "Type": "index",
139 "Name": "sqlite_autoindex_oficina_handoff_replay_1",
140 "Table": "oficina_handoff_replay",
141 "SQL": ""
142 },
143 {
144 "Type": "index",
145 "Name": "sqlite_autoindex_rastrillo_migrations_1",
146 "Table": "rastrillo_migrations",
147 "SQL": ""
148 },
149 {
150 "Type": "index",
151 "Name": "sqlite_autoindex_roster_consumers_1",
152 "Table": "roster_consumers",
153 "SQL": ""
154 },
155 {
156 "Type": "index",
157 "Name": "sqlite_autoindex_roster_operator_receipts_1",
158 "Table": "roster_operator_receipts",
159 "SQL": ""
160 },
161 {
162 "Type": "index",
163 "Name": "sqlite_autoindex_roster_tokens_1",
164 "Table": "roster_tokens",
165 "SQL": ""
166 },
167 {
168 "Type": "index",
169 "Name": "sqlite_autoindex_roster_tokens_2",
170 "Table": "roster_tokens",
171 "SQL": ""
172 },
173 {
174 "Type": "index",
175 "Name": "sqlite_autoindex_sessions_1",
176 "Table": "sessions",
177 "SQL": ""
178 },
179 {
180 "Type": "index",
181 "Name": "sqlite_autoindex_team_instances_1",
182 "Table": "team_instances",
183 "SQL": ""
184 },
185 {
186 "Type": "index",
187 "Name": "sqlite_autoindex_team_instances_2",
188 "Table": "team_instances",
189 "SQL": ""
190 },
191 {
192 "Type": "index",
193 "Name": "sqlite_autoindex_team_instances_3",
194 "Table": "team_instances",
195 "SQL": ""
196 },
197 {
198 "Type": "index",
199 "Name": "sqlite_autoindex_teams_1",
200 "Table": "teams",
201 "SQL": ""
202 },
203 {
204 "Type": "index",
205 "Name": "sqlite_autoindex_teams_2",
206 "Table": "teams",
207 "SQL": ""
208 },
209 {
210 "Type": "index",
211 "Name": "sqlite_autoindex_verification_active_1",
212 "Table": "verification_active",
213 "SQL": ""
214 },
215 {
216 "Type": "index",
217 "Name": "sqlite_autoindex_verification_active_2",
218 "Table": "verification_active",
219 "SQL": ""
220 },
221 {
222 "Type": "index",
223 "Name": "sqlite_autoindex_verification_attempts_1",
224 "Table": "verification_attempts",
225 "SQL": ""
226 },
227 {
228 "Type": "index",
229 "Name": "sqlite_autoindex_verification_outcomes_1",
230 "Table": "verification_outcomes",
231 "SQL": ""
232 },
233 {
234 "Type": "table",
235 "Name": "_litestream_lock",
236 "Table": "_litestream_lock",
237 "SQL": "CREATE TABLE _litestream_lock (id INTEGER)"
238 },
239 {
240 "Type": "table",
241 "Name": "_litestream_seq",
242 "Table": "_litestream_seq",
243 "SQL": "CREATE TABLE _litestream_seq (id INTEGER PRIMARY KEY, seq INTEGER)"
244 },
245 {
246 "Type": "table",
247 "Name": "directory_operator_consumer_records",
248 "Table": "directory_operator_consumer_records",
249 "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)"
250 },
251 {
252 "Type": "table",
253 "Name": "directory_operator_owner_receipts",
254 "Table": "directory_operator_owner_receipts",
255 "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)"
256 },
257 {
258 "Type": "table",
259 "Name": "directory_operator_results",
260 "Table": "directory_operator_results",
261 "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 '')"
262 },
263 {
264 "Type": "table",
265 "Name": "directory_operator_state",
266 "Table": "directory_operator_state",
267 "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 '')"
268 },
269 {
270 "Type": "table",
271 "Name": "memberships",
272 "Table": "memberships",
273 "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)"
274 },
275 {
276 "Type": "table",
277 "Name": "oficina_handoff_clock",
278 "Table": "oficina_handoff_clock",
279 "SQL": "CREATE TABLE oficina_handoff_clock (\n id INTEGER PRIMARY KEY CHECK (id = 1),\n observed INTEGER NOT NULL\n)"
280 },
281 {
282 "Type": "table",
283 "Name": "oficina_handoff_inbox",
284 "Table": "oficina_handoff_inbox",
285 "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)"
286 },
287 {
288 "Type": "table",
289 "Name": "oficina_handoff_pending",
290 "Table": "oficina_handoff_pending",
291 "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)"
292 },
293 {
294 "Type": "table",
295 "Name": "oficina_handoff_replay",
296 "Table": "oficina_handoff_replay",
297 "SQL": "CREATE TABLE oficina_handoff_replay (\n jti_hash TEXT PRIMARY KEY,\n expires_at INTEGER NOT NULL\n)"
298 },
299 {
300 "Type": "table",
301 "Name": "rastrillo_migrations",
302 "Table": "rastrillo_migrations",
303 "SQL": "CREATE TABLE rastrillo_migrations (\n id TEXT PRIMARY KEY,\n applied_at TEXT NOT NULL,\n checksum TEXT NOT NULL\n)"
304 },
305 {
306 "Type": "table",
307 "Name": "roster_consumers",
308 "Table": "roster_consumers",
309 "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)"
310 },
311 {
312 "Type": "table",
313 "Name": "roster_generation",
314 "Table": "roster_generation",
315 "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)"
316 },
317 {
318 "Type": "table",
319 "Name": "roster_operator_receipts",
320 "Table": "roster_operator_receipts",
321 "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)"
322 },
323 {
324 "Type": "table",
325 "Name": "roster_tokens",
326 "Table": "roster_tokens",
327 "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)"
328 },
329 {
330 "Type": "table",
331 "Name": "sessions",
332 "Table": "sessions",
333 "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)"
334 },
335 {
336 "Type": "table",
337 "Name": "sqlite_sequence",
338 "Table": "sqlite_sequence",
339 "SQL": "CREATE TABLE sqlite_sequence(name,seq)"
340 },
341 {
342 "Type": "table",
343 "Name": "team_instances",
344 "Table": "team_instances",
345 "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)"
346 },
347 {
348 "Type": "table",
349 "Name": "teams",
350 "Table": "teams",
351 "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)"
352 },
353 {
354 "Type": "table",
355 "Name": "verification_active",
356 "Table": "verification_active",
357 "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)"
358 },
359 {
360 "Type": "table",
361 "Name": "verification_attempts",
362 "Table": "verification_attempts",
363 "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)"
364 },
365 {
366 "Type": "table",
367 "Name": "verification_outcomes",
368 "Table": "verification_outcomes",
369 "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)"
370 },
371 {
372 "Type": "trigger",
373 "Name": "memberships_epoch_delete",
374 "Table": "memberships",
375 "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"
376 },
377 {
378 "Type": "trigger",
379 "Name": "memberships_epoch_insert",
380 "Table": "memberships",
381 "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"
382 },
383 {
384 "Type": "trigger",
385 "Name": "memberships_epoch_update",
386 "Table": "memberships",
387 "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"
388 }
389 ],
390 "empty_tables": []
391}
392