asherah 4.0.26 → 4.0.28

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 (2) hide show
  1. package/README.md +241 -17
  2. package/package.json +9 -9
package/README.md CHANGED
@@ -2,16 +2,9 @@
2
2
 
3
3
  Node.js bindings for the Asherah envelope encryption and key rotation library.
4
4
 
5
- Prebuilt native binaries are published to npm for Linux (x64/arm64, glibc and
6
- musl), macOS (x64/arm64), and Windows (x64/arm64). The correct binary is
7
- selected automatically at install time.
8
-
9
- ## Features
10
-
11
- - Synchronous and asynchronous encrypt/decrypt APIs
12
- - Compatible with Go, Python, Ruby, Java, and .NET Asherah implementations
13
- - SQLite, MySQL, PostgreSQL, and DynamoDB metastore support
14
- - AWS KMS and static key management
5
+ Prebuilt native binaries are published to npm for Linux (x64/ARM64, glibc and
6
+ musl), macOS (x64/ARM64), and Windows (x64/ARM64). The correct binary is
7
+ selected automatically at install time. No compilation needed.
15
8
 
16
9
  ## Installation
17
10
 
@@ -19,24 +12,255 @@ selected automatically at install time.
19
12
  npm install asherah
20
13
  ```
21
14
 
22
- ## Quick start
15
+ Requires Node.js >= 18.
16
+
17
+ ## Quick Start (Static API)
18
+
19
+ The static API uses a global singleton. Call `setup()` once, then `encrypt`/`decrypt` from anywhere.
23
20
 
24
21
  ```js
25
22
  const asherah = require('asherah');
26
23
 
24
+ // Static master key for local development only.
25
+ // In production, use kms: 'aws' with a region map.
26
+ process.env.STATIC_MASTER_KEY_HEX = '22'.repeat(32);
27
+
27
28
  asherah.setup({
28
- kms: 'static',
29
- metastore: 'memory',
30
- serviceName: 'myservice',
31
- productId: 'myproduct',
29
+ serviceName: 'my-service',
30
+ productId: 'my-product',
31
+ metastore: 'memory', // testing only
32
+ kms: 'static', // testing only
33
+ enableSessionCaching: true,
32
34
  });
33
35
 
34
- const encrypted = asherah.encrypt('partition', Buffer.from('hello world'));
35
- const decrypted = asherah.decrypt('partition', encrypted);
36
+ // Encrypt raw bytes
37
+ const ciphertext = asherah.encrypt('my-partition', Buffer.from('secret data'));
38
+ const plaintext = asherah.decrypt('my-partition', ciphertext);
39
+ console.log(plaintext.toString()); // 'secret data'
40
+
41
+ // Or use the string convenience methods
42
+ const ct = asherah.encryptString('my-partition', 'hello world');
43
+ const pt = asherah.decryptString('my-partition', ct);
44
+ console.log(pt); // 'hello world'
36
45
 
37
46
  asherah.shutdown();
