@noy-db/at-env 0.2.0-pre.30 → 0.2.0-pre.31
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/package.json +8 -15
- package/dist/index.cjs +0 -106
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -72
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noy-db/at-env",
|
|
3
|
-
"version": "0.2.0-pre.
|
|
3
|
+
"version": "0.2.0-pre.31",
|
|
4
4
|
"description": "Env-var sealing key provider for noy-db managed-passphrase mode — AES-256-GCM under a key from process.env. Smallest production-shape provider; pair with Kubernetes Secrets / Heroku env / AWS Secrets Manager.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "vLannaAi <vicio@lanna.ai>",
|
|
@@ -17,17 +17,10 @@
|
|
|
17
17
|
"sideEffects": false,
|
|
18
18
|
"exports": {
|
|
19
19
|
".": {
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
"default": "./dist/index.js"
|
|
23
|
-
},
|
|
24
|
-
"require": {
|
|
25
|
-
"types": "./dist/index.d.cts",
|
|
26
|
-
"default": "./dist/index.cjs"
|
|
27
|
-
}
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"default": "./dist/index.js"
|
|
28
22
|
}
|
|
29
23
|
},
|
|
30
|
-
"main": "./dist/index.cjs",
|
|
31
24
|
"module": "./dist/index.js",
|
|
32
25
|
"types": "./dist/index.d.ts",
|
|
33
26
|
"files": [
|
|
@@ -36,16 +29,16 @@
|
|
|
36
29
|
"LICENSE"
|
|
37
30
|
],
|
|
38
31
|
"engines": {
|
|
39
|
-
"node": ">=
|
|
32
|
+
"node": ">=22.0.0"
|
|
40
33
|
},
|
|
41
34
|
"peerDependencies": {
|
|
42
|
-
"@noy-db/hub": "0.2.0-pre.
|
|
35
|
+
"@noy-db/hub": "0.2.0-pre.31"
|
|
43
36
|
},
|
|
44
37
|
"devDependencies": {
|
|
45
38
|
"@types/node": "^22.0.0",
|
|
46
|
-
"@noy-db/
|
|
47
|
-
"@noy-db/to-memory": "0.2.0-pre.
|
|
48
|
-
"@noy-db/
|
|
39
|
+
"@noy-db/on-shamir": "0.2.0-pre.31",
|
|
40
|
+
"@noy-db/to-memory": "0.2.0-pre.31",
|
|
41
|
+
"@noy-db/hub": "0.2.0-pre.31"
|
|
49
42
|
},
|
|
50
43
|
"keywords": [
|
|
51
44
|
"noy-db",
|
package/dist/index.cjs
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/index.ts
|
|
21
|
-
var index_exports = {};
|
|
22
|
-
__export(index_exports, {
|
|
23
|
-
envSealingProvider: () => envSealingProvider
|
|
24
|
-
});
|
|
25
|
-
module.exports = __toCommonJS(index_exports);
|
|
26
|
-
function envSealingProvider(opts = {}) {
|
|
27
|
-
const envVar = opts.envVar ?? "NOYDB_SEALING_KEY";
|
|
28
|
-
const raw = process.env[envVar];
|
|
29
|
-
if (!raw) {
|
|
30
|
-
throw new Error(
|
|
31
|
-
`@noy-db/at-env: env var "${envVar}" is not set. Generate a key via \`openssl rand -base64 32\` and export it before starting the process.`
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
let keyBytes;
|
|
35
|
-
try {
|
|
36
|
-
keyBytes = base64ToBytes(raw);
|
|
37
|
-
} catch (err) {
|
|
38
|
-
throw new Error(
|
|
39
|
-
`@noy-db/at-env: env var "${envVar}" is not valid base64 \u2014 ` + (err instanceof Error ? err.message : String(err))
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
if (keyBytes.length !== 32) {
|
|
43
|
-
throw new Error(
|
|
44
|
-
`@noy-db/at-env: env var "${envVar}" decodes to ${keyBytes.length} bytes; expected 32 bytes (256-bit AES key). Regenerate via \`openssl rand -base64 32\`.`
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
let cachedKey = null;
|
|
48
|
-
const getKey = () => {
|
|
49
|
-
if (!cachedKey) {
|
|
50
|
-
cachedKey = globalThis.crypto.subtle.importKey(
|
|
51
|
-
"raw",
|
|
52
|
-
keyBytes,
|
|
53
|
-
"AES-GCM",
|
|
54
|
-
false,
|
|
55
|
-
["encrypt", "decrypt"]
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
return cachedKey;
|
|
59
|
-
};
|
|
60
|
-
return {
|
|
61
|
-
id: `env:${envVar}`,
|
|
62
|
-
async seal(passphrase) {
|
|
63
|
-
const key = await getKey();
|
|
64
|
-
const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));
|
|
65
|
-
const ciphertext = await globalThis.crypto.subtle.encrypt(
|
|
66
|
-
{ name: "AES-GCM", iv },
|
|
67
|
-
key,
|
|
68
|
-
passphrase
|
|
69
|
-
);
|
|
70
|
-
const out = new Uint8Array(12 + ciphertext.byteLength);
|
|
71
|
-
out.set(iv, 0);
|
|
72
|
-
out.set(new Uint8Array(ciphertext), 12);
|
|
73
|
-
return out;
|
|
74
|
-
},
|
|
75
|
-
async unseal(sealed) {
|
|
76
|
-
if (sealed.length < 12 + 16) {
|
|
77
|
-
throw new Error(
|
|
78
|
-
`@noy-db/at-env: sealed bytes too short (${sealed.length} < 28). Input is not a valid at-env-sealed envelope.`
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
const iv = sealed.subarray(0, 12);
|
|
82
|
-
const body = sealed.subarray(12);
|
|
83
|
-
const key = await getKey();
|
|
84
|
-
const plaintext = await globalThis.crypto.subtle.decrypt(
|
|
85
|
-
{ name: "AES-GCM", iv },
|
|
86
|
-
key,
|
|
87
|
-
body
|
|
88
|
-
);
|
|
89
|
-
return new Uint8Array(plaintext);
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
function base64ToBytes(b64) {
|
|
94
|
-
if (!/^[A-Za-z0-9+/]+={0,2}$/.test(b64.trim())) {
|
|
95
|
-
throw new Error("input contains characters outside the base64 alphabet");
|
|
96
|
-
}
|
|
97
|
-
const binary = atob(b64);
|
|
98
|
-
const out = new Uint8Array(binary.length);
|
|
99
|
-
for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
|
|
100
|
-
return out;
|
|
101
|
-
}
|
|
102
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
103
|
-
0 && (module.exports = {
|
|
104
|
-
envSealingProvider
|
|
105
|
-
});
|
|
106
|
-
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * **@noy-db/at-env** — env-var sealing key provider for noy-db\n * managed-passphrase mode.\n *\n * The smallest production-shape provider in the `at-*` family. Reads a\n * 32-byte AES-256-GCM key from an environment variable (base64-encoded)\n * and uses it to seal / unseal the hub-generated random passphrase.\n *\n * ## When to use\n *\n * - Single-node SaaS where the env var comes from Kubernetes Secrets,\n * Heroku env, Doppler, AWS Secrets Manager (mounted at boot), systemd\n * service env, etc. — anywhere your platform already manages secrets.\n * - Local dev / CI where you want persistence across process restarts\n * (which `MemorySealingKeyProvider` from `@noy-db/hub` can't do).\n * - Prototypes where setting up AWS KMS / GCP KMS isn't worth the effort.\n *\n * ## When NOT to use\n *\n * - Laptops / shared dev machines where other users have shell access.\n * They can `echo $NOYDB_SEALING_KEY` and exfiltrate the key. Use\n * `@noy-db/at-macos-keychain` / `@noy-db/at-wincred` /\n * `@noy-db/at-libsecret` for desktop apps.\n * - Compliance regimes requiring auditable key access (FedRAMP, HIPAA\n * with managed-encryption requirements). Use `@noy-db/at-aws-kms` for\n * KMS-backed key access logs.\n *\n * ## Setup\n *\n * ```bash\n * # 1. Generate a 256-bit key once. Store in your platform's secret manager.\n * export NOYDB_SEALING_KEY=$(openssl rand -base64 32)\n * ```\n *\n * ```ts\n * // 2. In your app:\n * import { createNoydb } from '@noy-db/hub'\n * import { envSealingProvider } from '@noy-db/at-env'\n *\n * const db = await createNoydb({\n * store,\n * user: 'alice',\n * passphraseMode: 'managed',\n * sealingKey: envSealingProvider(), // reads NOYDB_SEALING_KEY by default\n * })\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { SealingKeyProvider } from '@noy-db/hub'\n\n/** Options for {@link envSealingProvider}. */\nexport interface EnvSealingProviderOptions {\n /**\n * Environment variable name holding the base64-encoded 32-byte AES-256\n * key. Default `'NOYDB_SEALING_KEY'`.\n */\n readonly envVar?: string\n}\n\n/**\n * Build a {@link SealingKeyProvider} backed by an environment variable.\n *\n * The env var must contain a base64-encoded 32-byte (256-bit) key. The\n * provider validates the value at construction time and caches the\n * imported `CryptoKey` for the lifetime of the instance.\n *\n * @throws Error when the env var is unset, not base64, or not 32 bytes.\n */\nexport function envSealingProvider(\n opts: EnvSealingProviderOptions = {},\n): SealingKeyProvider {\n const envVar = opts.envVar ?? 'NOYDB_SEALING_KEY'\n const raw = process.env[envVar]\n if (!raw) {\n throw new Error(\n `@noy-db/at-env: env var \"${envVar}\" is not set. Generate a key via `\n + '`openssl rand -base64 32` and export it before starting the process.',\n )\n }\n\n let keyBytes: Uint8Array\n try {\n keyBytes = base64ToBytes(raw)\n } catch (err) {\n throw new Error(\n `@noy-db/at-env: env var \"${envVar}\" is not valid base64 — `\n + (err instanceof Error ? err.message : String(err)),\n )\n }\n\n if (keyBytes.length !== 32) {\n throw new Error(\n `@noy-db/at-env: env var \"${envVar}\" decodes to ${keyBytes.length} bytes; `\n + 'expected 32 bytes (256-bit AES key). Regenerate via `openssl rand -base64 32`.',\n )\n }\n\n // Lazy + cached import — the CryptoKey is created on first seal/unseal\n // and reused for the lifetime of the provider instance.\n let cachedKey: Promise<CryptoKey> | null = null\n const getKey = (): Promise<CryptoKey> => {\n if (!cachedKey) {\n cachedKey = globalThis.crypto.subtle.importKey(\n 'raw',\n keyBytes as unknown as BufferSource,\n 'AES-GCM',\n false,\n ['encrypt', 'decrypt'],\n )\n }\n return cachedKey\n }\n\n return {\n id: `env:${envVar}`,\n\n async seal(passphrase: Uint8Array): Promise<Uint8Array> {\n const key = await getKey()\n const iv = globalThis.crypto.getRandomValues(new Uint8Array(12))\n const ciphertext = await globalThis.crypto.subtle.encrypt(\n { name: 'AES-GCM', iv: iv as BufferSource },\n key,\n passphrase as unknown as BufferSource,\n )\n // Output format: [12-byte IV][ciphertext + 16-byte GCM tag]\n const out = new Uint8Array(12 + ciphertext.byteLength)\n out.set(iv, 0)\n out.set(new Uint8Array(ciphertext), 12)\n return out\n },\n\n async unseal(sealed: Uint8Array): Promise<Uint8Array> {\n // 12-byte IV + ≥ 16-byte GCM tag minimum.\n if (sealed.length < 12 + 16) {\n throw new Error(\n `@noy-db/at-env: sealed bytes too short (${sealed.length} < 28). `\n + 'Input is not a valid at-env-sealed envelope.',\n )\n }\n const iv = sealed.subarray(0, 12)\n const body = sealed.subarray(12)\n const key = await getKey()\n const plaintext = await globalThis.crypto.subtle.decrypt(\n { name: 'AES-GCM', iv: iv as BufferSource },\n key,\n body as unknown as BufferSource,\n )\n return new Uint8Array(plaintext)\n },\n }\n}\n\n// ─── base64 helpers (browser + node + cf-workers compatible) ──────────\n\nfunction base64ToBytes(b64: string): Uint8Array {\n // Reject obviously malformed strings before atob throws InvalidCharacterError,\n // so the wrapping error message is informative.\n if (!/^[A-Za-z0-9+/]+={0,2}$/.test(b64.trim())) {\n throw new Error('input contains characters outside the base64 alphabet')\n }\n const binary = atob(b64)\n const out = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i)\n return out\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAsEO,SAAS,mBACd,OAAkC,CAAC,GACf;AACpB,QAAM,SAAS,KAAK,UAAU;AAC9B,QAAM,MAAM,QAAQ,IAAI,MAAM;AAC9B,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR,4BAA4B,MAAM;AAAA,IAEpC;AAAA,EACF;AAEA,MAAI;AACJ,MAAI;AACF,eAAW,cAAc,GAAG;AAAA,EAC9B,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,4BAA4B,MAAM,mCAC/B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,IACpD;AAAA,EACF;AAEA,MAAI,SAAS,WAAW,IAAI;AAC1B,UAAM,IAAI;AAAA,MACR,4BAA4B,MAAM,gBAAgB,SAAS,MAAM;AAAA,IAEnE;AAAA,EACF;AAIA,MAAI,YAAuC;AAC3C,QAAM,SAAS,MAA0B;AACvC,QAAI,CAAC,WAAW;AACd,kBAAY,WAAW,OAAO,OAAO;AAAA,QACnC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,CAAC,WAAW,SAAS;AAAA,MACvB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,IAAI,OAAO,MAAM;AAAA,IAEjB,MAAM,KAAK,YAA6C;AACtD,YAAM,MAAM,MAAM,OAAO;AACzB,YAAM,KAAK,WAAW,OAAO,gBAAgB,IAAI,WAAW,EAAE,CAAC;AAC/D,YAAM,aAAa,MAAM,WAAW,OAAO,OAAO;AAAA,QAChD,EAAE,MAAM,WAAW,GAAuB;AAAA,QAC1C;AAAA,QACA;AAAA,MACF;AAEA,YAAM,MAAM,IAAI,WAAW,KAAK,WAAW,UAAU;AACrD,UAAI,IAAI,IAAI,CAAC;AACb,UAAI,IAAI,IAAI,WAAW,UAAU,GAAG,EAAE;AACtC,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,OAAO,QAAyC;AAEpD,UAAI,OAAO,SAAS,KAAK,IAAI;AAC3B,cAAM,IAAI;AAAA,UACR,2CAA2C,OAAO,MAAM;AAAA,QAE1D;AAAA,MACF;AACA,YAAM,KAAK,OAAO,SAAS,GAAG,EAAE;AAChC,YAAM,OAAO,OAAO,SAAS,EAAE;AAC/B,YAAM,MAAM,MAAM,OAAO;AACzB,YAAM,YAAY,MAAM,WAAW,OAAO,OAAO;AAAA,QAC/C,EAAE,MAAM,WAAW,GAAuB;AAAA,QAC1C;AAAA,QACA;AAAA,MACF;AACA,aAAO,IAAI,WAAW,SAAS;AAAA,IACjC;AAAA,EACF;AACF;AAIA,SAAS,cAAc,KAAyB;AAG9C,MAAI,CAAC,yBAAyB,KAAK,IAAI,KAAK,CAAC,GAAG;AAC9C,UAAM,IAAI,MAAM,uDAAuD;AAAA,EACzE;AACA,QAAM,SAAS,KAAK,GAAG;AACvB,QAAM,MAAM,IAAI,WAAW,OAAO,MAAM;AACxC,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,IAAK,KAAI,CAAC,IAAI,OAAO,WAAW,CAAC;AACpE,SAAO;AACT;","names":[]}
|
package/dist/index.d.cts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { SealingKeyProvider } from '@noy-db/hub';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* **@noy-db/at-env** — env-var sealing key provider for noy-db
|
|
5
|
-
* managed-passphrase mode.
|
|
6
|
-
*
|
|
7
|
-
* The smallest production-shape provider in the `at-*` family. Reads a
|
|
8
|
-
* 32-byte AES-256-GCM key from an environment variable (base64-encoded)
|
|
9
|
-
* and uses it to seal / unseal the hub-generated random passphrase.
|
|
10
|
-
*
|
|
11
|
-
* ## When to use
|
|
12
|
-
*
|
|
13
|
-
* - Single-node SaaS where the env var comes from Kubernetes Secrets,
|
|
14
|
-
* Heroku env, Doppler, AWS Secrets Manager (mounted at boot), systemd
|
|
15
|
-
* service env, etc. — anywhere your platform already manages secrets.
|
|
16
|
-
* - Local dev / CI where you want persistence across process restarts
|
|
17
|
-
* (which `MemorySealingKeyProvider` from `@noy-db/hub` can't do).
|
|
18
|
-
* - Prototypes where setting up AWS KMS / GCP KMS isn't worth the effort.
|
|
19
|
-
*
|
|
20
|
-
* ## When NOT to use
|
|
21
|
-
*
|
|
22
|
-
* - Laptops / shared dev machines where other users have shell access.
|
|
23
|
-
* They can `echo $NOYDB_SEALING_KEY` and exfiltrate the key. Use
|
|
24
|
-
* `@noy-db/at-macos-keychain` / `@noy-db/at-wincred` /
|
|
25
|
-
* `@noy-db/at-libsecret` for desktop apps.
|
|
26
|
-
* - Compliance regimes requiring auditable key access (FedRAMP, HIPAA
|
|
27
|
-
* with managed-encryption requirements). Use `@noy-db/at-aws-kms` for
|
|
28
|
-
* KMS-backed key access logs.
|
|
29
|
-
*
|
|
30
|
-
* ## Setup
|
|
31
|
-
*
|
|
32
|
-
* ```bash
|
|
33
|
-
* # 1. Generate a 256-bit key once. Store in your platform's secret manager.
|
|
34
|
-
* export NOYDB_SEALING_KEY=$(openssl rand -base64 32)
|
|
35
|
-
* ```
|
|
36
|
-
*
|
|
37
|
-
* ```ts
|
|
38
|
-
* // 2. In your app:
|
|
39
|
-
* import { createNoydb } from '@noy-db/hub'
|
|
40
|
-
* import { envSealingProvider } from '@noy-db/at-env'
|
|
41
|
-
*
|
|
42
|
-
* const db = await createNoydb({
|
|
43
|
-
* store,
|
|
44
|
-
* user: 'alice',
|
|
45
|
-
* passphraseMode: 'managed',
|
|
46
|
-
* sealingKey: envSealingProvider(), // reads NOYDB_SEALING_KEY by default
|
|
47
|
-
* })
|
|
48
|
-
* ```
|
|
49
|
-
*
|
|
50
|
-
* @packageDocumentation
|
|
51
|
-
*/
|
|
52
|
-
|
|
53
|
-
/** Options for {@link envSealingProvider}. */
|
|
54
|
-
interface EnvSealingProviderOptions {
|
|
55
|
-
/**
|
|
56
|
-
* Environment variable name holding the base64-encoded 32-byte AES-256
|
|
57
|
-
* key. Default `'NOYDB_SEALING_KEY'`.
|
|
58
|
-
*/
|
|
59
|
-
readonly envVar?: string;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Build a {@link SealingKeyProvider} backed by an environment variable.
|
|
63
|
-
*
|
|
64
|
-
* The env var must contain a base64-encoded 32-byte (256-bit) key. The
|
|
65
|
-
* provider validates the value at construction time and caches the
|
|
66
|
-
* imported `CryptoKey` for the lifetime of the instance.
|
|
67
|
-
*
|
|
68
|
-
* @throws Error when the env var is unset, not base64, or not 32 bytes.
|
|
69
|
-
*/
|
|
70
|
-
declare function envSealingProvider(opts?: EnvSealingProviderOptions): SealingKeyProvider;
|
|
71
|
-
|
|
72
|
-
export { type EnvSealingProviderOptions, envSealingProvider };
|