@package-broker/dist 0.0.0-stage → 2.0.0-rc.2
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.
- package/LICENSE +661 -0
- package/README.md +4 -2
- package/node/assets/app.b2abf3c46f6e.js +15 -0
- package/node/assets/app.e8cea8b32d3a.css +2 -0
- package/node/assets/index.html +13 -0
- package/node/assets/manifest.json +26 -0
- package/node/assets/setup.98343039898f.js +1 -0
- package/node/assets/setup.html +14 -0
- package/node/assets.json +15 -0
- package/node/build-info.json +12 -0
- package/node/generated/manifest.json +82 -0
- package/node/migrations/core/0001_init.sql +1065 -0
- package/node/server.js +89633 -0
- package/package.json +23 -3
- package/prebuilt.json +33 -0
|
@@ -0,0 +1,1065 @@
|
|
|
1
|
+
-- Package Broker 2.0, fresh baseline, migration 0001. SQLite dialect (Cloudflare D1, better-sqlite3, node:sqlite).
|
|
2
|
+
-- No BEGIN/COMMIT: the runner applies the file as one batch. No PRAGMA: D1 always enforces
|
|
3
|
+
-- foreign keys; better-sqlite3 and the sqlite3 CLI need PRAGMA foreign_keys = ON per connection.
|
|
4
|
+
-- Timestamps: ISO-8601 UTC text with milliseconds and a Z suffix (strftime('%Y-%m-%dT%H:%M:%fZ')).
|
|
5
|
+
-- Booleans: INTEGER 0/1 with CHECK. Keys: ULID text. org_id is a constant, see schema-notes.md.
|
|
6
|
+
|
|
7
|
+
-- ---------------------------------------------------------------------------
|
|
8
|
+
-- Migration, keyring and mutation guards
|
|
9
|
+
-- ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
CREATE TABLE access_mutation_guard (
|
|
12
|
+
key TEXT NOT NULL PRIMARY KEY CHECK (length(key) BETWEEN 1 AND 64)
|
|
13
|
+
) STRICT;
|
|
14
|
+
|
|
15
|
+
CREATE TABLE keyring_verifier (
|
|
16
|
+
key_version INTEGER NOT NULL PRIMARY KEY CHECK (key_version >= 1),
|
|
17
|
+
mac BLOB NOT NULL CHECK (length(mac) = 32),
|
|
18
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
19
|
+
) STRICT, WITHOUT ROWID;
|
|
20
|
+
|
|
21
|
+
CREATE TABLE migration_lock (
|
|
22
|
+
id INTEGER NOT NULL PRIMARY KEY CHECK (id = 1),
|
|
23
|
+
holder TEXT,
|
|
24
|
+
acquired_at TEXT,
|
|
25
|
+
expires_at TEXT
|
|
26
|
+
) STRICT;
|
|
27
|
+
|
|
28
|
+
-- ---------------------------------------------------------------------------
|
|
29
|
+
-- Organization, projects, roles and users
|
|
30
|
+
-- ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
CREATE TABLE organization (
|
|
33
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
34
|
+
singleton INTEGER NOT NULL DEFAULT 1 CHECK (singleton = 1),
|
|
35
|
+
slug TEXT NOT NULL CHECK (slug = lower(slug) AND length(slug) BETWEEN 1 AND 64),
|
|
36
|
+
name TEXT NOT NULL,
|
|
37
|
+
retention_removed_upstream_days INTEGER NOT NULL DEFAULT 365 CHECK (retention_removed_upstream_days >= 0),
|
|
38
|
+
token_default_expiry_days INTEGER NOT NULL DEFAULT 90 CHECK (token_default_expiry_days > 0),
|
|
39
|
+
token_ci_readonly_expiry_days INTEGER NOT NULL DEFAULT 365 CHECK (token_ci_readonly_expiry_days > 0),
|
|
40
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
41
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
42
|
+
audit_last_verified_at TEXT
|
|
43
|
+
) STRICT;
|
|
44
|
+
|
|
45
|
+
CREATE TABLE project (
|
|
46
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
47
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
48
|
+
slug TEXT NOT NULL CHECK (slug = lower(slug) AND length(slug) BETWEEN 1 AND 64),
|
|
49
|
+
name TEXT NOT NULL,
|
|
50
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'archived')),
|
|
51
|
+
token_max_scope TEXT NOT NULL DEFAULT 'full' CHECK (token_max_scope IN ('read', 'write', 'full')),
|
|
52
|
+
require_publish_approval INTEGER NOT NULL DEFAULT 0 CHECK (require_publish_approval IN (0, 1)),
|
|
53
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
54
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
55
|
+
revision INTEGER NOT NULL DEFAULT 1 CHECK (revision >= 1),
|
|
56
|
+
last_op_id TEXT
|
|
57
|
+
) STRICT;
|
|
58
|
+
|
|
59
|
+
CREATE TABLE representation_clock (
|
|
60
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
61
|
+
project_id TEXT NOT NULL REFERENCES project (id) ON DELETE CASCADE,
|
|
62
|
+
key_hash TEXT NOT NULL CHECK (length(key_hash) = 64 AND key_hash = lower(key_hash) AND key_hash NOT GLOB '*[^0-9a-f]*'),
|
|
63
|
+
form TEXT NOT NULL CHECK (form IN ('composer-p2', 'composer-p2-dev')),
|
|
64
|
+
validator TEXT NOT NULL CHECK (length(validator) = 64 AND validator = lower(validator) AND validator NOT GLOB '*[^0-9a-f]*'),
|
|
65
|
+
last_modified TEXT NOT NULL,
|
|
66
|
+
rev INTEGER NOT NULL DEFAULT 0 CHECK (rev >= 0),
|
|
67
|
+
package_id TEXT NOT NULL DEFAULT '',
|
|
68
|
+
PRIMARY KEY (project_id, key_hash, form)
|
|
69
|
+
) STRICT, WITHOUT ROWID;
|
|
70
|
+
|
|
71
|
+
CREATE TABLE role (
|
|
72
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
73
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
74
|
+
key TEXT NOT NULL CHECK (key = lower(key)),
|
|
75
|
+
name TEXT NOT NULL,
|
|
76
|
+
scope TEXT NOT NULL CHECK (scope IN ('organization', 'project')),
|
|
77
|
+
is_system INTEGER NOT NULL DEFAULT 0 CHECK (is_system IN (0, 1)),
|
|
78
|
+
protects_last_holder INTEGER NOT NULL DEFAULT 0 CHECK (protects_last_holder IN (0, 1)),
|
|
79
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
80
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
81
|
+
revision INTEGER NOT NULL DEFAULT 1 CHECK (revision >= 1),
|
|
82
|
+
last_op_id TEXT
|
|
83
|
+
) STRICT;
|
|
84
|
+
|
|
85
|
+
CREATE TABLE role_permission (
|
|
86
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
87
|
+
role_id TEXT NOT NULL REFERENCES role (id) ON DELETE CASCADE,
|
|
88
|
+
permission TEXT NOT NULL,
|
|
89
|
+
PRIMARY KEY (role_id, permission)
|
|
90
|
+
) STRICT, WITHOUT ROWID;
|
|
91
|
+
|
|
92
|
+
CREATE TABLE schema_migration (
|
|
93
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
94
|
+
name TEXT NOT NULL,
|
|
95
|
+
checksum TEXT NOT NULL CHECK (length(checksum) = 64),
|
|
96
|
+
applied_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
97
|
+
) STRICT, WITHOUT ROWID;
|
|
98
|
+
|
|
99
|
+
CREATE TABLE user_account (
|
|
100
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
101
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
102
|
+
email TEXT NOT NULL CHECK (email = lower(email)),
|
|
103
|
+
display_name TEXT,
|
|
104
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'suspended')),
|
|
105
|
+
last_login_at TEXT,
|
|
106
|
+
idp_validated_at TEXT,
|
|
107
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
108
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
109
|
+
pat_invalid_before TEXT
|
|
110
|
+
) STRICT;
|
|
111
|
+
|
|
112
|
+
CREATE TABLE user_group (
|
|
113
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
114
|
+
user_id TEXT NOT NULL REFERENCES user_account (id) ON DELETE CASCADE,
|
|
115
|
+
provider TEXT NOT NULL CHECK (length(provider) BETWEEN 1 AND 64 AND provider = trim(provider)),
|
|
116
|
+
group_name TEXT NOT NULL,
|
|
117
|
+
refreshed_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
118
|
+
PRIMARY KEY (user_id, provider, group_name)
|
|
119
|
+
) STRICT, WITHOUT ROWID;
|
|
120
|
+
|
|
121
|
+
-- ---------------------------------------------------------------------------
|
|
122
|
+
-- Advisory synchronization
|
|
123
|
+
-- ---------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
CREATE TABLE vulnerability_advisory (
|
|
126
|
+
org_id TEXT NOT NULL REFERENCES organization(id) ON DELETE CASCADE,
|
|
127
|
+
source TEXT NOT NULL DEFAULT 'osv' CHECK (source = 'osv'),
|
|
128
|
+
advisory_id TEXT NOT NULL CHECK (length(CAST(advisory_id AS BLOB)) BETWEEN 1 AND 200 AND instr(advisory_id, '/') = 0 AND instr(advisory_id, char(92)) = 0 AND advisory_id NOT IN ('.', '..')),
|
|
129
|
+
detail_json TEXT CHECK (detail_json IS NULL OR (length(CAST(detail_json AS BLOB)) <= 65536 AND json_valid(detail_json) AND json_type(detail_json) = 'object')),
|
|
130
|
+
summary TEXT CHECK (summary IS NULL OR length(CAST(summary AS BLOB)) <= 8192),
|
|
131
|
+
severity_json TEXT CHECK (severity_json IS NULL OR (length(CAST(severity_json AS BLOB)) <= 16384 AND json_valid(severity_json))),
|
|
132
|
+
modified_at TEXT CHECK (modified_at IS NULL OR (length(modified_at) = 24 AND strftime('%Y-%m-%dT%H:%M:%fZ', modified_at) IS modified_at)),
|
|
133
|
+
fetched_at TEXT CHECK (fetched_at IS NULL OR (length(fetched_at) = 24 AND strftime('%Y-%m-%dT%H:%M:%fZ', fetched_at) IS fetched_at)),
|
|
134
|
+
next_attempt_at TEXT NOT NULL CHECK (length(next_attempt_at) = 24 AND strftime('%Y-%m-%dT%H:%M:%fZ', next_attempt_at) IS next_attempt_at),
|
|
135
|
+
failure_count INTEGER NOT NULL DEFAULT 0 CHECK (failure_count BETWEEN 0 AND 1000),
|
|
136
|
+
last_error_code TEXT CHECK (last_error_code IN ('unavailable', 'protocol', 'config', 'limit')),
|
|
137
|
+
PRIMARY KEY (org_id, source, advisory_id)
|
|
138
|
+
) STRICT, WITHOUT ROWID;
|
|
139
|
+
|
|
140
|
+
CREATE TABLE vulnerability_sync_state (
|
|
141
|
+
org_id TEXT NOT NULL REFERENCES organization(id) ON DELETE CASCADE,
|
|
142
|
+
source TEXT NOT NULL DEFAULT 'osv' CHECK (source = 'osv'),
|
|
143
|
+
fence INTEGER NOT NULL DEFAULT 0 CHECK (fence >= 0),
|
|
144
|
+
lease_owner TEXT,
|
|
145
|
+
lease_until TEXT CHECK (lease_until IS NULL OR (length(lease_until) = 24 AND strftime('%Y-%m-%dT%H:%M:%fZ', lease_until) IS lease_until)),
|
|
146
|
+
next_request_at TEXT NOT NULL CHECK (length(next_request_at) = 24 AND strftime('%Y-%m-%dT%H:%M:%fZ', next_request_at) IS next_request_at),
|
|
147
|
+
consecutive_failures INTEGER NOT NULL DEFAULT 0 CHECK (consecutive_failures BETWEEN 0 AND 1000),
|
|
148
|
+
write_token TEXT,
|
|
149
|
+
prune_cursor TEXT NOT NULL DEFAULT '',
|
|
150
|
+
selection_cursor TEXT NOT NULL DEFAULT '{"due":"","id":""}' CHECK (length(selection_cursor) <= 1024 AND json_valid(selection_cursor) AND json_type(selection_cursor) = 'object'),
|
|
151
|
+
blocked_until TEXT CHECK (blocked_until IS NULL OR (length(blocked_until) = 24 AND strftime('%Y-%m-%dT%H:%M:%fZ', blocked_until) IS blocked_until)),
|
|
152
|
+
CHECK ((lease_owner IS NULL) = (lease_until IS NULL)),
|
|
153
|
+
PRIMARY KEY (org_id, source)
|
|
154
|
+
) STRICT, WITHOUT ROWID;
|
|
155
|
+
|
|
156
|
+
-- ---------------------------------------------------------------------------
|
|
157
|
+
-- Access, audit, storage and identity
|
|
158
|
+
-- ---------------------------------------------------------------------------
|
|
159
|
+
|
|
160
|
+
CREATE TABLE access_grant (
|
|
161
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
162
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
163
|
+
subject_type TEXT NOT NULL CHECK (subject_type IN ('group', 'email')),
|
|
164
|
+
subject_value TEXT NOT NULL CHECK (subject_type = 'group' OR subject_value = lower(subject_value)),
|
|
165
|
+
subject_provider TEXT CHECK ((subject_type = 'group' AND subject_provider IS NOT NULL AND length(subject_provider) BETWEEN 1 AND 64 AND subject_provider = trim(subject_provider)) OR (subject_type = 'email' AND subject_provider IS NULL)),
|
|
166
|
+
scope TEXT NOT NULL CHECK (scope IN ('organization', 'project')),
|
|
167
|
+
project_id TEXT REFERENCES project (id),
|
|
168
|
+
role_id TEXT NOT NULL REFERENCES role (id),
|
|
169
|
+
created_by TEXT,
|
|
170
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
171
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
172
|
+
revision INTEGER NOT NULL DEFAULT 1 CHECK (revision >= 1),
|
|
173
|
+
last_op_id TEXT,
|
|
174
|
+
CHECK ((scope = 'organization' AND project_id IS NULL) OR (scope = 'project' AND project_id IS NOT NULL))
|
|
175
|
+
) STRICT;
|
|
176
|
+
|
|
177
|
+
CREATE TABLE api_idempotency (
|
|
178
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
179
|
+
principal_key TEXT NOT NULL CHECK (length(principal_key) BETWEEN 1 AND 80),
|
|
180
|
+
key TEXT NOT NULL CHECK (length(key) BETWEEN 1 AND 128),
|
|
181
|
+
route TEXT NOT NULL,
|
|
182
|
+
request_hash TEXT NOT NULL CHECK (length(request_hash) = 64),
|
|
183
|
+
status INTEGER NOT NULL CHECK (status BETWEEN 100 AND 599),
|
|
184
|
+
response_json TEXT NOT NULL CHECK (json_valid(response_json)),
|
|
185
|
+
created_at TEXT NOT NULL,
|
|
186
|
+
expires_at TEXT NOT NULL,
|
|
187
|
+
PRIMARY KEY (org_id, principal_key, key)
|
|
188
|
+
) STRICT;
|
|
189
|
+
|
|
190
|
+
CREATE TABLE audit_event (
|
|
191
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
192
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
193
|
+
seq INTEGER NOT NULL CHECK (seq >= 1),
|
|
194
|
+
prev_hash TEXT NOT NULL CHECK (length(prev_hash) = 64),
|
|
195
|
+
hash TEXT NOT NULL CHECK (length(hash) = 64),
|
|
196
|
+
created_at TEXT NOT NULL,
|
|
197
|
+
actor_type TEXT NOT NULL CHECK (actor_type IN ('user', 'service_principal', 'customer', 'system')),
|
|
198
|
+
actor_id TEXT,
|
|
199
|
+
token_selector TEXT,
|
|
200
|
+
source_ip TEXT,
|
|
201
|
+
action TEXT NOT NULL,
|
|
202
|
+
target_type TEXT NOT NULL,
|
|
203
|
+
target_id TEXT,
|
|
204
|
+
project_id TEXT,
|
|
205
|
+
payload_json TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(payload_json))
|
|
206
|
+
) STRICT;
|
|
207
|
+
|
|
208
|
+
CREATE TABLE audit_pending (
|
|
209
|
+
id TEXT PRIMARY KEY,
|
|
210
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
211
|
+
created_at TEXT NOT NULL,
|
|
212
|
+
actor_type TEXT NOT NULL CHECK (actor_type IN ('user', 'service_principal', 'customer', 'system')),
|
|
213
|
+
actor_id TEXT,
|
|
214
|
+
token_selector TEXT,
|
|
215
|
+
source_ip TEXT,
|
|
216
|
+
action TEXT NOT NULL CHECK (length(action) BETWEEN 3 AND 64),
|
|
217
|
+
target_type TEXT NOT NULL,
|
|
218
|
+
target_id TEXT,
|
|
219
|
+
project_id TEXT,
|
|
220
|
+
payload_json TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(payload_json) AND length(CAST(payload_json AS BLOB)) <= 4096)
|
|
221
|
+
) STRICT;
|
|
222
|
+
|
|
223
|
+
CREATE TABLE blob (
|
|
224
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
225
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
226
|
+
sha256 TEXT NOT NULL CHECK (length(sha256) = 64 AND sha256 = lower(sha256)),
|
|
227
|
+
size_bytes INTEGER NOT NULL CHECK (size_bytes >= 0),
|
|
228
|
+
storage_key TEXT NOT NULL,
|
|
229
|
+
media_type TEXT NOT NULL DEFAULT 'application/octet-stream',
|
|
230
|
+
refetchable INTEGER NOT NULL DEFAULT 1 CHECK (refetchable IN (0, 1)),
|
|
231
|
+
upstream_url TEXT,
|
|
232
|
+
last_fetched_at TEXT,
|
|
233
|
+
last_accessed_at TEXT,
|
|
234
|
+
purge_after TEXT,
|
|
235
|
+
deleted_at TEXT,
|
|
236
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
237
|
+
purge_generation TEXT,
|
|
238
|
+
purge_lease_until TEXT,
|
|
239
|
+
published_at TEXT
|
|
240
|
+
) STRICT;
|
|
241
|
+
|
|
242
|
+
CREATE TABLE bundle (
|
|
243
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
244
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
245
|
+
name TEXT NOT NULL,
|
|
246
|
+
revision INTEGER NOT NULL DEFAULT 1 CHECK (revision >= 1),
|
|
247
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
248
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
249
|
+
) STRICT;
|
|
250
|
+
|
|
251
|
+
CREATE TABLE cache_epoch (
|
|
252
|
+
org_id TEXT NOT NULL PRIMARY KEY REFERENCES organization (id) ON DELETE CASCADE,
|
|
253
|
+
version TEXT NOT NULL
|
|
254
|
+
) STRICT;
|
|
255
|
+
|
|
256
|
+
CREATE TABLE credential (
|
|
257
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
258
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
259
|
+
project_id TEXT REFERENCES project (id),
|
|
260
|
+
kind TEXT NOT NULL,
|
|
261
|
+
ciphertext BLOB NOT NULL,
|
|
262
|
+
nonce BLOB NOT NULL CHECK (length(nonce) = 12),
|
|
263
|
+
key_version INTEGER NOT NULL CHECK (key_version >= 1),
|
|
264
|
+
aad TEXT NOT NULL,
|
|
265
|
+
rotated_at TEXT,
|
|
266
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
267
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
268
|
+
) STRICT;
|
|
269
|
+
|
|
270
|
+
CREATE TABLE customer (
|
|
271
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
272
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
273
|
+
name TEXT NOT NULL,
|
|
274
|
+
email TEXT CHECK (email IS NULL OR email = lower(email)),
|
|
275
|
+
external_ref TEXT,
|
|
276
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled')),
|
|
277
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
278
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
279
|
+
) STRICT;
|
|
280
|
+
|
|
281
|
+
CREATE TABLE fetch_lease (
|
|
282
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
283
|
+
lease_key TEXT NOT NULL PRIMARY KEY CHECK (length(lease_key) = 64 AND lease_key = lower(lease_key) AND lease_key NOT GLOB '*[^0-9a-f]*'),
|
|
284
|
+
owner TEXT NOT NULL,
|
|
285
|
+
fence INTEGER NOT NULL CHECK (fence >= 1),
|
|
286
|
+
lease_until TEXT NOT NULL,
|
|
287
|
+
acquired_at TEXT NOT NULL
|
|
288
|
+
) STRICT, WITHOUT ROWID;
|
|
289
|
+
|
|
290
|
+
CREATE TABLE identity (
|
|
291
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
292
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
293
|
+
user_id TEXT NOT NULL REFERENCES user_account (id),
|
|
294
|
+
provider TEXT NOT NULL CHECK (length(provider) BETWEEN 1 AND 64 AND provider = trim(provider)),
|
|
295
|
+
subject TEXT NOT NULL,
|
|
296
|
+
email TEXT NOT NULL CHECK (email = lower(email)),
|
|
297
|
+
email_verified INTEGER NOT NULL DEFAULT 0 CHECK (email_verified IN (0, 1)),
|
|
298
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
299
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
300
|
+
validated_at TEXT,
|
|
301
|
+
UNIQUE (org_id, id, user_id)
|
|
302
|
+
) STRICT;
|
|
303
|
+
|
|
304
|
+
CREATE TABLE identity_block (
|
|
305
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
306
|
+
provider TEXT NOT NULL CHECK (length(provider) BETWEEN 1 AND 64 AND provider = trim(provider)),
|
|
307
|
+
issuer TEXT NOT NULL CHECK (length(issuer) BETWEEN 1 AND 512),
|
|
308
|
+
subject TEXT NOT NULL CHECK (length(subject) >= 1),
|
|
309
|
+
blocked_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
310
|
+
blocked_by_user_id TEXT REFERENCES user_account (id),
|
|
311
|
+
PRIMARY KEY (org_id, provider, issuer, subject)
|
|
312
|
+
) STRICT, WITHOUT ROWID;
|
|
313
|
+
|
|
314
|
+
CREATE TABLE identity_provider_binding (
|
|
315
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
316
|
+
provider TEXT NOT NULL CHECK (length(provider) BETWEEN 1 AND 64 AND provider = trim(provider)),
|
|
317
|
+
anchor TEXT NOT NULL CHECK (length(anchor) BETWEEN 1 AND 512),
|
|
318
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
319
|
+
PRIMARY KEY (org_id, provider)
|
|
320
|
+
) STRICT, WITHOUT ROWID;
|
|
321
|
+
|
|
322
|
+
-- ---------------------------------------------------------------------------
|
|
323
|
+
-- Jobs, authentication and sources
|
|
324
|
+
-- ---------------------------------------------------------------------------
|
|
325
|
+
|
|
326
|
+
CREATE TABLE job (
|
|
327
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
328
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
329
|
+
type TEXT NOT NULL,
|
|
330
|
+
payload_json TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(payload_json) AND length(payload_json) <= 131072),
|
|
331
|
+
resource_key TEXT,
|
|
332
|
+
idempotency_key TEXT,
|
|
333
|
+
state TEXT NOT NULL DEFAULT 'queued' CHECK (state IN ('queued', 'running', 'succeeded', 'dead')),
|
|
334
|
+
attempts INTEGER NOT NULL DEFAULT 0 CHECK (attempts >= 0),
|
|
335
|
+
max_attempts INTEGER NOT NULL DEFAULT 5 CHECK (max_attempts >= 1),
|
|
336
|
+
lease_owner TEXT,
|
|
337
|
+
lease_until TEXT,
|
|
338
|
+
fence INTEGER NOT NULL DEFAULT 0 CHECK (fence >= 0),
|
|
339
|
+
run_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
340
|
+
last_error TEXT,
|
|
341
|
+
sync_error TEXT,
|
|
342
|
+
finished_at TEXT,
|
|
343
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
344
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
345
|
+
project_id TEXT REFERENCES project (id),
|
|
346
|
+
initiator_token_id TEXT,
|
|
347
|
+
CHECK (state <> 'running' OR (lease_owner IS NOT NULL AND lease_until IS NOT NULL))
|
|
348
|
+
) STRICT;
|
|
349
|
+
|
|
350
|
+
CREATE TABLE job_attempt (
|
|
351
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
352
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
353
|
+
job_id TEXT NOT NULL REFERENCES job (id) ON DELETE CASCADE,
|
|
354
|
+
attempt INTEGER NOT NULL CHECK (attempt >= 1),
|
|
355
|
+
fence INTEGER NOT NULL CHECK (fence >= 1),
|
|
356
|
+
worker TEXT NOT NULL,
|
|
357
|
+
outcome TEXT NOT NULL DEFAULT 'running' CHECK (outcome IN ('running', 'succeeded', 'failed', 'lease_expired', 'released')),
|
|
358
|
+
error TEXT,
|
|
359
|
+
started_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
360
|
+
finished_at TEXT,
|
|
361
|
+
next_run_at TEXT
|
|
362
|
+
) STRICT;
|
|
363
|
+
|
|
364
|
+
CREATE TABLE job_schedule (
|
|
365
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
366
|
+
name TEXT NOT NULL,
|
|
367
|
+
every_seconds INTEGER CHECK (every_seconds IS NULL OR every_seconds > 0),
|
|
368
|
+
cron TEXT,
|
|
369
|
+
next_run_at TEXT NOT NULL,
|
|
370
|
+
last_slot TEXT,
|
|
371
|
+
enabled INTEGER NOT NULL DEFAULT 1 CHECK (enabled IN (0, 1)),
|
|
372
|
+
PRIMARY KEY (org_id, name),
|
|
373
|
+
CHECK (every_seconds IS NOT NULL OR cron IS NOT NULL)
|
|
374
|
+
) STRICT, WITHOUT ROWID;
|
|
375
|
+
|
|
376
|
+
CREATE TABLE local_login (
|
|
377
|
+
user_id TEXT NOT NULL PRIMARY KEY REFERENCES user_account (id),
|
|
378
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
379
|
+
password_hash TEXT CHECK (password_hash IS NULL OR password_hash GLOB 'pbkdf2-sha256$*'),
|
|
380
|
+
totp_credential_id TEXT REFERENCES credential (id) ON DELETE SET NULL,
|
|
381
|
+
totp_enabled_at TEXT,
|
|
382
|
+
totp_last_step INTEGER CHECK (totp_last_step IS NULL OR totp_last_step >= 0),
|
|
383
|
+
credential_rev INTEGER NOT NULL DEFAULT 0 CHECK (credential_rev >= 0),
|
|
384
|
+
setup_hash TEXT CHECK (setup_hash IS NULL OR (length(setup_hash) = 64 AND setup_hash NOT GLOB '*[^0-9a-f]*')),
|
|
385
|
+
setup_expires_at TEXT,
|
|
386
|
+
failed_count INTEGER NOT NULL DEFAULT 0 CHECK (failed_count >= 0),
|
|
387
|
+
locked_until TEXT,
|
|
388
|
+
last_op_id TEXT,
|
|
389
|
+
last_login_at TEXT,
|
|
390
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
391
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
392
|
+
CHECK ((setup_hash IS NULL) = (setup_expires_at IS NULL))
|
|
393
|
+
) STRICT;
|
|
394
|
+
|
|
395
|
+
CREATE TABLE service_principal (
|
|
396
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
397
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
398
|
+
name TEXT NOT NULL,
|
|
399
|
+
project_id TEXT REFERENCES project (id),
|
|
400
|
+
created_by_user_id TEXT NOT NULL REFERENCES user_account (id),
|
|
401
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'suspended')),
|
|
402
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
403
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
404
|
+
revision INTEGER NOT NULL DEFAULT 1 CHECK (revision >= 1),
|
|
405
|
+
last_op_id TEXT
|
|
406
|
+
) STRICT;
|
|
407
|
+
|
|
408
|
+
CREATE TABLE service_principal_permission (
|
|
409
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
410
|
+
service_principal_id TEXT NOT NULL,
|
|
411
|
+
permission TEXT NOT NULL,
|
|
412
|
+
PRIMARY KEY (org_id, service_principal_id, permission),
|
|
413
|
+
FOREIGN KEY (org_id, service_principal_id) REFERENCES service_principal (org_id, id) ON DELETE CASCADE
|
|
414
|
+
) STRICT, WITHOUT ROWID;
|
|
415
|
+
|
|
416
|
+
CREATE TABLE session (
|
|
417
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
418
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
419
|
+
user_id TEXT NOT NULL REFERENCES user_account (id),
|
|
420
|
+
identity_id TEXT,
|
|
421
|
+
secret_hash TEXT NOT NULL CHECK (length(secret_hash) = 64),
|
|
422
|
+
csrf_hash TEXT NOT NULL CHECK (length(csrf_hash) = 64),
|
|
423
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
424
|
+
expires_at TEXT NOT NULL,
|
|
425
|
+
last_seen_at TEXT,
|
|
426
|
+
reauth_at TEXT,
|
|
427
|
+
revoked_at TEXT,
|
|
428
|
+
ip TEXT,
|
|
429
|
+
user_agent TEXT,
|
|
430
|
+
FOREIGN KEY (org_id, identity_id, user_id) REFERENCES identity (org_id, id, user_id)
|
|
431
|
+
) STRICT;
|
|
432
|
+
|
|
433
|
+
CREATE TABLE source (
|
|
434
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
435
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
436
|
+
project_id TEXT REFERENCES project (id),
|
|
437
|
+
name TEXT NOT NULL,
|
|
438
|
+
ecosystem TEXT NOT NULL CHECK (ecosystem IN ('composer', 'npm')),
|
|
439
|
+
type TEXT NOT NULL CHECK (type IN ('vcs', 'composer_repository', 'npm_registry', 'packagist_proxy', 'npmjs_proxy', 'hosted')),
|
|
440
|
+
url TEXT,
|
|
441
|
+
credential_id TEXT REFERENCES credential (id),
|
|
442
|
+
config_json TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(config_json)),
|
|
443
|
+
protocol_mode TEXT CHECK (protocol_mode IS NULL OR protocol_mode IN ('v2', 'v1-providers', 'v1-lazy', 'v1-includes', 'npm-packument')),
|
|
444
|
+
has_available_packages INTEGER NOT NULL DEFAULT 0 CHECK (has_available_packages IN (0, 1)),
|
|
445
|
+
user_agent TEXT,
|
|
446
|
+
default_visible_to_new_projects INTEGER NOT NULL DEFAULT 1 CHECK (default_visible_to_new_projects IN (0, 1)),
|
|
447
|
+
removal_policy TEXT NOT NULL DEFAULT 'flag' CHECK (removal_policy IN ('flag', 'exclude')),
|
|
448
|
+
retention_days_override INTEGER CHECK (retention_days_override IS NULL OR retention_days_override >= 0),
|
|
449
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'degraded', 'failing', 'paused')),
|
|
450
|
+
poll_interval_seconds INTEGER NOT NULL DEFAULT 86400 CHECK (poll_interval_seconds > 0),
|
|
451
|
+
next_poll_at TEXT,
|
|
452
|
+
last_polled_at TEXT,
|
|
453
|
+
last_success_at TEXT,
|
|
454
|
+
poll_etag TEXT,
|
|
455
|
+
poll_last_modified TEXT,
|
|
456
|
+
root_hash TEXT,
|
|
457
|
+
listing_hash TEXT,
|
|
458
|
+
consecutive_failures INTEGER NOT NULL DEFAULT 0 CHECK (consecutive_failures >= 0),
|
|
459
|
+
last_error TEXT,
|
|
460
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
461
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
462
|
+
listing_open_stamp TEXT,
|
|
463
|
+
listing_opened_at TEXT,
|
|
464
|
+
last_refusal_at TEXT,
|
|
465
|
+
last_refusal_absent INTEGER CHECK (last_refusal_absent IS NULL OR last_refusal_absent >= 0),
|
|
466
|
+
last_refusal_listed INTEGER CHECK (last_refusal_listed IS NULL OR last_refusal_listed >= 0),
|
|
467
|
+
last_refusal_successful INTEGER CHECK (last_refusal_successful IS NULL OR last_refusal_successful >= 0),
|
|
468
|
+
removal_accepted INTEGER NOT NULL DEFAULT 0 CHECK (removal_accepted IN (0, 1)),
|
|
469
|
+
sync_generation TEXT,
|
|
470
|
+
sync_counts TEXT NOT NULL DEFAULT '{"listing":true,"queued":0,"running":0,"retrying":0,"failed":0,"completed":0,"errors":{}}' CHECK (json_valid(sync_counts)),
|
|
471
|
+
CHECK ((last_refusal_at IS NULL) = (last_refusal_absent IS NULL)
|
|
472
|
+
AND (last_refusal_at IS NULL) = (last_refusal_listed IS NULL)
|
|
473
|
+
AND (last_refusal_at IS NULL) = (last_refusal_successful IS NULL)),
|
|
474
|
+
CHECK (removal_accepted = 0 OR last_refusal_at IS NOT NULL)
|
|
475
|
+
) STRICT;
|
|
476
|
+
|
|
477
|
+
CREATE TABLE source_sync_package (
|
|
478
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
479
|
+
source_id TEXT NOT NULL REFERENCES source (id) ON DELETE CASCADE,
|
|
480
|
+
package_key TEXT NOT NULL,
|
|
481
|
+
generation TEXT NOT NULL,
|
|
482
|
+
job_id TEXT NOT NULL REFERENCES job (id) ON DELETE CASCADE,
|
|
483
|
+
state TEXT NOT NULL CHECK (state IN ('queued', 'running', 'succeeded', 'dead')),
|
|
484
|
+
error TEXT,
|
|
485
|
+
PRIMARY KEY (org_id, source_id, package_key)
|
|
486
|
+
) STRICT, WITHOUT ROWID;
|
|
487
|
+
|
|
488
|
+
CREATE INDEX source_sync_package_job ON source_sync_package (org_id, job_id);
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
CREATE TABLE token (
|
|
492
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
493
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
494
|
+
kind TEXT NOT NULL CHECK (kind IN ('pat', 'svc', 'cst')),
|
|
495
|
+
selector TEXT NOT NULL CHECK (length(selector) = 12),
|
|
496
|
+
hash TEXT NOT NULL CHECK (length(hash) = 64),
|
|
497
|
+
pepper_version INTEGER NOT NULL CHECK (pepper_version >= 1),
|
|
498
|
+
name TEXT NOT NULL,
|
|
499
|
+
scopes_json TEXT NOT NULL CHECK (json_valid(scopes_json)),
|
|
500
|
+
read_only INTEGER NOT NULL DEFAULT 0 CHECK (read_only IN (0, 1)),
|
|
501
|
+
user_id TEXT REFERENCES user_account (id),
|
|
502
|
+
service_principal_id TEXT REFERENCES service_principal (id),
|
|
503
|
+
customer_id TEXT REFERENCES customer (id),
|
|
504
|
+
project_id TEXT REFERENCES project (id),
|
|
505
|
+
created_by_user_id TEXT REFERENCES user_account (id),
|
|
506
|
+
expires_at TEXT,
|
|
507
|
+
revoked_at TEXT,
|
|
508
|
+
last_used_at TEXT,
|
|
509
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
510
|
+
project_mode TEXT NOT NULL DEFAULT 'selected' CHECK (project_mode IN ('all', 'selected')),
|
|
511
|
+
validation_kind TEXT CHECK (validation_kind IS NULL OR (validation_kind IN ('sso', 'local') AND kind = 'pat')),
|
|
512
|
+
validation_identity_id TEXT REFERENCES identity (id) ON DELETE SET NULL CHECK (validation_identity_id IS NULL OR (validation_kind IS NOT 'local' AND kind = 'pat')),
|
|
513
|
+
last_op_id TEXT,
|
|
514
|
+
CHECK ( (kind = 'pat' AND user_id IS NOT NULL AND service_principal_id IS NULL AND customer_id IS NULL) OR (kind = 'svc' AND service_principal_id IS NOT NULL AND user_id IS NULL AND customer_id IS NULL) OR (kind = 'cst' AND customer_id IS NOT NULL AND user_id IS NULL AND service_principal_id IS NULL) ),
|
|
515
|
+
CHECK (kind = 'cst' OR expires_at IS NOT NULL),
|
|
516
|
+
-- The single-column FK clears only the identity on deletion; this FK enforces ownership.
|
|
517
|
+
FOREIGN KEY (org_id, validation_identity_id, user_id) REFERENCES identity (org_id, id, user_id)
|
|
518
|
+
) STRICT;
|
|
519
|
+
|
|
520
|
+
CREATE TABLE token_project (
|
|
521
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
522
|
+
token_id TEXT NOT NULL REFERENCES token (id) ON DELETE CASCADE,
|
|
523
|
+
project_id TEXT NOT NULL REFERENCES project (id),
|
|
524
|
+
PRIMARY KEY (token_id, project_id)
|
|
525
|
+
) STRICT, WITHOUT ROWID;
|
|
526
|
+
|
|
527
|
+
CREATE TABLE webhook_delivery (
|
|
528
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
529
|
+
provider TEXT NOT NULL,
|
|
530
|
+
delivery_id TEXT NOT NULL,
|
|
531
|
+
source_id TEXT REFERENCES source (id),
|
|
532
|
+
event TEXT NOT NULL,
|
|
533
|
+
outcome TEXT NOT NULL DEFAULT 'received' CHECK (outcome IN ('received', 'processed', 'ignored', 'failed')),
|
|
534
|
+
received_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
535
|
+
processed_at TEXT,
|
|
536
|
+
PRIMARY KEY (provider, delivery_id)
|
|
537
|
+
) STRICT, WITHOUT ROWID;
|
|
538
|
+
|
|
539
|
+
-- ---------------------------------------------------------------------------
|
|
540
|
+
-- Import and ingest
|
|
541
|
+
-- ---------------------------------------------------------------------------
|
|
542
|
+
|
|
543
|
+
CREATE TABLE import_run (
|
|
544
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
545
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
546
|
+
source_system TEXT NOT NULL,
|
|
547
|
+
state TEXT NOT NULL DEFAULT 'planned' CHECK (state IN ('planned', 'applying', 'failed', 'applied', 'verifying', 'verified', 'rolled_back', 'cancelled')),
|
|
548
|
+
plan_hash TEXT,
|
|
549
|
+
planned_count INTEGER NOT NULL DEFAULT 0 CHECK (planned_count >= 0),
|
|
550
|
+
applied_count INTEGER NOT NULL DEFAULT 0 CHECK (applied_count >= 0),
|
|
551
|
+
started_by_user_id TEXT REFERENCES user_account (id),
|
|
552
|
+
before_image_expires_at TEXT,
|
|
553
|
+
accepted_at TEXT,
|
|
554
|
+
last_error TEXT,
|
|
555
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
556
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
557
|
+
approved_by_user_id TEXT REFERENCES user_account (id),
|
|
558
|
+
approved_at TEXT,
|
|
559
|
+
credential_window_until TEXT,
|
|
560
|
+
started_by_service_principal_id TEXT REFERENCES service_principal (id),
|
|
561
|
+
manifest_blob_sha256 TEXT CHECK (manifest_blob_sha256 IS NULL OR length(manifest_blob_sha256) = 64),
|
|
562
|
+
rollback_restored_count INTEGER NOT NULL DEFAULT 0 CHECK (rollback_restored_count >= 0),
|
|
563
|
+
rollback_conflict_count INTEGER NOT NULL DEFAULT 0 CHECK (rollback_conflict_count >= 0),
|
|
564
|
+
rollback_expired_count INTEGER NOT NULL DEFAULT 0 CHECK (rollback_expired_count >= 0)
|
|
565
|
+
) STRICT;
|
|
566
|
+
|
|
567
|
+
CREATE TABLE ingest_session (
|
|
568
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
569
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
570
|
+
scope TEXT NOT NULL CHECK (scope IN ('source', 'package')),
|
|
571
|
+
scope_key TEXT NOT NULL CHECK (length(scope_key) = 64 AND scope_key = lower(scope_key) AND scope_key NOT GLOB '*[^0-9a-f]*'),
|
|
572
|
+
source_id TEXT NOT NULL REFERENCES source (id) ON DELETE CASCADE,
|
|
573
|
+
package_key TEXT,
|
|
574
|
+
generation INTEGER NOT NULL CHECK (generation >= 1),
|
|
575
|
+
owner TEXT NOT NULL,
|
|
576
|
+
fence INTEGER NOT NULL CHECK (fence >= 1),
|
|
577
|
+
lease_until TEXT NOT NULL,
|
|
578
|
+
state TEXT NOT NULL CHECK (state IN ('running', 'complete', 'abandoned')),
|
|
579
|
+
snapshot_id TEXT,
|
|
580
|
+
snapshot_blob_sha256 TEXT CHECK (snapshot_blob_sha256 IS NULL OR (length(snapshot_blob_sha256) = 64 AND snapshot_blob_sha256 = lower(snapshot_blob_sha256) AND snapshot_blob_sha256 NOT GLOB '*[^0-9a-f]*')),
|
|
581
|
+
stamp TEXT,
|
|
582
|
+
cursor TEXT CHECK (cursor IS NULL OR length(cursor) <= 4096),
|
|
583
|
+
seen_count INTEGER NOT NULL DEFAULT 0 CHECK (seen_count >= 0),
|
|
584
|
+
pending_validators TEXT CHECK (pending_validators IS NULL OR CASE WHEN json_valid(pending_validators) THEN json_type(pending_validators) = 'object' ELSE 0 END),
|
|
585
|
+
pages_so_far INTEGER NOT NULL DEFAULT 0 CHECK (pages_so_far >= 0),
|
|
586
|
+
started_at TEXT NOT NULL,
|
|
587
|
+
updated_at TEXT NOT NULL,
|
|
588
|
+
CHECK ((scope = 'package') = (package_key IS NOT NULL))
|
|
589
|
+
) STRICT;
|
|
590
|
+
|
|
591
|
+
-- ---------------------------------------------------------------------------
|
|
592
|
+
-- Catalogue and vulnerability results
|
|
593
|
+
-- ---------------------------------------------------------------------------
|
|
594
|
+
|
|
595
|
+
CREATE TABLE package (
|
|
596
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
597
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
598
|
+
source_id TEXT NOT NULL,
|
|
599
|
+
ecosystem TEXT NOT NULL CHECK (ecosystem IN ('composer', 'npm')),
|
|
600
|
+
name TEXT NOT NULL CHECK (name = lower(name)),
|
|
601
|
+
description TEXT,
|
|
602
|
+
visible_to_new_projects INTEGER NOT NULL DEFAULT 1 CHECK (visible_to_new_projects IN (0, 1)),
|
|
603
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'removed_upstream')),
|
|
604
|
+
removed_upstream_at TEXT,
|
|
605
|
+
rev INTEGER NOT NULL DEFAULT 1 CHECK (rev >= 1),
|
|
606
|
+
modified_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
607
|
+
last_synced_at TEXT,
|
|
608
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
609
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
610
|
+
removal_cause TEXT CHECK (removal_cause IS NULL OR removal_cause IN ('document_absent', 'source_listing')),
|
|
611
|
+
last_op_id TEXT,
|
|
612
|
+
listing_open_stamp TEXT,
|
|
613
|
+
listing_seen_stamp TEXT,
|
|
614
|
+
listing_opened_at TEXT,
|
|
615
|
+
FOREIGN KEY (org_id, source_id) REFERENCES source (org_id, id)
|
|
616
|
+
) STRICT;
|
|
617
|
+
|
|
618
|
+
CREATE TABLE project_package (
|
|
619
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
620
|
+
project_id TEXT NOT NULL,
|
|
621
|
+
package_id TEXT NOT NULL,
|
|
622
|
+
origin TEXT NOT NULL CHECK (origin IN ('subscription', 'explicit', 'pattern')),
|
|
623
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
624
|
+
ecosystem TEXT NOT NULL CHECK (ecosystem IN ('composer', 'npm')),
|
|
625
|
+
name TEXT NOT NULL CHECK (name = lower(name)),
|
|
626
|
+
PRIMARY KEY (project_id, package_id),
|
|
627
|
+
FOREIGN KEY (org_id, project_id) REFERENCES project (org_id, id),
|
|
628
|
+
FOREIGN KEY (org_id, package_id) REFERENCES package (org_id, id)
|
|
629
|
+
) STRICT, WITHOUT ROWID;
|
|
630
|
+
|
|
631
|
+
CREATE TABLE project_package_pattern (
|
|
632
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
633
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
634
|
+
project_id TEXT NOT NULL,
|
|
635
|
+
source_id TEXT NOT NULL,
|
|
636
|
+
pattern TEXT NOT NULL CHECK (pattern = lower(pattern)),
|
|
637
|
+
effect TEXT NOT NULL CHECK (effect IN ('include', 'exclude')),
|
|
638
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
639
|
+
FOREIGN KEY (org_id, project_id) REFERENCES project (org_id, id),
|
|
640
|
+
FOREIGN KEY (org_id, source_id) REFERENCES source (org_id, id)
|
|
641
|
+
) STRICT;
|
|
642
|
+
|
|
643
|
+
CREATE TABLE project_source (
|
|
644
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
645
|
+
project_id TEXT NOT NULL,
|
|
646
|
+
source_id TEXT NOT NULL,
|
|
647
|
+
precedence INTEGER NOT NULL DEFAULT 100 CHECK (precedence >= 0),
|
|
648
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
649
|
+
PRIMARY KEY (project_id, source_id),
|
|
650
|
+
FOREIGN KEY (org_id, project_id) REFERENCES project (org_id, id),
|
|
651
|
+
FOREIGN KEY (org_id, source_id) REFERENCES source (org_id, id)
|
|
652
|
+
) STRICT, WITHOUT ROWID;
|
|
653
|
+
|
|
654
|
+
CREATE TABLE registered_name (
|
|
655
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
656
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
657
|
+
source_id TEXT NOT NULL,
|
|
658
|
+
ecosystem TEXT NOT NULL CHECK (ecosystem IN ('composer', 'npm')),
|
|
659
|
+
pattern TEXT NOT NULL CHECK (pattern = lower(pattern)),
|
|
660
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
661
|
+
FOREIGN KEY (org_id, source_id) REFERENCES source (org_id, id) ON DELETE CASCADE
|
|
662
|
+
) STRICT;
|
|
663
|
+
|
|
664
|
+
CREATE TABLE upstream_state (
|
|
665
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
666
|
+
package_id TEXT NOT NULL REFERENCES package (id) ON DELETE CASCADE,
|
|
667
|
+
form TEXT NOT NULL CHECK (form IN ('composer-p2', 'composer-p2-dev', 'composer-v1-provider', 'npm-packument')),
|
|
668
|
+
etag TEXT,
|
|
669
|
+
last_modified TEXT,
|
|
670
|
+
content_sha256 TEXT CHECK (content_sha256 IS NULL OR (length(content_sha256) = 64 AND content_sha256 = lower(content_sha256) AND content_sha256 NOT GLOB '*[^0-9a-f]*')),
|
|
671
|
+
checked_at TEXT NOT NULL,
|
|
672
|
+
fresh_until TEXT NOT NULL,
|
|
673
|
+
not_found_count INTEGER NOT NULL DEFAULT 0 CHECK (not_found_count >= 0),
|
|
674
|
+
first_not_found_at TEXT,
|
|
675
|
+
PRIMARY KEY (package_id, form)
|
|
676
|
+
) STRICT, WITHOUT ROWID;
|
|
677
|
+
|
|
678
|
+
CREATE TABLE version (
|
|
679
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
680
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
681
|
+
package_id TEXT NOT NULL,
|
|
682
|
+
version TEXT NOT NULL,
|
|
683
|
+
version_normalized TEXT NOT NULL,
|
|
684
|
+
stability TEXT NOT NULL DEFAULT 'stable' CHECK (stability IN ('dev', 'alpha', 'beta', 'RC', 'stable')),
|
|
685
|
+
published_at TEXT NOT NULL,
|
|
686
|
+
published_at_basis TEXT NOT NULL,
|
|
687
|
+
upstream_released_at TEXT,
|
|
688
|
+
license TEXT,
|
|
689
|
+
blob_id TEXT REFERENCES blob (id),
|
|
690
|
+
dist_type TEXT CHECK (dist_type IS NULL OR dist_type IN ('zip', 'tar', 'tgz')),
|
|
691
|
+
composer_shasum TEXT,
|
|
692
|
+
npm_integrity TEXT,
|
|
693
|
+
source_ref TEXT,
|
|
694
|
+
metadata_json TEXT NOT NULL CHECK (json_valid(metadata_json)),
|
|
695
|
+
deprecated_message TEXT,
|
|
696
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'removed_upstream', 'yanked', 'quarantined', 'tombstoned')),
|
|
697
|
+
removed_upstream_at TEXT,
|
|
698
|
+
tombstoned_at TEXT,
|
|
699
|
+
tombstone_reason TEXT,
|
|
700
|
+
purge_after TEXT,
|
|
701
|
+
revision INTEGER NOT NULL DEFAULT 1 CHECK (revision >= 1),
|
|
702
|
+
last_op_id TEXT,
|
|
703
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
704
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
705
|
+
listing_seen_stamp TEXT,
|
|
706
|
+
CHECK (status <> 'tombstoned' OR tombstoned_at IS NOT NULL),
|
|
707
|
+
CHECK (status <> 'removed_upstream' OR removed_upstream_at IS NOT NULL),
|
|
708
|
+
FOREIGN KEY (org_id, package_id) REFERENCES package (org_id, id)
|
|
709
|
+
) STRICT;
|
|
710
|
+
|
|
711
|
+
CREATE TABLE version_revision (
|
|
712
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
713
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
714
|
+
version_id TEXT NOT NULL REFERENCES version (id),
|
|
715
|
+
revision INTEGER NOT NULL CHECK (revision >= 1),
|
|
716
|
+
blob_id TEXT REFERENCES blob (id),
|
|
717
|
+
source_ref TEXT,
|
|
718
|
+
metadata_json TEXT NOT NULL CHECK (json_valid(metadata_json)),
|
|
719
|
+
reason TEXT NOT NULL,
|
|
720
|
+
op_id TEXT,
|
|
721
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
722
|
+
) STRICT;
|
|
723
|
+
|
|
724
|
+
CREATE TABLE vulnerability_result (
|
|
725
|
+
org_id TEXT NOT NULL REFERENCES organization(id) ON DELETE CASCADE,
|
|
726
|
+
version_id TEXT NOT NULL,
|
|
727
|
+
source TEXT NOT NULL DEFAULT 'osv' CHECK (source = 'osv'),
|
|
728
|
+
package_id TEXT NOT NULL,
|
|
729
|
+
ecosystem TEXT NOT NULL CHECK (ecosystem IN ('composer', 'npm')),
|
|
730
|
+
package_key TEXT NOT NULL,
|
|
731
|
+
version TEXT NOT NULL,
|
|
732
|
+
query_version TEXT NOT NULL,
|
|
733
|
+
mapping_version INTEGER NOT NULL CHECK (mapping_version > 0),
|
|
734
|
+
assessment TEXT CHECK (assessment IN ('affected', 'not_affected')),
|
|
735
|
+
fetched_at TEXT CHECK (fetched_at IS NULL OR (length(fetched_at) = 24 AND strftime('%Y-%m-%dT%H:%M:%fZ', fetched_at) IS fetched_at)),
|
|
736
|
+
attempted_at TEXT NOT NULL CHECK (length(attempted_at) = 24 AND strftime('%Y-%m-%dT%H:%M:%fZ', attempted_at) IS attempted_at),
|
|
737
|
+
next_attempt_at TEXT NOT NULL CHECK (length(next_attempt_at) = 24 AND strftime('%Y-%m-%dT%H:%M:%fZ', next_attempt_at) IS next_attempt_at),
|
|
738
|
+
failure_count INTEGER NOT NULL DEFAULT 0 CHECK (failure_count BETWEEN 0 AND 1000),
|
|
739
|
+
last_error_code TEXT CHECK (last_error_code IN ('unavailable', 'protocol', 'config', 'limit')),
|
|
740
|
+
generation INTEGER NOT NULL CHECK (generation >= 0),
|
|
741
|
+
refresh_id TEXT,
|
|
742
|
+
PRIMARY KEY (org_id, version_id, source),
|
|
743
|
+
UNIQUE (org_id, ecosystem, package_id, version_id, source),
|
|
744
|
+
FOREIGN KEY (org_id, version_id) REFERENCES version(org_id, id) ON DELETE CASCADE,
|
|
745
|
+
FOREIGN KEY (org_id, package_id) REFERENCES package(org_id, id) ON DELETE CASCADE,
|
|
746
|
+
CHECK ((assessment IS NULL) = (fetched_at IS NULL))
|
|
747
|
+
) STRICT, WITHOUT ROWID;
|
|
748
|
+
|
|
749
|
+
-- ---------------------------------------------------------------------------
|
|
750
|
+
-- Package grants, tags and import history
|
|
751
|
+
-- ---------------------------------------------------------------------------
|
|
752
|
+
|
|
753
|
+
CREATE TABLE bundle_item (
|
|
754
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
755
|
+
bundle_id TEXT NOT NULL REFERENCES bundle (id),
|
|
756
|
+
bundle_revision INTEGER NOT NULL CHECK (bundle_revision >= 1),
|
|
757
|
+
package_id TEXT NOT NULL REFERENCES package (id),
|
|
758
|
+
version_constraint TEXT,
|
|
759
|
+
PRIMARY KEY (bundle_id, bundle_revision, package_id)
|
|
760
|
+
) STRICT, WITHOUT ROWID;
|
|
761
|
+
|
|
762
|
+
CREATE TABLE customer_grant (
|
|
763
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
764
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
765
|
+
customer_id TEXT NOT NULL REFERENCES customer (id),
|
|
766
|
+
project_id TEXT NOT NULL REFERENCES project (id),
|
|
767
|
+
package_id TEXT REFERENCES package (id),
|
|
768
|
+
bundle_id TEXT REFERENCES bundle (id),
|
|
769
|
+
version_constraint TEXT,
|
|
770
|
+
minimum_stability TEXT NOT NULL DEFAULT 'stable' CHECK (minimum_stability IN ('dev', 'alpha', 'beta', 'RC', 'stable')),
|
|
771
|
+
valid_until TEXT,
|
|
772
|
+
revoked_at TEXT,
|
|
773
|
+
row_version INTEGER NOT NULL DEFAULT 1 CHECK (row_version >= 1),
|
|
774
|
+
last_op_id TEXT,
|
|
775
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
776
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
777
|
+
CHECK ((package_id IS NOT NULL AND bundle_id IS NULL) OR (package_id IS NULL AND bundle_id IS NOT NULL))
|
|
778
|
+
) STRICT;
|
|
779
|
+
|
|
780
|
+
CREATE TABLE dist_tag (
|
|
781
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
782
|
+
package_id TEXT NOT NULL,
|
|
783
|
+
tag TEXT NOT NULL,
|
|
784
|
+
version_id TEXT NOT NULL,
|
|
785
|
+
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
786
|
+
PRIMARY KEY (package_id, tag),
|
|
787
|
+
FOREIGN KEY (org_id, package_id) REFERENCES package (org_id, id),
|
|
788
|
+
FOREIGN KEY (org_id, version_id) REFERENCES version (org_id, id)
|
|
789
|
+
) STRICT, WITHOUT ROWID;
|
|
790
|
+
|
|
791
|
+
CREATE TABLE dist_tag_history (
|
|
792
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
793
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
794
|
+
package_id TEXT NOT NULL,
|
|
795
|
+
tag TEXT NOT NULL,
|
|
796
|
+
version_id TEXT,
|
|
797
|
+
previous_version_id TEXT,
|
|
798
|
+
action TEXT NOT NULL CHECK (action IN ('set', 'remove')),
|
|
799
|
+
actor_type TEXT NOT NULL,
|
|
800
|
+
actor_id TEXT,
|
|
801
|
+
op_id TEXT,
|
|
802
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
803
|
+
FOREIGN KEY (org_id, package_id) REFERENCES package (org_id, id),
|
|
804
|
+
FOREIGN KEY (org_id, version_id) REFERENCES version (org_id, id),
|
|
805
|
+
FOREIGN KEY (org_id, previous_version_id) REFERENCES version (org_id, id)
|
|
806
|
+
) STRICT;
|
|
807
|
+
|
|
808
|
+
CREATE TABLE import_ref (
|
|
809
|
+
id TEXT NOT NULL PRIMARY KEY,
|
|
810
|
+
org_id TEXT NOT NULL REFERENCES organization (id),
|
|
811
|
+
import_run_id TEXT NOT NULL REFERENCES import_run (id),
|
|
812
|
+
entity_type TEXT NOT NULL,
|
|
813
|
+
entity_id TEXT NOT NULL,
|
|
814
|
+
external_ref TEXT NOT NULL,
|
|
815
|
+
action TEXT NOT NULL CHECK (action IN ('create', 'update')),
|
|
816
|
+
before_image_json TEXT CHECK (before_image_json IS NULL OR json_valid(before_image_json)),
|
|
817
|
+
content_hash TEXT,
|
|
818
|
+
conflict_state TEXT NOT NULL DEFAULT 'none' CHECK (conflict_state IN ('none', 'changed_after_import', 'resolved')),
|
|
819
|
+
rolled_back_at TEXT,
|
|
820
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
821
|
+
published_at_basis TEXT CHECK (published_at_basis IS NULL OR published_at_basis IN ('source_release', 'packagist_added', 'import_time')),
|
|
822
|
+
expired_at TEXT
|
|
823
|
+
) STRICT;
|
|
824
|
+
|
|
825
|
+
-- ---------------------------------------------------------------------------
|
|
826
|
+
-- Advisory matches and refreshes
|
|
827
|
+
-- ---------------------------------------------------------------------------
|
|
828
|
+
|
|
829
|
+
CREATE TABLE vulnerability_match (
|
|
830
|
+
org_id TEXT NOT NULL,
|
|
831
|
+
version_id TEXT NOT NULL,
|
|
832
|
+
source TEXT NOT NULL DEFAULT 'osv' CHECK (source = 'osv'),
|
|
833
|
+
advisory_id TEXT NOT NULL CHECK (length(CAST(advisory_id AS BLOB)) BETWEEN 1 AND 200 AND instr(advisory_id, '/') = 0 AND instr(advisory_id, char(92)) = 0 AND advisory_id NOT IN ('.', '..')),
|
|
834
|
+
advisory_modified_at TEXT CHECK (advisory_modified_at IS NULL OR (length(advisory_modified_at) = 24 AND strftime('%Y-%m-%dT%H:%M:%fZ', advisory_modified_at) IS advisory_modified_at)),
|
|
835
|
+
PRIMARY KEY (org_id, version_id, source, advisory_id),
|
|
836
|
+
FOREIGN KEY (org_id, version_id, source) REFERENCES vulnerability_result(org_id, version_id, source) ON DELETE CASCADE
|
|
837
|
+
) STRICT, WITHOUT ROWID;
|
|
838
|
+
|
|
839
|
+
CREATE TABLE vulnerability_refresh (
|
|
840
|
+
org_id TEXT NOT NULL,
|
|
841
|
+
refresh_id TEXT NOT NULL CHECK (length(refresh_id) BETWEEN 1 AND 200),
|
|
842
|
+
version_id TEXT NOT NULL,
|
|
843
|
+
source TEXT NOT NULL DEFAULT 'osv' CHECK (source = 'osv'),
|
|
844
|
+
generation INTEGER NOT NULL CHECK (generation >= 0),
|
|
845
|
+
mapping_version INTEGER NOT NULL CHECK (mapping_version > 0),
|
|
846
|
+
query_version TEXT NOT NULL,
|
|
847
|
+
continuation_token TEXT NOT NULL CHECK (length(CAST(continuation_token AS BLOB)) BETWEEN 1 AND 4096),
|
|
848
|
+
page_count INTEGER NOT NULL CHECK (page_count BETWEEN 1 AND 4),
|
|
849
|
+
accumulated_ids_json TEXT NOT NULL CHECK (length(CAST(accumulated_ids_json AS BLOB)) <= 65536 AND json_valid(accumulated_ids_json) AND json_type(accumulated_ids_json) = 'array' AND json_array_length(accumulated_ids_json) <= 200),
|
|
850
|
+
started_at TEXT NOT NULL CHECK (length(started_at) = 24 AND strftime('%Y-%m-%dT%H:%M:%fZ', started_at) IS started_at),
|
|
851
|
+
next_attempt_at TEXT NOT NULL CHECK (length(next_attempt_at) = 24 AND strftime('%Y-%m-%dT%H:%M:%fZ', next_attempt_at) IS next_attempt_at),
|
|
852
|
+
PRIMARY KEY (org_id, refresh_id),
|
|
853
|
+
UNIQUE (org_id, version_id, source),
|
|
854
|
+
FOREIGN KEY (org_id, version_id, source) REFERENCES vulnerability_result(org_id, version_id, source) ON DELETE CASCADE
|
|
855
|
+
) STRICT, WITHOUT ROWID;
|
|
856
|
+
|
|
857
|
+
-- ---------------------------------------------------------------------------
|
|
858
|
+
-- Indexes (original chain creation order)
|
|
859
|
+
-- ---------------------------------------------------------------------------
|
|
860
|
+
|
|
861
|
+
CREATE UNIQUE INDEX organization_singleton ON organization (singleton);
|
|
862
|
+
CREATE UNIQUE INDEX user_account_email ON user_account (org_id, email);
|
|
863
|
+
CREATE UNIQUE INDEX identity_subject ON identity (org_id, provider, subject);
|
|
864
|
+
CREATE INDEX identity_user ON identity (user_id);
|
|
865
|
+
CREATE INDEX user_group_name ON user_group (org_id, provider, group_name, user_id);
|
|
866
|
+
CREATE UNIQUE INDEX session_secret ON session (secret_hash);
|
|
867
|
+
CREATE INDEX session_user ON session (user_id, expires_at);
|
|
868
|
+
CREATE INDEX session_identity ON session (identity_id);
|
|
869
|
+
CREATE INDEX session_expiry ON session (expires_at);
|
|
870
|
+
CREATE UNIQUE INDEX role_key ON role (org_id, key);
|
|
871
|
+
CREATE UNIQUE INDEX project_slug ON project (org_id, slug);
|
|
872
|
+
CREATE UNIQUE INDEX access_grant_org_scope ON access_grant (org_id, subject_type, subject_provider, subject_value) WHERE subject_type = 'group' AND scope = 'organization';
|
|
873
|
+
CREATE UNIQUE INDEX access_grant_project_scope ON access_grant (project_id, subject_type, subject_provider, subject_value) WHERE subject_type = 'group' AND scope = 'project';
|
|
874
|
+
CREATE UNIQUE INDEX access_grant_org_scope_email ON access_grant (org_id, subject_type, subject_value) WHERE subject_type = 'email' AND scope = 'organization';
|
|
875
|
+
CREATE UNIQUE INDEX access_grant_project_scope_email ON access_grant (project_id, subject_type, subject_value) WHERE subject_type = 'email' AND scope = 'project';
|
|
876
|
+
CREATE INDEX access_grant_subject ON access_grant (org_id, subject_type, subject_provider, subject_value, project_id);
|
|
877
|
+
CREATE INDEX access_grant_project ON access_grant (project_id);
|
|
878
|
+
CREATE INDEX access_grant_role ON access_grant (role_id);
|
|
879
|
+
CREATE INDEX credential_key_version ON credential (org_id, key_version);
|
|
880
|
+
CREATE INDEX credential_project ON credential (project_id);
|
|
881
|
+
CREATE UNIQUE INDEX source_name_org ON source (org_id, name) WHERE project_id IS NULL;
|
|
882
|
+
CREATE UNIQUE INDEX source_name_project ON source (project_id, name) WHERE project_id IS NOT NULL;
|
|
883
|
+
CREATE INDEX source_project ON source (project_id);
|
|
884
|
+
CREATE INDEX source_credential ON source (credential_id);
|
|
885
|
+
CREATE INDEX source_due ON source (next_poll_at) WHERE status <> 'paused';
|
|
886
|
+
CREATE UNIQUE INDEX blob_digest ON blob (org_id, sha256);
|
|
887
|
+
CREATE UNIQUE INDEX blob_storage_key ON blob (org_id, storage_key);
|
|
888
|
+
CREATE INDEX blob_purge ON blob (purge_after) WHERE purge_after IS NOT NULL AND deleted_at IS NULL;
|
|
889
|
+
CREATE INDEX blob_idle ON blob (last_accessed_at) WHERE refetchable = 1 AND deleted_at IS NULL;
|
|
890
|
+
CREATE UNIQUE INDEX version_revision_identity ON version_revision (version_id, revision);
|
|
891
|
+
CREATE INDEX version_revision_blob ON version_revision (blob_id);
|
|
892
|
+
CREATE UNIQUE INDEX customer_external_ref ON customer (org_id, external_ref) WHERE external_ref IS NOT NULL;
|
|
893
|
+
CREATE UNIQUE INDEX bundle_name ON bundle (org_id, name);
|
|
894
|
+
CREATE INDEX bundle_item_package ON bundle_item (package_id);
|
|
895
|
+
CREATE UNIQUE INDEX customer_grant_package ON customer_grant (customer_id, project_id, package_id) WHERE package_id IS NOT NULL AND revoked_at IS NULL;
|
|
896
|
+
CREATE UNIQUE INDEX customer_grant_bundle ON customer_grant (customer_id, project_id, bundle_id) WHERE bundle_id IS NOT NULL AND revoked_at IS NULL;
|
|
897
|
+
CREATE INDEX customer_grant_customer ON customer_grant (customer_id);
|
|
898
|
+
CREATE INDEX customer_grant_project ON customer_grant (project_id);
|
|
899
|
+
CREATE INDEX customer_grant_package_fk ON customer_grant (package_id);
|
|
900
|
+
CREATE INDEX customer_grant_bundle_fk ON customer_grant (bundle_id);
|
|
901
|
+
CREATE UNIQUE INDEX service_principal_name ON service_principal (org_id, name);
|
|
902
|
+
CREATE INDEX service_principal_project ON service_principal (project_id);
|
|
903
|
+
CREATE INDEX service_principal_creator ON service_principal (created_by_user_id);
|
|
904
|
+
CREATE UNIQUE INDEX token_selector ON token (selector);
|
|
905
|
+
CREATE INDEX token_user ON token (user_id);
|
|
906
|
+
CREATE INDEX token_service_principal ON token (service_principal_id);
|
|
907
|
+
CREATE INDEX token_customer ON token (customer_id);
|
|
908
|
+
CREATE INDEX token_binding_project ON token (project_id);
|
|
909
|
+
CREATE INDEX token_creator ON token (created_by_user_id);
|
|
910
|
+
CREATE INDEX token_project_project ON token_project (project_id);
|
|
911
|
+
CREATE UNIQUE INDEX audit_event_seq ON audit_event (org_id, seq);
|
|
912
|
+
CREATE UNIQUE INDEX audit_event_prev_hash ON audit_event (org_id, prev_hash);
|
|
913
|
+
CREATE INDEX audit_event_project ON audit_event (org_id, project_id, seq);
|
|
914
|
+
CREATE INDEX audit_event_target ON audit_event (org_id, target_type, target_id, seq);
|
|
915
|
+
CREATE INDEX audit_event_actor ON audit_event (org_id, actor_id, seq);
|
|
916
|
+
CREATE UNIQUE INDEX job_one_running_per_resource ON job (org_id, resource_key) WHERE state = 'running' AND resource_key IS NOT NULL;
|
|
917
|
+
CREATE UNIQUE INDEX job_idempotency ON job (org_id, idempotency_key) WHERE idempotency_key IS NOT NULL;
|
|
918
|
+
CREATE INDEX job_queued_resource ON job (org_id, resource_key, type) WHERE state = 'queued' AND resource_key IS NOT NULL;
|
|
919
|
+
CREATE INDEX job_dead ON job (org_id, finished_at) WHERE state = 'dead';
|
|
920
|
+
CREATE INDEX job_schedule_due ON job_schedule (next_run_at) WHERE enabled = 1;
|
|
921
|
+
CREATE INDEX import_run_starter ON import_run (started_by_user_id);
|
|
922
|
+
CREATE INDEX import_run_state ON import_run (org_id, state);
|
|
923
|
+
CREATE UNIQUE INDEX import_ref_run_external ON import_ref (import_run_id, entity_type, external_ref);
|
|
924
|
+
CREATE INDEX import_ref_entity ON import_ref (org_id, entity_type, entity_id);
|
|
925
|
+
CREATE INDEX import_ref_external ON import_ref (org_id, entity_type, external_ref);
|
|
926
|
+
CREATE INDEX webhook_delivery_source ON webhook_delivery (source_id);
|
|
927
|
+
CREATE INDEX webhook_delivery_received ON webhook_delivery (received_at);
|
|
928
|
+
CREATE UNIQUE INDEX local_login_setup ON local_login (setup_hash) WHERE setup_hash IS NOT NULL;
|
|
929
|
+
CREATE INDEX local_login_org ON local_login (org_id);
|
|
930
|
+
CREATE UNIQUE INDEX local_login_totp ON local_login (totp_credential_id) WHERE totp_credential_id IS NOT NULL;
|
|
931
|
+
CREATE INDEX token_validation_identity ON token (validation_identity_id) WHERE validation_identity_id IS NOT NULL;
|
|
932
|
+
CREATE UNIQUE INDEX service_principal_org_id ON service_principal (org_id, id);
|
|
933
|
+
CREATE INDEX identity_block_user ON identity_block (blocked_by_user_id) WHERE blocked_by_user_id IS NOT NULL;
|
|
934
|
+
CREATE UNIQUE INDEX job_attempt_fence ON job_attempt (job_id, fence);
|
|
935
|
+
CREATE UNIQUE INDEX ingest_session_running_scope ON ingest_session (org_id, scope_key) WHERE state = 'running';
|
|
936
|
+
CREATE INDEX ingest_session_state_lease ON ingest_session (state, lease_until);
|
|
937
|
+
CREATE INDEX ingest_session_scope_generation ON ingest_session (org_id, scope_key, generation);
|
|
938
|
+
CREATE INDEX fetch_lease_expiry ON fetch_lease (lease_until);
|
|
939
|
+
CREATE INDEX upstream_state_content ON upstream_state (org_id, content_sha256);
|
|
940
|
+
CREATE INDEX ingest_session_snapshot_blob ON ingest_session (org_id, snapshot_blob_sha256);
|
|
941
|
+
CREATE INDEX job_claim_org ON job(org_id, run_at, id) WHERE state = 'queued';
|
|
942
|
+
CREATE INDEX job_lease_org ON job(org_id, lease_until) WHERE state = 'running';
|
|
943
|
+
CREATE INDEX audit_pending_org_id ON audit_pending (org_id, id);
|
|
944
|
+
CREATE INDEX api_idempotency_expiry ON api_idempotency (org_id, expires_at);
|
|
945
|
+
CREATE INDEX audit_event_action ON audit_event (org_id, action, seq);
|
|
946
|
+
CREATE INDEX import_run_approver ON import_run (approved_by_user_id);
|
|
947
|
+
CREATE INDEX import_run_sp_starter ON import_run (started_by_service_principal_id);
|
|
948
|
+
CREATE INDEX token_pepper_version ON token (pepper_version);
|
|
949
|
+
CREATE INDEX credential_version_in_use ON credential (key_version);
|
|
950
|
+
CREATE UNIQUE INDEX source_org_id ON source (id, org_id);
|
|
951
|
+
CREATE UNIQUE INDEX project_org_id ON project (id, org_id);
|
|
952
|
+
CREATE INDEX package_by_name ON package (org_id, ecosystem, name);
|
|
953
|
+
CREATE UNIQUE INDEX package_identity ON package (org_id, ecosystem, source_id, name);
|
|
954
|
+
CREATE INDEX package_source ON package (source_id);
|
|
955
|
+
CREATE UNIQUE INDEX package_org_id ON package (id, org_id);
|
|
956
|
+
CREATE INDEX dist_tag_version ON dist_tag (version_id);
|
|
957
|
+
CREATE UNIQUE INDEX project_package_pattern_identity ON project_package_pattern (project_id, source_id, pattern);
|
|
958
|
+
CREATE INDEX project_package_pattern_source ON project_package_pattern (source_id);
|
|
959
|
+
CREATE INDEX project_source_source ON project_source (source_id);
|
|
960
|
+
CREATE UNIQUE INDEX version_org_id ON version (id, org_id);
|
|
961
|
+
CREATE INDEX version_blob ON version (blob_id);
|
|
962
|
+
CREATE INDEX version_by_published ON version (package_id, published_at);
|
|
963
|
+
CREATE UNIQUE INDEX version_identity ON version (package_id, version_normalized);
|
|
964
|
+
CREATE INDEX version_op ON version (last_op_id) WHERE last_op_id IS NOT NULL;
|
|
965
|
+
CREATE INDEX version_purge ON version (purge_after) WHERE purge_after IS NOT NULL;
|
|
966
|
+
CREATE INDEX dist_tag_history_previous ON dist_tag_history (previous_version_id);
|
|
967
|
+
CREATE INDEX dist_tag_history_tag ON dist_tag_history (package_id, tag, created_at);
|
|
968
|
+
CREATE INDEX dist_tag_history_version ON dist_tag_history (version_id);
|
|
969
|
+
CREATE UNIQUE INDEX registered_name_pattern ON registered_name (org_id, ecosystem, pattern);
|
|
970
|
+
CREATE INDEX registered_name_source ON registered_name (source_id);
|
|
971
|
+
CREATE INDEX vulnerability_result_due ON vulnerability_result (org_id, source, next_attempt_at, version_id);
|
|
972
|
+
CREATE INDEX vulnerability_match_advisory ON vulnerability_match (org_id, source, advisory_id);
|
|
973
|
+
CREATE INDEX vulnerability_advisory_due ON vulnerability_advisory (org_id, source, next_attempt_at, advisory_id);
|
|
974
|
+
CREATE INDEX vulnerability_refresh_due ON vulnerability_refresh (org_id, next_attempt_at, refresh_id);
|
|
975
|
+
CREATE INDEX vulnerability_source_evidence ON source (org_id, ecosystem, id) WHERE ecosystem IN ('composer', 'npm');
|
|
976
|
+
CREATE INDEX vulnerability_version_due ON version (org_id, id) WHERE length(CAST(version AS BLOB)) BETWEEN 1 AND 200;
|
|
977
|
+
CREATE INDEX version_package_status ON version (package_id, status);
|
|
978
|
+
CREATE INDEX project_package_package ON project_package (package_id);
|
|
979
|
+
CREATE INDEX project_package_name ON project_package (org_id, project_id, ecosystem, name);
|
|
980
|
+
CREATE INDEX version_package_id ON version (package_id, id);
|
|
981
|
+
|
|
982
|
+
-- ---------------------------------------------------------------------------
|
|
983
|
+
-- Initial migration lock
|
|
984
|
+
-- ---------------------------------------------------------------------------
|
|
985
|
+
|
|
986
|
+
INSERT INTO migration_lock (id) VALUES (1);
|
|
987
|
+
|
|
988
|
+
CREATE TRIGGER package_ecosystem_matches_source_update
|
|
989
|
+
BEFORE UPDATE OF source_id, ecosystem ON package
|
|
990
|
+
WHEN (SELECT ecosystem FROM source WHERE id = NEW.source_id) IS NOT NEW.ecosystem
|
|
991
|
+
BEGIN
|
|
992
|
+
SELECT RAISE(ABORT, 'package ecosystem does not match source ecosystem');
|
|
993
|
+
END;
|
|
994
|
+
|
|
995
|
+
UPDATE package SET ecosystem = ecosystem
|
|
996
|
+
WHERE ecosystem IS NOT (SELECT s.ecosystem FROM source AS s WHERE s.id = package.source_id);
|
|
997
|
+
|
|
998
|
+
CREATE TRIGGER package_ecosystem_matches_source_insert
|
|
999
|
+
BEFORE INSERT ON package
|
|
1000
|
+
WHEN (SELECT ecosystem FROM source WHERE id = NEW.source_id) IS NOT NEW.ecosystem
|
|
1001
|
+
BEGIN
|
|
1002
|
+
SELECT RAISE(ABORT, 'package ecosystem does not match source ecosystem');
|
|
1003
|
+
END;
|
|
1004
|
+
|
|
1005
|
+
CREATE TRIGGER source_ecosystem_locked_with_packages
|
|
1006
|
+
BEFORE UPDATE OF ecosystem ON source
|
|
1007
|
+
WHEN NEW.ecosystem IS NOT OLD.ecosystem
|
|
1008
|
+
AND EXISTS (SELECT 1 FROM package WHERE source_id = OLD.id)
|
|
1009
|
+
BEGIN
|
|
1010
|
+
SELECT RAISE(ABORT, 'source ecosystem cannot change while the source holds packages');
|
|
1011
|
+
END;
|
|
1012
|
+
|
|
1013
|
+
CREATE TRIGGER source_sync_job_state
|
|
1014
|
+
AFTER UPDATE OF state, last_error ON job
|
|
1015
|
+
BEGIN
|
|
1016
|
+
UPDATE source_sync_package SET state = NEW.state,
|
|
1017
|
+
error = CASE WHEN NEW.state IN ('running', 'succeeded') OR (NEW.state = 'queued' AND EXISTS (SELECT 1 FROM job_attempt WHERE org_id = NEW.org_id AND job_id = NEW.id AND fence = NEW.fence AND outcome = 'released')) THEN NULL
|
|
1018
|
+
ELSE COALESCE(NEW.sync_error, CASE WHEN NEW.last_error = 'timeout' THEN 'timeout'
|
|
1019
|
+
WHEN NEW.last_error IS NOT NULL THEN 'internal' END) END
|
|
1020
|
+
WHERE org_id = NEW.org_id AND job_id = NEW.id;
|
|
1021
|
+
UPDATE source SET sync_counts = json_set(sync_counts, '$.listing', json('false'))
|
|
1022
|
+
WHERE org_id = NEW.org_id AND id = json_extract(CASE WHEN json_valid(NEW.payload_json) THEN NEW.payload_json ELSE '{}' END, '$.sourceId')
|
|
1023
|
+
AND NEW.type = 'core:source-sync:v1' AND NEW.state <> 'running'
|
|
1024
|
+
AND NOT (NEW.state = 'queued' AND NEW.last_error IS 'lease_lost')
|
|
1025
|
+
AND (NEW.state <> 'succeeded' OR NOT EXISTS (
|
|
1026
|
+
SELECT 1 FROM job continuation
|
|
1027
|
+
WHERE continuation.org_id = NEW.org_id AND continuation.resource_key = NEW.resource_key
|
|
1028
|
+
AND continuation.id <> NEW.id AND continuation.type = 'core:source-sync:v1'
|
|
1029
|
+
AND continuation.state IN ('queued', 'running')
|
|
1030
|
+
AND json_extract(CASE WHEN json_valid(continuation.payload_json) THEN continuation.payload_json ELSE '{}' END, '$.syncGeneration') = source.sync_generation))
|
|
1031
|
+
AND (json_extract(CASE WHEN json_valid(NEW.payload_json) THEN NEW.payload_json ELSE '{}' END, '$.syncGeneration') IS NULL
|
|
1032
|
+
OR sync_generation = json_extract(CASE WHEN json_valid(NEW.payload_json) THEN NEW.payload_json ELSE '{}' END, '$.syncGeneration'));
|
|
1033
|
+
END;
|
|
1034
|
+
|
|
1035
|
+
CREATE TRIGGER source_sync_counts_insert
|
|
1036
|
+
AFTER INSERT ON source_sync_package
|
|
1037
|
+
BEGIN
|
|
1038
|
+
UPDATE source SET sync_counts = json_set((CASE WHEN NEW.error IS NULL THEN (sync_counts) ELSE json_set((sync_counts), '$.errors.' || NEW.error, COALESCE(json_extract((sync_counts), '$.errors.' || NEW.error), 0) + 1) END),
|
|
1039
|
+
'$.queued', json_extract(sync_counts, '$.queued') + (NEW.state = 'queued' AND NEW.error IS NULL),
|
|
1040
|
+
'$.running', json_extract(sync_counts, '$.running') + (NEW.state = 'running'),
|
|
1041
|
+
'$.retrying', json_extract(sync_counts, '$.retrying') + (NEW.state = 'queued' AND NEW.error IS NOT NULL),
|
|
1042
|
+
'$.failed', json_extract(sync_counts, '$.failed') + (NEW.state = 'dead'),
|
|
1043
|
+
'$.completed', json_extract(sync_counts, '$.completed') + (NEW.state = 'succeeded'))
|
|
1044
|
+
WHERE org_id = NEW.org_id AND id = NEW.source_id AND sync_generation = NEW.generation;
|
|
1045
|
+
END;
|
|
1046
|
+
|
|
1047
|
+
CREATE TRIGGER source_sync_counts_update
|
|
1048
|
+
AFTER UPDATE ON source_sync_package
|
|
1049
|
+
BEGIN
|
|
1050
|
+
UPDATE source SET sync_counts = json_set(sync_counts, '$.errors.' || OLD.error,
|
|
1051
|
+
max(COALESCE(json_extract(sync_counts, '$.errors.' || OLD.error), 0) - 1, 0))
|
|
1052
|
+
WHERE org_id = NEW.org_id AND id = NEW.source_id AND sync_generation = NEW.generation
|
|
1053
|
+
AND OLD.error IS NOT NULL;
|
|
1054
|
+
UPDATE source SET sync_counts = json_set(sync_counts, '$.errors.' || NEW.error,
|
|
1055
|
+
COALESCE(json_extract(sync_counts, '$.errors.' || NEW.error), 0) + 1)
|
|
1056
|
+
WHERE org_id = NEW.org_id AND id = NEW.source_id AND sync_generation = NEW.generation
|
|
1057
|
+
AND NEW.error IS NOT NULL;
|
|
1058
|
+
UPDATE source SET sync_counts = json_set(sync_counts,
|
|
1059
|
+
'$.queued', json_extract(sync_counts, '$.queued') + ((NEW.state = 'queued' AND NEW.error IS NULL) - (OLD.state = 'queued' AND OLD.error IS NULL)),
|
|
1060
|
+
'$.running', json_extract(sync_counts, '$.running') + ((NEW.state = 'running') - (OLD.state = 'running')),
|
|
1061
|
+
'$.retrying', json_extract(sync_counts, '$.retrying') + ((NEW.state = 'queued' AND NEW.error IS NOT NULL) - (OLD.state = 'queued' AND OLD.error IS NOT NULL)),
|
|
1062
|
+
'$.failed', json_extract(sync_counts, '$.failed') + ((NEW.state = 'dead') - (OLD.state = 'dead')),
|
|
1063
|
+
'$.completed', json_extract(sync_counts, '$.completed') + ((NEW.state = 'succeeded') - (OLD.state = 'succeeded')))
|
|
1064
|
+
WHERE org_id = NEW.org_id AND id = NEW.source_id AND sync_generation = NEW.generation;
|
|
1065
|
+
END;
|