@molecule/api-encryption-aes 1.0.0 → 1.0.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/README.md +233 -0
- package/package.json +8 -7
package/README.md
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
AUTO-GENERATED — DO NOT EDIT THIS FILE.
|
|
3
|
+
Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
|
|
4
|
+
Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
|
|
5
|
+
To change this document, edit the module-level JSDoc in src/index.ts.
|
|
6
|
+
Generated: 2026-08-04T01:48:06.190Z
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
# @molecule/api-encryption-aes
|
|
10
|
+
|
|
11
|
+
> **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
|
|
12
|
+
> It is written to be read by coding agents as much as by people, and is generated from this
|
|
13
|
+
> package's source — edit `src/index.ts` JSDoc, not this file.
|
|
14
|
+
|
|
15
|
+
AES-256-GCM encryption provider for molecule.dev.
|
|
16
|
+
|
|
17
|
+
Uses Node.js built-in `crypto` for AES-256-GCM authenticated encryption,
|
|
18
|
+
SHA-256 hashing, and timing-safe verification. Supports key rotation with
|
|
19
|
+
versioned ciphertext format.
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { setProvider } from '@molecule/api-encryption'
|
|
25
|
+
import { provider } from '@molecule/api-encryption-aes'
|
|
26
|
+
|
|
27
|
+
// Wire the provider at startup (reads ENCRYPTION_KEY from env)
|
|
28
|
+
setProvider(provider)
|
|
29
|
+
|
|
30
|
+
// Or create with explicit config
|
|
31
|
+
import { createProvider } from '@molecule/api-encryption-aes'
|
|
32
|
+
const customProvider = createProvider({ key: 'your-64-char-hex-key' })
|
|
33
|
+
setProvider(customProvider)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Type
|
|
37
|
+
|
|
38
|
+
`provider`
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install @molecule/api-encryption-aes @molecule/api-encryption @molecule/api-secrets
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## API
|
|
47
|
+
|
|
48
|
+
### Interfaces
|
|
49
|
+
|
|
50
|
+
#### `AesConfig`
|
|
51
|
+
|
|
52
|
+
Configuration options for the AES-256-GCM encryption provider.
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
interface AesConfig {
|
|
56
|
+
/** The current 256-bit encryption key, hex-encoded (64 hex characters). */
|
|
57
|
+
key: string
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Version of the current `key`. Ciphertext produced by `encrypt()` is tagged
|
|
61
|
+
* with this version; `decrypt()` selects the key to use from the `v{n}` tag,
|
|
62
|
+
* so it must be stable for a given key. `rotateKey()` advances it.
|
|
63
|
+
*
|
|
64
|
+
* @default 1
|
|
65
|
+
*/
|
|
66
|
+
keyVersion?: number
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Prior keys to seed the keyring with, so ciphertext encrypted before a
|
|
70
|
+
* rotation still decrypts after this provider is (re)constructed — e.g. on a
|
|
71
|
+
* process restart, when the in-memory rotation state would otherwise be lost.
|
|
72
|
+
* Each entry maps a historical `v{n}` version to its key. The current `key`
|
|
73
|
+
* (at {@link AesConfig.keyVersion}) always wins over any prior entry sharing
|
|
74
|
+
* its version.
|
|
75
|
+
*
|
|
76
|
+
* @default []
|
|
77
|
+
*/
|
|
78
|
+
priorKeys?: PriorKey[]
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### `AesEncryptionProvider`
|
|
83
|
+
|
|
84
|
+
AES-256-GCM encryption provider.
|
|
85
|
+
|
|
86
|
+
Fully satisfies the core `EncryptionProvider` contract, so it bonds anywhere
|
|
87
|
+
an `EncryptionProvider` is expected. It additionally exposes
|
|
88
|
+
{@link AesEncryptionProvider.pruneKeyVersions} to explicitly retire old keys
|
|
89
|
+
once their ciphertext has been re-encrypted — rotation alone never drops a
|
|
90
|
+
key, so it can never orphan data.
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
interface AesEncryptionProvider extends EncryptionProvider {
|
|
94
|
+
/**
|
|
95
|
+
* Retires key versions from the in-memory keyring. Call this only AFTER every
|
|
96
|
+
* ciphertext encrypted under those versions has been re-encrypted under the
|
|
97
|
+
* current key — a pruned version can no longer decrypt its ciphertext.
|
|
98
|
+
*
|
|
99
|
+
* The current key version is ALWAYS retained (it cannot be pruned). By
|
|
100
|
+
* default every non-current version is removed; pass `keep` to retain
|
|
101
|
+
* specific older versions during a staged migration.
|
|
102
|
+
*
|
|
103
|
+
* @param keep - Old versions to retain in addition to the current one.
|
|
104
|
+
* @returns The versions that were removed.
|
|
105
|
+
*/
|
|
106
|
+
pruneKeyVersions(keep?: number[]): number[]
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
#### `PriorKey`
|
|
111
|
+
|
|
112
|
+
A historical (pre-rotation) key, retained so ciphertext encrypted under it
|
|
113
|
+
remains decryptable.
|
|
114
|
+
|
|
115
|
+
Ciphertext is tagged with the `v{version}` of the key that produced it; the
|
|
116
|
+
provider selects the matching key from its keyring at decrypt time. Seed the
|
|
117
|
+
keyring with the app's prior keys (from a secret store) so rotations survive
|
|
118
|
+
a process restart — see {@link AesConfig.priorKeys}.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
interface PriorKey {
|
|
122
|
+
/** The key version tag this key decrypts (matches the `v{n}` ciphertext prefix). */
|
|
123
|
+
version: number
|
|
124
|
+
|
|
125
|
+
/** The 256-bit key, hex-encoded (64 hex characters). */
|
|
126
|
+
key: string
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Functions
|
|
131
|
+
|
|
132
|
+
#### `createProvider(config)`
|
|
133
|
+
|
|
134
|
+
Creates an AES-256-GCM encryption provider.
|
|
135
|
+
|
|
136
|
+
Rotation is transition-safe: keys are held in a version-keyed keyring.
|
|
137
|
+
`encrypt()` tags each ciphertext with the current key version, `decrypt()`
|
|
138
|
+
selects the key named by that tag, and `rotateKey()` ADDS the new key while
|
|
139
|
+
keeping prior keys — so ciphertext from before a rotation still decrypts.
|
|
140
|
+
Retire old keys explicitly (once their data is re-encrypted) via
|
|
141
|
+
`pruneKeyVersions()`.
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
function createProvider(config: AesConfig): AesEncryptionProvider
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
- `config` — Provider configuration including the hex-encoded 256-bit key.
|
|
148
|
+
|
|
149
|
+
**Returns:** An `AesEncryptionProvider` using AES-256-GCM.
|
|
150
|
+
|
|
151
|
+
### Constants
|
|
152
|
+
|
|
153
|
+
#### `encryptionAesSecretDefinitions`
|
|
154
|
+
|
|
155
|
+
Secret definitions required by the AES encryption bond.
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
const encryptionAesSecretDefinitions: SecretDefinition[]
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### `provider`
|
|
162
|
+
|
|
163
|
+
Default AES-256-GCM encryption provider instance.
|
|
164
|
+
|
|
165
|
+
Lazily initializes on first property access using the `ENCRYPTION_KEY`
|
|
166
|
+
environment variable (hex-encoded 256-bit key). This singleton starts a
|
|
167
|
+
fresh keyring at version 1 on each process, so a rotation performed in a
|
|
168
|
+
prior process is NOT restored here — for rotation that survives restarts,
|
|
169
|
+
construct with `createProvider({ key, priorKeys })` seeding the historical
|
|
170
|
+
keys from your secret store.
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
const provider: AesEncryptionProvider
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Core Interface
|
|
177
|
+
|
|
178
|
+
Implements `@molecule/api-encryption` interface.
|
|
179
|
+
|
|
180
|
+
## Bond Wiring
|
|
181
|
+
|
|
182
|
+
Setup function to register this provider with the core interface:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
import { setProvider } from '@molecule/api-encryption'
|
|
186
|
+
import { provider } from '@molecule/api-encryption-aes'
|
|
187
|
+
|
|
188
|
+
export function setupEncryptionAes(): void {
|
|
189
|
+
setProvider(provider)
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Injection Notes
|
|
194
|
+
|
|
195
|
+
### Requirements
|
|
196
|
+
|
|
197
|
+
Peer dependencies:
|
|
198
|
+
|
|
199
|
+
- `@molecule/api-encryption` ^1.0.1
|
|
200
|
+
- `@molecule/api-secrets` ^1.0.1
|
|
201
|
+
|
|
202
|
+
### Environment Variables
|
|
203
|
+
|
|
204
|
+
- `ENCRYPTION_KEY` _(required)_ — AES-256 encryption key
|
|
205
|
+
- **Auto-generated at scaffold — no manual setup.**
|
|
206
|
+
|
|
207
|
+
### Runtime Dependencies
|
|
208
|
+
|
|
209
|
+
- `@molecule/api-encryption`
|
|
210
|
+
- `@molecule/api-secrets`
|
|
211
|
+
|
|
212
|
+
- **`rotateKey()` is transition-safe — it never orphans data.** Keys live in
|
|
213
|
+
a version-keyed keyring: `encrypt()` tags each ciphertext with the current
|
|
214
|
+
`v{n}`, `decrypt()` reads that tag and selects the matching key, and
|
|
215
|
+
`rotateKey(oldKey, newKey)` ADDS `newKey` at the next version while RETAINING
|
|
216
|
+
the prior key(s). So ciphertext written before a rotation still decrypts
|
|
217
|
+
afterward (the core contract's "previously encrypted data can still be
|
|
218
|
+
decrypted during a transition period"). Once you have re-encrypted the old
|
|
219
|
+
ciphertext under the new key, retire the old keys explicitly with
|
|
220
|
+
`pruneKeyVersions()` — rotation alone deliberately keeps them.
|
|
221
|
+
- A ciphertext whose `v{n}` version is not in the keyring (unknown/pruned key)
|
|
222
|
+
fails cleanly with a descriptive error — never a silent wrong decrypt.
|
|
223
|
+
- Rotation state is per-process and in-memory: the lazy `provider` singleton
|
|
224
|
+
starts a fresh keyring at version 1 from `ENCRYPTION_KEY` on each process,
|
|
225
|
+
so a rotation done in a prior process is not restored. For rotation that
|
|
226
|
+
survives restarts, build with `createProvider({ key, priorKeys })`, seeding
|
|
227
|
+
the historical `{ version, key }` entries from your secret store.
|
|
228
|
+
- `hash()`/`verify()` are plain unsalted SHA-256 — integrity checks only.
|
|
229
|
+
NEVER use them for passwords; use `@molecule/api-password` with a bond
|
|
230
|
+
like `@molecule/api-password-bcrypt`.
|
|
231
|
+
- `encrypt(plaintext, context)`: the optional `context` is GCM AAD — the
|
|
232
|
+
SAME context string must be supplied to `decrypt()` or authentication
|
|
233
|
+
fails.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@molecule/api-encryption-aes",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "AES-256-GCM encryption provider for molecule.dev — authenticated encryption, SHA-256 hashing, and key rotation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
|
-
"dist"
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md"
|
|
21
22
|
],
|
|
22
23
|
"keywords": [
|
|
23
24
|
"molecule",
|
|
@@ -29,21 +30,21 @@
|
|
|
29
30
|
],
|
|
30
31
|
"license": "Apache-2.0",
|
|
31
32
|
"peerDependencies": {
|
|
32
|
-
"@molecule/api-encryption": "^1.0.
|
|
33
|
-
"@molecule/api-secrets": "^1.0.
|
|
33
|
+
"@molecule/api-encryption": "^1.0.1",
|
|
34
|
+
"@molecule/api-secrets": "^1.0.1"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
|
-
"@molecule/api-encryption": "1.0.
|
|
37
|
+
"@molecule/api-encryption": "1.0.2",
|
|
37
38
|
"@types/node": "26.1.2",
|
|
38
39
|
"typescript": "6.0.3",
|
|
39
|
-
"vitest": "4.1.
|
|
40
|
+
"vitest": "4.1.11"
|
|
40
41
|
},
|
|
41
42
|
"repository": {
|
|
42
43
|
"type": "git",
|
|
43
44
|
"url": "https://github.com/molecule-dev/molecule.git",
|
|
44
45
|
"directory": "packages/api/bonds/encryption/aes"
|
|
45
46
|
},
|
|
46
|
-
"homepage": "https://
|
|
47
|
+
"homepage": "https://www.molecule.dev/packages/api-encryption-aes",
|
|
47
48
|
"bugs": "https://github.com/molecule-dev/molecule/issues",
|
|
48
49
|
"publishConfig": {
|
|
49
50
|
"access": "public"
|