@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/dist/portable.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { m as schema_d_exports } from "./schema-
|
|
2
|
-
import { t as Database } from "./database-
|
|
1
|
+
import { m as schema_d_exports } from "./schema-BZRkBJQZ.js";
|
|
2
|
+
import { t as Database } from "./database-BEnexh-3.js";
|
|
3
3
|
import { t as schema_sqlite_d_exports } from "./schema-sqlite.js";
|
|
4
4
|
import { LibSQLDatabase } from "drizzle-orm/libsql";
|
|
5
5
|
//#region src/portable.d.ts
|
|
@@ -216,6 +216,40 @@ declare const environments: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
216
216
|
identity: undefined;
|
|
217
217
|
generated: undefined;
|
|
218
218
|
}, {}, {}>;
|
|
219
|
+
parentId: import("drizzle-orm/pg-core").PgColumn<{
|
|
220
|
+
name: "parent_id";
|
|
221
|
+
tableName: "environments";
|
|
222
|
+
dataType: "string";
|
|
223
|
+
columnType: "PgUUID";
|
|
224
|
+
data: string;
|
|
225
|
+
driverParam: string;
|
|
226
|
+
notNull: false;
|
|
227
|
+
hasDefault: false;
|
|
228
|
+
isPrimaryKey: false;
|
|
229
|
+
isAutoincrement: false;
|
|
230
|
+
hasRuntimeDefault: false;
|
|
231
|
+
enumValues: undefined;
|
|
232
|
+
baseColumn: never;
|
|
233
|
+
identity: undefined;
|
|
234
|
+
generated: undefined;
|
|
235
|
+
}, {}, {}>;
|
|
236
|
+
owner: import("drizzle-orm/pg-core").PgColumn<{
|
|
237
|
+
name: "owner";
|
|
238
|
+
tableName: "environments";
|
|
239
|
+
dataType: "string";
|
|
240
|
+
columnType: "PgText";
|
|
241
|
+
data: string;
|
|
242
|
+
driverParam: string;
|
|
243
|
+
notNull: false;
|
|
244
|
+
hasDefault: false;
|
|
245
|
+
isPrimaryKey: false;
|
|
246
|
+
isAutoincrement: false;
|
|
247
|
+
hasRuntimeDefault: false;
|
|
248
|
+
enumValues: [string, ...string[]];
|
|
249
|
+
baseColumn: never;
|
|
250
|
+
identity: undefined;
|
|
251
|
+
generated: undefined;
|
|
252
|
+
}, {}, {}>;
|
|
219
253
|
};
|
|
220
254
|
dialect: "pg";
|
|
221
255
|
}>;
|
|
@@ -629,6 +663,57 @@ declare const secretVersions: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
629
663
|
identity: undefined;
|
|
630
664
|
generated: undefined;
|
|
631
665
|
}, {}, {}>;
|
|
666
|
+
recipe: import("drizzle-orm/pg-core").PgColumn<{
|
|
667
|
+
name: "recipe";
|
|
668
|
+
tableName: "secret_versions";
|
|
669
|
+
dataType: "string";
|
|
670
|
+
columnType: "PgText";
|
|
671
|
+
data: string;
|
|
672
|
+
driverParam: string;
|
|
673
|
+
notNull: false;
|
|
674
|
+
hasDefault: false;
|
|
675
|
+
isPrimaryKey: false;
|
|
676
|
+
isAutoincrement: false;
|
|
677
|
+
hasRuntimeDefault: false;
|
|
678
|
+
enumValues: [string, ...string[]];
|
|
679
|
+
baseColumn: never;
|
|
680
|
+
identity: undefined;
|
|
681
|
+
generated: undefined;
|
|
682
|
+
}, {}, {}>;
|
|
683
|
+
pairId: import("drizzle-orm/pg-core").PgColumn<{
|
|
684
|
+
name: "pair_id";
|
|
685
|
+
tableName: "secret_versions";
|
|
686
|
+
dataType: "string";
|
|
687
|
+
columnType: "PgUUID";
|
|
688
|
+
data: string;
|
|
689
|
+
driverParam: string;
|
|
690
|
+
notNull: false;
|
|
691
|
+
hasDefault: false;
|
|
692
|
+
isPrimaryKey: false;
|
|
693
|
+
isAutoincrement: false;
|
|
694
|
+
hasRuntimeDefault: false;
|
|
695
|
+
enumValues: undefined;
|
|
696
|
+
baseColumn: never;
|
|
697
|
+
identity: undefined;
|
|
698
|
+
generated: undefined;
|
|
699
|
+
}, {}, {}>;
|
|
700
|
+
pairHalf: import("drizzle-orm/pg-core").PgColumn<{
|
|
701
|
+
name: "pair_half";
|
|
702
|
+
tableName: "secret_versions";
|
|
703
|
+
dataType: "string";
|
|
704
|
+
columnType: "PgText";
|
|
705
|
+
data: string;
|
|
706
|
+
driverParam: string;
|
|
707
|
+
notNull: false;
|
|
708
|
+
hasDefault: false;
|
|
709
|
+
isPrimaryKey: false;
|
|
710
|
+
isAutoincrement: false;
|
|
711
|
+
hasRuntimeDefault: false;
|
|
712
|
+
enumValues: [string, ...string[]];
|
|
713
|
+
baseColumn: never;
|
|
714
|
+
identity: undefined;
|
|
715
|
+
generated: undefined;
|
|
716
|
+
}, {}, {}>;
|
|
632
717
|
};
|
|
633
718
|
dialect: "pg";
|
|
634
719
|
}>;
|
|
@@ -2123,6 +2208,23 @@ declare const serviceBindings: import("drizzle-orm/pg-core").PgTableWithColumns<
|
|
|
2123
2208
|
identity: undefined;
|
|
2124
2209
|
generated: undefined;
|
|
2125
2210
|
}, {}, {}>;
|
|
2211
|
+
jwks: import("drizzle-orm/pg-core").PgColumn<{
|
|
2212
|
+
name: "jwks";
|
|
2213
|
+
tableName: "service_bindings";
|
|
2214
|
+
dataType: "string";
|
|
2215
|
+
columnType: "PgText";
|
|
2216
|
+
data: string;
|
|
2217
|
+
driverParam: string;
|
|
2218
|
+
notNull: false;
|
|
2219
|
+
hasDefault: false;
|
|
2220
|
+
isPrimaryKey: false;
|
|
2221
|
+
isAutoincrement: false;
|
|
2222
|
+
hasRuntimeDefault: false;
|
|
2223
|
+
enumValues: [string, ...string[]];
|
|
2224
|
+
baseColumn: never;
|
|
2225
|
+
identity: undefined;
|
|
2226
|
+
generated: undefined;
|
|
2227
|
+
}, {}, {}>;
|
|
2126
2228
|
claims: import("drizzle-orm/pg-core").PgColumn<{
|
|
2127
2229
|
name: "claims";
|
|
2128
2230
|
tableName: "service_bindings";
|
package/dist/schema-sqlite.d.ts
CHANGED
|
@@ -232,6 +232,44 @@ export declare const environments: import("drizzle-orm/sqlite-core").SQLiteTable
|
|
|
232
232
|
identity: undefined;
|
|
233
233
|
generated: undefined;
|
|
234
234
|
}, {}, {}>;
|
|
235
|
+
parentId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
236
|
+
name: "parent_id";
|
|
237
|
+
tableName: "environments";
|
|
238
|
+
dataType: "string";
|
|
239
|
+
columnType: "SQLiteText";
|
|
240
|
+
data: string;
|
|
241
|
+
driverParam: string;
|
|
242
|
+
notNull: false;
|
|
243
|
+
hasDefault: false;
|
|
244
|
+
isPrimaryKey: false;
|
|
245
|
+
isAutoincrement: false;
|
|
246
|
+
hasRuntimeDefault: false;
|
|
247
|
+
enumValues: [string, ...string[]];
|
|
248
|
+
baseColumn: never;
|
|
249
|
+
identity: undefined;
|
|
250
|
+
generated: undefined;
|
|
251
|
+
}, {}, {
|
|
252
|
+
length: number | undefined;
|
|
253
|
+
}>;
|
|
254
|
+
owner: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
255
|
+
name: "owner";
|
|
256
|
+
tableName: "environments";
|
|
257
|
+
dataType: "string";
|
|
258
|
+
columnType: "SQLiteText";
|
|
259
|
+
data: string;
|
|
260
|
+
driverParam: string;
|
|
261
|
+
notNull: false;
|
|
262
|
+
hasDefault: false;
|
|
263
|
+
isPrimaryKey: false;
|
|
264
|
+
isAutoincrement: false;
|
|
265
|
+
hasRuntimeDefault: false;
|
|
266
|
+
enumValues: [string, ...string[]];
|
|
267
|
+
baseColumn: never;
|
|
268
|
+
identity: undefined;
|
|
269
|
+
generated: undefined;
|
|
270
|
+
}, {}, {
|
|
271
|
+
length: undefined;
|
|
272
|
+
}>;
|
|
235
273
|
};
|
|
236
274
|
dialect: "sqlite";
|
|
237
275
|
}>;
|
|
@@ -661,6 +699,63 @@ export declare const secretVersions: import("drizzle-orm/sqlite-core").SQLiteTab
|
|
|
661
699
|
}, {}, {
|
|
662
700
|
length: number | undefined;
|
|
663
701
|
}>;
|
|
702
|
+
recipe: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
703
|
+
name: "recipe";
|
|
704
|
+
tableName: "secret_versions";
|
|
705
|
+
dataType: "string";
|
|
706
|
+
columnType: "SQLiteText";
|
|
707
|
+
data: string;
|
|
708
|
+
driverParam: string;
|
|
709
|
+
notNull: false;
|
|
710
|
+
hasDefault: false;
|
|
711
|
+
isPrimaryKey: false;
|
|
712
|
+
isAutoincrement: false;
|
|
713
|
+
hasRuntimeDefault: false;
|
|
714
|
+
enumValues: [string, ...string[]];
|
|
715
|
+
baseColumn: never;
|
|
716
|
+
identity: undefined;
|
|
717
|
+
generated: undefined;
|
|
718
|
+
}, {}, {
|
|
719
|
+
length: undefined;
|
|
720
|
+
}>;
|
|
721
|
+
pairId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
722
|
+
name: "pair_id";
|
|
723
|
+
tableName: "secret_versions";
|
|
724
|
+
dataType: "string";
|
|
725
|
+
columnType: "SQLiteText";
|
|
726
|
+
data: string;
|
|
727
|
+
driverParam: string;
|
|
728
|
+
notNull: false;
|
|
729
|
+
hasDefault: false;
|
|
730
|
+
isPrimaryKey: false;
|
|
731
|
+
isAutoincrement: false;
|
|
732
|
+
hasRuntimeDefault: false;
|
|
733
|
+
enumValues: [string, ...string[]];
|
|
734
|
+
baseColumn: never;
|
|
735
|
+
identity: undefined;
|
|
736
|
+
generated: undefined;
|
|
737
|
+
}, {}, {
|
|
738
|
+
length: number | undefined;
|
|
739
|
+
}>;
|
|
740
|
+
pairHalf: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
741
|
+
name: "pair_half";
|
|
742
|
+
tableName: "secret_versions";
|
|
743
|
+
dataType: "string";
|
|
744
|
+
columnType: "SQLiteText";
|
|
745
|
+
data: string;
|
|
746
|
+
driverParam: string;
|
|
747
|
+
notNull: false;
|
|
748
|
+
hasDefault: false;
|
|
749
|
+
isPrimaryKey: false;
|
|
750
|
+
isAutoincrement: false;
|
|
751
|
+
hasRuntimeDefault: false;
|
|
752
|
+
enumValues: [string, ...string[]];
|
|
753
|
+
baseColumn: never;
|
|
754
|
+
identity: undefined;
|
|
755
|
+
generated: undefined;
|
|
756
|
+
}, {}, {
|
|
757
|
+
length: number | undefined;
|
|
758
|
+
}>;
|
|
664
759
|
};
|
|
665
760
|
dialect: "sqlite";
|
|
666
761
|
}>;
|
|
@@ -2212,6 +2307,25 @@ export declare const serviceBindings: import("drizzle-orm/sqlite-core").SQLiteTa
|
|
|
2212
2307
|
}, {}, {
|
|
2213
2308
|
length: number | undefined;
|
|
2214
2309
|
}>;
|
|
2310
|
+
jwks: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
2311
|
+
name: "jwks";
|
|
2312
|
+
tableName: "service_bindings";
|
|
2313
|
+
dataType: "string";
|
|
2314
|
+
columnType: "SQLiteText";
|
|
2315
|
+
data: string;
|
|
2316
|
+
driverParam: string;
|
|
2317
|
+
notNull: false;
|
|
2318
|
+
hasDefault: false;
|
|
2319
|
+
isPrimaryKey: false;
|
|
2320
|
+
isAutoincrement: false;
|
|
2321
|
+
hasRuntimeDefault: false;
|
|
2322
|
+
enumValues: [string, ...string[]];
|
|
2323
|
+
baseColumn: never;
|
|
2324
|
+
identity: undefined;
|
|
2325
|
+
generated: undefined;
|
|
2326
|
+
}, {}, {
|
|
2327
|
+
length: undefined;
|
|
2328
|
+
}>;
|
|
2215
2329
|
claims: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
2216
2330
|
name: "claims";
|
|
2217
2331
|
tableName: "service_bindings";
|
package/dist/schema-sqlite.js
CHANGED
|
@@ -54,16 +54,27 @@ const environments = sqliteTable("environments", {
|
|
|
54
54
|
slug: text().notNull(),
|
|
55
55
|
name: text().notNull(),
|
|
56
56
|
createdAt: createdAt(),
|
|
57
|
-
archivedAt: time("archived_at")
|
|
57
|
+
archivedAt: time("archived_at"),
|
|
58
|
+
parentId: text("parent_id"),
|
|
59
|
+
owner: text()
|
|
58
60
|
}, (table) => [
|
|
59
61
|
unique("environments_project_id_slug_key").on(table.projectId, table.slug),
|
|
60
62
|
unique("environments_project_scoped").on(table.id, table.projectId),
|
|
61
63
|
check("environments_slug_check", isPlaceSlug(table.slug)),
|
|
64
|
+
check("environments_parent_check", sql`${table.parentId} IS NULL OR ${table.parentId} <> ${table.id}`),
|
|
65
|
+
check("environments_owner_check", sql`${table.owner} IS NULL OR (${table.parentId} IS NOT NULL AND ${table.owner} LIKE 'user:_%')`),
|
|
66
|
+
uniqueIndex("environments_parent_id_owner_key").on(table.parentId, table.owner).where(sql`instr(${table.slug}, '~') = 0`),
|
|
62
67
|
foreignKey({
|
|
63
68
|
name: "environments_project_id_fkey",
|
|
64
69
|
columns: [table.projectId],
|
|
65
70
|
foreignColumns: [projects.id]
|
|
66
|
-
}).onDelete("restrict")
|
|
71
|
+
}).onDelete("restrict"),
|
|
72
|
+
foreignKey({
|
|
73
|
+
name: "environments_parent_in_project",
|
|
74
|
+
columns: [table.parentId, table.projectId],
|
|
75
|
+
foreignColumns: [table.id, table.projectId]
|
|
76
|
+
}).onDelete("no action"),
|
|
77
|
+
index("environments_parent_idx").on(table.parentId)
|
|
67
78
|
]);
|
|
68
79
|
const secrets = sqliteTable("secrets", {
|
|
69
80
|
id: text().primaryKey(),
|
|
@@ -110,7 +121,10 @@ const secretVersions = sqliteTable("secret_versions", {
|
|
|
110
121
|
kekId: text("kek_id").notNull(),
|
|
111
122
|
kekVersion: text("kek_version").notNull(),
|
|
112
123
|
createdAt: createdAt(),
|
|
113
|
-
createdBy: text("created_by").notNull()
|
|
124
|
+
createdBy: text("created_by").notNull(),
|
|
125
|
+
recipe: text(),
|
|
126
|
+
pairId: text("pair_id"),
|
|
127
|
+
pairHalf: text("pair_half")
|
|
114
128
|
}, (table) => [
|
|
115
129
|
unique("secret_versions_secret_id_version_key").on(table.secretId, table.version),
|
|
116
130
|
check("secret_versions_version_check", sql`${table.version} > 0`),
|
|
@@ -267,6 +281,7 @@ const auditLog = sqliteTable("audit_log", {
|
|
|
267
281
|
index("audit_log_subject_idx").on(table.author, table.subjectPrincipal, table.seq),
|
|
268
282
|
index("audit_log_unbind_idx").on(sql`json_extract(${table.metadata}, '$.bindingId')`).where(sql`${table.author} = 'app' AND ${table.action} = 'token.unbind' AND ${table.decision} = 'allow'`),
|
|
269
283
|
index("audit_log_exchange_idx").on(sql`json_extract(${table.metadata}, '$.credentialId')`).where(sql`${table.author} = 'app' AND ${table.action} = 'token.exchange' AND ${table.decision} = 'allow'`),
|
|
284
|
+
index("audit_log_token_create_idx").on(sql`json_extract(${table.metadata}, '$.targetCredentialId')`).where(sql`${table.author} = 'app' AND ${table.action} = 'token.create' AND ${table.decision} = 'allow'`),
|
|
270
285
|
index("audit_log_reference_end_idx").on(table.relatedSeq).where(sql`${table.author} = 'vault' AND ${table.action} = 'reference.end' AND ${table.decision} = 'allow'`),
|
|
271
286
|
index("audit_log_also_project_idx").on(sql`json_extract(${table.metadata}, '$.also.projectId')`, table.seq).where(sql`json_extract(${table.metadata}, '$.also') IS NOT NULL`),
|
|
272
287
|
index("audit_log_also_environment_idx").on(sql`json_extract(${table.metadata}, '$.also.environmentId')`, table.seq).where(sql`json_extract(${table.metadata}, '$.also') IS NOT NULL`),
|
|
@@ -351,6 +366,7 @@ const serviceBindings = sqliteTable("service_bindings", {
|
|
|
351
366
|
profile: text().notNull(),
|
|
352
367
|
issuer: text().notNull(),
|
|
353
368
|
jwksUri: text("jwks_uri").notNull(),
|
|
369
|
+
jwks: text(),
|
|
354
370
|
claims: text().notNull(),
|
|
355
371
|
scope: text(),
|
|
356
372
|
label: text(),
|
|
@@ -481,7 +497,7 @@ const credentials = sqliteTable("credentials", {
|
|
|
481
497
|
revokedBy: text("revoked_by")
|
|
482
498
|
}, (table) => [
|
|
483
499
|
unique("credentials_token_hash_key").on(table.tokenHash),
|
|
484
|
-
check("credentials_kind_check", sql`${table.kind} IN ('browser', 'cli', 'service')`),
|
|
500
|
+
check("credentials_kind_check", sql`${table.kind} IN ('browser', 'cli', 'service', 'personal')`),
|
|
485
501
|
check("credentials_principal_check", sql`${table.principal} LIKE 'user:%' OR ${table.principal} LIKE 'token:%'`),
|
|
486
502
|
check("credentials_kind_matches_principal", sql`(${table.kind} = 'service') = (${table.principal} LIKE 'token:%')`),
|
|
487
503
|
check("credentials_token_hash_check", sql`octet_length(${table.tokenHash}) = 32`),
|
package/dist/schema-version.d.ts
CHANGED
package/dist/schema-version.js
CHANGED
|
@@ -36,6 +36,41 @@ const KNOWN_MIGRATIONS = {
|
|
|
36
36
|
"when": 1791676486914,
|
|
37
37
|
"tag": "0003_credential_scopes",
|
|
38
38
|
"breakpoints": true
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"idx": 4,
|
|
42
|
+
"version": "7",
|
|
43
|
+
"when": 1792244020330,
|
|
44
|
+
"tag": "0004_environment_parents",
|
|
45
|
+
"breakpoints": true
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"idx": 5,
|
|
49
|
+
"version": "7",
|
|
50
|
+
"when": 1792250909587,
|
|
51
|
+
"tag": "0005_personal_tokens",
|
|
52
|
+
"breakpoints": true
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"idx": 6,
|
|
56
|
+
"version": "7",
|
|
57
|
+
"when": 1792252693050,
|
|
58
|
+
"tag": "0006_uploaded_keys",
|
|
59
|
+
"breakpoints": true
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"idx": 7,
|
|
63
|
+
"version": "7",
|
|
64
|
+
"when": 1792253794481,
|
|
65
|
+
"tag": "0007_personal_configs",
|
|
66
|
+
"breakpoints": true
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"idx": 8,
|
|
70
|
+
"version": "7",
|
|
71
|
+
"when": 1792255559691,
|
|
72
|
+
"tag": "0008_recipes",
|
|
73
|
+
"breakpoints": true
|
|
39
74
|
}
|
|
40
75
|
].map((entry) => entry.tag),
|
|
41
76
|
sqlite: [
|
|
@@ -66,6 +101,41 @@ const KNOWN_MIGRATIONS = {
|
|
|
66
101
|
"when": 1791676487593,
|
|
67
102
|
"tag": "0003_credential_scopes",
|
|
68
103
|
"breakpoints": true
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"idx": 4,
|
|
107
|
+
"version": "6",
|
|
108
|
+
"when": 1792244020959,
|
|
109
|
+
"tag": "0004_environment_parents",
|
|
110
|
+
"breakpoints": true
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"idx": 5,
|
|
114
|
+
"version": "6",
|
|
115
|
+
"when": 1792250910230,
|
|
116
|
+
"tag": "0005_personal_tokens",
|
|
117
|
+
"breakpoints": true
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"idx": 6,
|
|
121
|
+
"version": "6",
|
|
122
|
+
"when": 1792252693912,
|
|
123
|
+
"tag": "0006_uploaded_keys",
|
|
124
|
+
"breakpoints": true
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"idx": 7,
|
|
128
|
+
"version": "6",
|
|
129
|
+
"when": 1792253795070,
|
|
130
|
+
"tag": "0007_personal_configs",
|
|
131
|
+
"breakpoints": true
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"idx": 8,
|
|
135
|
+
"version": "6",
|
|
136
|
+
"when": 1792255560312,
|
|
137
|
+
"tag": "0008_recipes",
|
|
138
|
+
"breakpoints": true
|
|
69
139
|
}
|
|
70
140
|
].map((entry) => entry.tag)
|
|
71
141
|
};
|
package/dist/schema.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as secrets, a as deviceAuthorizations, b as vaultGrants, c as environmentsRelations, d as mcpConnections, f as oauthClients, g as secretVersions, h as secretReferences, i as credentials, l as identities, n as auditLog, o as dismissedKeys, p as projects, r as consumedTokens, s as environments, t as auditChainHead, u as mcpApprovals, v as secretsRelations, x as vaultMembers, y as serviceBindings } from "./schema-
|
|
1
|
+
import { _ as secrets, a as deviceAuthorizations, b as vaultGrants, c as environmentsRelations, d as mcpConnections, f as oauthClients, g as secretVersions, h as secretReferences, i as credentials, l as identities, n as auditLog, o as dismissedKeys, p as projects, r as consumedTokens, s as environments, t as auditChainHead, u as mcpApprovals, v as secretsRelations, x as vaultMembers, y as serviceBindings } from "./schema-BZRkBJQZ.js";
|
|
2
2
|
export { auditChainHead, auditLog, consumedTokens, credentials, deviceAuthorizations, dismissedKeys, environments, environmentsRelations, identities, mcpApprovals, mcpConnections, oauthClients, projects, secretReferences, secretVersions, secrets, secretsRelations, serviceBindings, vaultGrants, vaultMembers };
|
package/dist/schema.js
CHANGED
|
@@ -59,16 +59,27 @@ const environments = pgTable("environments", {
|
|
|
59
59
|
slug: text().notNull(),
|
|
60
60
|
name: text().notNull(),
|
|
61
61
|
createdAt: createdAt(),
|
|
62
|
-
archivedAt: timestamp("archived_at", { withTimezone: true })
|
|
62
|
+
archivedAt: timestamp("archived_at", { withTimezone: true }),
|
|
63
|
+
parentId: uuid("parent_id"),
|
|
64
|
+
owner: text()
|
|
63
65
|
}, (table) => [
|
|
64
66
|
unique("environments_project_id_slug_key").on(table.projectId, table.slug),
|
|
65
67
|
unique("environments_project_scoped").on(table.id, table.projectId),
|
|
66
68
|
check("environments_slug_check", sql`${table.slug} ~ ${PLACE_SLUG}`),
|
|
69
|
+
check("environments_parent_check", sql`${table.parentId} IS NULL OR ${table.parentId} <> ${table.id}`),
|
|
70
|
+
check("environments_owner_check", sql`${table.owner} IS NULL OR (${table.parentId} IS NOT NULL AND ${table.owner} LIKE 'user:_%')`),
|
|
71
|
+
uniqueIndex("environments_parent_id_owner_key").on(table.parentId, table.owner).where(sql`strpos(${table.slug}, '~') = 0`),
|
|
67
72
|
foreignKey({
|
|
68
73
|
name: "environments_project_id_fkey",
|
|
69
74
|
columns: [table.projectId],
|
|
70
75
|
foreignColumns: [projects.id]
|
|
71
|
-
}).onDelete("restrict")
|
|
76
|
+
}).onDelete("restrict"),
|
|
77
|
+
foreignKey({
|
|
78
|
+
name: "environments_parent_in_project",
|
|
79
|
+
columns: [table.parentId, table.projectId],
|
|
80
|
+
foreignColumns: [table.id, table.projectId]
|
|
81
|
+
}).onDelete("no action"),
|
|
82
|
+
index("environments_parent_idx").on(table.parentId)
|
|
72
83
|
]);
|
|
73
84
|
const secrets = pgTable("secrets", {
|
|
74
85
|
id: uuid().primaryKey().defaultRandom(),
|
|
@@ -115,7 +126,10 @@ const secretVersions = pgTable("secret_versions", {
|
|
|
115
126
|
kekId: text("kek_id").notNull(),
|
|
116
127
|
kekVersion: text("kek_version").notNull(),
|
|
117
128
|
createdAt: createdAt(),
|
|
118
|
-
createdBy: text("created_by").notNull()
|
|
129
|
+
createdBy: text("created_by").notNull(),
|
|
130
|
+
recipe: text(),
|
|
131
|
+
pairId: uuid("pair_id"),
|
|
132
|
+
pairHalf: text("pair_half")
|
|
119
133
|
}, (table) => [
|
|
120
134
|
unique("secret_versions_secret_id_version_key").on(table.secretId, table.version),
|
|
121
135
|
check("secret_versions_version_check", sql`${table.version} > 0`),
|
|
@@ -292,6 +306,7 @@ const auditLog = pgTable("audit_log", {
|
|
|
292
306
|
index("audit_log_subject_idx").on(table.author, table.subjectPrincipal, table.seq),
|
|
293
307
|
index("audit_log_unbind_idx").on(sql`((${table.metadata})::jsonb ->> 'bindingId')`).where(sql`${table.author} = 'app' AND ${table.action} = 'token.unbind' AND ${table.decision} = 'allow'`),
|
|
294
308
|
index("audit_log_exchange_idx").on(sql`((${table.metadata})::jsonb ->> 'credentialId')`).where(sql`${table.author} = 'app' AND ${table.action} = 'token.exchange' AND ${table.decision} = 'allow'`),
|
|
309
|
+
index("audit_log_token_create_idx").on(sql`((${table.metadata})::jsonb ->> 'targetCredentialId')`).where(sql`${table.author} = 'app' AND ${table.action} = 'token.create' AND ${table.decision} = 'allow'`),
|
|
295
310
|
index("audit_log_reference_end_idx").on(table.relatedSeq).where(sql`${table.author} = 'vault' AND ${table.action} = 'reference.end' AND ${table.decision} = 'allow'`),
|
|
296
311
|
index("audit_log_also_project_idx").on(sql`(((${table.metadata})::jsonb -> 'also') ->> 'projectId')`, table.seq).where(sql`((${table.metadata})::jsonb -> 'also') IS NOT NULL`),
|
|
297
312
|
index("audit_log_also_environment_idx").on(sql`(((${table.metadata})::jsonb -> 'also') ->> 'environmentId')`, table.seq).where(sql`((${table.metadata})::jsonb -> 'also') IS NOT NULL`),
|
|
@@ -399,6 +414,7 @@ const serviceBindings = pgTable("service_bindings", {
|
|
|
399
414
|
profile: text().notNull(),
|
|
400
415
|
issuer: text().notNull(),
|
|
401
416
|
jwksUri: text("jwks_uri").notNull(),
|
|
417
|
+
jwks: text(),
|
|
402
418
|
claims: text().notNull(),
|
|
403
419
|
scope: text(),
|
|
404
420
|
label: text(),
|
|
@@ -547,7 +563,7 @@ const credentials = pgTable("credentials", {
|
|
|
547
563
|
revokedBy: text("revoked_by")
|
|
548
564
|
}, (table) => [
|
|
549
565
|
unique("credentials_token_hash_key").on(table.tokenHash),
|
|
550
|
-
check("credentials_kind_check", sql`${table.kind} IN ('browser', 'cli', 'service')`),
|
|
566
|
+
check("credentials_kind_check", sql`${table.kind} IN ('browser', 'cli', 'service', 'personal')`),
|
|
551
567
|
check("credentials_principal_check", sql`${table.principal} LIKE 'user:%' OR ${table.principal} LIKE 'token:%'`),
|
|
552
568
|
check("credentials_kind_matches_principal", sql`(${table.kind} = 'service') = (${table.principal} LIKE 'token:%')`),
|
|
553
569
|
check("credentials_token_hash_check", sql`octet_length(${table.tokenHash}) = 32`),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coffre/db",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "coffre's database: the schema, its migrations and the connections, on Postgres or SQLite.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"drizzle-orm": "0.45.2",
|
|
80
80
|
"pg": "8.16.3",
|
|
81
81
|
"pg-connection-string": "2.14.0",
|
|
82
|
-
"@coffre/core": "0.
|
|
82
|
+
"@coffre/core": "0.7.0"
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|
|
85
85
|
"@types/node": "26.1.1",
|
package/src/log.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GENESIS_HASH, sealEntry, type LogFields, type LogKey, type StoredEntry } from '@coffre/core/audit';
|
|
2
|
-
import { and, desc, eq, lt } from 'drizzle-orm';
|
|
2
|
+
import { and, asc, desc, eq, lt, sql, type SQL } from 'drizzle-orm';
|
|
3
3
|
|
|
4
4
|
import { afterCommit } from './commits.ts';
|
|
5
5
|
import { tablesOf, type Queryable, type Transaction } from './database.ts';
|
|
@@ -218,6 +218,45 @@ export function entryColumns(db: Queryable) {
|
|
|
218
218
|
};
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
/**
|
|
222
|
+
* Metadata a reader of many entries holds at once, at most: an entry about
|
|
223
|
+
* a read of several secrets lists them, a thousand some 150 kilobytes, so a
|
|
224
|
+
* count of entries alone does not bound a batch's or a page's memory within
|
|
225
|
+
* a Worker's 128 megabytes.
|
|
226
|
+
*/
|
|
227
|
+
export const BATCH_BYTES = 8 * 1024 * 1024;
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Of the entries `where` matches, in `order` of seq, the first `limit` or
|
|
231
|
+
* fewer that hold no more than `BATCH_BYTES` of metadata between them, but
|
|
232
|
+
* always the first: the seqs they run from and to, and whether the bytes
|
|
233
|
+
* cut the run short of `limit`. Null when none matches. A reader asks this
|
|
234
|
+
* first, then reads `where` between the two.
|
|
235
|
+
*/
|
|
236
|
+
export async function withinBytes(
|
|
237
|
+
db: Queryable,
|
|
238
|
+
where: SQL | undefined,
|
|
239
|
+
limit: number,
|
|
240
|
+
order: 'asc' | 'desc' = 'asc',
|
|
241
|
+
): Promise<{ from: bigint; to: bigint; cut: boolean; count: number } | null> {
|
|
242
|
+
const { auditLog } = tablesOf(db);
|
|
243
|
+
const sizes = await db
|
|
244
|
+
.select({ seq: auditLog.seq, bytes: sql<number | string>`length(${auditLog.metadata})` })
|
|
245
|
+
.from(auditLog)
|
|
246
|
+
.where(where)
|
|
247
|
+
.orderBy(order === 'asc' ? asc(auditLog.seq) : desc(auditLog.seq))
|
|
248
|
+
.limit(limit);
|
|
249
|
+
let held = 0;
|
|
250
|
+
let count = 0;
|
|
251
|
+
for (const { bytes } of sizes) {
|
|
252
|
+
held += Number(bytes);
|
|
253
|
+
if (count > 0 && held > BATCH_BYTES) break;
|
|
254
|
+
count += 1;
|
|
255
|
+
}
|
|
256
|
+
if (count === 0) return null;
|
|
257
|
+
return { from: sizes[0]!.seq, to: sizes[count - 1]!.seq, cut: count < sizes.length, count };
|
|
258
|
+
}
|
|
259
|
+
|
|
221
260
|
/**
|
|
222
261
|
* How a reader tells the vault's own entries from those forged in its name:
|
|
223
262
|
* by their MAC, which only the vault holds the key to check. Each it finds
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
-- The app sets parent_id when it makes an environment, and never after: it gets no UPDATE grant, so a parent cannot change.
|
|
2
|
+
ALTER TABLE "environments" ADD COLUMN "parent_id" uuid;--> statement-breakpoint
|
|
3
|
+
ALTER TABLE "environments" ADD CONSTRAINT "environments_parent_in_project" FOREIGN KEY ("parent_id","project_id") REFERENCES "public"."environments"("id","project_id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
|
4
|
+
CREATE INDEX "environments_parent_idx" ON "environments" USING btree ("parent_id");--> statement-breakpoint
|
|
5
|
+
ALTER TABLE "environments" ADD CONSTRAINT "environments_parent_check" CHECK ("environments"."parent_id" IS NULL OR "environments"."parent_id" <> "environments"."id");
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
-- Personal tokens (0.7). The check widens to their kind: the release before makes none, and refuses one, whose prefix, coffre_pat_, it does not take for a token.
|
|
2
|
+
ALTER TABLE "credentials" DROP CONSTRAINT "credentials_kind_check";--> statement-breakpoint
|
|
3
|
+
ALTER TABLE "credentials" ADD CONSTRAINT "credentials_kind_check" CHECK ("credentials"."kind" IN ('browser', 'cli', 'service', 'personal'));--> statement-breakpoint
|
|
4
|
+
-- A token's creation, by the credential's ID: the label a personal token's entries are shown with.
|
|
5
|
+
CREATE INDEX "audit_log_token_create_idx" ON "audit_log" USING btree ((("metadata")::jsonb ->> 'targetCredentialId')) WHERE "audit_log"."author" = 'app' AND "audit_log"."action" = 'token.create' AND "audit_log"."decision" = 'allow';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
-- The app sets owner when it makes a personal config, and never after: it gets no UPDATE grant, so an owner cannot change. The vault binds each value to it, so a forged owner opens nothing.
|
|
2
|
+
ALTER TABLE "environments" ADD COLUMN "owner" text;--> statement-breakpoint
|
|
3
|
+
CREATE UNIQUE INDEX "environments_parent_id_owner_key" ON "environments" USING btree ("parent_id","owner") WHERE strpos("environments"."slug", '~') = 0;--> statement-breakpoint
|
|
4
|
+
ALTER TABLE "environments" ADD CONSTRAINT "environments_owner_check" CHECK ("environments"."owner" IS NULL OR ("environments"."parent_id" IS NOT NULL AND "environments"."owner" LIKE 'user:_%'));
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
ALTER TABLE "secret_versions" ADD COLUMN "recipe" text;--> statement-breakpoint
|
|
2
|
+
ALTER TABLE "secret_versions" ADD COLUMN "pair_id" uuid;--> statement-breakpoint
|
|
3
|
+
ALTER TABLE "secret_versions" ADD COLUMN "pair_half" text;--> statement-breakpoint
|
|
4
|
+
-- A version is still only ever erased: its recipe and its pair with it.
|
|
5
|
+
CREATE OR REPLACE FUNCTION secret_versions_erase_only() RETURNS trigger LANGUAGE plpgsql AS $$
|
|
6
|
+
BEGIN
|
|
7
|
+
IF octet_length(NEW.ciphertext) <> 0 OR octet_length(NEW.wrapped_dek) <> 0
|
|
8
|
+
OR (NEW.id, NEW.secret_id, NEW.version, NEW.envelope_version, NEW.iv, NEW.auth_tag,
|
|
9
|
+
NEW.kek_provider, NEW.kek_id, NEW.kek_version, NEW.created_at, NEW.created_by,
|
|
10
|
+
NEW.recipe, NEW.pair_id, NEW.pair_half)
|
|
11
|
+
IS DISTINCT FROM
|
|
12
|
+
(OLD.id, OLD.secret_id, OLD.version, OLD.envelope_version, OLD.iv, OLD.auth_tag,
|
|
13
|
+
OLD.kek_provider, OLD.kek_id, OLD.kek_version, OLD.created_at, OLD.created_by,
|
|
14
|
+
OLD.recipe, OLD.pair_id, OLD.pair_half) THEN
|
|
15
|
+
RAISE EXCEPTION 'a secret version is only ever erased' USING ERRCODE = 'insufficient_privilege';
|
|
16
|
+
END IF;
|
|
17
|
+
RETURN NEW;
|
|
18
|
+
END
|
|
19
|
+
$$;
|