38
47
  ```
39
48
 
49
+ ## Session-Based API
50
+
51
+ The `SessionFactory` / `AsherahSession` pattern is preferred for production. It
52
+ avoids the global singleton and gives you explicit control over session
53
+ lifetimes.
54
+
55
+ ```js
56
+ const { SessionFactory } = require('asherah');
57
+
58
+ process.env.STATIC_MASTER_KEY_HEX = '22'.repeat(32);
59
+
60
+ const factory = new SessionFactory({
61
+ serviceName: 'my-service',
62
+ productId: 'my-product',
63
+ metastore: 'memory', // testing only
64
+ kms: 'static', // testing only
65
+ });
66
+
67
+ const session = factory.getSession('my-partition');
68
+
69
+ const ct = session.encrypt(Buffer.from('secret'));
70
+ const pt = session.decrypt(ct);
71
+ console.log(pt.toString()); // 'secret'
72
+
73
+ // String variants
74
+ const ct2 = session.encryptString('hello');
75
+ const pt2 = session.decryptString(ct2);
76
+ console.log(pt2); // 'hello'
77
+
78
+ session.close();
79
+ factory.close();
80
+ ```
81
+
82
+ You can also create a factory from environment variables:
83
+
84
+ ```js
85
+ const factory = SessionFactory.fromEnv();
86
+ ```
87
+
88
+ ## Async API
89
+
90
+ Every sync function has an async counterpart that returns a Promise and never
91
+ blocks the Node.js event loop.
92
+
93
+ ```js
94
+ const asherah = require('asherah');
95
+
96
+ process.env.STATIC_MASTER_KEY_HEX = '22'.repeat(32);
97
+
98
+ await asherah.setupAsync({
99
+ serviceName: 'my-service',
100
+ productId: 'my-product',
101
+ metastore: 'memory', // testing only
102
+ kms: 'static', // testing only
103
+ });
104
+
105
+ const ct = await asherah.encryptStringAsync('my-partition', 'secret');
106
+ const pt = await asherah.decryptStringAsync('my-partition', ct);
107
+ console.log(pt); // 'secret'
108
+
109
+ await asherah.shutdownAsync();
110
+ ```
111
+
112
+ ## Async Behavior
113
+
114
+ Async operations run on a Rust tokio runtime, separate from the Node.js event
115
+ loop. The exact execution strategy depends on the metastore:
116
+
117
+ | Metastore | Async Encrypt/Decrypt | Blocks Event Loop? |
118
+ |-----------|----------------------|-------------------|
119
+ | In-Memory | Runs on tokio worker thread | No |
120
+ | DynamoDB | True async AWS SDK calls on tokio | No |
121
+ | MySQL | `spawn_blocking` (sync driver on tokio thread pool) | No |
122
+ | Postgres | `spawn_blocking` (sync driver on tokio thread pool) | No |
123
+
124
+ **Async never blocks the Node.js event loop.** The tradeoff is ~12us overhead
125
+ per async call vs ~1us for sync (hot cache, 64B payload). Use sync in tight
126
+ loops where latency matters; use async when you need to keep the event loop
127
+ responsive.
128
+
129
+ ## Configuration
130
+
131
+ Pass a config object to `setup()`, `setupAsync()`, or the `SessionFactory`
132
+ constructor. Both camelCase and PascalCase field names are accepted (PascalCase
133
+ is auto-mapped for backward compatibility with the canonical Go-based package).
134
+
135
+ | Field | Type | Default | Description |
136
+ |-------|------|---------|-------------|
137
+ | `serviceName` | `string` | **(required)** | Service identifier for key hierarchy |
138
+ | `productId` | `string` | **(required)** | Product identifier for key hierarchy |
139
+ | `metastore` | `string` | **(required)** | `"rdbms"`, `"dynamodb"`, `"memory"` (testing) |
140
+ | `kms` | `string` | `"static"` | `"static"` or `"aws"` |
141
+ | `connectionString` | `string` | | Connection string for sqlite or rdbms metastore |
142
+ | `sqlMetastoreDBType` | `string` | | `"mysql"` or `"postgres"` (for rdbms metastore) |
143
+ | `enableSessionCaching` | `boolean` | `true` | Cache sessions by partition ID |
144
+ | `sessionCacheMaxSize` | `number` | `1000` | Max cached sessions |
145
+ | `sessionCacheDuration` | `number` | | Session cache TTL in milliseconds |
146
+ | `regionMap` | `object` | | `{ "us-west-2": "arn:aws:kms:..." }` for AWS KMS multi-region |
147
+ | `preferredRegion` | `string` | | Preferred AWS region for KMS |
148
+ | `enableRegionSuffix` | `boolean` | | Append region suffix to system key IDs |
149
+ | `expireAfter` | `number` | | Key expiration in milliseconds |
150
+ | `checkInterval` | `number` | | Key rotation check interval in milliseconds |
151
+ | `dynamoDBEndpoint` | `string` | | Custom DynamoDB endpoint URL |
152
+ | `dynamoDBRegion` | `string` | | DynamoDB region |
153
+ | `dynamoDBTableName` | `string` | | DynamoDB table name |
154
+ | `replicaReadConsistency` | `string` | | DynamoDB read consistency |
155
+ | `verbose` | `boolean` | `false` | Enable debug logging |
156
+ | `enableCanaries` | `boolean` | | Enable canary key verification |
157
+ | `disableZeroCopy` | `boolean` | | Disable zero-copy optimizations |
158
+ | `nullDataCheck` | `boolean` | | Verify data is not null before decrypt |
159
+
160
+ ### Environment Variables
161
+
162
+ - `STATIC_MASTER_KEY_HEX` -- 64 hex chars (32 bytes) for static KMS. **Testing only.**
163
+ - `ASHERAH_NODE_DEBUG=1` -- Enable native debug logging.
164
+
165
+ ## Performance
166
+
167
+ This is a native Rust implementation compiled via napi-rs. Typical latencies on
168
+ Apple M4 Max (in-memory metastore, session caching enabled, 64-byte payload):
169
+
170
+ | Operation | Sync | Async |
171
+ |-----------|------|-------|
172
+ | Encrypt | ~970 ns | ~12 us |
173
+ | Decrypt | ~1,200 ns | ~12 us |
174
+
175
+ See `scripts/benchmark.sh` for head-to-head comparisons with the canonical
176
+ Go-based implementation.
177
+
178
+ ## Migration from Canonical (v3.x)
179
+
180
+ This package is a drop-in replacement for the Go-based `asherah` npm package
181
+ (v3.x). The JavaScript wrapper provides full backward compatibility:
182
+
183
+ - **PascalCase config** -- `ServiceName`, `ProductID`, `Metastore`, etc. are
184
+ auto-mapped to camelCase equivalents.
185
+ - **snake_case function aliases** -- `set_log_hook`, `get_setup_status`,
186
+ `encrypt_string`, `decrypt_string_async`, etc. all work.
187
+ - **Metastore/KMS aliases** -- `"test-debug-memory"`, `"test-debug-static"`,
188
+ etc. are normalized to their short forms.
189
+ - **`set_log_hook` callback signature** -- Both the canonical
190
+ `(level: number, message: string)` and the native
191
+ `(event: { level, message, target })` signatures are supported.
192
+
193
+ To migrate, update your package version. No code changes required.
194
+
195
+ ## Supported Platforms
196
+
197
+ | Platform | Architecture | Notes |
198
+ |----------|-------------|-------|
199
+ | Linux | x64 | glibc (most distros) |
200
+ | Linux | x64 | musl (Alpine) |
201
+ | Linux | ARM64 | glibc |
202
+ | Linux | ARM64 | musl (Alpine) |
203
+ | macOS | x64 | Intel Macs |
204
+ | macOS | ARM64 | Apple Silicon |
205
+ | Windows | x64 | MSVC |
206
+ | Windows | ARM64 | MSVC |
207
+
208
+ ## API Reference
209
+
210
+ ### Setup / Teardown
211
+
212
+ - `setup(config)` -- Initialize the global Asherah instance.
213
+ - `setupAsync(config)` -- Async variant of `setup`.
214
+ - `shutdown()` -- Shut down and release all resources.
215
+ - `shutdownAsync()` -- Async variant of `shutdown`.
216
+ - `getSetupStatus()` -- Returns `true` if `setup` has been called.
217
+
218
+ ### Encrypt / Decrypt (Static API)
219
+
220
+ - `encrypt(partitionId, data: Buffer)` -- Returns JSON string (DataRowRecord).
221
+ - `encryptAsync(partitionId, data: Buffer)` -- Async variant.
222
+ - `encryptString(partitionId, data: string)` -- String-in, string-out convenience.
223
+ - `encryptStringAsync(partitionId, data: string)` -- Async variant.
224
+ - `decrypt(partitionId, dataRowRecord: string | Buffer)` -- Returns `Buffer`.
225
+ - `decryptAsync(partitionId, dataRowRecord: string | Buffer)` -- Async variant.
226
+ - `decryptString(partitionId, dataRowRecord: string)` -- Returns `string`.
227
+ - `decryptStringAsync(partitionId, dataRowRecord: string)` -- Async variant.
228
+
229
+ ### Session-Based API
230
+
231
+ - `new SessionFactory(config)` -- Create a factory with explicit config.
232
+ - `SessionFactory.fromEnv()` -- Create a factory from environment variables.
233
+ - `factory.getSession(partitionId)` -- Get a session for a partition.
234
+ - `factory.close()` -- Close the factory and release resources.
235
+ - `session.encrypt(data: Buffer)` -- Returns JSON string.
236
+ - `session.encryptString(data: string)` -- String convenience.
237
+ - `session.decrypt(dataRowRecord: string)` -- Returns `Buffer`.
238
+ - `session.decryptString(dataRowRecord: string)` -- Returns `string`.
239
+ - `session.close()` -- Close the session.
240
+
241
+ ### Hooks
242
+
243
+ - `setLogHook(callback)` / `set_log_hook(callback)` -- Receive log events.
244
+ Pass `null` to disable.
245
+ - `setMetricsHook(callback)` -- Receive metrics events
246
+ (`{ type, durationNs?, name? }`). Pass `null` to disable.
247
+
248
+ ### Utility
249
+
250
+ - `setenv(lines: string)` -- Set environment variables from `KEY=VALUE` lines.
251
+ - `setMaxStackAllocItemSize(n)` -- No-op (compatibility stub).
252
+ - `setSafetyPaddingOverhead(n)` -- No-op (compatibility stub).
253
+
254
+ ## Features
255
+
256
+ - Synchronous and asynchronous encrypt/decrypt APIs
257
+ - Session-based API with factory pattern
258
+ - Compatible with Go, Python, Ruby, Java, and .NET Asherah implementations
259
+ - SQLite, MySQL, PostgreSQL, and DynamoDB metastore support
260
+ - AWS KMS and static key management
261
+ - Log and metrics hooks
262
+ - Automatic key rotation with configurable intervals
263
+
40
264
  ## License
41
265
 
42
266
  Licensed under the Apache License, Version 2.0.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "asherah",
3
- "version": "4.0.26",
3
+ "version": "4.0.28",
4
4
  "private": false,
5
5
  "description": "Asherah envelope encryption and key rotation library",
6
6
  "main": "npm/index.js",
@@ -40,13 +40,13 @@
40
40
  "node": ">= 18"
41
41
  },
42
42
  "optionalDependencies": {
43
- "asherah-darwin-arm64": "4.0.26",
44
- "asherah-darwin-x64": "4.0.26",
45
- "asherah-linux-x64-gnu": "4.0.26",
46
- "asherah-linux-arm64-gnu": "4.0.26",
47
- "asherah-linux-x64-musl": "4.0.26",
48
- "asherah-linux-arm64-musl": "4.0.26",
49
- "asherah-windows-x64": "4.0.26",
50
- "asherah-windows-arm64": "4.0.26"
43
+ "asherah-darwin-arm64": "4.0.28",
44
+ "asherah-darwin-x64": "4.0.28",
45
+ "asherah-linux-x64-gnu": "4.0.28",
46
+ "asherah-linux-arm64-gnu": "4.0.28",
47
+ "asherah-linux-x64-musl": "4.0.28",
48
+ "asherah-linux-arm64-musl": "4.0.28",
49
+ "asherah-windows-x64": "4.0.28",
50
+ "asherah-windows-arm64": "4.0.28"
51
51
  }
52
52
  }