@coffre/db 0.8.2 → 0.10.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.
Files changed (65) hide show
  1. package/dist/connect.d.ts +1 -1
  2. package/dist/{database-BEnexh-3.d.ts → database-BFnO2b2P.d.ts} +1 -1
  3. package/dist/dialect.d.ts +1 -1
  4. package/dist/grants.d.ts +2 -2
  5. package/dist/hyperdrive.d.ts +1 -1
  6. package/dist/index.d.ts +2 -2
  7. package/dist/log.d.ts +7 -1
  8. package/dist/log.js +11 -1
  9. package/dist/migrations/postgres/0010_handle_places.sql +2 -0
  10. package/dist/migrations/postgres/0011_plain_values.sql +19 -0
  11. package/dist/migrations/postgres/0012_sensitive_values.sql +18 -0
  12. package/dist/migrations/postgres/0013_contributor_role.sql +5 -0
  13. package/dist/migrations/postgres/0014_custom_roles.sql +4 -0
  14. package/dist/migrations/postgres/meta/0010_snapshot.json +2991 -0
  15. package/dist/migrations/postgres/meta/0011_snapshot.json +2997 -0
  16. package/dist/migrations/postgres/meta/0012_snapshot.json +3004 -0
  17. package/dist/migrations/postgres/meta/0013_snapshot.json +3004 -0
  18. package/dist/migrations/postgres/meta/0014_snapshot.json +3000 -0
  19. package/dist/migrations/postgres/meta/_journal.json +35 -0
  20. package/dist/migrations/sqlite/0010_handle_places.sql +2 -0
  21. package/dist/migrations/sqlite/0011_plain_values.sql +14 -0
  22. package/dist/migrations/sqlite/0012_sensitive_values.sql +15 -0
  23. package/dist/migrations/sqlite/0013_contributor_role.sql +28 -0
  24. package/dist/migrations/sqlite/0014_custom_roles.sql +27 -0
  25. package/dist/migrations/sqlite/meta/0010_snapshot.json +2711 -0
  26. package/dist/migrations/sqlite/meta/0011_snapshot.json +2718 -0
  27. package/dist/migrations/sqlite/meta/0012_snapshot.json +2726 -0
  28. package/dist/migrations/sqlite/meta/0013_snapshot.json +2726 -0
  29. package/dist/migrations/sqlite/meta/0014_snapshot.json +2722 -0
  30. package/dist/migrations/sqlite/meta/_journal.json +35 -0
  31. package/dist/portable.d.ts +2 -2
  32. package/dist/{schema-BZRkBJQZ.d.ts → schema-NBVA9q-s.d.ts} +51 -0
  33. package/dist/schema-sqlite.d.ts +55 -0
  34. package/dist/schema-sqlite.js +5 -6
  35. package/dist/schema-version.d.ts +1 -1
  36. package/dist/schema-version.js +70 -0
  37. package/dist/schema.d.ts +1 -1
  38. package/dist/schema.js +5 -7
  39. package/package.json +2 -2
  40. package/src/grants.ts +3 -2
  41. package/src/log.ts +16 -0
  42. package/src/migrations/postgres/0010_handle_places.sql +2 -0
  43. package/src/migrations/postgres/0011_plain_values.sql +19 -0
  44. package/src/migrations/postgres/0012_sensitive_values.sql +18 -0
  45. package/src/migrations/postgres/0013_contributor_role.sql +5 -0
  46. package/src/migrations/postgres/0014_custom_roles.sql +4 -0
  47. package/src/migrations/postgres/meta/0010_snapshot.json +2991 -0
  48. package/src/migrations/postgres/meta/0011_snapshot.json +2997 -0
  49. package/src/migrations/postgres/meta/0012_snapshot.json +3004 -0
  50. package/src/migrations/postgres/meta/0013_snapshot.json +3004 -0
  51. package/src/migrations/postgres/meta/0014_snapshot.json +3000 -0
  52. package/src/migrations/postgres/meta/_journal.json +35 -0
  53. package/src/migrations/sqlite/0010_handle_places.sql +2 -0
  54. package/src/migrations/sqlite/0011_plain_values.sql +14 -0
  55. package/src/migrations/sqlite/0012_sensitive_values.sql +15 -0
  56. package/src/migrations/sqlite/0013_contributor_role.sql +28 -0
  57. package/src/migrations/sqlite/0014_custom_roles.sql +27 -0
  58. package/src/migrations/sqlite/meta/0010_snapshot.json +2711 -0
  59. package/src/migrations/sqlite/meta/0011_snapshot.json +2718 -0
  60. package/src/migrations/sqlite/meta/0012_snapshot.json +2726 -0
  61. package/src/migrations/sqlite/meta/0013_snapshot.json +2726 -0
  62. package/src/migrations/sqlite/meta/0014_snapshot.json +2722 -0
  63. package/src/migrations/sqlite/meta/_journal.json +35 -0
  64. package/src/schema.sqlite.ts +15 -9
  65. package/src/schema.ts +15 -10
