@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
package/dist/connect.d.ts
CHANGED
package/dist/dialect.d.ts
CHANGED
package/dist/grants.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as Queryable } from "./database-
|
|
1
|
+
import { i as Queryable } from "./database-BEnexh-3.js";
|
|
2
2
|
import { Authenticity, Newest } from "./log.js";
|
|
3
3
|
import { Conversion, ConversionInput, InstanceRole, Scope } from "@coffre/core/access";
|
|
4
4
|
import { StoredEntry } from "@coffre/core/audit";
|
package/dist/hyperdrive.d.ts
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { m as schema_d_exports } from "./schema-
|
|
2
|
-
import { a as Transaction, c as tablesOf, i as Queryable, n as PoolLike, o as createDatabase, r as QueryOutsideTransaction, s as own, t as Database } from "./database-
|
|
1
|
+
import { m as schema_d_exports } from "./schema-BZRkBJQZ.js";
|
|
2
|
+
import { a as Transaction, c as tablesOf, i as Queryable, n as PoolLike, o as createDatabase, r as QueryOutsideTransaction, s as own, t as Database } from "./database-BEnexh-3.js";
|
|
3
3
|
export { Database, PoolLike, QueryOutsideTransaction, Queryable, Transaction, createDatabase, own, schema_d_exports as schema, tablesOf };
|
package/dist/log.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { a as Transaction, i as Queryable } from "./database-
|
|
1
|
+
import { a as Transaction, i as Queryable } from "./database-BEnexh-3.js";
|
|
2
|
+
import { SQL } from "drizzle-orm";
|
|
2
3
|
import { LogKey, StoredEntry } from "@coffre/core/audit";
|
|
3
4
|
//#region src/log.d.ts
|
|
4
5
|
/**
|
|
@@ -441,6 +442,26 @@ export declare function entryColumns(db: Queryable): {
|
|
|
441
442
|
pgColumnBuilderBrand: "PgCustomColumnBuilderBrand";
|
|
442
443
|
}>;
|
|
443
444
|
};
|
|
445
|
+
/**
|
|
446
|
+
* Metadata a reader of many entries holds at once, at most: an entry about
|
|
447
|
+
* a read of several secrets lists them, a thousand some 150 kilobytes, so a
|
|
448
|
+
* count of entries alone does not bound a batch's or a page's memory within
|
|
449
|
+
* a Worker's 128 megabytes.
|
|
450
|
+
*/
|
|
451
|
+
export declare const BATCH_BYTES: number;
|
|
452
|
+
/**
|
|
453
|
+
* Of the entries `where` matches, in `order` of seq, the first `limit` or
|
|
454
|
+
* fewer that hold no more than `BATCH_BYTES` of metadata between them, but
|
|
455
|
+
* always the first: the seqs they run from and to, and whether the bytes
|
|
456
|
+
* cut the run short of `limit`. Null when none matches. A reader asks this
|
|
457
|
+
* first, then reads `where` between the two.
|
|
458
|
+
*/
|
|
459
|
+
export declare function withinBytes(db: Queryable, where: SQL | undefined, limit: number, order?: 'asc' | 'desc'): Promise<{
|
|
460
|
+
from: bigint;
|
|
461
|
+
to: bigint;
|
|
462
|
+
cut: boolean;
|
|
463
|
+
count: number;
|
|
464
|
+
} | null>;
|
|
444
465
|
/**
|
|
445
466
|
* How a reader tells the vault's own entries from those forged in its name:
|
|
446
467
|
* by their MAC, which only the vault holds the key to check. Each it finds
|
package/dist/log.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as afterCommit } from "./commits-BuW-NL3n.js";
|
|
2
2
|
import { tablesOf } from "./index.js";
|
|
3
3
|
import { clockMillis, forUpdate } from "./dialect.js";
|
|
4
|
-
import { and, desc, eq, lt } from "drizzle-orm";
|
|
4
|
+
import { and, asc, desc, eq, lt, sql } from "drizzle-orm";
|
|
5
5
|
import { GENESIS_HASH, sealEntry } from "@coffre/core/audit";
|
|
6
6
|
//#region src/log.ts
|
|
7
7
|
/** The log's head does not name its last entry: something moved it, or cut the log behind it. */
|
|
@@ -163,6 +163,41 @@ function entryColumns(db) {
|
|
|
163
163
|
hash: auditLog.hash
|
|
164
164
|
};
|
|
165
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* Metadata a reader of many entries holds at once, at most: an entry about
|
|
168
|
+
* a read of several secrets lists them, a thousand some 150 kilobytes, so a
|
|
169
|
+
* count of entries alone does not bound a batch's or a page's memory within
|
|
170
|
+
* a Worker's 128 megabytes.
|
|
171
|
+
*/
|
|
172
|
+
const BATCH_BYTES = 8388608;
|
|
173
|
+
/**
|
|
174
|
+
* Of the entries `where` matches, in `order` of seq, the first `limit` or
|
|
175
|
+
* fewer that hold no more than `BATCH_BYTES` of metadata between them, but
|
|
176
|
+
* always the first: the seqs they run from and to, and whether the bytes
|
|
177
|
+
* cut the run short of `limit`. Null when none matches. A reader asks this
|
|
178
|
+
* first, then reads `where` between the two.
|
|
179
|
+
*/
|
|
180
|
+
async function withinBytes(db, where, limit, order = "asc") {
|
|
181
|
+
const { auditLog } = tablesOf(db);
|
|
182
|
+
const sizes = await db.select({
|
|
183
|
+
seq: auditLog.seq,
|
|
184
|
+
bytes: sql`length(${auditLog.metadata})`
|
|
185
|
+
}).from(auditLog).where(where).orderBy(order === "asc" ? asc(auditLog.seq) : desc(auditLog.seq)).limit(limit);
|
|
186
|
+
let held = 0;
|
|
187
|
+
let count = 0;
|
|
188
|
+
for (const { bytes } of sizes) {
|
|
189
|
+
held += Number(bytes);
|
|
190
|
+
if (count > 0 && held > 8388608) break;
|
|
191
|
+
count += 1;
|
|
192
|
+
}
|
|
193
|
+
if (count === 0) return null;
|
|
194
|
+
return {
|
|
195
|
+
from: sizes[0].seq,
|
|
196
|
+
to: sizes[count - 1].seq,
|
|
197
|
+
cut: count < sizes.length,
|
|
198
|
+
count
|
|
199
|
+
};
|
|
200
|
+
}
|
|
166
201
|
/** Up to `limit` of the entries in the vault's name, allowed, of one `action`, before `before` (the newest when undefined), newest first. */
|
|
167
202
|
async function newestVaultEntries(db, action, limit, before) {
|
|
168
203
|
const { auditLog } = tablesOf(db);
|
|
@@ -194,4 +229,4 @@ async function newestAuthentic(page, check) {
|
|
|
194
229
|
return "unreached";
|
|
195
230
|
}
|
|
196
231
|
//#endregion
|
|
197
|
-
export { LOOK_BACK, LogHeadMismatch, LogRewound, appendEntries, entryColumns, forgetLogHeads, lockLogHead, newestAuthentic, newestVaultEntries };
|
|
232
|
+
export { BATCH_BYTES, LOOK_BACK, LogHeadMismatch, LogRewound, appendEntries, entryColumns, forgetLogHeads, lockLogHead, newestAuthentic, newestVaultEntries, withinBytes };
|
|
@@ -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
|
+
$$;
|