@vibesdotdev/secrets 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/README.md +59 -0
  2. package/SPEC.md +47 -0
  3. package/dist/cli/check/schemas/check-result.d.ts +9 -0
  4. package/dist/cli/check/schemas/check-result.d.ts.map +1 -0
  5. package/dist/cli/check/schemas/check-result.js +2 -0
  6. package/dist/cli/check/schemas/check-result.js.map +1 -0
  7. package/dist/cli/check/secrets.check.cli-command.descriptor.d.ts +4 -0
  8. package/dist/cli/check/secrets.check.cli-command.descriptor.d.ts.map +1 -0
  9. package/dist/cli/check/secrets.check.cli-command.descriptor.js +19 -0
  10. package/dist/cli/check/secrets.check.cli-command.descriptor.js.map +1 -0
  11. package/dist/cli/check/secrets.check.cli-command.impl.d.ts +5 -0
  12. package/dist/cli/check/secrets.check.cli-command.impl.d.ts.map +1 -0
  13. package/dist/cli/check/secrets.check.cli-command.impl.js +135 -0
  14. package/dist/cli/check/secrets.check.cli-command.impl.js.map +1 -0
  15. package/dist/cli/export/secrets.export.cli-command.descriptor.d.ts +4 -0
  16. package/dist/cli/export/secrets.export.cli-command.descriptor.d.ts.map +1 -0
  17. package/dist/cli/export/secrets.export.cli-command.descriptor.js +20 -0
  18. package/dist/cli/export/secrets.export.cli-command.descriptor.js.map +1 -0
  19. package/dist/cli/export/secrets.export.cli-command.impl.d.ts +5 -0
  20. package/dist/cli/export/secrets.export.cli-command.impl.d.ts.map +1 -0
  21. package/dist/cli/export/secrets.export.cli-command.impl.js +104 -0
  22. package/dist/cli/export/secrets.export.cli-command.impl.js.map +1 -0
  23. package/dist/cli/hooks/pre-commit-secrets.d.ts +2 -0
  24. package/dist/cli/hooks/pre-commit-secrets.d.ts.map +1 -0
  25. package/dist/cli/hooks/pre-commit-secrets.js +68 -0
  26. package/dist/cli/hooks/pre-commit-secrets.js.map +1 -0
  27. package/dist/cli/import/secrets.import.cli-command.descriptor.d.ts +4 -0
  28. package/dist/cli/import/secrets.import.cli-command.descriptor.d.ts.map +1 -0
  29. package/dist/cli/import/secrets.import.cli-command.descriptor.js +19 -0
  30. package/dist/cli/import/secrets.import.cli-command.descriptor.js.map +1 -0
  31. package/dist/cli/import/secrets.import.cli-command.impl.d.ts +5 -0
  32. package/dist/cli/import/secrets.import.cli-command.impl.d.ts.map +1 -0
  33. package/dist/cli/import/secrets.import.cli-command.impl.js +155 -0
  34. package/dist/cli/import/secrets.import.cli-command.impl.js.map +1 -0
  35. package/dist/cli/list/secrets.list.cli-command.descriptor.d.ts +4 -0
  36. package/dist/cli/list/secrets.list.cli-command.descriptor.d.ts.map +1 -0
  37. package/dist/cli/list/secrets.list.cli-command.descriptor.js +18 -0
  38. package/dist/cli/list/secrets.list.cli-command.descriptor.js.map +1 -0
  39. package/dist/cli/list/secrets.list.cli-command.impl.d.ts +5 -0
  40. package/dist/cli/list/secrets.list.cli-command.impl.d.ts.map +1 -0
  41. package/dist/cli/list/secrets.list.cli-command.impl.js +61 -0
  42. package/dist/cli/list/secrets.list.cli-command.impl.js.map +1 -0
  43. package/dist/cli/pre-commit/secrets.pre-commit-check.cli-command.descriptor.d.ts +4 -0
  44. package/dist/cli/pre-commit/secrets.pre-commit-check.cli-command.descriptor.d.ts.map +1 -0
  45. package/dist/cli/pre-commit/secrets.pre-commit-check.cli-command.descriptor.js +16 -0
  46. package/dist/cli/pre-commit/secrets.pre-commit-check.cli-command.descriptor.js.map +1 -0
  47. package/dist/cli/pre-commit/secrets.pre-commit-check.cli-command.impl.d.ts +5 -0
  48. package/dist/cli/pre-commit/secrets.pre-commit-check.cli-command.impl.d.ts.map +1 -0
  49. package/dist/cli/pre-commit/secrets.pre-commit-check.cli-command.impl.js +10 -0
  50. package/dist/cli/pre-commit/secrets.pre-commit-check.cli-command.impl.js.map +1 -0
  51. package/dist/cli/pull/secrets.pull.cli-command.descriptor.d.ts +4 -0
  52. package/dist/cli/pull/secrets.pull.cli-command.descriptor.d.ts.map +1 -0
  53. package/dist/cli/pull/secrets.pull.cli-command.descriptor.js +20 -0
  54. package/dist/cli/pull/secrets.pull.cli-command.descriptor.js.map +1 -0
  55. package/dist/cli/pull/secrets.pull.cli-command.impl.d.ts +5 -0
  56. package/dist/cli/pull/secrets.pull.cli-command.impl.d.ts.map +1 -0
  57. package/dist/cli/pull/secrets.pull.cli-command.impl.js +76 -0
  58. package/dist/cli/pull/secrets.pull.cli-command.impl.js.map +1 -0
  59. package/dist/cli/push/secrets.push.cli-command.descriptor.d.ts +4 -0
  60. package/dist/cli/push/secrets.push.cli-command.descriptor.d.ts.map +1 -0
  61. package/dist/cli/push/secrets.push.cli-command.descriptor.js +22 -0
  62. package/dist/cli/push/secrets.push.cli-command.descriptor.js.map +1 -0
  63. package/dist/cli/push/secrets.push.cli-command.impl.d.ts +5 -0
  64. package/dist/cli/push/secrets.push.cli-command.impl.d.ts.map +1 -0
  65. package/dist/cli/push/secrets.push.cli-command.impl.js +109 -0
  66. package/dist/cli/push/secrets.push.cli-command.impl.js.map +1 -0
  67. package/dist/cli/reveal/secrets.reveal.cli-command.descriptor.d.ts +4 -0
  68. package/dist/cli/reveal/secrets.reveal.cli-command.descriptor.d.ts.map +1 -0
  69. package/dist/cli/reveal/secrets.reveal.cli-command.descriptor.js +19 -0
  70. package/dist/cli/reveal/secrets.reveal.cli-command.descriptor.js.map +1 -0
  71. package/dist/cli/reveal/secrets.reveal.cli-command.impl.d.ts +5 -0
  72. package/dist/cli/reveal/secrets.reveal.cli-command.impl.d.ts.map +1 -0
  73. package/dist/cli/reveal/secrets.reveal.cli-command.impl.js +85 -0
  74. package/dist/cli/reveal/secrets.reveal.cli-command.impl.js.map +1 -0
  75. package/dist/cli/secrets.cli-group.descriptor.d.ts +4 -0
  76. package/dist/cli/secrets.cli-group.descriptor.d.ts.map +1 -0
  77. package/dist/cli/secrets.cli-group.descriptor.js +11 -0
  78. package/dist/cli/secrets.cli-group.descriptor.js.map +1 -0
  79. package/dist/cli/set/secrets.set.cli-command.descriptor.d.ts +4 -0
  80. package/dist/cli/set/secrets.set.cli-command.descriptor.d.ts.map +1 -0
  81. package/dist/cli/set/secrets.set.cli-command.descriptor.js +21 -0
  82. package/dist/cli/set/secrets.set.cli-command.descriptor.js.map +1 -0
  83. package/dist/cli/set/secrets.set.cli-command.impl.d.ts +5 -0
  84. package/dist/cli/set/secrets.set.cli-command.impl.d.ts.map +1 -0
  85. package/dist/cli/set/secrets.set.cli-command.impl.js +59 -0
  86. package/dist/cli/set/secrets.set.cli-command.impl.js.map +1 -0
  87. package/dist/cli/shared/resolve-environment.d.ts +14 -0
  88. package/dist/cli/shared/resolve-environment.d.ts.map +1 -0
  89. package/dist/cli/shared/resolve-environment.js +45 -0
  90. package/dist/cli/shared/resolve-environment.js.map +1 -0
  91. package/dist/cli/unset/secrets.unset.cli-command.descriptor.d.ts +4 -0
  92. package/dist/cli/unset/secrets.unset.cli-command.descriptor.d.ts.map +1 -0
  93. package/dist/cli/unset/secrets.unset.cli-command.descriptor.js +20 -0
  94. package/dist/cli/unset/secrets.unset.cli-command.descriptor.js.map +1 -0
  95. package/dist/cli/unset/secrets.unset.cli-command.impl.d.ts +5 -0
  96. package/dist/cli/unset/secrets.unset.cli-command.impl.d.ts.map +1 -0
  97. package/dist/cli/unset/secrets.unset.cli-command.impl.js +31 -0
  98. package/dist/cli/unset/secrets.unset.cli-command.impl.js.map +1 -0
  99. package/dist/docs/backends.docs.descriptor.d.ts +4 -0
  100. package/dist/docs/backends.docs.descriptor.d.ts.map +1 -0
  101. package/dist/docs/backends.docs.descriptor.js +149 -0
  102. package/dist/docs/backends.docs.descriptor.js.map +1 -0
  103. package/dist/docs/encryption.docs.descriptor.d.ts +4 -0
  104. package/dist/docs/encryption.docs.descriptor.d.ts.map +1 -0
  105. package/dist/docs/encryption.docs.descriptor.js +163 -0
  106. package/dist/docs/encryption.docs.descriptor.js.map +1 -0
  107. package/dist/docs/env-file.docs.descriptor.d.ts +4 -0
  108. package/dist/docs/env-file.docs.descriptor.d.ts.map +1 -0
  109. package/dist/docs/env-file.docs.descriptor.js +207 -0
  110. package/dist/docs/env-file.docs.descriptor.js.map +1 -0
  111. package/dist/index.d.ts +13 -0
  112. package/dist/index.d.ts.map +1 -0
  113. package/dist/index.js +13 -0
  114. package/dist/index.js.map +1 -0
  115. package/dist/kinds/index.d.ts +4 -0
  116. package/dist/kinds/index.d.ts.map +1 -0
  117. package/dist/kinds/index.js +3 -0
  118. package/dist/kinds/index.js.map +1 -0
  119. package/dist/kinds/schemas/store.schema.d.ts +49 -0
  120. package/dist/kinds/schemas/store.schema.d.ts.map +1 -0
  121. package/dist/kinds/schemas/store.schema.js +34 -0
  122. package/dist/kinds/schemas/store.schema.js.map +1 -0
  123. package/dist/kinds/schemas/store.types.d.ts +28 -0
  124. package/dist/kinds/schemas/store.types.d.ts.map +1 -0
  125. package/dist/kinds/schemas/store.types.js +2 -0
  126. package/dist/kinds/schemas/store.types.js.map +1 -0
  127. package/dist/kinds/store.interface.d.ts +2 -0
  128. package/dist/kinds/store.interface.d.ts.map +1 -0
  129. package/dist/kinds/store.interface.js +2 -0
  130. package/dist/kinds/store.interface.js.map +1 -0
  131. package/dist/kinds/store.kind.d.ts +10 -0
  132. package/dist/kinds/store.kind.d.ts.map +1 -0
  133. package/dist/kinds/store.kind.js +36 -0
  134. package/dist/kinds/store.kind.js.map +1 -0
  135. package/dist/kinds/store.schema.d.ts +2 -0
  136. package/dist/kinds/store.schema.d.ts.map +1 -0
  137. package/dist/kinds/store.schema.js +2 -0
  138. package/dist/kinds/store.schema.js.map +1 -0
  139. package/dist/manifest/canonical.d.ts +30 -0
  140. package/dist/manifest/canonical.d.ts.map +1 -0
  141. package/dist/manifest/canonical.js +313 -0
  142. package/dist/manifest/canonical.js.map +1 -0
  143. package/dist/manifest/import-manifest.schema.d.ts +77 -0
  144. package/dist/manifest/import-manifest.schema.d.ts.map +1 -0
  145. package/dist/manifest/import-manifest.schema.js +55 -0
  146. package/dist/manifest/import-manifest.schema.js.map +1 -0
  147. package/dist/manifest/index.d.ts +3 -0
  148. package/dist/manifest/index.d.ts.map +1 -0
  149. package/dist/manifest/index.js +3 -0
  150. package/dist/manifest/index.js.map +1 -0
  151. package/dist/requirements/index.d.ts +2 -0
  152. package/dist/requirements/index.d.ts.map +1 -0
  153. package/dist/requirements/index.js +2 -0
  154. package/dist/requirements/index.js.map +1 -0
  155. package/dist/requirements/resolver.d.ts +52 -0
  156. package/dist/requirements/resolver.d.ts.map +1 -0
  157. package/dist/requirements/resolver.js +196 -0
  158. package/dist/requirements/resolver.js.map +1 -0
  159. package/dist/requirements/schemas/requirements.d.ts +27 -0
  160. package/dist/requirements/schemas/requirements.d.ts.map +1 -0
  161. package/dist/requirements/schemas/requirements.js +2 -0
  162. package/dist/requirements/schemas/requirements.js.map +1 -0
  163. package/dist/secrets.plugin.d.ts +8 -0
  164. package/dist/secrets.plugin.d.ts.map +1 -0
  165. package/dist/secrets.plugin.js +59 -0
  166. package/dist/secrets.plugin.js.map +1 -0
  167. package/package.json +108 -0
  168. package/src/cli/check/schemas/check-result.ts +8 -0
  169. package/src/cli/check/secrets.check.cli-command.descriptor.ts +21 -0
  170. package/src/cli/check/secrets.check.cli-command.impl.ts +163 -0
  171. package/src/cli/export/secrets.export.cli-command.descriptor.ts +22 -0
  172. package/src/cli/export/secrets.export.cli-command.impl.ts +139 -0
  173. package/src/cli/hooks/pre-commit-secrets.ts +73 -0
  174. package/src/cli/import/secrets.import.cli-command.descriptor.ts +21 -0
  175. package/src/cli/import/secrets.import.cli-command.impl.ts +178 -0
  176. package/src/cli/list/secrets.list.cli-command.descriptor.ts +21 -0
  177. package/src/cli/list/secrets.list.cli-command.impl.ts +79 -0
  178. package/src/cli/pre-commit/secrets.pre-commit-check.cli-command.descriptor.ts +18 -0
  179. package/src/cli/pre-commit/secrets.pre-commit-check.cli-command.impl.ts +11 -0
  180. package/src/cli/pull/secrets.pull.cli-command.descriptor.ts +22 -0
  181. package/src/cli/pull/secrets.pull.cli-command.impl.ts +103 -0
  182. package/src/cli/push/secrets.push.cli-command.descriptor.ts +24 -0
  183. package/src/cli/push/secrets.push.cli-command.impl.ts +149 -0
  184. package/src/cli/reveal/secrets.reveal.cli-command.descriptor.ts +21 -0
  185. package/src/cli/reveal/secrets.reveal.cli-command.impl.ts +108 -0
  186. package/src/cli/secrets.cli-group.descriptor.ts +13 -0
  187. package/src/cli/set/secrets.set.cli-command.descriptor.ts +23 -0
  188. package/src/cli/set/secrets.set.cli-command.impl.ts +77 -0
  189. package/src/cli/shared/resolve-environment.ts +57 -0
  190. package/src/cli/unset/secrets.unset.cli-command.descriptor.ts +22 -0
  191. package/src/cli/unset/secrets.unset.cli-command.impl.ts +41 -0
  192. package/src/docs/backends.docs.descriptor.ts +151 -0
  193. package/src/docs/encryption.docs.descriptor.ts +165 -0
  194. package/src/docs/env-file.docs.descriptor.ts +209 -0
  195. package/src/index.ts +35 -0
  196. package/src/kinds/index.ts +12 -0
  197. package/src/kinds/schemas/store.schema.ts +47 -0
  198. package/src/kinds/schemas/store.types.ts +35 -0
  199. package/src/kinds/store.interface.ts +1 -0
  200. package/src/kinds/store.kind.ts +52 -0
  201. package/src/kinds/store.schema.ts +8 -0
  202. package/src/manifest/canonical.ts +324 -0
  203. package/src/manifest/import-manifest.schema.ts +63 -0
  204. package/src/manifest/index.ts +12 -0
  205. package/src/requirements/index.ts +6 -0
  206. package/src/requirements/resolver.ts +216 -0
  207. package/src/requirements/schemas/requirements.ts +29 -0
  208. package/src/secrets.plugin.ts +65 -0