@@ -71,6 +71,41 @@
71
71
  "when": 1792281880190,
72
72
  "tag": "0009_handles",
73
73
  "breakpoints": true
74
+ },
75
+ {
76
+ "idx": 10,
77
+ "version": "6",
78
+ "when": 1792315000277,
79
+ "tag": "0010_handle_places",
80
+ "breakpoints": true
81
+ },
82
+ {
83
+ "idx": 11,
84
+ "version": "6",
85
+ "when": 1792317461299,
86
+ "tag": "0011_plain_values",
87
+ "breakpoints": true
88
+ },
89
+ {
90
+ "idx": 12,
91
+ "version": "6",
92
+ "when": 1792340920098,
93
+ "tag": "0012_sensitive_values",
94
+ "breakpoints": true
95
+ },
96
+ {
97
+ "idx": 13,
98
+ "version": "6",
99
+ "when": 1792343374795,
100
+ "tag": "0013_contributor_role",
101
+ "breakpoints": true
102
+ },
103
+ {
104
+ "idx": 14,
105
+ "version": "6",
106
+ "when": 1792345777255,
107
+ "tag": "0014_custom_roles",
108
+ "breakpoints": true
74
109
  }
75
110
  ]
76
111
  }
@@ -1,5 +1,5 @@
1
- import { m as schema_d_exports } from "./schema-BZRkBJQZ.js";
2
- import { t as Database } from "./database-BEnexh-3.js";
1
+ import { m as schema_d_exports } from "./schema-NBVA9q-s.js";
2
+ import { t as Database } from "./database-BFnO2b2P.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
@@ -714,6 +714,40 @@ declare const secretVersions: import("drizzle-orm/pg-core").PgTableWithColumns<{
714
714
  identity: undefined;
715
715
  generated: undefined;
716
716
  }, {}, {}>;
717
+ plaintext: import("drizzle-orm/pg-core").PgColumn<{
718
+ name: "plaintext";
719
+ tableName: "secret_versions";
720
+ dataType: "string";
721
+ columnType: "PgText";
722
+ data: string;
723
+ driverParam: string;
724
+ notNull: false;
725
+ hasDefault: false;
726
+ isPrimaryKey: false;
727
+ isAutoincrement: false;
728
+ hasRuntimeDefault: false;
729
+ enumValues: [string, ...string[]];
730
+ baseColumn: never;
731
+ identity: undefined;
732
+ generated: undefined;
733
+ }, {}, {}>;
734
+ sensitive: import("drizzle-orm/pg-core").PgColumn<{
735
+ name: "sensitive";
736
+ tableName: "secret_versions";
737
+ dataType: "boolean";
738
+ columnType: "PgBoolean";
739
+ data: boolean;
740
+ driverParam: boolean;
741
+ notNull: true;
742
+ hasDefault: true;
743
+ isPrimaryKey: false;
744
+ isAutoincrement: false;
745
+ hasRuntimeDefault: false;
746
+ enumValues: undefined;
747
+ baseColumn: never;
748
+ identity: undefined;
749
+ generated: undefined;
750
+ }, {}, {}>;
717
751
  };
718
752
  dialect: "pg";
719
753
  }>;
@@ -3298,6 +3332,23 @@ declare const credentials: import("drizzle-orm/pg-core").PgTableWithColumns<{
3298
3332
  identity: undefined;
3299
3333
  generated: undefined;
3300
3334
  }, {}, {}>;
