@mstone6969/vault 0.1.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/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # @mstone6969/vault
2
+
3
+ A write-only credential vault. Values go in encrypted; the only way one comes
4
+ back out is `open()` or `resolve()`, so a vault can sit behind an API without a
5
+ reveal endpoint.
6
+
7
+ ```bash
8
+ bun add @mstone6969/vault
9
+ ```
10
+
11
+ ## Use
12
+
13
+ ```ts
14
+ import { generateKey, MemoryStore, Vault } from "@mstone6969/vault"
15
+
16
+ const vault = new Vault({
17
+ key: process.env.VAULT_KEY ?? generateKey(), // 32 bytes, base64
18
+ store: new MemoryStore(),
19
+ })
20
+
21
+ await vault.put("alice", "stripe_key", "sk_live_…")
22
+
23
+ await vault.list("alice")
24
+ // [{ owner: "alice", name: "stripe_key", createdAt: …, updatedAt: … }]
25
+ // — no value, ever
26
+
27
+ await vault.open("alice", "stripe_key") // "sk_live_…"
28
+ ```
29
+
30
+ Everything is scoped by an owner, so one vault serves many accounts and two
31
+ people can both keep a `token` without seeing each other's.
32
+
33
+ ## References
34
+
35
+ Configuration can name a secret instead of holding one. `resolve` swaps
36
+ `@vault:<name>` for the stored value and leaves everything else alone:
37
+
38
+ ```ts
39
+ await vault.resolve("alice", {
40
+ NODE_ENV: "production",
41
+ API_KEY: "@vault:stripe_key",
42
+ })
43
+ // { NODE_ENV: "production", API_KEY: "sk_live_…" }
44
+ ```
45
+
46
+ A reference to a secret that isn't there **throws**. Running a job with a blank
47
+ credential is worse than not running it. Change the prefix with
48
+ `new Vault({ …, prefix: "secret://" })`.
49
+
50
+ ## Storage
51
+
52
+ `MemoryStore` ships in the main entry. `SqliteStore` is Bun-only — it imports
53
+ `bun:sqlite`, so it lives behind a subpath and never loads unless you ask for
54
+ it:
55
+
56
+ ```ts
57
+ import { SqliteStore } from "@mstone6969/vault/stores/sqlite"
58
+
59
+ const store = new SqliteStore("./vault.sqlite") // or ":memory:", or a Database
60
+ ```
61
+
62
+ Everything else runs on Node 18+ as well as Bun.
63
+
64
+ To keep secrets in a database you already run, implement `VaultStore` — four
65
+ methods, all scoped by owner, all dealing in sealed strings and never plaintext:
66
+
67
+ ```ts
68
+ type VaultStore = {
69
+ get(owner: string, name: string): Promise<SecretRecord | null>
70
+ list(owner: string): Promise<SecretRecord[]>
71
+ put(record: { owner: string; name: string; sealed: string }): Promise<SecretRecord>
72
+ remove(owner: string, name: string): Promise<boolean>
73
+ }
74
+ ```
75
+
76
+ ## Encryption
77
+
78
+ AES-256-GCM, a fresh 12-byte IV per write, stored as `iv:payload` in base64.
79
+ GCM's authentication tag means an altered value fails to open rather than
80
+ decrypting to something wrong — both cases are covered by tests.
81
+
82
+ The key never leaves your process, and the package never writes it anywhere.
83
+
84
+ > [!WARNING]
85
+ > Losing the key loses every value stored under it. There is no recovery path,
86
+ > by design. Back it up where you would back up a password.
87
+
88
+ ## API
89
+
90
+ | | |
91
+ | --- | --- |
92
+ | `new Vault({ key, store, prefix? })` | Key is base64 or an imported `CryptoKey` |
93
+ | `put(owner, name, value)` | Store or replace; returns a summary, no value |
94
+ | `list(owner)` | Names and dates, sorted by name |
95
+ | `open(owner, name)` | The plaintext — keep it in memory |
96
+ | `has(owner, name)` | Whether it exists |
97
+ | `remove(owner, name)` | `false` if there was nothing to remove |
98
+ | `resolve(owner, values)` | Substitute `@vault:` references |
99
+ | `generateKey()` | A new base64 key |
100
+
101
+ Names are up to 64 characters of letters, numbers, dot, dash or underscore.
102
+ Bad input throws `VaultError` (with a suggested HTTP `status`); key and
103
+ ciphertext problems throw `VaultKeyError`.
104
+
105
+ ## Development
106
+
107
+ ```bash
108
+ bun test
109
+ bun run typecheck
110
+ ```
@@ -0,0 +1,7 @@
1
+ /** A new random key, base64 encoded — store it somewhere safe. */
2
+ export declare function generateKey(): string;
3
+ /** Imports a base64 key produced by `generateKey`. */
4
+ export declare function importKey(base64Key: string): Promise<CryptoKey>;
5
+ export declare function seal(key: CryptoKey, plaintext: string): Promise<string>;
6
+ export declare function open(key: CryptoKey, sealed: string): Promise<string>;
7
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAUA,kEAAkE;AAClE,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,sDAAsD;AACtD,wBAAsB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAQrE;AAED,wBAAsB,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ7E;AAED,wBAAsB,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkB1E"}
@@ -0,0 +1,13 @@
1
+ /** A caller mistake: a bad name, an empty value, a missing secret. */
2
+ export declare class VaultError extends Error {
3
+ /** Suggested HTTP status, for callers putting this behind an API. */
4
+ readonly status: number;
5
+ constructor(message: string,
6
+ /** Suggested HTTP status, for callers putting this behind an API. */
7
+ status?: number);
8
+ }
9
+ /** The key is the wrong shape, or cannot open what it was given. */
10
+ export declare class VaultKeyError extends VaultError {
11
+ constructor(message: string);
12
+ }
13
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,qBAAa,UAAW,SAAQ,KAAK;IAG7B,qEAAqE;IACrE,QAAQ,CAAC,MAAM,EAAE,MAAM;gBAFvB,OAAO,EAAE,MAAM;IACf,qEAAqE;IAC5D,MAAM,GAAE,MAAY;CAKpC;AAED,oEAAoE;AACpE,qBAAa,aAAc,SAAQ,UAAU;gBAC7B,OAAO,EAAE,MAAM;CAI9B"}
@@ -0,0 +1,6 @@
1
+ export * from "./vault";
2
+ export * from "./crypto";
3
+ export * from "./errors";
4
+ export * from "./stores/memory";
5
+ export * from "./types";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,UAAU,CAAA;AACxB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,SAAS,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,148 @@
1
+ // src/errors.ts
2
+ class VaultError extends Error {
3
+ status;
4
+ constructor(message, status = 422) {
5
+ super(message);
6
+ this.status = status;
7
+ this.name = "VaultError";
8
+ }
9
+ }
10
+
11
+ class VaultKeyError extends VaultError {
12
+ constructor(message) {
13
+ super(message, 500);
14
+ this.name = "VaultKeyError";
15
+ }
16
+ }
17
+
18
+ // src/crypto.ts
19
+ var IV_BYTES = 12;
20
+ var KEY_BYTES = 32;
21
+ function generateKey() {
22
+ return Buffer.from(crypto.getRandomValues(new Uint8Array(KEY_BYTES))).toString("base64");
23
+ }
24
+ async function importKey(base64Key) {
25
+ const raw = Buffer.from(base64Key, "base64");
26
+ if (raw.length !== KEY_BYTES) {
27
+ throw new VaultKeyError(`A vault key must be ${KEY_BYTES} bytes, base64 encoded — got ${raw.length}.`);
28
+ }
29
+ return crypto.subtle.importKey("raw", raw, "AES-GCM", false, ["encrypt", "decrypt"]);
30
+ }
31
+ async function seal(key, plaintext) {
32
+ const iv = crypto.getRandomValues(new Uint8Array(IV_BYTES));
33
+ const sealed = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, new TextEncoder().encode(plaintext));
34
+ return `${Buffer.from(iv).toString("base64")}:${Buffer.from(sealed).toString("base64")}`;
35
+ }
36
+ async function open(key, sealed) {
37
+ const [iv, payload] = sealed.split(":");
38
+ if (!iv || !payload) {
39
+ throw new VaultKeyError("A sealed value must look like iv:payload.");
40
+ }
41
+ try {
42
+ const opened = await crypto.subtle.decrypt({ name: "AES-GCM", iv: Buffer.from(iv, "base64") }, key, Buffer.from(payload, "base64"));
43
+ return new TextDecoder().decode(opened);
44
+ } catch {
45
+ throw new VaultKeyError("That value could not be opened — wrong key, or it has been altered.");
46
+ }
47
+ }
48
+
49
+ // src/vault.ts
50
+ var DEFAULT_PREFIX = "@vault:";
51
+ var NAME_PATTERN = /^[A-Za-z0-9_.-]{1,64}$/;
52
+
53
+ class Vault {
54
+ key;
55
+ store;
56
+ prefix;
57
+ constructor({ key, store, prefix = DEFAULT_PREFIX }) {
58
+ this.key = typeof key === "string" ? importKey(key) : Promise.resolve(key);
59
+ this.store = store;
60
+ this.prefix = prefix;
61
+ }
62
+ async list(owner) {
63
+ const records = await this.store.list(owner);
64
+ return records.map(({ sealed: _sealed, ...summary }) => summary).sort((a, b) => a.name.localeCompare(b.name));
65
+ }
66
+ async put(owner, name, value) {
67
+ const clean = this.checkName(name);
68
+ if (!value)
69
+ throw new VaultError("A secret needs a value.");
70
+ const sealed = await seal(await this.key, value);
71
+ const { sealed: _sealed, ...summary } = await this.store.put({
72
+ owner,
73
+ name: clean,
74
+ sealed
75
+ });
76
+ return summary;
77
+ }
78
+ async has(owner, name) {
79
+ return await this.store.get(owner, this.checkName(name)) !== null;
80
+ }
81
+ remove(owner, name) {
82
+ return this.store.remove(owner, this.checkName(name));
83
+ }
84
+ async open(owner, name) {
85
+ const record = await this.store.get(owner, this.checkName(name));
86
+ if (!record)
87
+ throw new VaultError(`No secret named "${name}" in the vault.`, 404);
88
+ return open(await this.key, record.sealed);
89
+ }
90
+ async resolve(owner, values) {
91
+ const references = Object.entries(values).filter(([, value]) => value.startsWith(this.prefix));
92
+ if (references.length === 0)
93
+ return values;
94
+ const resolved = { ...values };
95
+ for (const [key, value] of references) {
96
+ resolved[key] = await this.open(owner, value.slice(this.prefix.length).trim());
97
+ }
98
+ return resolved;
99
+ }
100
+ checkName(name) {
101
+ const clean = String(name ?? "").trim();
102
+ if (!NAME_PATTERN.test(clean)) {
103
+ throw new VaultError("A name can be up to 64 characters: letters, numbers, dot, dash or underscore.");
104
+ }
105
+ return clean;
106
+ }
107
+ }
108
+ // src/stores/memory.ts
109
+ class MemoryStore {
110
+ records = new Map;
111
+ static key(owner, name) {
112
+ return `${owner} ${name}`;
113
+ }
114
+ async get(owner, name) {
115
+ return this.records.get(MemoryStore.key(owner, name)) ?? null;
116
+ }
117
+ async list(owner) {
118
+ return [...this.records.values()].filter((record) => record.owner === owner);
119
+ }
120
+ async put(record) {
121
+ const key = MemoryStore.key(record.owner, record.name);
122
+ const now = new Date;
123
+ const stored = {
124
+ ...record,
125
+ createdAt: this.records.get(key)?.createdAt ?? now,
126
+ updatedAt: now
127
+ };
128
+ this.records.set(key, stored);
129
+ return stored;
130
+ }
131
+ async remove(owner, name) {
132
+ return this.records.delete(MemoryStore.key(owner, name));
133
+ }
134
+ }
135
+ export {
136
+ seal,
137
+ open,
138
+ importKey,
139
+ generateKey,
140
+ VaultKeyError,
141
+ VaultError,
142
+ Vault,
143
+ MemoryStore,
144
+ DEFAULT_PREFIX
145
+ };
146
+
147
+ //# debugId=593665718555ECE464756E2164756E21
148
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,13 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/errors.ts", "../src/crypto.ts", "../src/vault.ts", "../src/stores/memory.ts"],
4
+ "sourcesContent": [
5
+ "/** A caller mistake: a bad name, an empty value, a missing secret. */\nexport class VaultError extends Error {\n constructor(\n message: string,\n /** Suggested HTTP status, for callers putting this behind an API. */\n readonly status: number = 422\n ) {\n super(message)\n this.name = \"VaultError\"\n }\n}\n\n/** The key is the wrong shape, or cannot open what it was given. */\nexport class VaultKeyError extends VaultError {\n constructor(message: string) {\n super(message, 500)\n this.name = \"VaultKeyError\"\n }\n}\n",
6
+ "import { VaultKeyError } from \"./errors\"\n\n/**\n * AES-256-GCM. Sealed values are `iv:payload`, both base64: the IV is fresh per\n * write, and GCM's tag means a tampered value fails to open rather than\n * decrypting to something wrong.\n */\nconst IV_BYTES = 12\nconst KEY_BYTES = 32\n\n/** A new random key, base64 encoded — store it somewhere safe. */\nexport function generateKey(): string {\n return Buffer.from(crypto.getRandomValues(new Uint8Array(KEY_BYTES))).toString(\"base64\")\n}\n\n/** Imports a base64 key produced by `generateKey`. */\nexport async function importKey(base64Key: string): Promise<CryptoKey> {\n const raw = Buffer.from(base64Key, \"base64\")\n if (raw.length !== KEY_BYTES) {\n throw new VaultKeyError(\n `A vault key must be ${KEY_BYTES} bytes, base64 encoded — got ${raw.length}.`\n )\n }\n return crypto.subtle.importKey(\"raw\", raw, \"AES-GCM\", false, [\"encrypt\", \"decrypt\"])\n}\n\nexport async function seal(key: CryptoKey, plaintext: string): Promise<string> {\n const iv = crypto.getRandomValues(new Uint8Array(IV_BYTES))\n const sealed = await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv },\n key,\n new TextEncoder().encode(plaintext)\n )\n return `${Buffer.from(iv).toString(\"base64\")}:${Buffer.from(sealed).toString(\"base64\")}`\n}\n\nexport async function open(key: CryptoKey, sealed: string): Promise<string> {\n const [iv, payload] = sealed.split(\":\")\n if (!iv || !payload) {\n throw new VaultKeyError(\"A sealed value must look like iv:payload.\")\n }\n\n try {\n const opened = await crypto.subtle.decrypt(\n { name: \"AES-GCM\", iv: Buffer.from(iv, \"base64\") },\n key,\n Buffer.from(payload, \"base64\")\n )\n return new TextDecoder().decode(opened)\n } catch {\n throw new VaultKeyError(\n \"That value could not be opened — wrong key, or it has been altered.\"\n )\n }\n}\n",
7
+ "import { importKey, open, seal } from \"./crypto\"\nimport { VaultError } from \"./errors\"\nimport type { SecretSummary, VaultStore } from \"./types\"\n\n/** How a stored value is referenced from configuration. */\nexport const DEFAULT_PREFIX = \"@vault:\"\n\nconst NAME_PATTERN = /^[A-Za-z0-9_.-]{1,64}$/\n\nexport type VaultOptions = {\n /** Base64 key from `generateKey()`, or an already imported CryptoKey. */\n key: string | CryptoKey\n store: VaultStore\n /** Reference prefix, `@vault:` unless you say otherwise. */\n prefix?: string\n}\n\n/**\n * A write-only credential store: values go in, and only `open` and `resolve`\n * take them out again. Listing never exposes a value, so a vault can back an\n * API without a reveal endpoint.\n */\nexport class Vault {\n private readonly key: Promise<CryptoKey>\n private readonly store: VaultStore\n readonly prefix: string\n\n constructor({ key, store, prefix = DEFAULT_PREFIX }: VaultOptions) {\n this.key = typeof key === \"string\" ? importKey(key) : Promise.resolve(key)\n this.store = store\n this.prefix = prefix\n }\n\n /** Names and dates for one owner. Never values. */\n async list(owner: string): Promise<SecretSummary[]> {\n const records = await this.store.list(owner)\n return records\n .map(({ sealed: _sealed, ...summary }) => summary)\n .sort((a, b) => a.name.localeCompare(b.name))\n }\n\n /** Stores a value, replacing whatever was under that name. */\n async put(owner: string, name: string, value: string): Promise<SecretSummary> {\n const clean = this.checkName(name)\n if (!value) throw new VaultError(\"A secret needs a value.\")\n\n const sealed = await seal(await this.key, value)\n const { sealed: _sealed, ...summary } = await this.store.put({\n owner,\n name: clean,\n sealed,\n })\n return summary\n }\n\n /** True when the owner has a secret under that name. */\n async has(owner: string, name: string): Promise<boolean> {\n return (await this.store.get(owner, this.checkName(name))) !== null\n }\n\n /** Removes a secret, returning false if it wasn't there. */\n remove(owner: string, name: string): Promise<boolean> {\n return this.store.remove(owner, this.checkName(name))\n }\n\n /**\n * Reads one value back. The only way plaintext leaves the vault — keep it\n * in memory and out of logs and responses.\n */\n async open(owner: string, name: string): Promise<string> {\n const record = await this.store.get(owner, this.checkName(name))\n if (!record) throw new VaultError(`No secret named \"${name}\" in the vault.`, 404)\n return open(await this.key, record.sealed)\n }\n\n /**\n * Substitutes `@vault:<name>` references in a set of values — an\n * environment, a config object — leaving everything else alone.\n *\n * A reference to a secret that isn't there throws: running with a blank\n * credential is worse than not running.\n */\n async resolve(\n owner: string,\n values: Record<string, string>\n ): Promise<Record<string, string>> {\n const references = Object.entries(values).filter(([, value]) =>\n value.startsWith(this.prefix)\n )\n if (references.length === 0) return values\n\n const resolved = { ...values }\n for (const [key, value] of references) {\n resolved[key] = await this.open(owner, value.slice(this.prefix.length).trim())\n }\n return resolved\n }\n\n private checkName(name: string): string {\n const clean = String(name ?? \"\").trim()\n if (!NAME_PATTERN.test(clean)) {\n throw new VaultError(\n \"A name can be up to 64 characters: letters, numbers, dot, dash or underscore.\"\n )\n }\n return clean\n }\n}\n",
8
+ "import type { SecretRecord, VaultStore } from \"../types\"\n\n/** Keeps sealed values in a Map. Handy for tests and short-lived processes. */\nexport class MemoryStore implements VaultStore {\n private readonly records = new Map<string, SecretRecord>()\n\n private static key(owner: string, name: string): string {\n return `${owner} ${name}`\n }\n\n async get(owner: string, name: string): Promise<SecretRecord | null> {\n return this.records.get(MemoryStore.key(owner, name)) ?? null\n }\n\n async list(owner: string): Promise<SecretRecord[]> {\n return [...this.records.values()].filter((record) => record.owner === owner)\n }\n\n async put(record: {\n owner: string\n name: string\n sealed: string\n }): Promise<SecretRecord> {\n const key = MemoryStore.key(record.owner, record.name)\n const now = new Date()\n const stored: SecretRecord = {\n ...record,\n // Replacing a value keeps the date it was first stored.\n createdAt: this.records.get(key)?.createdAt ?? now,\n updatedAt: now,\n }\n this.records.set(key, stored)\n return stored\n }\n\n async remove(owner: string, name: string): Promise<boolean> {\n return this.records.delete(MemoryStore.key(owner, name))\n }\n}\n"
9
+ ],
10
+ "mappings": "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",
11
+ "debugId": "593665718555ECE464756E2164756E21",
12
+ "names": []
13
+ }
@@ -0,0 +1,15 @@
1
+ import type { SecretRecord, VaultStore } from "../types";
2
+ /** Keeps sealed values in a Map. Handy for tests and short-lived processes. */
3
+ export declare class MemoryStore implements VaultStore {
4
+ private readonly records;
5
+ private static key;
6
+ get(owner: string, name: string): Promise<SecretRecord | null>;
7
+ list(owner: string): Promise<SecretRecord[]>;
8
+ put(record: {
9
+ owner: string;
10
+ name: string;
11
+ sealed: string;
12
+ }): Promise<SecretRecord>;
13
+ remove(owner: string, name: string): Promise<boolean>;
14
+ }
15
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/stores/memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAExD,+EAA+E;AAC/E,qBAAa,WAAY,YAAW,UAAU;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAE1D,OAAO,CAAC,MAAM,CAAC,GAAG;IAIZ,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAI9D,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAI5C,GAAG,CAAC,MAAM,EAAE;QACd,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,EAAE,MAAM,CAAA;QACZ,MAAM,EAAE,MAAM,CAAA;KACjB,GAAG,OAAO,CAAC,YAAY,CAAC;IAanB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAG9D"}
@@ -0,0 +1 @@
1
+ export { MemoryStore } from "../index.js";
@@ -0,0 +1,23 @@
1
+ import { Database } from "bun:sqlite";
2
+ import type { SecretRecord, VaultStore } from "../types";
3
+ /**
4
+ * Keeps sealed values in SQLite through `bun:sqlite`. Pass a file path,
5
+ * ":memory:", or a Database you already opened.
6
+ */
7
+ export declare class SqliteStore implements VaultStore {
8
+ private readonly db;
9
+ private readonly table;
10
+ constructor(database?: string | Database, table?: string);
11
+ private static toRecord;
12
+ get(owner: string, name: string): Promise<SecretRecord | null>;
13
+ list(owner: string): Promise<SecretRecord[]>;
14
+ put(record: {
15
+ owner: string;
16
+ name: string;
17
+ sealed: string;
18
+ }): Promise<SecretRecord>;
19
+ remove(owner: string, name: string): Promise<boolean>;
20
+ /** Closes the underlying database. */
21
+ close(): void;
22
+ }
23
+ //# sourceMappingURL=sqlite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/stores/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAUxD;;;GAGG;AACH,qBAAa,WAAY,YAAW,UAAU;IAC1C,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAU;IAC7B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;gBAElB,QAAQ,GAAE,MAAM,GAAG,QAAqB,EAAE,KAAK,SAAkB;IAe7E,OAAO,CAAC,MAAM,CAAC,QAAQ;IAUjB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAS9D,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAO5C,GAAG,CAAC,MAAM,EAAE;QACd,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,EAAE,MAAM,CAAA;QACZ,MAAM,EAAE,MAAM,CAAA;KACjB,GAAG,OAAO,CAAC,YAAY,CAAC;IAiBnB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAS3D,sCAAsC;IACtC,KAAK,IAAI,IAAI;CAGhB"}
@@ -0,0 +1,63 @@
1
+ // @bun
2
+ // src/stores/sqlite.ts
3
+ import { Database } from "bun:sqlite";
4
+
5
+ class SqliteStore {
6
+ db;
7
+ table;
8
+ constructor(database = ":memory:", table = "vault_secrets") {
9
+ this.db = typeof database === "string" ? new Database(database) : database;
10
+ this.table = table;
11
+ this.db.run(`CREATE TABLE IF NOT EXISTS ${this.table} (
12
+ owner TEXT NOT NULL,
13
+ name TEXT NOT NULL,
14
+ sealed TEXT NOT NULL,
15
+ created_at INTEGER NOT NULL,
16
+ updated_at INTEGER NOT NULL,
17
+ PRIMARY KEY (owner, name)
18
+ )`);
19
+ }
20
+ static toRecord(row) {
21
+ return {
22
+ owner: row.owner,
23
+ name: row.name,
24
+ sealed: row.sealed,
25
+ createdAt: new Date(row.created_at),
26
+ updatedAt: new Date(row.updated_at)
27
+ };
28
+ }
29
+ async get(owner, name) {
30
+ const row = this.db.query(`SELECT * FROM ${this.table} WHERE owner = ? AND name = ?`).get(owner, name);
31
+ return row ? SqliteStore.toRecord(row) : null;
32
+ }
33
+ async list(owner) {
34
+ return this.db.query(`SELECT * FROM ${this.table} WHERE owner = ?`).all(owner).map(SqliteStore.toRecord);
35
+ }
36
+ async put(record) {
37
+ const now = Date.now();
38
+ this.db.query(`INSERT INTO ${this.table} (owner, name, sealed, created_at, updated_at)
39
+ VALUES (?, ?, ?, ?, ?)
40
+ ON CONFLICT(owner, name)
41
+ DO UPDATE SET sealed = excluded.sealed, updated_at = excluded.updated_at`).run(record.owner, record.name, record.sealed, now, now);
42
+ const stored = await this.get(record.owner, record.name);
43
+ if (!stored)
44
+ throw new Error("The secret vanished immediately after writing it.");
45
+ return stored;
46
+ }
47
+ async remove(owner, name) {
48
+ const existing = await this.get(owner, name);
49
+ if (!existing)
50
+ return false;
51
+ this.db.query(`DELETE FROM ${this.table} WHERE owner = ? AND name = ?`).run(owner, name);
52
+ return true;
53
+ }
54
+ close() {
55
+ this.db.close();
56
+ }
57
+ }
58
+ export {
59
+ SqliteStore
60
+ };
61
+
62
+ //# debugId=E8AE1F33B2C0F61864756E2164756E21
63
+ //# sourceMappingURL=sqlite.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/stores/sqlite.ts"],
4
+ "sourcesContent": [
5
+ "import { Database } from \"bun:sqlite\"\nimport type { SecretRecord, VaultStore } from \"../types\"\n\ntype Row = {\n owner: string\n name: string\n sealed: string\n created_at: number\n updated_at: number\n}\n\n/**\n * Keeps sealed values in SQLite through `bun:sqlite`. Pass a file path,\n * \":memory:\", or a Database you already opened.\n */\nexport class SqliteStore implements VaultStore {\n private readonly db: Database\n private readonly table: string\n\n constructor(database: string | Database = \":memory:\", table = \"vault_secrets\") {\n this.db = typeof database === \"string\" ? new Database(database) : database\n this.table = table\n this.db.run(\n `CREATE TABLE IF NOT EXISTS ${this.table} (\n owner TEXT NOT NULL,\n name TEXT NOT NULL,\n sealed TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n PRIMARY KEY (owner, name)\n )`\n )\n }\n\n private static toRecord(row: Row): SecretRecord {\n return {\n owner: row.owner,\n name: row.name,\n sealed: row.sealed,\n createdAt: new Date(row.created_at),\n updatedAt: new Date(row.updated_at),\n }\n }\n\n async get(owner: string, name: string): Promise<SecretRecord | null> {\n const row = this.db\n .query<Row, [string, string]>(\n `SELECT * FROM ${this.table} WHERE owner = ? AND name = ?`\n )\n .get(owner, name)\n return row ? SqliteStore.toRecord(row) : null\n }\n\n async list(owner: string): Promise<SecretRecord[]> {\n return this.db\n .query<Row, [string]>(`SELECT * FROM ${this.table} WHERE owner = ?`)\n .all(owner)\n .map(SqliteStore.toRecord)\n }\n\n async put(record: {\n owner: string\n name: string\n sealed: string\n }): Promise<SecretRecord> {\n const now = Date.now()\n // Replacing a value keeps the row's original created_at.\n this.db\n .query(\n `INSERT INTO ${this.table} (owner, name, sealed, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(owner, name)\n DO UPDATE SET sealed = excluded.sealed, updated_at = excluded.updated_at`\n )\n .run(record.owner, record.name, record.sealed, now, now)\n\n const stored = await this.get(record.owner, record.name)\n if (!stored) throw new Error(\"The secret vanished immediately after writing it.\")\n return stored\n }\n\n async remove(owner: string, name: string): Promise<boolean> {\n const existing = await this.get(owner, name)\n if (!existing) return false\n this.db\n .query(`DELETE FROM ${this.table} WHERE owner = ? AND name = ?`)\n .run(owner, name)\n return true\n }\n\n /** Closes the underlying database. */\n close(): void {\n this.db.close()\n }\n}\n"
6
+ ],
7
+ "mappings": ";;AAAA;AAAA;AAeO,MAAM,YAAkC;AAAA,EAC1B;AAAA,EACA;AAAA,EAEjB,WAAW,CAAC,WAA8B,YAAY,QAAQ,iBAAiB;AAAA,IAC3E,KAAK,KAAK,OAAO,aAAa,WAAW,IAAI,SAAS,QAAQ,IAAI;AAAA,IAClE,KAAK,QAAQ;AAAA,IACb,KAAK,GAAG,IACJ,8BAA8B,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAQvC;AAAA;AAAA,SAGW,QAAQ,CAAC,KAAwB;AAAA,IAC5C,OAAO;AAAA,MACH,OAAO,IAAI;AAAA,MACX,MAAM,IAAI;AAAA,MACV,QAAQ,IAAI;AAAA,MACZ,WAAW,IAAI,KAAK,IAAI,UAAU;AAAA,MAClC,WAAW,IAAI,KAAK,IAAI,UAAU;AAAA,IACtC;AAAA;AAAA,OAGE,IAAG,CAAC,OAAe,MAA4C;AAAA,IACjE,MAAM,MAAM,KAAK,GACZ,MACG,iBAAiB,KAAK,oCAC1B,EACC,IAAI,OAAO,IAAI;AAAA,IACpB,OAAO,MAAM,YAAY,SAAS,GAAG,IAAI;AAAA;AAAA,OAGvC,KAAI,CAAC,OAAwC;AAAA,IAC/C,OAAO,KAAK,GACP,MAAqB,iBAAiB,KAAK,uBAAuB,EAClE,IAAI,KAAK,EACT,IAAI,YAAY,QAAQ;AAAA;AAAA,OAG3B,IAAG,CAAC,QAIgB;AAAA,IACtB,MAAM,MAAM,KAAK,IAAI;AAAA,IAErB,KAAK,GACA,MACG,eAAe,KAAK;AAAA;AAAA;AAAA,0FAIxB,EACC,IAAI,OAAO,OAAO,OAAO,MAAM,OAAO,QAAQ,KAAK,GAAG;AAAA,IAE3D,MAAM,SAAS,MAAM,KAAK,IAAI,OAAO,OAAO,OAAO,IAAI;AAAA,IACvD,IAAI,CAAC;AAAA,MAAQ,MAAM,IAAI,MAAM,mDAAmD;AAAA,IAChF,OAAO;AAAA;AAAA,OAGL,OAAM,CAAC,OAAe,MAAgC;AAAA,IACxD,MAAM,WAAW,MAAM,KAAK,IAAI,OAAO,IAAI;AAAA,IAC3C,IAAI,CAAC;AAAA,MAAU,OAAO;AAAA,IACtB,KAAK,GACA,MAAM,eAAe,KAAK,oCAAoC,EAC9D,IAAI,OAAO,IAAI;AAAA,IACpB,OAAO;AAAA;AAAA,EAIX,KAAK,GAAS;AAAA,IACV,KAAK,GAAG,MAAM;AAAA;AAEtB;",
8
+ "debugId": "E8AE1F33B2C0F61864756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,29 @@
1
+ /** A stored secret, as the store keeps it. */
2
+ export type SecretRecord = {
3
+ owner: string;
4
+ name: string;
5
+ /** Sealed value, `iv:payload`. Never the plaintext. */
6
+ sealed: string;
7
+ createdAt: Date;
8
+ updatedAt: Date;
9
+ };
10
+ /** A stored secret, as callers are allowed to see it: no value. */
11
+ export type SecretSummary = Omit<SecretRecord, "sealed">;
12
+ /**
13
+ * Where sealed values live. Implement this to keep secrets in whatever database
14
+ * you already run; `MemoryStore` and `SqliteStore` ship with the package.
15
+ *
16
+ * Every method is scoped by `owner`, so one store serves many accounts.
17
+ */
18
+ export type VaultStore = {
19
+ get(owner: string, name: string): Promise<SecretRecord | null>;
20
+ list(owner: string): Promise<SecretRecord[]>;
21
+ /** Insert or replace, returning what was stored. */
22
+ put(record: {
23
+ owner: string;
24
+ name: string;
25
+ sealed: string;
26
+ }): Promise<SecretRecord>;
27
+ remove(owner: string, name: string): Promise<boolean>;
28
+ };
29
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,MAAM,MAAM,YAAY,GAAG;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,IAAI,CAAA;IACf,SAAS,EAAE,IAAI,CAAA;CAClB,CAAA;AAED,mEAAmE;AACnE,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAA;AAExD;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG;IACrB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAA;IAC9D,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAA;IAC5C,oDAAoD;IACpD,GAAG,CAAC,MAAM,EAAE;QACR,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,EAAE,MAAM,CAAA;QACZ,MAAM,EAAE,MAAM,CAAA;KACjB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IACzB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACxD,CAAA"}
@@ -0,0 +1,44 @@
1
+ import type { SecretSummary, VaultStore } from "./types";
2
+ /** How a stored value is referenced from configuration. */
3
+ export declare const DEFAULT_PREFIX = "@vault:";
4
+ export type VaultOptions = {
5
+ /** Base64 key from `generateKey()`, or an already imported CryptoKey. */
6
+ key: string | CryptoKey;
7
+ store: VaultStore;
8
+ /** Reference prefix, `@vault:` unless you say otherwise. */
9
+ prefix?: string;
10
+ };
11
+ /**
12
+ * A write-only credential store: values go in, and only `open` and `resolve`
13
+ * take them out again. Listing never exposes a value, so a vault can back an
14
+ * API without a reveal endpoint.
15
+ */
16
+ export declare class Vault {
17
+ private readonly key;
18
+ private readonly store;
19
+ readonly prefix: string;
20
+ constructor({ key, store, prefix }: VaultOptions);
21
+ /** Names and dates for one owner. Never values. */
22
+ list(owner: string): Promise<SecretSummary[]>;
23
+ /** Stores a value, replacing whatever was under that name. */
24
+ put(owner: string, name: string, value: string): Promise<SecretSummary>;
25
+ /** True when the owner has a secret under that name. */
26
+ has(owner: string, name: string): Promise<boolean>;
27
+ /** Removes a secret, returning false if it wasn't there. */
28
+ remove(owner: string, name: string): Promise<boolean>;
29
+ /**
30
+ * Reads one value back. The only way plaintext leaves the vault — keep it
31
+ * in memory and out of logs and responses.
32
+ */
33
+ open(owner: string, name: string): Promise<string>;
34
+ /**
35
+ * Substitutes `@vault:<name>` references in a set of values — an
36
+ * environment, a config object — leaving everything else alone.
37
+ *
38
+ * A reference to a secret that isn't there throws: running with a blank
39
+ * credential is worse than not running.
40
+ */
41
+ resolve(owner: string, values: Record<string, string>): Promise<Record<string, string>>;
42
+ private checkName;
43
+ }
44
+ //# sourceMappingURL=vault.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../src/vault.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAExD,2DAA2D;AAC3D,eAAO,MAAM,cAAc,YAAY,CAAA;AAIvC,MAAM,MAAM,YAAY,GAAG;IACvB,yEAAyE;IACzE,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;IACvB,KAAK,EAAE,UAAU,CAAA;IACjB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA;AAED;;;;GAIG;AACH,qBAAa,KAAK;IACd,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAY;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;gBAEX,EAAE,GAAG,EAAE,KAAK,EAAE,MAAuB,EAAE,EAAE,YAAY;IAMjE,mDAAmD;IAC7C,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAOnD,8DAA8D;IACxD,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAa7E,wDAAwD;IAClD,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIxD,4DAA4D;IAC5D,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrD;;;OAGG;IACG,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAMxD;;;;;;OAMG;IACG,OAAO,CACT,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAalC,OAAO,CAAC,SAAS;CASpB"}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@mstone6969/vault",
3
+ "version": "0.1.0",
4
+ "description": "A write-only credential vault: AES-256-GCM at rest, per-owner scoping, and @vault: references resolved at use.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Malcolmston",
8
+ "keywords": [
9
+ "vault",
10
+ "secrets",
11
+ "credentials",
12
+ "encryption",
13
+ "aes-gcm",
14
+ "bun"
15
+ ],
16
+ "main": "./dist/index.js",
17
+ "module": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/index.d.ts",
22
+ "import": "./dist/index.js"
23
+ },
24
+ "./stores/memory": {
25
+ "types": "./dist/stores/memory.d.ts",
26
+ "import": "./dist/stores/memory.js"
27
+ },
28
+ "./stores/sqlite": {
29
+ "types": "./dist/stores/sqlite.d.ts",
30
+ "import": "./dist/stores/sqlite.js"
31
+ },
32
+ "./package.json": "./package.json"
33
+ },
34
+ "files": [
35
+ "dist",
36
+ "README.md"
37
+ ],
38
+ "sideEffects": false,
39
+ "engines": {
40
+ "node": ">=18"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public"
44
+ },
45
+ "scripts": {
46
+ "build": "bun run build.ts",
47
+ "test": "bun test",
48
+ "typecheck": "tsc --noEmit",
49
+ "prepublishOnly": "bun run typecheck && bun test && bun run build"
50
+ },
51
+ "devDependencies": {
52
+ "@types/bun": "latest"
53
+ },
54
+ "peerDependencies": {
55
+ "typescript": "^5"
56
+ },
57
+ "peerDependenciesMeta": {
58
+ "typescript": {
59
+ "optional": true
60
+ }
61
+ }
62
+ }