@@ -0,0 +1,57 @@
1
+ import type { UIContext } from '@vibesdotdev/cli/providers';
2
+ import { resolveCurrentEnvironmentConfig } from '@vibesdotdev/config/environment/current';
3
+ import type { EnvironmentTier } from '../../kinds/store.schema';
4
+
5
+ export interface ResolvedSecretsEnvironment {
6
+ name: string;
7
+ tier: EnvironmentTier;
8
+ }
9
+
10
+ const ENVIRONMENT_TIERS = new Set<EnvironmentTier>([
11
+ 'local',
12
+ 'dev',
13
+ 'staging',
14
+ 'production'
15
+ ]);
16
+
17
+ /**
18
+ * Resolve the active environment for secrets commands.
19
+ *
20
+ * Falls back to local when the environment config manifest/plugin graph is
21
+ * unavailable so end-users can still run local secrets workflows.
22
+ */
23
+ export async function resolveSecretsEnvironment(
24
+ ui: UIContext,
25
+ environmentOverride?: string
26
+ ): Promise<ResolvedSecretsEnvironment> {
27
+ if (environmentOverride && environmentOverride.length > 0) {
28
+ if (ENVIRONMENT_TIERS.has(environmentOverride as EnvironmentTier)) {
29
+ return {
30
+ name: environmentOverride,
31
+ tier: environmentOverride as EnvironmentTier
32
+ };
33
+ }
34
+ try {
35
+ const envConfig = await resolveCurrentEnvironmentConfig();
36
+ return {
37
+ name: environmentOverride,
38
+ tier: (envConfig.tier ?? 'local') as EnvironmentTier
39
+ };
40
+ } catch {
41
+ return { name: environmentOverride, tier: 'local' };
42
+ }
43
+ }
44
+
45
+ try {
46
+ const envConfig = await resolveCurrentEnvironmentConfig();
47
+ return {
48
+ name: envConfig.name,
49
+ tier: (envConfig.tier ?? 'local') as EnvironmentTier
50
+ };
51
+ } catch {
52
+ ui.warn(
53
+ "Environment config manifest is unavailable; defaulting to 'local'. Register @vibesdotdev/config/environment/plugin to enable named environment selection."
54
+ );
55
+ return { name: 'local', tier: 'local' };
56
+ }
57
+ }
@@ -0,0 +1,22 @@
1
+ import type { CLICommandAssetDescriptor } from '@vibesdotdev/cli/schemas/types';
2
+
3
+ const descriptor: CLICommandAssetDescriptor = {
4
+ kind: 'cli/command',
5
+ id: 'secrets.unset',
6
+ name: 'unset',
7
+ description: 'Remove a secret from the current environment',
8
+ group: 'secrets',
9
+ arguments: [
10
+ { name: 'key', description: 'Secret key name to remove', required: true }
11
+ ],
12
+ options: [
13
+ { flags: '--environment <name>', description: 'Target environment (default: current)' },
14
+ { flags: '--backend <id>', description: 'Target backend (default: primary for tier)' }
15
+ ],
16
+ surfaces: ['cli'],
17
+ hardware: ['consumer'],
18
+ enabled: true,
19
+ order: 30
20
+ };
21
+
22
+ export default descriptor;
@@ -0,0 +1,41 @@
1
+ import { getVibesRuntime } from '@vibesdotdev/runtime';
2
+ import type { UIContext } from '@vibesdotdev/cli/providers';
3
+ import type { SecretsStoreImplementation } from '../../kinds/store.interface';
4
+ import type { SecretsStoreDescriptor } from '../../kinds/store.schema';
5
+ import { resolveSecretsEnvironment } from '../shared/resolve-environment';
6
+
7
+ export default {
8
+ async execute(args: Record<string, unknown>, opts: Record<string, unknown>): Promise<void> {
9
+ const runtime = getVibesRuntime();
10
+ const ui = (await runtime.context('cli/ui')) as UIContext;
11
+ const key = args.key as string;
12
+ const options = opts as { environment?: string; backend?: string };
13
+
14
+ const { name: envName, tier: envTier } = await resolveSecretsEnvironment(
15
+ ui,
16
+ options.environment
17
+ );
18
+
19
+ const descriptors = runtime.assets('secrets/store').descriptors() as SecretsStoreDescriptor[];
20
+ const tierStores = descriptors
21
+ .filter((d) => {
22
+ if (options.backend) return d.id === options.backend;
23
+ return d.tiers.includes(envTier);
24
+ })
25
+ .sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
26
+
27
+ const target = tierStores[0];
28
+ if (!target) {
29
+ ui.error(`No secrets store available for tier: ${envTier}`);
30
+ process.exit(1);
31
+ }
32
+
33
+ const impl = (await runtime
34
+ .query('secrets/store')
35
+ .withId(target.id)
36
+ .resolve()) as SecretsStoreImplementation;
37
+ await impl.unset(envName, key);
38
+
39
+ ui.success(`Removed ${key} from [${target.id}] for environment: ${envName}`);
40
+ }
41
+ };
@@ -0,0 +1,151 @@
1
+ import type { DocsTopicDescriptor } from '@vibesdotdev/docs';
2
+
3
+ const descriptor: DocsTopicDescriptor = {
4
+ kind: 'docs/topic',
5
+ id: 'secrets.backends',
6
+ title: 'Secret Backends',
7
+ summary: 'Pluggable secret storage backends (vault, wrangler, encrypted-local)',
8
+ body: {
9
+ type: 'markdown',
10
+ sourceType: 'raw',
11
+ source: `---
12
+ title: Secret Backends
13
+ summary: Pluggable secret storage backends for different environments
14
+ tags: [secrets, backends, vault, wrangler, encryption]
15
+ parent: secrets
16
+ order: 1
17
+ surfaces: [cli, web, in-app]
18
+ hardware: [consumer, cloud]
19
+ ---
20
+
21
+ The secrets package uses a **pluggable backend architecture** to support different secret storage mechanisms across environments. Backends register themselves as \`secrets/store\` descriptors with the runtime.
22
+
23
+ ## Available backends
24
+
25
+ ### Vault backend (production cloud)
26
+
27
+ HashiCorp Vault backend for production cloud deployments:
28
+
29
+ \`\`\`ts
30
+ import { vaultBackend } from '@vibesdotdev/secrets-backend-vault';
31
+
32
+ await runtime.registerPlugin(vaultBackend.plugin({
33
+ address: process.env.VAULT_ADDR,
34
+ token: process.env.VAULT_TOKEN
35
+ }));
36
+ \`\`\`
37
+
38
+ **Use when:** Running in production cloud environments with Vault infrastructure.
39
+
40
+ ### Wrangler secrets (Cloudflare Workers)
41
+
42
+ Cloudflare Workers secrets via Wrangler:
43
+
44
+ \`\`\`ts
45
+ import { wranglerBackend } from '@vibesdotdev/secrets-backend-wrangler';
46
+
47
+ await runtime.registerPlugin(wranglerBackend.plugin());
48
+ \`\`\`
49
+
50
+ **Use when:** Deploying to Cloudflare Workers edge runtime.
51
+
52
+ ### Encrypted local backend (development)
53
+
54
+ Local development with encryption at rest:
55
+
56
+ \`\`\`ts
57
+ import { encryptedLocalBackend } from '@vibesdotdev/secrets-backend-encrypted-local';
58
+
59
+ await runtime.registerPlugin(encryptedLocalBackend.plugin({
60
+ keyPath: '.vibes/secrets.key',
61
+ dataPath: '.vibes/secrets.enc.json'
62
+ }));
63
+ \`\`\`
64
+
65
+ **Use when:** Local development, testing, or single-user consumer apps.
66
+
67
+ ## Backend resolution
68
+
69
+ The runtime selects backends based on **environment tier** and **priority**:
70
+
71
+ \`\`\`ts
72
+ const store = await runtime.query('secrets/store')
73
+ .forHardware('consumer')
74
+ .resolve();
75
+
76
+ // Resolution logic:
77
+ // 1. Filter by environment tier (from scope qualifiers)
78
+ // 2. Sort by priority (higher = preferred)
79
+ // 3. Return first match
80
+ \`\`\`
81
+
82
+ Environment tiers:
83
+ - \`local\` — Development environments
84
+ - \`staging\` — Staging/pre-production
85
+ - \`production\` — Production cloud
86
+
87
+ ## WRONG: Hardcoding backend selection
88
+
89
+ \`\`\`ts
90
+ // ❌ NEVER — bypasses tier-based resolution
91
+ const store = await runtime.query('secrets/store')
92
+ .withId('encrypted-local')
93
+ .resolve();
94
+ \`\`\`
95
+
96
+ ## RIGHT: Hardware-scoped resolution
97
+
98
+ \`\`\`ts
99
+ // ✅ Let runtime select based on tier + priority
100
+ const store = await runtime.query('secrets/store')
101
+ .forHardware('consumer')
102
+ .resolve();
103
+ \`\`\`
104
+
105
+ ## Backend registration
106
+
107
+ Backends register as \`secrets/store\` descriptors:
108
+
109
+ \`\`\`ts
110
+ export const vaultBackend = createRuntimePlugin({
111
+ id: 'secrets-backend-vault',
112
+ descriptors: [{
113
+ kind: 'secrets/store',
114
+ id: 'vault',
115
+ title: 'HashiCorp Vault',
116
+ tiers: ['production'],
117
+ priority: 100, // highest priority for production
118
+ implementation: VaultStoreImplementation
119
+ }]
120
+ });
121
+ \`\`\`
122
+
123
+ ## Missing backend behavior
124
+
125
+ **Hard rule:** Missing backends cause hard failure. The \`resolve()\` method throws if no backend has registered for the current scope.
126
+
127
+ \`\`\`ts
128
+ try {
129
+ const store = await runtime.query('secrets/store').resolve();
130
+ } catch (error) {
131
+ // No backend registered for current tier/hardware
132
+ console.error('No secrets backend available');
133
+ }
134
+ \`\`\`
135
+
136
+ :::card{title="See also"}
137
+ - [\`secrets.encryption\`](secrets.encryption) — Encryption at rest, key management
138
+ - [\`secrets.env-file\`](secrets.env-file) — .env file handling
139
+ - [\`config/environment\`](config.environment) — Environment tier configuration
140
+ :::
141
+ `
142
+ },
143
+ parent: 'secrets',
144
+ order: 1,
145
+ tags: ['secrets', 'backends', 'vault', 'wrangler', 'encryption'],
146
+ surfaces: ['cli', 'web', 'in-app'],
147
+ hardware: ['consumer', 'cloud'],
148
+ enabled: true
149
+ };
150
+
151
+ export default descriptor;
@@ -0,0 +1,165 @@
1
+ import type { DocsTopicDescriptor } from '@vibesdotdev/docs';
2
+
3
+ const descriptor: DocsTopicDescriptor = {
4
+ kind: 'docs/topic',
5
+ id: 'secrets.encryption',
6
+ title: 'Encryption at Rest',
7
+ summary: 'Secret encryption, key management, and rotation strategies',
8
+ body: {
9
+ type: 'markdown',
10
+ sourceType: 'raw',
11
+ source: `---
12
+ title: Encryption at Rest
13
+ summary: Secret encryption, key management, and rotation strategies
14
+ tags: [secrets, encryption, key-management, rotation, security]
15
+ parent: secrets
16
+ order: 2
17
+ surfaces: [cli, web, in-app]
18
+ hardware: [consumer, cloud]
19
+ ---
20
+
21
+ Secrets are **encrypted at rest** using industry-standard algorithms. Encryption is handled automatically by backends — applications never see plaintext encryption keys.
22
+
23
+ ## Encryption algorithms
24
+
25
+ ### AES-256-GCM (symmetric)
26
+
27
+ Default algorithm for secret encryption:
28
+
29
+ - **Key size:** 256 bits
30
+ - **Mode:** GCM (Galois/Counter Mode)
31
+ - **IV:** 96-bit random nonce per encryption
32
+ - **Tag:** 128-bit authentication tag
33
+
34
+ \`\`\`ts
35
+ import { encrypt, decrypt } from '@vibesdotdev/secrets/encryption';
36
+
37
+ const key = await deriveKeyFromMasterKey(masterKey, salt);
38
+ const ciphertext = await encrypt(plaintext, key);
39
+ // ciphertext = { iv, tag, data }
40
+ \`\`\`
41
+
42
+ ### Key derivation (PBKDF2)
43
+
44
+ Master keys are derived from user-provided passphrases:
45
+
46
+ - **Algorithm:** PBKDF2-HMAC-SHA256
47
+ - **Iterations:** 100,000 (adjusts with hardware)
48
+ - **Salt:** 32-byte random per key
49
+
50
+ \`\`\`ts
51
+ import { deriveKeyFromPassphrase } from '@vibesdotdev/secrets/encryption';
52
+
53
+ const masterKey = await deriveKeyFromPassphrase(passphrase, salt, {
54
+ iterations: 100_000,
55
+ keyLength: 32
56
+ });
57
+ \`\`\`
58
+
59
+ ## Key management
60
+
61
+ ### Master key storage
62
+
63
+ Master keys are stored **separately** from encrypted data:
64
+
65
+ 1. **Hardware security module (HSM)** — Production cloud
66
+ 2. **OS keychain** — Local development (macOS Keychain, Windows Credential Manager)
67
+ 3. **Environment variable** — CI/CD (never committed)
68
+
69
+ ### Key rotation
70
+
71
+ Keys can be rotated without decrypting data:
72
+
73
+ \`\`\`ts
74
+ import { rotateKey, reEncryptSecrets } from '@vibesdotdev/secrets/encryption';
75
+
76
+ // Generate new key
77
+ const newKey = await generateRandomKey(32);
78
+
79
+ // Re-encrypt all secrets with new key
80
+ await reEncryptSecrets(oldKey, newKey);
81
+
82
+ // Update key storage
83
+ await storeMasterKey(newKey);
84
+ \`\`\`
85
+
86
+ **Rotation triggers:**
87
+ - Scheduled rotation (every 90 days)
88
+ - Suspected compromise
89
+ - Employee offboarding
90
+ - Compliance requirements
91
+
92
+ ## Encryption envelope
93
+
94
+ Encrypted secrets use a standard envelope format:
95
+
96
+ \`\`\`json
97
+ {
98
+ "version": 1,
99
+ "algorithm": "aes-256-gcm",
100
+ "kdf": "pbkdf2-hmac-sha256",
101
+ "salt": "base64-encoded-salt",
102
+ "iv": "base64-encoded-iv",
103
+ "tag": "base64-encoded-tag",
104
+ "ciphertext": "base64-encoded-ciphertext"
105
+ }
106
+ \`\`\`
107
+
108
+ ## WRONG: Manual encryption
109
+
110
+ \`\`\`ts
111
+ // ❌ NEVER — use backend APIs instead
112
+ import { encrypt } from '@vibesdotdev/secrets/encryption';
113
+
114
+ const encrypted = await encrypt(secret, key);
115
+ await fs.writeFile('.secrets.enc.json', JSON.stringify(encrypted));
116
+ \`\`\`
117
+
118
+ ## RIGHT: Backend-managed encryption
119
+
120
+ \`\`\`ts
121
+ // ✅ Backend handles encryption automatically
122
+ const store = await runtime.query('secrets/store').resolve();
123
+ await store.set('API_KEY', '<api-key>');
124
+ // Encryption happens transparently
125
+ \`\`\`
126
+
127
+ ## Compliance considerations
128
+
129
+ ### SOC 2 Type II
130
+
131
+ - ✅ Encryption at rest (AES-256)
132
+ - ✅ Key rotation policies
133
+ - ✅ Access logging and auditing
134
+ - ✅ Separation of duties (key custodians)
135
+
136
+ ### GDPR
137
+
138
+ - ✅ Data minimization (encrypt only what's needed)
139
+ - ✅ Right to erasure (delete keys = delete data)
140
+ - ✅ Data processing records (audit logs)
141
+
142
+ ## Key backup
143
+
144
+ **Critical:** Losing the master key means **permanent data loss**. Backups are essential:
145
+
146
+ 1. **Shamir's Secret Sharing** — Split key into N shares, require M to reconstruct
147
+ 2. **Geographic distribution** — Store shares in different regions
148
+ 3. **Hardware tokens** — YubiKey, smartcards for key storage
149
+
150
+ :::card{title="See also"}
151
+ - [\`secrets.backends\`](secrets.backends) — Secret backend implementations
152
+ - [\`secrets.env-file\`](secrets.env-file) — Encrypted .env files
153
+ - [\`config/secrets\`](config.secrets) — Secret requirements and validation
154
+ :::
155
+ `
156
+ },
157
+ parent: 'secrets',
158
+ order: 2,
159
+ tags: ['secrets', 'encryption', 'key-management', 'rotation', 'security'],
160
+ surfaces: ['cli', 'web', 'in-app'],
161
+ hardware: ['consumer', 'cloud'],
162
+ enabled: true
163
+ };
164
+
165
+ export default descriptor;
@@ -0,0 +1,209 @@
1
+ import type { DocsTopicDescriptor } from '@vibesdotdev/docs';
2
+
3
+ const descriptor: DocsTopicDescriptor = {
4
+ kind: 'docs/topic',
5
+ id: 'secrets.env-file',
6
+ title: 'Encrypted Environment Files',
7
+ summary: '.env file encryption, decryption, and secure handling',
8
+ body: {
9
+ type: 'markdown',
10
+ sourceType: 'raw',
11
+ source: `---
12
+ title: Encrypted Environment Files
13
+ summary: Secure .env file handling with encryption at rest
14
+ tags: [secrets, env, dotenv, encryption, configuration]
15
+ parent: secrets
16
+ order: 3
17
+ surfaces: [cli, web, in-app]
18
+ hardware: [consumer, cloud]
19
+ man:
20
+ name: "secrets.env-file — Encrypted environment files"
21
+ section: 1
22
+ synopsis: "vibes man secrets.env-file"
23
+ seeAlso: ["secrets.set", "secrets.reveal", "config.environment"]
24
+ ---
25
+
26
+ Environment files (\`.env\`) can be **encrypted at rest** to protect secrets in version control and local storage.
27
+
28
+ ## File formats
29
+
30
+ ### Plaintext .env (development only)
31
+
32
+ \`\`\`bash
33
+ # .env (development only — never commit)
34
+ DATABASE_URL=postgres://localhost/mydb
35
+ API_KEY=sk-test-123456789
36
+ \`\`\`
37
+
38
+ **Security:** ❌ No encryption — suitable for local development only.
39
+
40
+ ### Encrypted .env.enc
41
+
42
+ \`\`\`bash
43
+ # .env.enc (safe to commit)
44
+ {
45
+ "version": 1,
46
+ "algorithm": "aes-256-gcm",
47
+ "salt": "...",
48
+ "iv": "...",
49
+ "tag": "...",
50
+ "ciphertext": "..."
51
+ }
52
+ \`\`\`
53
+
54
+ **Security:** ✅ Encrypted — safe to commit to version control.
55
+
56
+ ## CLI commands
57
+
58
+ ### Encrypt a .env file
59
+
60
+ \`\`\`bash
61
+ vibes secrets encrypt .env --output .env.enc
62
+ \`\`\`
63
+
64
+ Prompts for encryption passphrase (or uses \`VIBES_SECRET_PASSPHRASE\` env var).
65
+
66
+ ### Decrypt a .env.enc file
67
+
68
+ \`\`\`bash
69
+ vibes secrets decrypt .env.enc --output .env
70
+ \`\`\`
71
+
72
+ Creates decrypted file in memory only — never writes plaintext to disk.
73
+
74
+ ### Reveal secrets (one-time)
75
+
76
+ \`\`\`bash
77
+ vibes secrets reveal --format export
78
+ # Output:
79
+ # export DATABASE_URL="postgres://..."
80
+ # export API_KEY="<api-key>"
81
+ \`\`\`
82
+
83
+ ## Programmatic usage
84
+
85
+ ### Load encrypted .env
86
+
87
+ \`\`\`ts
88
+ import { loadEncryptedEnv } from '@vibesdotdev/secrets/env-file';
89
+
90
+ const secrets = await loadEncryptedEnv('.env.enc', {
91
+ passphrase: process.env.VIBES_SECRET_PASSPHRASE
92
+ });
93
+
94
+ // Secrets available in memory only
95
+ console.log(secrets.DATABASE_URL);
96
+ \`\`\`
97
+
98
+ ### Write encrypted .env
99
+
100
+ \`\`\`ts
101
+ import { writeEncryptedEnv } from '@vibesdotdev/secrets/env-file';
102
+
103
+ await writeEncryptedEnv('.env.enc', {
104
+ DATABASE_URL: 'postgres://...',
105
+ API_KEY: '<api-key>'
106
+ }, {
107
+ passphrase: 'your-passphrase'
108
+ });
109
+ \`\`\`
110
+
111
+ ## Integration with backends
112
+
113
+ Encrypted .env files can **sync** with secret backends:
114
+
115
+ ### Push to backend
116
+
117
+ \`\`\`bash
118
+ vibes secrets push --from .env.enc
119
+ \`\`\`
120
+
121
+ Decrypts and uploads secrets to the configured backend (Vault, Wrangler, etc.).
122
+
123
+ ### Pull from backend
124
+
125
+ \`\`\`bash
126
+ vibes secrets pull --to .env.enc
127
+ \`\`\`
128
+
129
+ Downloads secrets from backend and writes encrypted file.
130
+
131
+ ## WRONG: Committing plaintext .env
132
+
133
+ \`\`\`bash
134
+ # ❌ NEVER — add to .gitignore immediately
135
+ echo ".env" >> .gitignore
136
+ git add .env # DON'T DO THIS
137
+ \`\`\`
138
+
139
+ ## RIGHT: Commit encrypted version
140
+
141
+ \`\`\`bash
142
+ # ✅ Safe to commit
143
+ vibes secrets encrypt .env --output .env.enc
144
+ git add .env.enc
145
+ git commit -m "Add encrypted environment"
146
+ \`\`\`
147
+
148
+ ## Pre-commit hook
149
+
150
+ The pre-commit hook **blocks** plaintext secrets:
151
+
152
+ \`\`\`bash
153
+ # .git/hooks/pre-commit
154
+ vibes secrets pre-commit-check
155
+ \`\`\`
156
+
157
+ Scans staged files for:
158
+ - AWS access keys (\`AKIA...\`)
159
+ - PEM private-key headers
160
+ - API tokens (Stripe, GitHub, etc.)
161
+ - High-entropy strings (potential secrets)
162
+
163
+ ## Environment variable injection
164
+
165
+ Decrypted secrets are injected as environment variables:
166
+
167
+ \`\`\`ts
168
+ // apps/my-app/src/server/hooks/env.ts
169
+ import { loadEncryptedEnv } from '@vibesdotdev/secrets/env-file';
170
+
171
+ export async function handle(): Promise<void> {
172
+ const secrets = await loadEncryptedEnv('.env.enc');
173
+ Object.entries(secrets).forEach(([key, value]) => {
174
+ process.env[key] = value;
175
+ });
176
+ }
177
+ \`\`\`
178
+
179
+ ## File permissions
180
+
181
+ Encrypted files should have restrictive permissions:
182
+
183
+ \`\`\`bash
184
+ chmod 600 .env.enc # Owner read/write only
185
+ chmod 600 .vibes/ # Secrets directory
186
+ \`\`\`
187
+
188
+ :::card{title="See also"}
189
+ - [\`secrets.backends\`](secrets.backends) — Secret storage backends
190
+ - [\`secrets.encryption\`](secrets.encryption) — Encryption algorithms
191
+ - [\`secrets.check\`](secrets.check) — Secret scanning and validation
192
+ :::
193
+ `
194
+ },
195
+ parent: 'secrets',
196
+ order: 3,
197
+ tags: ['secrets', 'env', 'dotenv', 'encryption', 'configuration'],
198
+ surfaces: ['cli', 'web', 'in-app'],
199
+ hardware: ['consumer', 'cloud'],
200
+ man: {
201
+ name: 'secrets.env-file — Encrypted environment files',
202
+ section: 1,
203
+ synopsis: 'vibes man secrets.env-file',
204
+ seeAlso: ['secrets.set', 'secrets.reveal', 'config.environment']
205
+ },
206
+ enabled: true
207
+ };
208
+
209
+ export default descriptor;
package/src/index.ts ADDED
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @vibesdotdev/secrets
3
+ *
4
+ * Environment-aware secrets management with pluggable backends.
5
+ * Provides the secrets/store runtime kind. Backends live in sibling packages.
6
+ *
7
+ * Hard rule: missing-backend means hard failure. resolve() throws if no
8
+ * backend has registered for the current scope.
9
+ */
10
+
11
+ export {
12
+ SecretsStoreDescriptorSchema,
13
+ EnvironmentTierSchema,
14
+ SecretsBackendSchema,
15
+ type SecretsStoreDescriptor,
16
+ type EnvironmentTier,
17
+ type SecretsBackend,
18
+ type SecretEntry,
19
+ type SecretsStoreImplementation,
20
+ secretsStoreKind
21
+ } from './kinds/index';
22
+
23
+ export {
24
+ SecretsImportManifestSchema,
25
+ SecretManifestEntrySchema,
26
+ SecretSourceSchema,
27
+ SecretCategorySchema,
28
+ canonicalImportManifest,
29
+ type SecretsImportManifest,
30
+ type SecretManifestEntry,
31
+ type SecretSource,
32
+ type SecretCategory
33
+ } from './manifest/index';
34
+
35
+ export { default as secretsPlugin } from './secrets.plugin';
@@ -0,0 +1,12 @@
1
+ export {
2
+ SecretsStoreDescriptorSchema,
3
+ EnvironmentTierSchema,
4
+ SecretsBackendSchema,
5
+ type SecretsStoreDescriptor,
6
+ type EnvironmentTier,
7
+ type SecretsBackend
8
+ } from './store.schema';
9
+
10
+ export type { SecretEntry, SecretsStoreImplementation } from './store.interface';
11
+
12
+ export { secretsStoreKind } from './store.kind';