@agent-native/core 0.100.1 → 0.100.2
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +8 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/AssistantChat.tsx +4 -0
- package/corpus/core/src/client/composer/PromptComposer.tsx +4 -0
- package/corpus/core/src/client/composer/TiptapComposer.tsx +30 -3
- package/corpus/core/src/integrations/webhook-handler.ts +4 -2
- package/corpus/core/src/secrets/crypto.ts +98 -47
- package/corpus/core/src/secrets/index.ts +2 -1
- package/corpus/core/src/secrets/schema.ts +3 -0
- package/corpus/core/src/secrets/storage.ts +164 -31
- package/corpus/templates/assets/app/components/layout/Layout.tsx +4 -0
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +3 -1
- package/corpus/templates/assets/changelog/2026-07-13-assets-keeps-the-right-chat-sidebar-closed-when-the-full-pag.md +6 -0
- package/dist/client/AssistantChat.d.ts +2 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +2 -2
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/composer/PromptComposer.d.ts +2 -0
- package/dist/client/composer/PromptComposer.d.ts.map +1 -1
- package/dist/client/composer/PromptComposer.js +2 -2
- package/dist/client/composer/PromptComposer.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +3 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +20 -5
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +3 -2
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/secrets/crypto.d.ts +35 -13
- package/dist/secrets/crypto.d.ts.map +1 -1
- package/dist/secrets/crypto.js +78 -41
- package/dist/secrets/crypto.js.map +1 -1
- package/dist/secrets/index.d.ts.map +1 -1
- package/dist/secrets/index.js +2 -1
- package/dist/secrets/index.js.map +1 -1
- package/dist/secrets/schema.d.ts +20 -1
- package/dist/secrets/schema.d.ts.map +1 -1
- package/dist/secrets/schema.js +3 -0
- package/dist/secrets/schema.js.map +1 -1
- package/dist/secrets/storage.d.ts +7 -7
- package/dist/secrets/storage.d.ts.map +1 -1
- package/dist/secrets/storage.js +121 -30
- package/dist/secrets/storage.js.map +1 -1
- package/package.json +1 -1
package/dist/secrets/crypto.js
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared AES-256-GCM encryption for secret values at rest.
|
|
3
3
|
*
|
|
4
|
-
* Used by
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Used by the framework secrets vault, per-user/per-org credentials
|
|
5
|
+
* (`resolveCredential` / `saveCredential`, stored in `settings`), and other
|
|
6
|
+
* column-level encrypted values. The `app_secrets` storage layer uses the
|
|
7
|
+
* shared-key variant below because workspace-scoped vault rows are readable by
|
|
8
|
+
* sibling apps in the same workspace. When a deployment has not been given
|
|
9
|
+
* shared key material yet, that variant falls back to the app-scoped key so
|
|
10
|
+
* existing single-app deployments can keep reading and writing secrets while
|
|
11
|
+
* they migrate to the shared key.
|
|
7
12
|
*
|
|
8
|
-
* The encryption key is derived from
|
|
9
|
-
* set, then `SECRETS_ENCRYPTION_KEY`,
|
|
10
|
-
* key lets a local multi-app
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* The default encryption key is derived from
|
|
14
|
+
* `<APP_NAME>_SECRETS_ENCRYPTION_KEY` when set, then `SECRETS_ENCRYPTION_KEY`,
|
|
15
|
+
* then `BETTER_AUTH_SECRET`. The app-scoped key lets a local multi-app
|
|
16
|
+
* workspace read one app's encrypted production data without replacing the
|
|
17
|
+
* shared local auth secret. In production we refuse to start without
|
|
18
|
+
* configured key material — a CWD-derived fallback would be effectively
|
|
19
|
+
* static (e.g. `/var/task` on Lambda), so anyone with read access to the DB
|
|
20
|
+
* could decrypt every secret.
|
|
15
21
|
*
|
|
16
22
|
* Encrypted values are tagged `v1:<iv-hex>:<ct-hex>:<tag-hex>`. The `v1:` prefix
|
|
17
23
|
* lets readers distinguish ciphertext from legacy plaintext during migration.
|
|
@@ -54,55 +60,71 @@ function appScopedEncryptionKey() {
|
|
|
54
60
|
? process.env[`${appName}_SECRETS_ENCRYPTION_KEY`] // guard:allow-env-credential — deploy-level app encryption material, never a user credential.
|
|
55
61
|
: undefined;
|
|
56
62
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
function sharedEncryptionKeyMaterial() {
|
|
64
|
+
if (typeof process === "undefined")
|
|
65
|
+
return undefined;
|
|
66
|
+
return process.env.SECRETS_ENCRYPTION_KEY || process.env.BETTER_AUTH_SECRET;
|
|
67
|
+
}
|
|
68
|
+
function deriveSecretEncryptionKey(explicit, errorMessage, warningMessage) {
|
|
62
69
|
const { createHash } = requireNodeCrypto();
|
|
63
|
-
const explicit = appScopedEncryptionKey() ||
|
|
64
|
-
(typeof process === "undefined"
|
|
65
|
-
? undefined
|
|
66
|
-
: process.env.SECRETS_ENCRYPTION_KEY) ||
|
|
67
|
-
(typeof process === "undefined"
|
|
68
|
-
? undefined
|
|
69
|
-
: process.env.BETTER_AUTH_SECRET);
|
|
70
70
|
if (!explicit) {
|
|
71
71
|
if (processNodeEnv() === "production") {
|
|
72
|
-
|
|
73
|
-
? undefined
|
|
74
|
-
: process.env.APP_NAME?.trim() // guard:allow-env-credential — deploy-level app configuration selects the scoped encryption key.
|
|
75
|
-
.toUpperCase()
|
|
76
|
-
.replace(/[^A-Z0-9]+/g, "_")
|
|
77
|
-
.replace(/^_+|_+$/g, "");
|
|
78
|
-
throw new Error("[agent-native/secrets] Refusing to start in production without an encryption key. " +
|
|
79
|
-
`Set ${appName ? `${appName}_SECRETS_ENCRYPTION_KEY, ` : ""}SECRETS_ENCRYPTION_KEY, or BETTER_AUTH_SECRET in the deploy environment. ` +
|
|
80
|
-
"The previous CWD-derived fallback was effectively static (e.g. `/var/task` on Lambda), " +
|
|
81
|
-
"which means anyone with read access to the secrets table could decrypt every user's secrets.");
|
|
72
|
+
throw new Error(errorMessage);
|
|
82
73
|
}
|
|
83
74
|
if (!_warnedFallback) {
|
|
84
75
|
_warnedFallback = true;
|
|
85
76
|
// eslint-disable-next-line no-console
|
|
86
|
-
console.warn(
|
|
87
|
-
"Set an app-scoped *_SECRETS_ENCRYPTION_KEY, SECRETS_ENCRYPTION_KEY, or BETTER_AUTH_SECRET for production. " +
|
|
88
|
-
"Production deploys without one of these env vars now hard-fail.");
|
|
77
|
+
console.warn(warningMessage);
|
|
89
78
|
}
|
|
90
79
|
}
|
|
91
80
|
const material = explicit || `agent-native-secrets:${processCwd()}`;
|
|
92
81
|
return createHash("sha256").update(material).digest();
|
|
93
82
|
}
|
|
94
|
-
/**
|
|
95
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Derive the key used by generic encrypted values such as credentials and
|
|
85
|
+
* OAuth tokens. App-specific key material is allowed for these app-local
|
|
86
|
+
* values.
|
|
87
|
+
*/
|
|
88
|
+
export function getSecretEncryptionKey() {
|
|
89
|
+
const appName = typeof process === "undefined"
|
|
90
|
+
? undefined
|
|
91
|
+
: process.env.APP_NAME?.trim() // guard:allow-env-credential — deploy-level app configuration selects the scoped encryption key.
|
|
92
|
+
.toUpperCase()
|
|
93
|
+
.replace(/[^A-Z0-9]+/g, "_")
|
|
94
|
+
.replace(/^_+|_+$/g, "");
|
|
95
|
+
return deriveSecretEncryptionKey(appScopedEncryptionKey() || sharedEncryptionKeyMaterial(), "[agent-native/secrets] Refusing to start in production without an encryption key. " +
|
|
96
|
+
`Set ${appName ? `${appName}_SECRETS_ENCRYPTION_KEY, ` : ""}SECRETS_ENCRYPTION_KEY, or BETTER_AUTH_SECRET in the deploy environment. ` +
|
|
97
|
+
"The previous CWD-derived fallback was effectively static (e.g. `/var/task` on Lambda), " +
|
|
98
|
+
"which means anyone with read access to the DB could decrypt every secret.", "[agent-native/secrets] SECRETS_ENCRYPTION_KEY not set — using a machine-local fallback. " +
|
|
99
|
+
"Set an app-scoped *_SECRETS_ENCRYPTION_KEY, SECRETS_ENCRYPTION_KEY, or BETTER_AUTH_SECRET for production. " +
|
|
100
|
+
"Production deploys without one of these env vars now hard-fail.");
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Derive the preferred workspace-shared key used by `app_secrets` rows.
|
|
104
|
+
* Unlike generic column-level encryption, workspace vault data should decrypt
|
|
105
|
+
* in sibling apps, so `SECRETS_ENCRYPTION_KEY` / `BETTER_AUTH_SECRET` take
|
|
106
|
+
* precedence. The app-scoped key remains a compatibility fallback for
|
|
107
|
+
* deployments that have not configured shared material yet; once the shared
|
|
108
|
+
* key is configured, writes use it and reads still fall back to old rows.
|
|
109
|
+
*/
|
|
110
|
+
export function getSharedSecretEncryptionKey() {
|
|
111
|
+
return deriveSecretEncryptionKey(sharedEncryptionKeyMaterial() || appScopedEncryptionKey(), "[agent-native/secrets] Refusing to start in production without encryption key material for workspace secrets. " +
|
|
112
|
+
"Set SECRETS_ENCRYPTION_KEY, BETTER_AUTH_SECRET, or the app-scoped *_SECRETS_ENCRYPTION_KEY in the deploy environment.", "[agent-native/secrets] SECRETS_ENCRYPTION_KEY not set — using app-scoped or machine-local fallback for workspace secrets. " +
|
|
113
|
+
"Set SECRETS_ENCRYPTION_KEY or BETTER_AUTH_SECRET in every workspace app so sibling apps share vault rows.");
|
|
114
|
+
}
|
|
115
|
+
/** Whether this deployment has stable workspace-shared key material. */
|
|
116
|
+
export function hasSharedSecretEncryptionKeyMaterial() {
|
|
117
|
+
return Boolean(sharedEncryptionKeyMaterial());
|
|
118
|
+
}
|
|
119
|
+
function encryptWithKey(plaintext, key) {
|
|
96
120
|
const { createCipheriv, randomBytes } = requireNodeCrypto();
|
|
97
|
-
const key = getSecretEncryptionKey();
|
|
98
121
|
const iv = randomBytes(12);
|
|
99
122
|
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
100
123
|
const ct = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
101
124
|
const tag = cipher.getAuthTag();
|
|
102
125
|
return `v1:${iv.toString("hex")}:${ct.toString("hex")}:${tag.toString("hex")}`;
|
|
103
126
|
}
|
|
104
|
-
|
|
105
|
-
export function decryptSecretValue(encrypted) {
|
|
127
|
+
function decryptWithKey(encrypted, key) {
|
|
106
128
|
const { createDecipheriv } = requireNodeCrypto();
|
|
107
129
|
if (!encrypted.startsWith("v1:")) {
|
|
108
130
|
throw new Error("Unrecognised secret encoding");
|
|
@@ -111,7 +133,6 @@ export function decryptSecretValue(encrypted) {
|
|
|
111
133
|
if (!ivHex || !ctHex || !tagHex) {
|
|
112
134
|
throw new Error("Corrupt secret payload");
|
|
113
135
|
}
|
|
114
|
-
const key = getSecretEncryptionKey();
|
|
115
136
|
const decipher = createDecipheriv("aes-256-gcm", key, Buffer.from(ivHex, "hex"));
|
|
116
137
|
decipher.setAuthTag(Buffer.from(tagHex, "hex"));
|
|
117
138
|
const pt = Buffer.concat([
|
|
@@ -120,6 +141,22 @@ export function decryptSecretValue(encrypted) {
|
|
|
120
141
|
]);
|
|
121
142
|
return pt.toString("utf8");
|
|
122
143
|
}
|
|
144
|
+
/** Encrypt a plain-text value with the generic app-local key. */
|
|
145
|
+
export function encryptSecretValue(plaintext) {
|
|
146
|
+
return encryptWithKey(plaintext, getSecretEncryptionKey());
|
|
147
|
+
}
|
|
148
|
+
/** Decrypt a value produced by `encryptSecretValue`. Throws on tampering. */
|
|
149
|
+
export function decryptSecretValue(encrypted) {
|
|
150
|
+
return decryptWithKey(encrypted, getSecretEncryptionKey());
|
|
151
|
+
}
|
|
152
|
+
/** Encrypt a workspace-shared `app_secrets` value. */
|
|
153
|
+
export function encryptSharedSecretValue(plaintext) {
|
|
154
|
+
return encryptWithKey(plaintext, getSharedSecretEncryptionKey());
|
|
155
|
+
}
|
|
156
|
+
/** Decrypt a workspace-shared `app_secrets` value. */
|
|
157
|
+
export function decryptSharedSecretValue(encrypted) {
|
|
158
|
+
return decryptWithKey(encrypted, getSharedSecretEncryptionKey());
|
|
159
|
+
}
|
|
123
160
|
/**
|
|
124
161
|
* Strict check for a value produced by `encryptSecretValue`: `v1:` followed by
|
|
125
162
|
* three hex segments. Intentionally strict so a legacy plaintext credential
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/secrets/crypto.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared AES-256-GCM encryption for secret values at rest.\n *\n * Used by both the framework secrets vault (`app_secrets`) and per-user/per-org\n * credentials (`resolveCredential` / `saveCredential`, stored in `settings`) so\n * there is a single crypto implementation and a single key story.\n *\n * The encryption key is derived from `<APP_NAME>_SECRETS_ENCRYPTION_KEY` when\n * set, then `SECRETS_ENCRYPTION_KEY`, then `BETTER_AUTH_SECRET`. The app-scoped\n * key lets a local multi-app workspace read one app's encrypted production data\n * without replacing the shared local auth secret. In production we refuse to\n * start without configured key material — a CWD-derived fallback would be\n * effectively static (e.g. `/var/task` on Lambda), so anyone with read access\n * to the DB could decrypt every secret.\n *\n * Encrypted values are tagged `v1:<iv-hex>:<ct-hex>:<tag-hex>`. The `v1:` prefix\n * lets readers distinguish ciphertext from legacy plaintext during migration.\n */\n\ntype NodeCryptoModule = typeof import(\"node:crypto\");\n\nfunction getNodeCrypto(): NodeCryptoModule | undefined {\n if (\n typeof window !== \"undefined\" ||\n typeof process === \"undefined\" ||\n !process.versions?.node ||\n typeof process.getBuiltinModule !== \"function\"\n ) {\n return undefined;\n }\n return process.getBuiltinModule(\"node:crypto\") as\n | NodeCryptoModule\n | undefined;\n}\n\nconst nodeCrypto = getNodeCrypto();\n\nlet _warnedFallback = false;\n\nfunction requireNodeCrypto(): NonNullable<typeof nodeCrypto> {\n if (!nodeCrypto) {\n throw new Error(\n \"[agent-native/secrets] Secret encryption is only available in server/runtime code.\",\n );\n }\n return nodeCrypto;\n}\n\nfunction processNodeEnv(): string | undefined {\n if (typeof process === \"undefined\") return undefined;\n return process.env.NODE_ENV;\n}\n\nfunction processCwd(): string {\n if (typeof process === \"undefined\") return \".\";\n return process.cwd();\n}\n\nfunction appScopedEncryptionKey(): string | undefined {\n if (typeof process === \"undefined\") return undefined;\n const appName = process.env.APP_NAME?.trim() // guard:allow-env-credential — deploy-level app configuration selects the scoped encryption key.\n .toUpperCase()\n .replace(/[^A-Z0-9]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\");\n return appName\n ? process.env[`${appName}_SECRETS_ENCRYPTION_KEY`] // guard:allow-env-credential — deploy-level app encryption material, never a user credential.\n : undefined;\n}\n\n/**\n * Derive a 32-byte AES key from the configured secret material via SHA-256.\n * Re-derived per call (cheap, stateless, and makes rotation easy).\n */\nexport function getSecretEncryptionKey(): Buffer {\n const { createHash } = requireNodeCrypto();\n const explicit =\n appScopedEncryptionKey() ||\n (typeof process === \"undefined\"\n ? undefined\n : process.env.SECRETS_ENCRYPTION_KEY) ||\n (typeof process === \"undefined\"\n ? undefined\n : process.env.BETTER_AUTH_SECRET);\n\n if (!explicit) {\n if (processNodeEnv() === \"production\") {\n const appName =\n typeof process === \"undefined\"\n ? undefined\n : process.env.APP_NAME?.trim() // guard:allow-env-credential — deploy-level app configuration selects the scoped encryption key.\n .toUpperCase()\n .replace(/[^A-Z0-9]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\");\n throw new Error(\n \"[agent-native/secrets] Refusing to start in production without an encryption key. \" +\n `Set ${appName ? `${appName}_SECRETS_ENCRYPTION_KEY, ` : \"\"}SECRETS_ENCRYPTION_KEY, or BETTER_AUTH_SECRET in the deploy environment. ` +\n \"The previous CWD-derived fallback was effectively static (e.g. `/var/task` on Lambda), \" +\n \"which means anyone with read access to the secrets table could decrypt every user's secrets.\",\n );\n }\n if (!_warnedFallback) {\n _warnedFallback = true;\n // eslint-disable-next-line no-console\n console.warn(\n \"[agent-native/secrets] SECRETS_ENCRYPTION_KEY not set — using a machine-local fallback. \" +\n \"Set an app-scoped *_SECRETS_ENCRYPTION_KEY, SECRETS_ENCRYPTION_KEY, or BETTER_AUTH_SECRET for production. \" +\n \"Production deploys without one of these env vars now hard-fail.\",\n );\n }\n }\n\n const material = explicit || `agent-native-secrets:${processCwd()}`;\n return createHash(\"sha256\").update(material).digest();\n}\n\n/** Encrypt a plain-text value. Returns `v1:<iv-hex>:<ct-hex>:<tag-hex>`. */\nexport function encryptSecretValue(plaintext: string): string {\n const { createCipheriv, randomBytes } = requireNodeCrypto();\n const key = getSecretEncryptionKey();\n const iv = randomBytes(12);\n const cipher = createCipheriv(\"aes-256-gcm\", key, iv);\n const ct = Buffer.concat([cipher.update(plaintext, \"utf8\"), cipher.final()]);\n const tag = cipher.getAuthTag();\n return `v1:${iv.toString(\"hex\")}:${ct.toString(\"hex\")}:${tag.toString(\"hex\")}`;\n}\n\n/** Decrypt a value produced by `encryptSecretValue`. Throws on tampering. */\nexport function decryptSecretValue(encrypted: string): string {\n const { createDecipheriv } = requireNodeCrypto();\n if (!encrypted.startsWith(\"v1:\")) {\n throw new Error(\"Unrecognised secret encoding\");\n }\n const [, ivHex, ctHex, tagHex] = encrypted.split(\":\");\n if (!ivHex || !ctHex || !tagHex) {\n throw new Error(\"Corrupt secret payload\");\n }\n const key = getSecretEncryptionKey();\n const decipher = createDecipheriv(\n \"aes-256-gcm\",\n key,\n Buffer.from(ivHex, \"hex\"),\n );\n decipher.setAuthTag(Buffer.from(tagHex, \"hex\"));\n const pt = Buffer.concat([\n decipher.update(Buffer.from(ctHex, \"hex\")),\n decipher.final(),\n ]);\n return pt.toString(\"utf8\");\n}\n\n/**\n * Strict check for a value produced by `encryptSecretValue`: `v1:` followed by\n * three hex segments. Intentionally strict so a legacy plaintext credential\n * that merely happens to start with `v1:` is treated as plaintext (and read via\n * the legacy fallback) rather than mis-decrypted.\n */\nconst ENCRYPTED_VALUE_RE = /^v1:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+$/;\n\nexport function isEncryptedSecretValue(value: unknown): value is string {\n return typeof value === \"string\" && ENCRYPTED_VALUE_RE.test(value);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/secrets/crypto.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared AES-256-GCM encryption for secret values at rest.\n *\n * Used by the framework secrets vault, per-user/per-org credentials\n * (`resolveCredential` / `saveCredential`, stored in `settings`), and other\n * column-level encrypted values. The `app_secrets` storage layer uses the\n * shared-key variant below because workspace-scoped vault rows are readable by\n * sibling apps in the same workspace. When a deployment has not been given\n * shared key material yet, that variant falls back to the app-scoped key so\n * existing single-app deployments can keep reading and writing secrets while\n * they migrate to the shared key.\n *\n * The default encryption key is derived from\n * `<APP_NAME>_SECRETS_ENCRYPTION_KEY` when set, then `SECRETS_ENCRYPTION_KEY`,\n * then `BETTER_AUTH_SECRET`. The app-scoped key lets a local multi-app\n * workspace read one app's encrypted production data without replacing the\n * shared local auth secret. In production we refuse to start without\n * configured key material — a CWD-derived fallback would be effectively\n * static (e.g. `/var/task` on Lambda), so anyone with read access to the DB\n * could decrypt every secret.\n *\n * Encrypted values are tagged `v1:<iv-hex>:<ct-hex>:<tag-hex>`. The `v1:` prefix\n * lets readers distinguish ciphertext from legacy plaintext during migration.\n */\n\ntype NodeCryptoModule = typeof import(\"node:crypto\");\n\nfunction getNodeCrypto(): NodeCryptoModule | undefined {\n if (\n typeof window !== \"undefined\" ||\n typeof process === \"undefined\" ||\n !process.versions?.node ||\n typeof process.getBuiltinModule !== \"function\"\n ) {\n return undefined;\n }\n return process.getBuiltinModule(\"node:crypto\") as\n | NodeCryptoModule\n | undefined;\n}\n\nconst nodeCrypto = getNodeCrypto();\n\nlet _warnedFallback = false;\n\nfunction requireNodeCrypto(): NonNullable<typeof nodeCrypto> {\n if (!nodeCrypto) {\n throw new Error(\n \"[agent-native/secrets] Secret encryption is only available in server/runtime code.\",\n );\n }\n return nodeCrypto;\n}\n\nfunction processNodeEnv(): string | undefined {\n if (typeof process === \"undefined\") return undefined;\n return process.env.NODE_ENV;\n}\n\nfunction processCwd(): string {\n if (typeof process === \"undefined\") return \".\";\n return process.cwd();\n}\n\nfunction appScopedEncryptionKey(): string | undefined {\n if (typeof process === \"undefined\") return undefined;\n const appName = process.env.APP_NAME?.trim() // guard:allow-env-credential — deploy-level app configuration selects the scoped encryption key.\n .toUpperCase()\n .replace(/[^A-Z0-9]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\");\n return appName\n ? process.env[`${appName}_SECRETS_ENCRYPTION_KEY`] // guard:allow-env-credential — deploy-level app encryption material, never a user credential.\n : undefined;\n}\n\nfunction sharedEncryptionKeyMaterial(): string | undefined {\n if (typeof process === \"undefined\") return undefined;\n return process.env.SECRETS_ENCRYPTION_KEY || process.env.BETTER_AUTH_SECRET;\n}\n\nfunction deriveSecretEncryptionKey(\n explicit: string | undefined,\n errorMessage: string,\n warningMessage: string,\n): Buffer {\n const { createHash } = requireNodeCrypto();\n\n if (!explicit) {\n if (processNodeEnv() === \"production\") {\n throw new Error(errorMessage);\n }\n if (!_warnedFallback) {\n _warnedFallback = true;\n // eslint-disable-next-line no-console\n console.warn(warningMessage);\n }\n }\n\n const material = explicit || `agent-native-secrets:${processCwd()}`;\n return createHash(\"sha256\").update(material).digest();\n}\n\n/**\n * Derive the key used by generic encrypted values such as credentials and\n * OAuth tokens. App-specific key material is allowed for these app-local\n * values.\n */\nexport function getSecretEncryptionKey(): Buffer {\n const appName =\n typeof process === \"undefined\"\n ? undefined\n : process.env.APP_NAME?.trim() // guard:allow-env-credential — deploy-level app configuration selects the scoped encryption key.\n .toUpperCase()\n .replace(/[^A-Z0-9]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\");\n return deriveSecretEncryptionKey(\n appScopedEncryptionKey() || sharedEncryptionKeyMaterial(),\n \"[agent-native/secrets] Refusing to start in production without an encryption key. \" +\n `Set ${appName ? `${appName}_SECRETS_ENCRYPTION_KEY, ` : \"\"}SECRETS_ENCRYPTION_KEY, or BETTER_AUTH_SECRET in the deploy environment. ` +\n \"The previous CWD-derived fallback was effectively static (e.g. `/var/task` on Lambda), \" +\n \"which means anyone with read access to the DB could decrypt every secret.\",\n \"[agent-native/secrets] SECRETS_ENCRYPTION_KEY not set — using a machine-local fallback. \" +\n \"Set an app-scoped *_SECRETS_ENCRYPTION_KEY, SECRETS_ENCRYPTION_KEY, or BETTER_AUTH_SECRET for production. \" +\n \"Production deploys without one of these env vars now hard-fail.\",\n );\n}\n\n/**\n * Derive the preferred workspace-shared key used by `app_secrets` rows.\n * Unlike generic column-level encryption, workspace vault data should decrypt\n * in sibling apps, so `SECRETS_ENCRYPTION_KEY` / `BETTER_AUTH_SECRET` take\n * precedence. The app-scoped key remains a compatibility fallback for\n * deployments that have not configured shared material yet; once the shared\n * key is configured, writes use it and reads still fall back to old rows.\n */\nexport function getSharedSecretEncryptionKey(): Buffer {\n return deriveSecretEncryptionKey(\n sharedEncryptionKeyMaterial() || appScopedEncryptionKey(),\n \"[agent-native/secrets] Refusing to start in production without encryption key material for workspace secrets. \" +\n \"Set SECRETS_ENCRYPTION_KEY, BETTER_AUTH_SECRET, or the app-scoped *_SECRETS_ENCRYPTION_KEY in the deploy environment.\",\n \"[agent-native/secrets] SECRETS_ENCRYPTION_KEY not set — using app-scoped or machine-local fallback for workspace secrets. \" +\n \"Set SECRETS_ENCRYPTION_KEY or BETTER_AUTH_SECRET in every workspace app so sibling apps share vault rows.\",\n );\n}\n\n/** Whether this deployment has stable workspace-shared key material. */\nexport function hasSharedSecretEncryptionKeyMaterial(): boolean {\n return Boolean(sharedEncryptionKeyMaterial());\n}\n\nfunction encryptWithKey(plaintext: string, key: Buffer): string {\n const { createCipheriv, randomBytes } = requireNodeCrypto();\n const iv = randomBytes(12);\n const cipher = createCipheriv(\"aes-256-gcm\", key, iv);\n const ct = Buffer.concat([cipher.update(plaintext, \"utf8\"), cipher.final()]);\n const tag = cipher.getAuthTag();\n return `v1:${iv.toString(\"hex\")}:${ct.toString(\"hex\")}:${tag.toString(\"hex\")}`;\n}\n\nfunction decryptWithKey(encrypted: string, key: Buffer): string {\n const { createDecipheriv } = requireNodeCrypto();\n if (!encrypted.startsWith(\"v1:\")) {\n throw new Error(\"Unrecognised secret encoding\");\n }\n const [, ivHex, ctHex, tagHex] = encrypted.split(\":\");\n if (!ivHex || !ctHex || !tagHex) {\n throw new Error(\"Corrupt secret payload\");\n }\n const decipher = createDecipheriv(\n \"aes-256-gcm\",\n key,\n Buffer.from(ivHex, \"hex\"),\n );\n decipher.setAuthTag(Buffer.from(tagHex, \"hex\"));\n const pt = Buffer.concat([\n decipher.update(Buffer.from(ctHex, \"hex\")),\n decipher.final(),\n ]);\n return pt.toString(\"utf8\");\n}\n\n/** Encrypt a plain-text value with the generic app-local key. */\nexport function encryptSecretValue(plaintext: string): string {\n return encryptWithKey(plaintext, getSecretEncryptionKey());\n}\n\n/** Decrypt a value produced by `encryptSecretValue`. Throws on tampering. */\nexport function decryptSecretValue(encrypted: string): string {\n return decryptWithKey(encrypted, getSecretEncryptionKey());\n}\n\n/** Encrypt a workspace-shared `app_secrets` value. */\nexport function encryptSharedSecretValue(plaintext: string): string {\n return encryptWithKey(plaintext, getSharedSecretEncryptionKey());\n}\n\n/** Decrypt a workspace-shared `app_secrets` value. */\nexport function decryptSharedSecretValue(encrypted: string): string {\n return decryptWithKey(encrypted, getSharedSecretEncryptionKey());\n}\n\n/**\n * Strict check for a value produced by `encryptSecretValue`: `v1:` followed by\n * three hex segments. Intentionally strict so a legacy plaintext credential\n * that merely happens to start with `v1:` is treated as plaintext (and read via\n * the legacy fallback) rather than mis-decrypted.\n */\nconst ENCRYPTED_VALUE_RE = /^v1:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+$/;\n\nexport function isEncryptedSecretValue(value: unknown): value is string {\n return typeof value === \"string\" && ENCRYPTED_VALUE_RE.test(value);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/secrets/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,eAAe,GACrB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,sBAAsB,EACtB,KAAK,EACL,KAAK,SAAS,EACd,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,UAAU,GAChB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/secrets/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,eAAe,GACrB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,sBAAsB,EACtB,KAAK,EACL,KAAK,SAAS,EACd,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,UAAU,GAChB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAMjE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,wBAAwB,EACxB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,KAAK,0BAA0B,GAChC,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,iCAAiC,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/secrets/index.js
CHANGED
|
@@ -12,7 +12,8 @@ export { writeAppSecret, readAppSecret, readAppSecretMeta, deleteAppSecret, getA
|
|
|
12
12
|
export { APP_SECRETS_CREATE_SQL, appSecrets } from "./schema.js";
|
|
13
13
|
// AES-256-GCM helpers for column-level encryption of per-row secret values
|
|
14
14
|
// (e.g. a per-record share password) that don't fit the keyed app_secrets /
|
|
15
|
-
// credentials stores.
|
|
15
|
+
// credentials stores. These use the app-local key, unlike the workspace-shared
|
|
16
|
+
// app_secrets storage layer.
|
|
16
17
|
export { encryptSecretValue, decryptSecretValue, isEncryptedSecretValue, } from "./crypto.js";
|
|
17
18
|
export { createListSecretsHandler, createWriteSecretHandler, createTestSecretHandler, createAdHocSecretHandler, } from "./routes.js";
|
|
18
19
|
export { resolveKeyReferences, validateUrlAllowlist, getKeyAllowlist, } from "./substitution.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/secrets/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,GAMvB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,sBAAsB,EACtB,KAAK,GAKN,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEjE,2EAA2E;AAC3E,4EAA4E;AAC5E,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/secrets/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,GAMvB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,sBAAsB,EACtB,KAAK,GAKN,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEjE,2EAA2E;AAC3E,4EAA4E;AAC5E,+EAA+E;AAC/E,6BAA6B;AAC7B,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,wBAAwB,GAGzB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,GAEhB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,iCAAiC,EAAE,MAAM,iBAAiB,CAAC","sourcesContent":["/**\n * Framework secrets registry.\n *\n * Lets templates declaratively register required API keys / credentials so\n * they appear in the sidebar settings UI and the onboarding checklist, and\n * so actions can read them by a stable key.\n *\n * See `.agents/skills/secrets/SKILL.md` for usage.\n */\n\nexport {\n registerRequiredSecret,\n listRequiredSecrets,\n getRequiredSecret,\n __resetSecretsRegistry,\n type RegisteredSecret,\n type SecretScope,\n type SecretKind,\n type SecretValidator,\n type ValidatorResult,\n} from \"./register.js\";\n\nexport {\n writeAppSecret,\n readAppSecret,\n readAppSecretMeta,\n deleteAppSecret,\n getAppSecretMeta,\n listAppSecretsForScope,\n last4,\n type SecretRef,\n type WriteSecretArgs,\n type ReadSecretResult,\n type SecretMeta,\n} from \"./storage.js\";\n\nexport { APP_SECRETS_CREATE_SQL, appSecrets } from \"./schema.js\";\n\n// AES-256-GCM helpers for column-level encryption of per-row secret values\n// (e.g. a per-record share password) that don't fit the keyed app_secrets /\n// credentials stores. These use the app-local key, unlike the workspace-shared\n// app_secrets storage layer.\nexport {\n encryptSecretValue,\n decryptSecretValue,\n isEncryptedSecretValue,\n} from \"./crypto.js\";\n\nexport {\n createListSecretsHandler,\n createWriteSecretHandler,\n createTestSecretHandler,\n createAdHocSecretHandler,\n type SecretStatusPayload,\n type AdHocSecretPayload,\n} from \"./routes.js\";\n\nexport {\n resolveKeyReferences,\n validateUrlAllowlist,\n getKeyAllowlist,\n type ResolveKeyReferencesResult,\n} from \"./substitution.js\";\n\nexport { maybeRegisterSecretOnboardingStep } from \"./onboarding.js\";\n"]}
|
package/dist/secrets/schema.d.ts
CHANGED
|
@@ -108,6 +108,25 @@ export declare const appSecrets: import("drizzle-orm/sqlite-core").SQLiteTableWi
|
|
|
108
108
|
}, {}, {
|
|
109
109
|
length: number;
|
|
110
110
|
}>;
|
|
111
|
+
sharedEncryptedValue: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
112
|
+
name: "shared_encrypted_value";
|
|
113
|
+
tableName: "app_secrets";
|
|
114
|
+
dataType: "string";
|
|
115
|
+
columnType: "SQLiteText";
|
|
116
|
+
data: string;
|
|
117
|
+
driverParam: string;
|
|
118
|
+
notNull: false;
|
|
119
|
+
hasDefault: false;
|
|
120
|
+
isPrimaryKey: false;
|
|
121
|
+
isAutoincrement: false;
|
|
122
|
+
hasRuntimeDefault: false;
|
|
123
|
+
enumValues: [string, ...string[]];
|
|
124
|
+
baseColumn: never;
|
|
125
|
+
identity: undefined;
|
|
126
|
+
generated: undefined;
|
|
127
|
+
}, {}, {
|
|
128
|
+
length: number;
|
|
129
|
+
}>;
|
|
111
130
|
description: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
112
131
|
name: "description";
|
|
113
132
|
tableName: "app_secrets";
|
|
@@ -188,5 +207,5 @@ export declare const appSecrets: import("drizzle-orm/sqlite-core").SQLiteTableWi
|
|
|
188
207
|
* `ensureTable()` path in `storage.ts` and by any template-level migration
|
|
189
208
|
* that wants to create the table up-front.
|
|
190
209
|
*/
|
|
191
|
-
export declare const APP_SECRETS_CREATE_SQL = "CREATE TABLE IF NOT EXISTS app_secrets (\n id TEXT PRIMARY KEY,\n scope TEXT NOT NULL,\n scope_id TEXT NOT NULL,\n key TEXT NOT NULL,\n encrypted_value TEXT NOT NULL,\n description TEXT,\n url_allowlist TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n UNIQUE(scope, scope_id, key)\n)";
|
|
210
|
+
export declare const APP_SECRETS_CREATE_SQL = "CREATE TABLE IF NOT EXISTS app_secrets (\n id TEXT PRIMARY KEY,\n scope TEXT NOT NULL,\n scope_id TEXT NOT NULL,\n key TEXT NOT NULL,\n encrypted_value TEXT NOT NULL,\n shared_encrypted_value TEXT,\n description TEXT,\n url_allowlist TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n UNIQUE(scope, scope_id, key)\n)";
|
|
192
211
|
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/secrets/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,eAAO,MAAM,UAAU
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/secrets/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkBrB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,+VAYjC,CAAC"}
|
package/dist/secrets/schema.js
CHANGED
|
@@ -20,6 +20,8 @@ export const appSecrets = table("app_secrets", {
|
|
|
20
20
|
key: text("key").notNull(),
|
|
21
21
|
/** Encrypted value — never return this through any API. */
|
|
22
22
|
encryptedValue: text("encrypted_value").notNull(),
|
|
23
|
+
/** Preferred workspace-shared ciphertext; nullable during key migration. */
|
|
24
|
+
sharedEncryptedValue: text("shared_encrypted_value"),
|
|
23
25
|
/** Optional human-readable description (used for ad-hoc keys). */
|
|
24
26
|
description: text("description"),
|
|
25
27
|
/** JSON array of allowed URL origins. Null = allow all. */
|
|
@@ -38,6 +40,7 @@ export const APP_SECRETS_CREATE_SQL = `CREATE TABLE IF NOT EXISTS app_secrets (
|
|
|
38
40
|
scope_id TEXT NOT NULL,
|
|
39
41
|
key TEXT NOT NULL,
|
|
40
42
|
encrypted_value TEXT NOT NULL,
|
|
43
|
+
shared_encrypted_value TEXT,
|
|
41
44
|
description TEXT,
|
|
42
45
|
url_allowlist TEXT,
|
|
43
46
|
created_at INTEGER NOT NULL,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/secrets/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAEvD,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,CAAC,aAAa,EAAE;IAC7C,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE;IAC3B,2DAA2D;IAC3D,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE;IAC9B,+DAA+D;IAC/D,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE;IACnC,yDAAyD;IACzD,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE;IAC1B,2DAA2D;IAC3D,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,EAAE;IACjD,kEAAkE;IAClE,WAAW,EAAE,IAAI,CAAC,aAAa,CAAC;IAChC,2DAA2D;IAC3D,YAAY,EAAE,IAAI,CAAC,eAAe,CAAC;IACnC,SAAS,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;IAC1C,SAAS,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;CAC3C,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/secrets/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAEvD,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,CAAC,aAAa,EAAE;IAC7C,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE;IAC3B,2DAA2D;IAC3D,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE;IAC9B,+DAA+D;IAC/D,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE;IACnC,yDAAyD;IACzD,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE;IAC1B,2DAA2D;IAC3D,cAAc,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,EAAE;IACjD,4EAA4E;IAC5E,oBAAoB,EAAE,IAAI,CAAC,wBAAwB,CAAC;IACpD,kEAAkE;IAClE,WAAW,EAAE,IAAI,CAAC,aAAa,CAAC;IAChC,2DAA2D;IAC3D,YAAY,EAAE,IAAI,CAAC,eAAe,CAAC;IACnC,SAAS,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;IAC1C,SAAS,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;CAC3C,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG;;;;;;;;;;;;EAYpC,CAAC","sourcesContent":["/**\n * Drizzle schema for the framework secrets registry.\n *\n * The `app_secrets` table stores API keys and service credentials that\n * templates register via `registerRequiredSecret()`. Values are always\n * stored encrypted at rest — see `storage.ts` for the crypto layer.\n *\n * Rows are scoped either to a user (by email) or a workspace / organization\n * (by orgId). OAuth-kind secrets never create a row here — they surface via\n * `@agent-native/core/oauth-tokens` instead.\n */\n\nimport { table, text, integer } from \"../db/schema.js\";\n\nexport const appSecrets = table(\"app_secrets\", {\n id: text(\"id\").primaryKey(),\n /** \"user\" or \"workspace\" — who the secret is scoped to. */\n scope: text(\"scope\").notNull(),\n /** Session email for user-scope, orgId for workspace-scope. */\n scopeId: text(\"scope_id\").notNull(),\n /** The registered secret key (e.g. \"OPENAI_API_KEY\"). */\n key: text(\"key\").notNull(),\n /** Encrypted value — never return this through any API. */\n encryptedValue: text(\"encrypted_value\").notNull(),\n /** Preferred workspace-shared ciphertext; nullable during key migration. */\n sharedEncryptedValue: text(\"shared_encrypted_value\"),\n /** Optional human-readable description (used for ad-hoc keys). */\n description: text(\"description\"),\n /** JSON array of allowed URL origins. Null = allow all. */\n urlAllowlist: text(\"url_allowlist\"),\n createdAt: integer(\"created_at\").notNull(),\n updatedAt: integer(\"updated_at\").notNull(),\n});\n\n/**\n * Raw SQL for creating the app_secrets table. Used by the on-demand\n * `ensureTable()` path in `storage.ts` and by any template-level migration\n * that wants to create the table up-front.\n */\nexport const APP_SECRETS_CREATE_SQL = `CREATE TABLE IF NOT EXISTS app_secrets (\n id TEXT PRIMARY KEY,\n scope TEXT NOT NULL,\n scope_id TEXT NOT NULL,\n key TEXT NOT NULL,\n encrypted_value TEXT NOT NULL,\n shared_encrypted_value TEXT,\n description TEXT,\n url_allowlist TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n UNIQUE(scope, scope_id, key)\n)`;\n"]}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Storage layer for the framework secrets registry.
|
|
3
3
|
*
|
|
4
|
-
* Values are encrypted at rest with AES-256-GCM. The
|
|
5
|
-
* derived from `SECRETS_ENCRYPTION_KEY` (preferred) or the
|
|
6
|
-
* `BETTER_AUTH_SECRET` env var (fallback so templates don't need a
|
|
7
|
-
* secret during development).
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* for
|
|
4
|
+
* Values are encrypted at rest with AES-256-GCM. The workspace-shared
|
|
5
|
+
* encryption key is derived from `SECRETS_ENCRYPTION_KEY` (preferred) or the
|
|
6
|
+
* existing `BETTER_AUTH_SECRET` env var (fallback so templates don't need a
|
|
7
|
+
* second secret during development). Deployments that only have the legacy
|
|
8
|
+
* app-scoped key continue to work until shared key material is configured;
|
|
9
|
+
* once configured, new rows use the shared key and reads retain a legacy-key
|
|
10
|
+
* fallback for existing rows.
|
|
11
11
|
*
|
|
12
12
|
* Secret values are NEVER logged and NEVER returned from any route handler.
|
|
13
13
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/secrets/storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/secrets/storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAaH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AA+FjD;;;GAGG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI3C;AAMD,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAgB,SAAQ,SAAS;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAuE3E;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AA8GD;;;GAGG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,SAAS,GACb,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA0BlC;AAED,wEAAwE;AACxE,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAgCzC;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,SAAS,GACb,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAItD;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,SAAS,GACb,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAkC5B;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,UAAU,EAAE,CAAC,CAmCvB;AAeD,wBAAsB,eAAe,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAStE"}
|