3335
+ places: import("drizzle-orm/pg-core").PgColumn<{
3336
+ name: "places";
3337
+ tableName: "credentials";
3338
+ dataType: "string";
3339
+ columnType: "PgText";
3340
+ data: string;
3341
+ driverParam: string;
3342
+ notNull: false;
3343
+ hasDefault: false;
3344
+ isPrimaryKey: false;
3345
+ isAutoincrement: false;
3346
+ hasRuntimeDefault: false;
3347
+ enumValues: [string, ...string[]];
3348
+ baseColumn: never;
3349
+ identity: undefined;
3350
+ generated: undefined;
3351
+ }, {}, {}>;
3301
3352
  label: import("drizzle-orm/pg-core").PgColumn<{
3302
3353
  name: "label";
3303
3354
  tableName: "credentials";
@@ -756,6 +756,42 @@ export declare const secretVersions: import("drizzle-orm/sqlite-core").SQLiteTab
756
756
  }, {}, {
757
757
  length: number | undefined;
758
758
  }>;
759
+ plaintext: import("drizzle-orm/sqlite-core").SQLiteColumn<{
760
+ name: "plaintext";
761
+ tableName: "secret_versions";
762
+ dataType: "string";
763
+ columnType: "SQLiteText";
764
+ data: string;
765
+ driverParam: string;
766
+ notNull: false;
767
+ hasDefault: false;
768
+ isPrimaryKey: false;
769
+ isAutoincrement: false;
770
+ hasRuntimeDefault: false;
771
+ enumValues: [string, ...string[]];
772
+ baseColumn: never;
773
+ identity: undefined;
774
+ generated: undefined;
775
+ }, {}, {
776
+ length: undefined;
777
+ }>;
778
+ sensitive: import("drizzle-orm/sqlite-core").SQLiteColumn<{
779
+ name: string;
780
+ tableName: "secret_versions";
781
+ dataType: "boolean";
782
+ columnType: "SQLiteBoolean";
783
+ data: boolean;
784
+ driverParam: number;
785
+ notNull: true;
786
+ hasDefault: true;
787
+ isPrimaryKey: false;
788
+ isAutoincrement: false;
789
+ hasRuntimeDefault: false;
790
+ enumValues: undefined;
791
+ baseColumn: never;
792
+ identity: undefined;
793
+ generated: undefined;
794
+ }, {}, {}>;
759
795
  };
760
796
  dialect: "sqlite";
761
797
  }>;
@@ -3427,6 +3463,25 @@ export declare const credentials: import("drizzle-orm/sqlite-core").SQLiteTableW
3427
3463
  }, {}, {
3428
3464
  length: undefined;
3429
3465
  }>;
