@coffre/db 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/connect.d.ts +1 -1
- package/dist/{database-C63WjCrC.d.ts → database-BEnexh-3.d.ts} +1 -1
- package/dist/dialect.d.ts +1 -1
- package/dist/grants.d.ts +1 -1
- package/dist/hyperdrive.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/log.d.ts +22 -1
- package/dist/log.js +37 -2
- package/dist/migrations/postgres/0004_environment_parents.sql +5 -0
- package/dist/migrations/postgres/0005_personal_tokens.sql +5 -0
- package/dist/migrations/postgres/0006_uploaded_keys.sql +2 -0
- package/dist/migrations/postgres/0007_personal_configs.sql +4 -0
- package/dist/migrations/postgres/0008_recipes.sql +19 -0
- package/dist/migrations/postgres/meta/0004_snapshot.json +2913 -0
- package/dist/migrations/postgres/meta/0005_snapshot.json +2929 -0
- package/dist/migrations/postgres/meta/0006_snapshot.json +2935 -0
- package/dist/migrations/postgres/meta/0007_snapshot.json +2967 -0
- package/dist/migrations/postgres/meta/0008_snapshot.json +2985 -0
- package/dist/migrations/postgres/meta/_journal.json +35 -0
- package/dist/migrations/sqlite/0004_environment_parents.sql +3 -0
- package/dist/migrations/sqlite/0005_personal_tokens.sql +37 -0
- package/dist/migrations/sqlite/0006_uploaded_keys.sql +1 -0
- package/dist/migrations/sqlite/0007_personal_configs.sql +3 -0
- package/dist/migrations/sqlite/0008_recipes.sql +15 -0
- package/dist/migrations/sqlite/meta/0004_snapshot.json +2641 -0
- package/dist/migrations/sqlite/meta/0005_snapshot.json +2656 -0
- package/dist/migrations/sqlite/meta/0006_snapshot.json +2663 -0
- package/dist/migrations/sqlite/meta/0007_snapshot.json +2683 -0
- package/dist/migrations/sqlite/meta/0008_snapshot.json +2704 -0
- package/dist/migrations/sqlite/meta/_journal.json +35 -0
- package/dist/portable.d.ts +2 -2
- package/dist/{schema-a7PkFgea.d.ts → schema-BZRkBJQZ.d.ts} +102 -0
- package/dist/schema-sqlite.d.ts +114 -0
- package/dist/schema-sqlite.js +20 -4
- package/dist/schema-version.d.ts +1 -1
- package/dist/schema-version.js +70 -0
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +20 -4
- package/package.json +2 -2
- package/src/log.ts +40 -1
- package/src/migrations/postgres/0004_environment_parents.sql +5 -0
- package/src/migrations/postgres/0005_personal_tokens.sql +5 -0
- package/src/migrations/postgres/0006_uploaded_keys.sql +2 -0
- package/src/migrations/postgres/0007_personal_configs.sql +4 -0
- package/src/migrations/postgres/0008_recipes.sql +19 -0
- package/src/migrations/postgres/meta/0004_snapshot.json +2913 -0
- package/src/migrations/postgres/meta/0005_snapshot.json +2929 -0
- package/src/migrations/postgres/meta/0006_snapshot.json +2935 -0
- package/src/migrations/postgres/meta/0007_snapshot.json +2967 -0
- package/src/migrations/postgres/meta/0008_snapshot.json +2985 -0
- package/src/migrations/postgres/meta/_journal.json +35 -0
- package/src/migrations/sqlite/0004_environment_parents.sql +3 -0
- package/src/migrations/sqlite/0005_personal_tokens.sql +37 -0
- package/src/migrations/sqlite/0006_uploaded_keys.sql +1 -0
- package/src/migrations/sqlite/0007_personal_configs.sql +3 -0
- package/src/migrations/sqlite/0008_recipes.sql +15 -0
- package/src/migrations/sqlite/meta/0004_snapshot.json +2641 -0
- package/src/migrations/sqlite/meta/0005_snapshot.json +2656 -0
- package/src/migrations/sqlite/meta/0006_snapshot.json +2663 -0
- package/src/migrations/sqlite/meta/0007_snapshot.json +2683 -0
- package/src/migrations/sqlite/meta/0008_snapshot.json +2704 -0
- package/src/migrations/sqlite/meta/_journal.json +35 -0
- package/src/schema.sqlite.ts +31 -3
- package/src/schema.ts +37 -3
|
@@ -29,6 +29,41 @@
|
|
|
29
29
|
"when": 1791676487593,
|
|
30
30
|
"tag": "0003_credential_scopes",
|
|
31
31
|
"breakpoints": true
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"idx": 4,
|
|
35
|
+
"version": "6",
|
|
36
|
+
"when": 1792244020959,
|
|
37
|
+
"tag": "0004_environment_parents",
|
|
38
|
+
"breakpoints": true
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"idx": 5,
|
|
42
|
+
"version": "6",
|
|
43
|
+
"when": 1792250910230,
|
|
44
|
+
"tag": "0005_personal_tokens",
|
|
45
|
+
"breakpoints": true
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"idx": 6,
|
|
49
|
+
"version": "6",
|
|
50
|
+
"when": 1792252693912,
|
|
51
|
+
"tag": "0006_uploaded_keys",
|
|
52
|
+
"breakpoints": true
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"idx": 7,
|
|
56
|
+
"version": "6",
|
|
57
|
+
"when": 1792253795070,
|
|
58
|
+
"tag": "0007_personal_configs",
|
|
59
|
+
"breakpoints": true
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"idx": 8,
|
|
63
|
+
"version": "6",
|
|
64
|
+
"when": 1792255560312,
|
|
65
|
+
"tag": "0008_recipes",
|
|
66
|
+
"breakpoints": true
|
|
32
67
|
}
|
|
33
68
|
]
|
|
34
69
|
}
|
package/src/schema.sqlite.ts
CHANGED
|
@@ -85,16 +85,29 @@ export const environments = sqliteTable(
|
|
|
85
85
|
name: text().notNull(),
|
|
86
86
|
createdAt: createdAt(),
|
|
87
87
|
archivedAt: time('archived_at'),
|
|
88
|
+
parentId: text('parent_id'),
|
|
89
|
+
owner: text(),
|
|
88
90
|
},
|
|
89
91
|
(table) => [
|
|
90
92
|
unique('environments_project_id_slug_key').on(table.projectId, table.slug),
|
|
91
93
|
unique('environments_project_scoped').on(table.id, table.projectId),
|
|
92
94
|
check('environments_slug_check', isPlaceSlug(table.slug)),
|
|
95
|
+
// An environment is never its own parent. Longer loops, written around the app, are refused at read (`parentsOf`).
|
|
96
|
+
check('environments_parent_check', sql`${table.parentId} IS NULL OR ${table.parentId} <> ${table.id}`),
|
|
97
|
+
check('environments_owner_check', sql`${table.owner} IS NULL OR (${table.parentId} IS NOT NULL AND ${table.owner} LIKE 'user:_%')`),
|
|
98
|
+
uniqueIndex('environments_parent_id_owner_key').on(table.parentId, table.owner).where(sql`instr(${table.slug}, '~') = 0`),
|
|
93
99
|
foreignKey({
|
|
94
100
|
name: 'environments_project_id_fkey',
|
|
95
101
|
columns: [table.projectId],
|
|
96
102
|
foreignColumns: [projects.id],
|
|
97
103
|
}).onDelete('restrict'),
|
|
104
|
+
// Checked at the end of a statement, not at each row: one that deletes a fork and its parent together is no orphan.
|
|
105
|
+
foreignKey({
|
|
106
|
+
name: 'environments_parent_in_project',
|
|
107
|
+
columns: [table.parentId, table.projectId],
|
|
108
|
+
foreignColumns: [table.id, table.projectId],
|
|
109
|
+
}).onDelete('no action'),
|
|
110
|
+
index('environments_parent_idx').on(table.parentId),
|
|
98
111
|
],
|
|
99
112
|
);
|
|
100
113
|
|
|
@@ -156,6 +169,14 @@ export const secretVersions = sqliteTable(
|
|
|
156
169
|
kekVersion: text('kek_version').notNull(),
|
|
157
170
|
createdAt: createdAt(),
|
|
158
171
|
createdBy: text('created_by').notNull(),
|
|
172
|
+
// How a client made this value, `bytes:32:hex` (`@coffre/core/recipe`),
|
|
173
|
+
// when it was generated rather than typed: Regenerate makes the next one
|
|
174
|
+
// the same way. Null for a typed value, so writing one by hand clears it.
|
|
175
|
+
recipe: text(),
|
|
176
|
+
// A generated key pair's two versions, written together, share an id,
|
|
177
|
+
// and each says which half it holds: 'private' or 'public'.
|
|
178
|
+
pairId: text('pair_id'),
|
|
179
|
+
pairHalf: text('pair_half'),
|
|
159
180
|
},
|
|
160
181
|
(table) => [
|
|
161
182
|
unique('secret_versions_secret_id_version_key').on(table.secretId, table.version),
|
|
@@ -364,6 +385,10 @@ export const auditLog = sqliteTable(
|
|
|
364
385
|
index('audit_log_exchange_idx')
|
|
365
386
|
.on(sql`json_extract(${table.metadata}, '$.credentialId')`)
|
|
366
387
|
.where(sql`${table.author} = 'app' AND ${table.action} = 'token.exchange' AND ${table.decision} = 'allow'`),
|
|
388
|
+
// The token a credential is, when one was issued: its creation's entry, by the credential's ID, whose label names a personal token's entries.
|
|
389
|
+
index('audit_log_token_create_idx')
|
|
390
|
+
.on(sql`json_extract(${table.metadata}, '$.targetCredentialId')`)
|
|
391
|
+
.where(sql`${table.author} = 'app' AND ${table.action} = 'token.create' AND ${table.decision} = 'allow'`),
|
|
367
392
|
index('audit_log_reference_end_idx')
|
|
368
393
|
.on(table.relatedSeq)
|
|
369
394
|
.where(sql`${table.author} = 'vault' AND ${table.action} = 'reference.end' AND ${table.decision} = 'allow'`),
|
|
@@ -476,8 +501,11 @@ export const serviceBindings = sqliteTable(
|
|
|
476
501
|
generation: integer().notNull(),
|
|
477
502
|
profile: text().notNull(),
|
|
478
503
|
issuer: text().notNull(),
|
|
479
|
-
// From the issuer's discovery when the binding was made, never refetched
|
|
504
|
+
// From the issuer's discovery when the binding was made, never refetched;
|
|
505
|
+
// or, for keys uploaded with the binding, `sha256:<hex>` of `jwks`.
|
|
480
506
|
jwksUri: text('jwks_uri').notNull(),
|
|
507
|
+
// The issuer's public keys, uploaded with the binding (`uploadedKeys`), JSON; null when they are fetched.
|
|
508
|
+
jwks: text(),
|
|
481
509
|
// JSON, sorted by claim name.
|
|
482
510
|
claims: text().notNull(),
|
|
483
511
|
// What the credentials it issues reach (`CredentialScope`), JSON; null on a binding of 0.4 the app has not sealed yet.
|
|
@@ -641,7 +669,7 @@ export const credentials = sqliteTable(
|
|
|
641
669
|
// The member it acts as, `user:<email>` or `token:<id>`, under the generation it was issued in.
|
|
642
670
|
principal: text().notNull(),
|
|
643
671
|
identityId: text('identity_id'),
|
|
644
|
-
// What a service token reaches (`CredentialScope`), JSON: null for a session, one a binding issued (its binding's), or one of 0.4 the app has not sealed yet.
|
|
672
|
+
// What a service or personal token reaches (`CredentialScope`), JSON: null for a session, one a binding issued (its binding's), or one of 0.4 the app has not sealed yet.
|
|
645
673
|
scope: text(),
|
|
646
674
|
label: text(),
|
|
647
675
|
createdAt: createdAt(),
|
|
@@ -654,7 +682,7 @@ export const credentials = sqliteTable(
|
|
|
654
682
|
},
|
|
655
683
|
(table) => [
|
|
656
684
|
unique('credentials_token_hash_key').on(table.tokenHash),
|
|
657
|
-
check('credentials_kind_check', sql`${table.kind} IN ('browser', 'cli', 'service')`),
|
|
685
|
+
check('credentials_kind_check', sql`${table.kind} IN ('browser', 'cli', 'service', 'personal')`),
|
|
658
686
|
check('credentials_principal_check', sql`${table.principal} LIKE 'user:%' OR ${table.principal} LIKE 'token:%'`),
|
|
659
687
|
check('credentials_kind_matches_principal', sql`(${table.kind} = 'service') = (${table.principal} LIKE 'token:%')`),
|
|
660
688
|
check('credentials_token_hash_check', sql`octet_length(${table.tokenHash}) = 32`),
|
package/src/schema.ts
CHANGED
|
@@ -68,16 +68,35 @@ export const environments = pgTable(
|
|
|
68
68
|
name: text().notNull(),
|
|
69
69
|
createdAt: createdAt(),
|
|
70
70
|
archivedAt: timestamp('archived_at', { withTimezone: true }),
|
|
71
|
+
// The environment it inherits from, in the same project: its keys are
|
|
72
|
+
// this one's, but for those it sets or removes (docs/design/forking.md).
|
|
73
|
+
// Set when it is made and never after, so no chain can loop.
|
|
74
|
+
parentId: uuid('parent_id'),
|
|
75
|
+
// A personal config's owner, `user:<email>`: a fork whose values only they
|
|
76
|
+
// read, the vault binding each to them. Set when it is made and never after.
|
|
77
|
+
owner: text(),
|
|
71
78
|
},
|
|
72
79
|
(table) => [
|
|
73
80
|
unique('environments_project_id_slug_key').on(table.projectId, table.slug),
|
|
74
81
|
unique('environments_project_scoped').on(table.id, table.projectId),
|
|
75
82
|
check('environments_slug_check', sql`${table.slug} ~ ${PLACE_SLUG}`),
|
|
83
|
+
// An environment is never its own parent. Longer loops, written around the app, are refused at read (`parentsOf`).
|
|
84
|
+
check('environments_parent_check', sql`${table.parentId} IS NULL OR ${table.parentId} <> ${table.id}`),
|
|
85
|
+
check('environments_owner_check', sql`${table.owner} IS NULL OR (${table.parentId} IS NOT NULL AND ${table.owner} LIKE 'user:_%')`),
|
|
86
|
+
// One personal config per person per environment; a deleted one, a tombstone, holds no place.
|
|
87
|
+
uniqueIndex('environments_parent_id_owner_key').on(table.parentId, table.owner).where(sql`strpos(${table.slug}, '~') = 0`),
|
|
76
88
|
foreignKey({
|
|
77
89
|
name: 'environments_project_id_fkey',
|
|
78
90
|
columns: [table.projectId],
|
|
79
91
|
foreignColumns: [projects.id],
|
|
80
92
|
}).onDelete('restrict'),
|
|
93
|
+
// Checked at the end of a statement, not at each row: one that deletes a fork and its parent together is no orphan.
|
|
94
|
+
foreignKey({
|
|
95
|
+
name: 'environments_parent_in_project',
|
|
96
|
+
columns: [table.parentId, table.projectId],
|
|
97
|
+
foreignColumns: [table.id, table.projectId],
|
|
98
|
+
}).onDelete('no action'),
|
|
99
|
+
index('environments_parent_idx').on(table.parentId),
|
|
81
100
|
],
|
|
82
101
|
);
|
|
83
102
|
|
|
@@ -144,6 +163,14 @@ export const secretVersions = pgTable(
|
|
|
144
163
|
kekVersion: text('kek_version').notNull(),
|
|
145
164
|
createdAt: createdAt(),
|
|
146
165
|
createdBy: text('created_by').notNull(),
|
|
166
|
+
// How a client made this value, `bytes:32:hex` (`@coffre/core/recipe`),
|
|
167
|
+
// when it was generated rather than typed: Regenerate makes the next one
|
|
168
|
+
// the same way. Null for a typed value, so writing one by hand clears it.
|
|
169
|
+
recipe: text(),
|
|
170
|
+
// A generated key pair's two versions, written together, share an id,
|
|
171
|
+
// and each says which half it holds: 'private' or 'public'.
|
|
172
|
+
pairId: uuid('pair_id'),
|
|
173
|
+
pairHalf: text('pair_half'),
|
|
147
174
|
},
|
|
148
175
|
(table) => [
|
|
149
176
|
unique('secret_versions_secret_id_version_key').on(table.secretId, table.version),
|
|
@@ -376,6 +403,10 @@ export const auditLog = pgTable(
|
|
|
376
403
|
index('audit_log_exchange_idx')
|
|
377
404
|
.on(sql`((${table.metadata})::jsonb ->> 'credentialId')`)
|
|
378
405
|
.where(sql`${table.author} = 'app' AND ${table.action} = 'token.exchange' AND ${table.decision} = 'allow'`),
|
|
406
|
+
// The token a credential is, when one was issued: its creation's entry, by the credential's ID, whose label names a personal token's entries.
|
|
407
|
+
index('audit_log_token_create_idx')
|
|
408
|
+
.on(sql`((${table.metadata})::jsonb ->> 'targetCredentialId')`)
|
|
409
|
+
.where(sql`${table.author} = 'app' AND ${table.action} = 'token.create' AND ${table.decision} = 'allow'`),
|
|
379
410
|
// Whether a reference has ended: the vault's `reference.end` naming its creation.
|
|
380
411
|
index('audit_log_reference_end_idx')
|
|
381
412
|
.on(table.relatedSeq)
|
|
@@ -520,8 +551,11 @@ export const serviceBindings = pgTable(
|
|
|
520
551
|
generation: integer().notNull(),
|
|
521
552
|
profile: text().notNull(),
|
|
522
553
|
issuer: text().notNull(),
|
|
523
|
-
// From the issuer's discovery when the binding was made, never refetched
|
|
554
|
+
// From the issuer's discovery when the binding was made, never refetched;
|
|
555
|
+
// or, for keys uploaded with the binding, `sha256:<hex>` of `jwks`.
|
|
524
556
|
jwksUri: text('jwks_uri').notNull(),
|
|
557
|
+
// The issuer's public keys, uploaded with the binding (`uploadedKeys`), JSON; null when they are fetched.
|
|
558
|
+
jwks: text(),
|
|
525
559
|
// JSON, sorted by claim name.
|
|
526
560
|
claims: text().notNull(),
|
|
527
561
|
// What the credentials it issues reach (`CredentialScope`), JSON; null on a binding of 0.4 the app has not sealed yet.
|
|
@@ -709,7 +743,7 @@ export const credentials = pgTable(
|
|
|
709
743
|
// The member it acts as, `user:<email>` or `token:<id>`, under the generation it was issued in.
|
|
710
744
|
principal: text().notNull(),
|
|
711
745
|
identityId: uuid('identity_id'),
|
|
712
|
-
// What a service token reaches (`CredentialScope`), JSON: null for a session, one a binding issued (its binding's), or one of 0.4 the app has not sealed yet.
|
|
746
|
+
// What a service or personal token reaches (`CredentialScope`), JSON: null for a session, one a binding issued (its binding's), or one of 0.4 the app has not sealed yet.
|
|
713
747
|
scope: text(),
|
|
714
748
|
label: text(),
|
|
715
749
|
createdAt: createdAt(),
|
|
@@ -722,7 +756,7 @@ export const credentials = pgTable(
|
|
|
722
756
|
},
|
|
723
757
|
(table) => [
|
|
724
758
|
unique('credentials_token_hash_key').on(table.tokenHash),
|
|
725
|
-
check('credentials_kind_check', sql`${table.kind} IN ('browser', 'cli', 'service')`),
|
|
759
|
+
check('credentials_kind_check', sql`${table.kind} IN ('browser', 'cli', 'service', 'personal')`),
|
|
726
760
|
check('credentials_principal_check', sql`${table.principal} LIKE 'user:%' OR ${table.principal} LIKE 'token:%'`),
|
|
727
761
|
check('credentials_kind_matches_principal', sql`(${table.kind} = 'service') = (${table.principal} LIKE 'token:%')`),
|
|
728
762
|
check('credentials_token_hash_check', sql`octet_length(${table.tokenHash}) = 32`),
|