3466
+ places: import("drizzle-orm/sqlite-core").SQLiteColumn<{
3467
+ name: "places";
3468
+ tableName: "credentials";
3469
+ dataType: "string";
3470
+ columnType: "SQLiteText";
3471
+ data: string;
3472
+ driverParam: string;
3473
+ notNull: false;
3474
+ hasDefault: false;
3475
+ isPrimaryKey: false;
3476
+ isAutoincrement: false;
3477
+ hasRuntimeDefault: false;
3478
+ enumValues: [string, ...string[]];
3479
+ baseColumn: never;
3480
+ identity: undefined;
3481
+ generated: undefined;
3482
+ }, {}, {
3483
+ length: undefined;
3484
+ }>;
3430
3485
  label: import("drizzle-orm/sqlite-core").SQLiteColumn<{
3431
3486
  name: "label";
3432
3487
  tableName: "credentials";
@@ -1,7 +1,6 @@
1
1
  import { n as relationsOf, t as ACTIVE_SUBJECT } from "./relations-BfR3JjT1.js";
2
2
  import { asPostgres } from "./portable.js";
3
3
  import { sql } from "drizzle-orm";
4
- import { ROLE_NAMES, assignableToEnvironment } from "@coffre/core/access";
5
4
  import { blob, check, customType, foreignKey, index, integer, primaryKey, sqliteTable, text, unique, uniqueIndex } from "drizzle-orm/sqlite-core";
6
5
  //#region src/schema.sqlite.ts
7
6
  /**
@@ -124,7 +123,9 @@ const secretVersions = sqliteTable("secret_versions", {
124
123
  createdBy: text("created_by").notNull(),
125
124
  recipe: text(),
126
125
  pairId: text("pair_id"),
127
- pairHalf: text("pair_half")
126
+ pairHalf: text("pair_half"),
127
+ plaintext: text(),
128
+ sensitive: flag("sensitive").notNull().default(false)
128
129
  }, (table) => [
129
130
  unique("secret_versions_secret_id_version_key").on(table.secretId, table.version),
130
131
  check("secret_versions_version_check", sql`${table.version} > 0`),
@@ -156,8 +157,6 @@ const dismissedKeys = sqliteTable("dismissed_keys", {
156
157
  foreignColumns: [environments.id]
157
158
  }).onDelete("restrict")
158
159
  ]);
159
- const ROLES = sql.raw(ROLE_NAMES.map((role) => `'${role}'`).join(", "));
160
- const ENVIRONMENT_ROLES = sql.raw(ROLE_NAMES.filter(assignableToEnvironment).map((role) => `'${role}'`).join(", "));
161
160
  const vaultMembers = sqliteTable("vault_members", {
162
161
  principal: text().primaryKey(),
163
162
  status: text().notNull(),
@@ -194,8 +193,7 @@ const vaultGrants = sqliteTable("vault_grants", {
194
193
  }, (table) => [
195
194
  check("vault_grants_one_place", sql`((${table.projectId} IS NULL) <> (${table.environmentId} IS NULL) AND ${table.environmentSlug} IS NULL) OR (${table.projectId} IS NULL AND ${table.environmentId} IS NULL)`),
196
195
  check("vault_grants_environment_slug_check", sql`${table.environmentSlug} IS NULL OR (${isSlug(table.environmentSlug, 63)})`),
197
- check("vault_grants_role_check", sql`${table.role} IN (${ROLES})`),
198
- check("vault_grants_environment_role_check", sql`(${table.environmentId} IS NULL AND ${table.environmentSlug} IS NULL) OR ${table.role} IN (${ENVIRONMENT_ROLES})`),
196
+ check("vault_grants_role_check", sql`length(${table.role}) BETWEEN 2 AND 32 AND ${table.role} GLOB '[a-z]*' AND ${table.role} NOT GLOB '*[^a-z0-9-]*'`),
199
197
  unique("vault_grants_on_project").on(table.principal, table.projectId),
200
198
  unique("vault_grants_on_environment").on(table.principal, table.environmentId),
201
199
  unique("vault_grants_on_environment_slug").on(table.principal, table.environmentSlug),
@@ -487,6 +485,7 @@ const credentials = sqliteTable("credentials", {
487
485
  principal: text().notNull(),
488
486
  identityId: text("identity_id"),
489
487
  scope: text(),
488
+ places: text(),
490
489
  label: text(),
491
490
  createdAt: createdAt(),
492
491
  createdBy: text("created_by").notNull(),
@@ -1,4 +1,4 @@
1
- import { i as Queryable } from "./database-BEnexh-3.js";
1
+ import { i as Queryable } from "./database-BFnO2b2P.js";
2
2
  import { Engine } from "./dialect.js";
3
3
  //#region src/schema-version.d.ts
4
4
  /**
@@ -78,6 +78,41 @@ const KNOWN_MIGRATIONS = {
78
78
  "when": 1792281879553,
79
79
  "tag": "0009_handles",
80
80
  "breakpoints": true
81
+ },
82
+ {
83
+ "idx": 10,
84
+ "version": "7",
85
+ "when": 1792314999425,
86
+ "tag": "0010_handle_places",
87
+ "breakpoints": true
88
+ },
89
+ {
90
+ "idx": 11,
91
+ "version": "7",
92
+ "when": 1792317460399,
93
+ "tag": "0011_plain_values",
94
+ "breakpoints": true
95
+ },
96
+ {
97
+ "idx": 12,
98
+ "version": "7",
99
+ "when": 1792340919452,
100
+ "tag": "0012_sensitive_values",
101
+ "breakpoints": true
102
+ },
103
+ {
104
+ "idx": 13,
105
+ "version": "7",
106
+ "when": 1792343374226,
107
+ "tag": "0013_contributor_role",
108
+ "breakpoints": true
109
+ },
110
+ {
111
+ "idx": 14,
112
+ "version": "7",
113
+ "when": 1792345776262,
114
+ "tag": "0014_custom_roles",
115
+ "breakpoints": true
81
116
  }
82
117
  ].map((entry) => entry.tag),
83
118
  sqlite: [
@@ -150,6 +185,41 @@ const KNOWN_MIGRATIONS = {
150
185
  "when": 1792281880190,
151
186
  "tag": "0009_handles",
152
187
  "breakpoints": true
188
+ },
189
+ {
190
+ "idx": 10,
191
+ "version": "6",
192
+ "when": 1792315000277,
193
+ "tag": "0010_handle_places",
194
+ "breakpoints": true
195
+ },
196
+ {
197
+ "idx": 11,
198
+ "version": "6",
199
+ "when": 1792317461299,
200
+ "tag": "0011_plain_values",
201
+ "breakpoints": true
202
+ },
203
+ {
204
+ "idx": 12,
205
+ "version": "6",
206
+ "when": 1792340920098,
207
+ "tag": "0012_sensitive_values",
208
+ "breakpoints": true
209
+ },
210
+ {
211
+ "idx": 13,
212
+ "version": "6",
213
+ "when": 1792343374795,
214
+ "tag": "0013_contributor_role",
215
+ "breakpoints": true
216
+ },
217
+ {
218
+ "idx": 14,
219
+ "version": "6",
220
+ "when": 1792345777255,
221
+ "tag": "0014_custom_roles",
222
+ "breakpoints": true
153
223
  }
154
224
  ].map((entry) => entry.tag)
155
225
  };
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-BZRkBJQZ.js";
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-NBVA9q-s.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
@@ -1,7 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
2
  import { n as relationsOf, t as ACTIVE_SUBJECT } from "./relations-BfR3JjT1.js";
3
3
  import { sql } from "drizzle-orm";
4
- import { ROLE_NAMES, assignableToEnvironment } from "@coffre/core/access";
5
4
  import { bigint, boolean, check, customType, foreignKey, index, integer, pgTable, primaryKey, text, timestamp, unique, uniqueIndex, uuid } from "drizzle-orm/pg-core";
6
5
  //#region src/schema.ts
7
6
  var schema_exports = /* @__PURE__ */ __exportAll({
@@ -129,7 +128,9 @@ const secretVersions = pgTable("secret_versions", {
129
128
  createdBy: text("created_by").notNull(),
130
129
  recipe: text(),
131
130
  pairId: uuid("pair_id"),
132
- pairHalf: text("pair_half")
131
+ pairHalf: text("pair_half"),
132
+ plaintext: text(),
133
+ sensitive: boolean().notNull().default(false)
133
134
  }, (table) => [
134
135
  unique("secret_versions_secret_id_version_key").on(table.secretId, table.version),
135
136
  check("secret_versions_version_check", sql`${table.version} > 0`),
@@ -168,9 +169,6 @@ const dismissedKeys = pgTable("dismissed_keys", {
168
169
  foreignColumns: [environments.id]
169
170
  }).onDelete("restrict")
170
171
  ]);
171
- /** The role catalogue, as SQL: every role, and those an environment may be granted. */
172
- const ROLES = sql.raw(ROLE_NAMES.map((role) => `'${role}'`).join(", "));
173
- const ENVIRONMENT_ROLES = sql.raw(ROLE_NAMES.filter(assignableToEnvironment).map((role) => `'${role}'`).join(", "));
174
172
  /**
175
173
  * The vault's member directory: everyone it has admitted, written by the
176
174
  * vault alone, read by both. A principal with no row is no member.
@@ -219,8 +217,7 @@ const vaultGrants = pgTable("vault_grants", {
219
217
  }, (table) => [
220
218
  check("vault_grants_one_place", sql`((${table.projectId} IS NULL) <> (${table.environmentId} IS NULL) AND ${table.environmentSlug} IS NULL) OR (${table.projectId} IS NULL AND ${table.environmentId} IS NULL)`),
221
219
  check("vault_grants_environment_slug_check", sql`${table.environmentSlug} IS NULL OR ${table.environmentSlug} ~ '^[a-z0-9][a-z0-9-]{0,62}$'`),
222
- check("vault_grants_role_check", sql`${table.role} IN (${ROLES})`),
223
- check("vault_grants_environment_role_check", sql`(${table.environmentId} IS NULL AND ${table.environmentSlug} IS NULL) OR ${table.role} IN (${ENVIRONMENT_ROLES})`),
220
+ check("vault_grants_role_check", sql`${table.role} ~ '^[a-z][a-z0-9-]{1,31}$'`),
224
221
  unique("vault_grants_on_project").on(table.principal, table.projectId),
225
222
  unique("vault_grants_on_environment").on(table.principal, table.environmentId),
226
223
  unique("vault_grants_on_environment_slug").on(table.principal, table.environmentSlug),
@@ -553,6 +550,7 @@ const credentials = pgTable("credentials", {
553
550
  principal: text().notNull(),
554
551
  identityId: uuid("identity_id"),
555
552
  scope: text(),
553
+ places: text(),
556
554
  label: text(),
557
555
  createdAt: createdAt(),
558
556
  createdBy: text("created_by").notNull(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coffre/db",
3
- "version": "0.8.2",
3
+ "version": "0.10.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.8.2"
82
+ "@coffre/core": "0.10.0"
83
83
  },
84
84
  "devDependencies": {
85
85
  "@types/node": "26.1.1",
package/src/grants.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  type Conversion,
10
10
  type ConversionInput,
11
11
  type InstanceRole,
12
+ type BuiltinRole,
12
13
  type Role,
13
14
  type Scope,
14
15
  } from '@coffre/core/access';
@@ -129,10 +130,10 @@ export function everyProjectConversion(
129
130
  role: person ? role : 'member',
130
131
  everyProject: live
131
132
  .filter((grant) => grant.projectId === null && grant.environmentId === null)
132
- .map((grant) => ({ environmentSlug: grant.environmentSlug, role: grant.role as Role, expiresAt: grant.expiresAt })),
133
+ .map((grant) => ({ environmentSlug: grant.environmentSlug, role: grant.role as BuiltinRole, expiresAt: grant.expiresAt })),
133
134
  grants: live
134
135
  .filter((grant) => grant.projectId !== null)
135
- .map((grant) => ({ projectId: grant.projectId, environmentId: grant.environmentId, role: grant.role as Role, expiresAt: grant.expiresAt })),
136
+ .map((grant) => ({ projectId: grant.projectId, environmentId: grant.environmentId, role: grant.role as BuiltinRole, expiresAt: grant.expiresAt })),
136
137
  projects,
137
138
  });
138
139
  }
package/src/log.ts CHANGED
@@ -283,6 +283,22 @@ export async function newestVaultEntries(db: Queryable, action: string, limit: n
283
283
  return rows as StoredEntry[];
284
284
  }
285
285
 
286
+ /**
287
+ * The number of the newest entry in the vault's name, allowed, of one
288
+ * `action`, or null for none: whether one was appended since, and nothing
289
+ * of what it says, so its MAC is not checked. One indexed read.
290
+ */
291
+ export async function newestVaultSeq(db: Queryable, action: string): Promise<bigint | null> {
292
+ const { auditLog } = tablesOf(db);
293
+ const [row] = await db
294
+ .select({ seq: auditLog.seq })
295
+ .from(auditLog)
296
+ .where(and(eq(auditLog.author, 'vault'), eq(auditLog.action, action), eq(auditLog.decision, 'allow')))
297
+ .orderBy(desc(auditLog.seq))
298
+ .limit(1);
299
+ return (row?.seq as bigint | undefined) ?? null;
300
+ }
301
+
286
302
  /** How many entries in its name the vault reads back, a page at a time, for its own newest of a kind (`newestAuthentic`). */
287
303
  export const LOOK_BACK = { page: 32, pages: 32 };
288
304
 
@@ -0,0 +1,2 @@
1
+ -- A handle's places (0.9): the environments it was made for, by id and whether as @me, which the proxy's stable URL resolves to. A nullable column the release before neither reads nor writes; the app sets it when it makes a handle, and never after (no UPDATE grant).
2
+ ALTER TABLE "credentials" ADD COLUMN "places" text;
@@ -0,0 +1,19 @@
1
+ -- Plain values (0.9): a version of a key marked not secret holds its value in the clear too. Nullable, so the release before writes on as it did.
2
+ ALTER TABLE "secret_versions" ADD COLUMN "plaintext" text;--> statement-breakpoint
3
+ -- A version is still only ever erased, and an erased one holds no value, in the clear or not.
4
+ CREATE OR REPLACE FUNCTION secret_versions_erase_only() RETURNS trigger LANGUAGE plpgsql AS $$
5
+ BEGIN
6
+ IF octet_length(NEW.ciphertext) <> 0 OR octet_length(NEW.wrapped_dek) <> 0 OR NEW.plaintext IS NOT NULL
7
+ OR (NEW.id, NEW.secret_id, NEW.version, NEW.envelope_version, NEW.iv, NEW.auth_tag,
8
+ NEW.kek_provider, NEW.kek_id, NEW.kek_version, NEW.created_at, NEW.created_by,
9
+ NEW.recipe, NEW.pair_id, NEW.pair_half)
10
+ IS DISTINCT FROM
11
+ (OLD.id, OLD.secret_id, OLD.version, OLD.envelope_version, OLD.iv, OLD.auth_tag,
12
+ OLD.kek_provider, OLD.kek_id, OLD.kek_version, OLD.created_at, OLD.created_by,
13
+ OLD.recipe, OLD.pair_id, OLD.pair_half) THEN
14
+ RAISE EXCEPTION 'a secret version is only ever erased' USING ERRCODE = 'insufficient_privilege';
15
+ END IF;
16
+ RETURN NEW;
17
+ END
18
+ $$;--> statement-breakpoint
19
+ GRANT UPDATE (plaintext) ON secret_versions TO coffre_app;
@@ -0,0 +1,18 @@
1
+ -- Sensitive values (0.10): a version never handed back to a person, which the vault's wrap of its data key binds. Defaults to false, so the release before writes on as it did.
2
+ ALTER TABLE "secret_versions" ADD COLUMN "sensitive" boolean DEFAULT false NOT NULL;--> statement-breakpoint
3
+ -- A version is still only ever erased: its sensitivity with the rest of it.
4
+ CREATE OR REPLACE FUNCTION secret_versions_erase_only() RETURNS trigger LANGUAGE plpgsql AS $$
5
+ BEGIN
6
+ IF octet_length(NEW.ciphertext) <> 0 OR octet_length(NEW.wrapped_dek) <> 0 OR NEW.plaintext IS NOT NULL
7
+ OR (NEW.id, NEW.secret_id, NEW.version, NEW.envelope_version, NEW.iv, NEW.auth_tag,
8
+ NEW.kek_provider, NEW.kek_id, NEW.kek_version, NEW.created_at, NEW.created_by,
9
+ NEW.recipe, NEW.pair_id, NEW.pair_half, NEW.sensitive)
10
+ IS DISTINCT FROM
11
+ (OLD.id, OLD.secret_id, OLD.version, OLD.envelope_version, OLD.iv, OLD.auth_tag,
12
+ OLD.kek_provider, OLD.kek_id, OLD.kek_version, OLD.created_at, OLD.created_by,
13
+ OLD.recipe, OLD.pair_id, OLD.pair_half, OLD.sensitive) THEN
14
+ RAISE EXCEPTION 'a secret version is only ever erased' USING ERRCODE = 'insufficient_privilege';
15
+ END IF;
16
+ RETURN NEW;
17
+ END
18
+ $$;
@@ -0,0 +1,5 @@
1
+ -- The Contributor role (0.10): use and write, never read. The role checks widen to it, which the release before never grants.
2
+ ALTER TABLE "vault_grants" DROP CONSTRAINT "vault_grants_role_check";--> statement-breakpoint
3
+ ALTER TABLE "vault_grants" DROP CONSTRAINT "vault_grants_environment_role_check";--> statement-breakpoint
4
+ ALTER TABLE "vault_grants" ADD CONSTRAINT "vault_grants_role_check" CHECK ("vault_grants"."role" IN ('viewer', 'contributor', 'developer', 'maintainer', 'access-manager', 'auditor', 'owner'));--> statement-breakpoint
5
+ ALTER TABLE "vault_grants" ADD CONSTRAINT "vault_grants_environment_role_check" CHECK (("vault_grants"."environment_id" IS NULL AND "vault_grants"."environment_slug" IS NULL) OR "vault_grants"."role" IN ('viewer', 'contributor', 'developer', 'auditor'));
@@ -0,0 +1,4 @@
1
+ -- Roles an Owner makes (0.10): a grant names a role by its id, which the vault's policy defines; which of them an environment may be granted, the vault decides. The checks widen: the release before grants only the roles it ships.
2
+ ALTER TABLE "vault_grants" DROP CONSTRAINT "vault_grants_environment_role_check";--> statement-breakpoint
3
+ ALTER TABLE "vault_grants" DROP CONSTRAINT "vault_grants_role_check";--> statement-breakpoint
4
+ ALTER TABLE "vault_grants" ADD CONSTRAINT "vault_grants_role_check" CHECK ("vault_grants"."role" ~ '^[a-z][a-z0-9-]{1,31}$');