@venizia/ignis-docs 0.2.0 → 0.2.1-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 (174) hide show
  1. package/README.md +14 -14
  2. package/content/best-practices/api-usage-examples.md +39 -19
  3. package/content/best-practices/architectural-patterns.md +24 -13
  4. package/content/best-practices/architecture-decisions.md +29 -16
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
  6. package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
  7. package/content/best-practices/code-style-standards/control-flow.md +33 -1
  8. package/content/best-practices/code-style-standards/documentation.md +28 -8
  9. package/content/best-practices/code-style-standards/function-patterns.md +15 -4
  10. package/content/best-practices/code-style-standards/index.md +7 -2
  11. package/content/best-practices/code-style-standards/naming-conventions.md +27 -3
  12. package/content/best-practices/code-style-standards/route-definitions.md +9 -5
  13. package/content/best-practices/code-style-standards/tooling.md +14 -1
  14. package/content/best-practices/code-style-standards/type-safety.md +39 -6
  15. package/content/best-practices/common-pitfalls.md +59 -35
  16. package/content/best-practices/contribution-workflow.md +8 -4
  17. package/content/best-practices/data-modeling.md +78 -62
  18. package/content/best-practices/deployment-strategies.md +56 -19
  19. package/content/best-practices/error-handling.md +247 -153
  20. package/content/best-practices/index.md +6 -0
  21. package/content/best-practices/performance-optimization.md +26 -13
  22. package/content/best-practices/security-guidelines.md +35 -9
  23. package/content/best-practices/testing-strategies.md +7 -2
  24. package/content/best-practices/troubleshooting-tips.md +27 -17
  25. package/content/extensions/components/api-reference.md +109 -323
  26. package/content/extensions/components/authentication/api.md +489 -602
  27. package/content/extensions/components/authentication/errors.md +135 -498
  28. package/content/extensions/components/authentication/index.md +89 -801
  29. package/content/extensions/components/authentication/usage.md +231 -955
  30. package/content/extensions/components/authorization/api.md +991 -644
  31. package/content/extensions/components/authorization/errors.md +204 -208
  32. package/content/extensions/components/authorization/getting-started.md +227 -0
  33. package/content/extensions/components/authorization/index.md +88 -795
  34. package/content/extensions/components/authorization/usage.md +219 -528
  35. package/content/extensions/components/health-check.md +77 -243
  36. package/content/extensions/components/index.md +24 -90
  37. package/content/extensions/components/mail/api.md +553 -285
  38. package/content/extensions/components/mail/errors.md +76 -62
  39. package/content/extensions/components/mail/index.md +111 -463
  40. package/content/extensions/components/mail/usage.md +139 -173
  41. package/content/extensions/components/request-tracker.md +70 -173
  42. package/content/extensions/components/socket-io/api.md +462 -785
  43. package/content/extensions/components/socket-io/errors.md +49 -51
  44. package/content/extensions/components/socket-io/index.md +69 -372
  45. package/content/extensions/components/socket-io/usage.md +212 -105
  46. package/content/extensions/components/static-asset/api.md +461 -141
  47. package/content/extensions/components/static-asset/errors.md +121 -53
  48. package/content/extensions/components/static-asset/index.md +84 -606
  49. package/content/extensions/components/static-asset/usage.md +184 -299
  50. package/content/extensions/components/template/index.md +3 -3
  51. package/content/extensions/components/websocket/api.md +311 -404
  52. package/content/extensions/components/websocket/errors.md +47 -56
  53. package/content/extensions/components/websocket/index.md +75 -407
  54. package/content/extensions/components/websocket/usage.md +120 -338
  55. package/content/extensions/helpers/cron/index.md +52 -160
  56. package/content/extensions/helpers/crypto/index.md +67 -480
  57. package/content/extensions/helpers/crypto/reference.md +528 -0
  58. package/content/extensions/helpers/env/index.md +64 -178
  59. package/content/extensions/helpers/error/index.md +286 -196
  60. package/content/extensions/helpers/index.md +61 -47
  61. package/content/extensions/helpers/inversion/index.md +76 -550
  62. package/content/extensions/helpers/inversion/reference.md +530 -0
  63. package/content/extensions/helpers/kafka/admin.md +23 -3
  64. package/content/extensions/helpers/kafka/compile-binary.md +83 -52
  65. package/content/extensions/helpers/kafka/consumer.md +65 -28
  66. package/content/extensions/helpers/kafka/examples.md +46 -253
  67. package/content/extensions/helpers/kafka/index.md +55 -617
  68. package/content/extensions/helpers/kafka/producer.md +156 -22
  69. package/content/extensions/helpers/kafka/schema-registry.md +59 -79
  70. package/content/extensions/helpers/logger/hf-logger.md +220 -0
  71. package/content/extensions/helpers/logger/index.md +78 -552
  72. package/content/extensions/helpers/logger/pino.md +105 -0
  73. package/content/extensions/helpers/logger/reference.md +937 -0
  74. package/content/extensions/helpers/network/api.md +276 -195
  75. package/content/extensions/helpers/network/index.md +87 -524
  76. package/content/extensions/helpers/queue/index.md +80 -897
  77. package/content/extensions/helpers/queue/reference.md +494 -0
  78. package/content/extensions/helpers/redis/index.md +86 -640
  79. package/content/extensions/helpers/redis/reference.md +784 -0
  80. package/content/extensions/helpers/secrets/index.md +136 -0
  81. package/content/extensions/helpers/socket-io/api.md +325 -204
  82. package/content/extensions/helpers/socket-io/index.md +79 -429
  83. package/content/extensions/helpers/storage/api.md +584 -464
  84. package/content/extensions/helpers/storage/index.md +80 -573
  85. package/content/extensions/helpers/types/index.md +75 -495
  86. package/content/extensions/helpers/types/reference.md +689 -0
  87. package/content/extensions/helpers/uid/index.md +176 -189
  88. package/content/extensions/helpers/websocket/api.md +366 -214
  89. package/content/extensions/helpers/websocket/index.md +68 -503
  90. package/content/extensions/helpers/worker-thread/index.md +62 -396
  91. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  92. package/content/extensions/index.md +38 -39
  93. package/content/extensions/src-details/mcp-server.md +96 -548
  94. package/content/guides/core-concepts/persistent/datasources.md +2 -2
  95. package/content/guides/core-concepts/persistent/index.md +5 -1
  96. package/content/guides/core-concepts/persistent/models.md +1 -1
  97. package/content/guides/core-concepts/persistent/pglite.md +218 -0
  98. package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
  99. package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
  100. package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
  101. package/content/guides/core-concepts/persistent/sqlite.md +296 -0
  102. package/content/guides/core-concepts/persistent/transactions.md +12 -11
  103. package/content/guides/core-concepts/secrets-vault.md +177 -0
  104. package/content/guides/core-concepts/services.md +1 -1
  105. package/content/guides/get-started/5-minute-quickstart.md +59 -206
  106. package/content/guides/get-started/philosophy.md +135 -670
  107. package/content/guides/get-started/setup.md +53 -74
  108. package/content/guides/migrations/redis-helpers-migration.md +5 -4
  109. package/content/guides/migrations/scoped-rbac-migration.md +5 -5
  110. package/content/guides/migrations/unified-connectors-migration.md +8 -7
  111. package/content/guides/tutorials/ecommerce-api.md +3 -8
  112. package/content/guides/tutorials/realtime-chat.md +1 -1
  113. package/content/references/base/application.md +64 -22
  114. package/content/references/base/components.md +3 -3
  115. package/content/references/base/connectors.md +79 -136
  116. package/content/references/base/controllers.md +12 -12
  117. package/content/references/base/datasources-reference.md +600 -0
  118. package/content/references/base/datasources.md +84 -444
  119. package/content/references/base/dependency-injection.md +25 -46
  120. package/content/references/base/filter-system/application-usage.md +95 -123
  121. package/content/references/base/filter-system/array-operators.md +33 -43
  122. package/content/references/base/filter-system/comparison-operators.md +55 -63
  123. package/content/references/base/filter-system/default-filter.md +144 -350
  124. package/content/references/base/filter-system/fields-order-pagination.md +116 -148
  125. package/content/references/base/filter-system/index.md +114 -253
  126. package/content/references/base/filter-system/json-filtering.md +52 -181
  127. package/content/references/base/filter-system/list-operators.md +28 -44
  128. package/content/references/base/filter-system/logical-operators.md +69 -114
  129. package/content/references/base/filter-system/null-operators.md +41 -98
  130. package/content/references/base/filter-system/pattern-matching.md +48 -51
  131. package/content/references/base/filter-system/quick-reference.md +93 -196
  132. package/content/references/base/filter-system/range-operators.md +22 -38
  133. package/content/references/base/filter-system/tips.md +70 -133
  134. package/content/references/base/filter-system/use-cases.md +173 -232
  135. package/content/references/base/grpc-controllers.md +53 -17
  136. package/content/references/base/index.md +5 -3
  137. package/content/references/base/middlewares.md +43 -28
  138. package/content/references/base/models-reference.md +886 -0
  139. package/content/references/base/models.md +81 -1452
  140. package/content/references/base/providers.md +8 -8
  141. package/content/references/base/repositories/advanced.md +281 -419
  142. package/content/references/base/repositories/index.md +84 -644
  143. package/content/references/base/repositories/mixins.md +25 -21
  144. package/content/references/base/repositories/relations.md +194 -375
  145. package/content/references/base/repositories/soft-deletable.md +67 -55
  146. package/content/references/base/secrets.md +267 -0
  147. package/content/references/base/services.md +8 -6
  148. package/content/references/configuration/environment-variables.md +73 -21
  149. package/content/references/configuration/index.md +51 -31
  150. package/content/references/index.md +1 -1
  151. package/content/references/quick-reference.md +3 -16
  152. package/content/references/utilities/crypto.md +35 -76
  153. package/content/references/utilities/date.md +33 -73
  154. package/content/references/utilities/duration.md +85 -0
  155. package/content/references/utilities/index.md +6 -2
  156. package/content/references/utilities/jsx-reference.md +298 -0
  157. package/content/references/utilities/jsx.md +82 -525
  158. package/content/references/utilities/module.md +81 -61
  159. package/content/references/utilities/parse.md +34 -64
  160. package/content/references/utilities/performance.md +33 -58
  161. package/content/references/utilities/promise.md +28 -62
  162. package/content/references/utilities/request.md +58 -218
  163. package/content/references/utilities/retry.md +139 -0
  164. package/content/references/utilities/schema.md +43 -137
  165. package/content/references/utilities/statuses-reference.md +361 -0
  166. package/content/references/utilities/statuses.md +63 -667
  167. package/dist/mcp-server/index.js +0 -0
  168. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  169. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
  170. package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
  171. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  172. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
  173. package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
  174. package/package.json +24 -23
@@ -1,537 +1,124 @@
1
- # Crypto
2
-
3
- Cryptographic utilities for AES symmetric encryption, RSA asymmetric encryption, ECDH key exchange, and hashing.
4
-
5
- ## Quick Reference
6
-
7
- | Class | Extends | Use Case |
8
- |-------|---------|----------|
9
- | **AES** | BaseCryptoAlgorithm | Fast symmetric encryption (AES-256-CBC, AES-256-GCM) |
10
- | **RSA** | BaseCryptoAlgorithm | Public-key encryption with DER key pairs |
11
- | **ECDH** | AbstractCryptoAlgorithm | Ephemeral key exchange with AES-256-GCM session encryption |
12
- | **hash()** | _(standalone function)_ | MD5 and SHA256 HMAC hashing |
13
-
14
- #### Algorithm Comparison
15
-
16
- | Feature | AES | RSA | ECDH |
17
- |---------|-----|-----|------|
18
- | Type | Symmetric | Asymmetric | Asymmetric + Symmetric |
19
- | Key exchange | Shared secret | Public/private | Diffie-Hellman |
20
- | Speed | Fast | Slow (large keys) | Fast (small keys) |
21
- | Max message | Unlimited | ~214 bytes (2048-bit, OAEP) | Unlimited |
22
- | Async | No | No | Yes (Web Crypto) |
23
- | Runtime | Node.js `crypto` | Node.js `crypto` | `crypto.subtle` (Bun/Browser) |
24
-
25
- #### Import Paths
26
-
27
- ```typescript
28
- // Algorithm classes
29
- import { AES, RSA, ECDH } from '@venizia/ignis-helpers';
30
-
31
- // Hash utility function
32
- import { hash } from '@venizia/ignis-helpers';
33
-
34
- // Types
35
- import type {
36
- AESAlgorithmType,
37
- RSAAlgorithmType,
38
- ECDHAlgorithmType,
39
- IECDHEncryptedPayload,
40
- IECDHExtraOptions,
41
- ICryptoAlgorithm,
42
- } from '@venizia/ignis-helpers';
43
- ```
44
-
45
- #### Type Hierarchy
46
-
47
- All crypto algorithms share a common type hierarchy with 7 generic type parameters:
48
-
49
- ```
50
- ICryptoAlgorithm (interface)
51
- └── AbstractCryptoAlgorithm (extends BaseHelper)
52
- ├── BaseCryptoAlgorithm (adds normalizeSecretKey, getAlgorithmKeySize)
53
- │ ├── AES
54
- │ └── RSA
55
- └── ECDH (uses CryptoKey objects, not string secrets)
56
- ```
1
+ ---
2
+ title: Crypto
3
+ description: AES symmetric encryption, RSA asymmetric encryption, and ECDH key exchange helpers
4
+ difficulty: intermediate
5
+ ---
57
6
 
58
- **Why two base classes?**
59
- - `BaseCryptoAlgorithm` adds `normalizeSecretKey()` and `getAlgorithmKeySize()` -- useful for AES/RSA which use string secrets with size normalization
60
- - `ECDH` extends `AbstractCryptoAlgorithm` directly because it uses `CryptoKey` objects (Web Crypto), not string secrets
7
+ # Crypto
61
8
 
62
- ```typescript
63
- interface ICryptoAlgorithm<
64
- AlgorithmNameType extends string,
65
- EncryptInputType = unknown,
66
- DecryptInputType = unknown,
67
- SecretKeyType = unknown,
68
- EncryptReturnType = unknown,
69
- DecryptReturnType = unknown,
70
- ExtraOptions = unknown,
71
- > {
72
- algorithm: AlgorithmNameType;
73
- encrypt(opts: { message: EncryptInputType; secret: SecretKeyType; opts?: ExtraOptions }): EncryptReturnType;
74
- decrypt(opts: { message: DecryptInputType; secret: SecretKeyType; opts?: ExtraOptions }): DecryptReturnType;
75
- }
76
- ```
9
+ Cryptographic helpers for AES symmetric encryption, RSA asymmetric encryption, and ECDH ephemeral key exchange, each wrapped in a scoped `BaseHelper` class.
77
10
 
78
- ## Creating an Instance
11
+ ## In one example
79
12
 
80
- All crypto algorithm classes extend `BaseHelper` (via `AbstractCryptoAlgorithm`), providing scoped logging. Each class uses a static `withAlgorithm()` factory method.
13
+ The smallest real use: encrypt and decrypt a string with AES-256-GCM.
81
14
 
82
15
  ```typescript
83
- import { AES, RSA, ECDH } from '@venizia/ignis-helpers';
84
-
85
- // AES -- choose CBC or GCM mode
86
- const aesCbc = AES.withAlgorithm('aes-256-cbc');
87
- const aesGcm = AES.withAlgorithm('aes-256-gcm');
88
-
89
- // RSA -- single algorithm, no parameters
90
- const rsa = RSA.withAlgorithm();
91
-
92
- // ECDH -- optional HKDF info for key isolation
93
- const ecdh = ECDH.withAlgorithm();
94
- const ecdhCustom = ECDH.withAlgorithm({
95
- algorithm: 'ecdh-p256',
96
- hkdfInfo: 'my-app-session-keys',
97
- });
98
- ```
99
-
100
- #### AES Constructor Options
101
-
102
- | Algorithm | Mode | Features |
103
- |-----------|------|----------|
104
- | `aes-256-cbc` | CBC | Standard block cipher, widely compatible |
105
- | `aes-256-gcm` | GCM | Authenticated encryption -- detects tampering |
106
-
107
- #### ECDH Constructor Options
108
-
109
- | Option | Type | Default | Description |
110
- |--------|------|---------|-------------|
111
- | `algorithm` | `'ecdh-p256'` | `'ecdh-p256'` | Curve algorithm |
112
- | `hkdfInfo` | `string` | `'ignis-ecdh-p256-aes-256-gcm-v1'` | HKDF info string for key derivation isolation |
113
-
114
- Different `hkdfInfo` values produce **incompatible keys** from the same ECDH shared secret. Use this to isolate key derivation between different application contexts.
16
+ import { AES } from '@venizia/ignis-helpers';
115
17
 
116
- ## Usage
117
-
118
- ### AES Encryption
119
-
120
- The `AES` class provides encryption and decryption using the Advanced Encryption Standard with 256-bit keys.
121
-
122
- ```typescript
123
18
  const aes = AES.withAlgorithm('aes-256-gcm');
124
19
  const secret = 'my-application-secret-key';
125
20
 
126
- // Encrypt
127
21
  const encrypted = aes.encrypt({ message: 'This is a secret message.', secret });
128
- // => base64 encoded string containing IV + ciphertext
22
+ // => base64 encoded string containing IV + auth tag + ciphertext
129
23
 
130
- // Decrypt
131
24
  const decrypted = aes.decrypt({ message: encrypted, secret });
132
25
  // => 'This is a secret message.'
133
26
  ```
134
27
 
135
- > [!TIP]
136
- > Prefer `aes-256-gcm` for new applications. It provides **authenticated encryption** -- if the ciphertext is tampered with, decryption will throw an error rather than silently returning corrupted data. This does not happen with CBC mode.
137
-
138
- #### AES Extra Options
139
-
140
- ```typescript
141
- import C from 'node:crypto';
142
-
143
- const encrypted = aes.encrypt({
144
- message: 'hello',
145
- secret: 'my-secret',
146
- opts: {
147
- iv: C.randomBytes(16), // Custom IV (default: random 16 bytes)
148
- inputEncoding: 'utf-8', // Message input encoding (default: 'utf-8')
149
- outputEncoding: 'hex', // Ciphertext output encoding (default: 'base64')
150
- doThrow: false, // Return original message on error (default: true)
151
- },
152
- });
153
- ```
154
-
155
- | Option | Type | Default (encrypt) | Default (decrypt) | Description |
156
- |--------|------|-------------------|-------------------|-------------|
157
- | `iv` | `Buffer` | `crypto.randomBytes(16)` | Extracted from ciphertext | Initialization vector |
158
- | `inputEncoding` | `crypto.Encoding` | `'utf-8'` | `'base64'` | Encoding of the input message |
159
- | `outputEncoding` | `crypto.Encoding` | `'base64'` | `'utf-8'` | Encoding of the output |
160
- | `doThrow` | `boolean` | `true` | `true` | If `false`, returns the original message on error instead of throwing |
161
-
162
- #### File Encryption
163
-
164
- ```typescript
165
- // Encrypt file contents -> returns encrypted string
166
- const encrypted = aes.encryptFile({
167
- absolutePath: '/path/to/config.json',
168
- secret: 'my-secret',
169
- });
170
-
171
- // Decrypt file contents -> returns decrypted string
172
- const decrypted = aes.decryptFile({
173
- absolutePath: '/path/to/config.json.enc',
174
- secret: 'my-secret',
175
- });
176
- ```
177
-
178
- Both methods read the file synchronously via `fs.readFileSync`, convert to UTF-8, then encrypt/decrypt as a string. If `absolutePath` is empty or falsy, they return an empty string.
28
+ `RSA` and `ECDH` follow the same `withAlgorithm()` factory + `encrypt`/`decrypt` shape. Only the secret type and the speed/message-size trade-offs differ.
179
29
 
180
- ### RSA Encryption
30
+ ## How it works
181
31
 
182
- The `RSA` class provides public-key encryption using RSA with DER-formatted keys.
32
+ - **One factory pattern.** Every algorithm class exposes a static `withAlgorithm()` that returns an instance. There is no public constructor to call directly.
33
+ - **`ECDH` extends the neutral `AbstractCryptoAlgorithm` directly.** It uses `CryptoKey` objects from the Web Crypto API (`crypto.subtle`), not string secrets. It skips the string-normalization helpers entirely.
34
+ - **Options objects, throw-by-default.** Every `encrypt`/`decrypt` takes `{ message, secret, opts? }`. On internal error, each throws by default. Pass `opts.doThrow: false` to get the original input back unchanged instead.
183
35
 
184
- #### Generating a Key Pair
36
+ **`AES` and `RSA` share `BaseCryptoAlgorithm`**, which adds two helpers:
185
37
 
186
- Keys are generated in DER format (binary, compact).
38
+ | Method | Does |
39
+ |---|---|
40
+ | `normalizeSecretKey()` | Pads or truncates a string secret to the algorithm's key size |
41
+ | `getAlgorithmKeySize()` | Parses the bit size out of the algorithm name - `256` from `aes-256-gcm` |
187
42
 
188
- ```typescript
189
- const rsa = RSA.withAlgorithm();
43
+ **Class comparison**
190
44
 
191
- // Default: 2048-bit modulus
192
- const { publicKey, privateKey } = rsa.generateDERKeyPair();
193
-
194
- // Custom modulus length
195
- const keys = rsa.generateDERKeyPair({ modulus: 4096 });
196
- ```
45
+ | Class | Base class | Secret type | Async | Best for |
46
+ |-------|-----------|--------------|-------|----------|
47
+ | `AES` | `BaseCryptoAlgorithm` | `string` | No | Encrypting data at rest, fast bulk encryption |
48
+ | `RSA` | `BaseCryptoAlgorithm` | `string` (base64 DER key) | No | Public-key encryption, small payloads |
49
+ | `ECDH` | `AbstractCryptoAlgorithm` | `CryptoKey` | Yes | Session key exchange with forward secrecy |
197
50
 
198
- | Option | Type | Default | Description |
199
- |--------|------|---------|-------------|
200
- | `modulus` | `number` | `2048` | RSA modulus length in bits |
51
+ `AES` supports two modes selected at construction: `aes-256-cbc` (plain block cipher) and `aes-256-gcm` (authenticated - detects tampering). Everything on this page uses the default options.
201
52
 
202
- The returned `publicKey` is a `Buffer` in SPKI/DER format and `privateKey` is a `Buffer` in PKCS8/DER format.
53
+ See the [Full reference](/extensions/helpers/crypto/reference) for every option, the ECDH key-exchange flow, `IECDHEncryptedPayload`, and the standalone `hash()` utility.
203
54
 
204
- #### Encrypting and Decrypting
55
+ ## Common tasks
205
56
 
206
- ```typescript
207
- // Encrypt using the public key (base64-encoded DER)
208
- const pubKeyB64 = publicKey.toString('base64');
209
- const encrypted = rsa.encrypt({ message: 'secret data', secret: pubKeyB64 });
210
-
211
- // Decrypt using the private key (base64-encoded DER)
212
- const privKeyB64 = privateKey.toString('base64');
213
- const decrypted = rsa.decrypt({ message: encrypted, secret: privKeyB64 });
214
- // => 'secret data'
215
- ```
57
+ ### Choose an AES mode
216
58
 
217
- #### RSA Extra Options
59
+ `aes-256-gcm` is authenticated encryption - tampering makes decryption throw instead of returning corrupted data. Prefer it for new code.
218
60
 
219
61
  ```typescript
220
- const encrypted = rsa.encrypt({
221
- message: 'hello',
222
- secret: pubKeyB64,
223
- opts: {
224
- inputEncoding: {
225
- key: 'base64', // Key encoding (default: 'base64')
226
- message: 'utf-8', // Message encoding (default: 'utf-8')
227
- },
228
- outputEncoding: 'hex', // Ciphertext output (default: 'base64')
229
- doThrow: false, // Return original on error (default: true)
230
- },
231
- });
62
+ const aesGcm = AES.withAlgorithm('aes-256-gcm'); // recommended
63
+ const aesCbc = AES.withAlgorithm('aes-256-cbc'); // no tamper detection
232
64
  ```
233
65
 
234
- | Option | Type | Default (encrypt) | Default (decrypt) | Description |
235
- |--------|------|-------------------|-------------------|-------------|
236
- | `inputEncoding.key` | `crypto.Encoding` | `'base64'` | `'base64'` | Encoding of the key buffer |
237
- | `inputEncoding.message` | `crypto.Encoding` | `'utf-8'` | `'base64'` | Encoding of the input message |
238
- | `outputEncoding` | `crypto.Encoding` | `'base64'` | `'utf-8'` | Encoding of the output |
239
- | `doThrow` | `boolean` | `true` | `true` | If `false`, returns the original message on error instead of throwing |
66
+ ### Encrypt a file
240
67
 
241
- #### Error Handling
68
+ `encryptFile`/`decryptFile` read the file synchronously, treat its contents as UTF-8, then run the same `encrypt`/`decrypt` as strings.
242
69
 
243
70
  ```typescript
244
- // Default: throws on invalid key
245
- try {
246
- rsa.encrypt({ message: 'test', secret: 'invalid-key' });
247
- } catch (error) {
248
- // Handle encryption error
249
- }
250
-
251
- // Graceful: return original message on error
252
- const result = rsa.encrypt({
253
- message: 'test',
254
- secret: 'invalid-key',
255
- opts: { doThrow: false },
256
- });
257
- // result === 'test' (original message returned)
71
+ const encrypted = aes.encryptFile({ absolutePath: '/path/to/config.json', secret });
72
+ const decrypted = aes.decryptFile({ absolutePath: '/path/to/config.json.enc', secret });
258
73
  ```
259
74
 
260
- ### ECDH Key Exchange
261
-
262
- The `ECDH` class provides **ephemeral key exchange** using ECDH P-256 with HKDF-derived AES-256-GCM session encryption. It uses the Web Crypto API (`crypto.subtle`) and is fully async.
263
-
264
- #### When to Use ECDH
265
-
266
- | Scenario | Use ECDH | Use AES/RSA |
267
- |----------|----------|-------------|
268
- | Two parties need a shared secret without pre-sharing | Yes | No |
269
- | WebSocket session encryption | Yes | No |
270
- | Encrypting data at rest | No | AES |
271
- | Signing/verifying tokens | No | RSA |
272
- | Forward secrecy needed | Yes | No |
75
+ ### Generate an RSA key pair and encrypt with it
273
76
 
274
- #### Complete Key Exchange Flow
77
+ Keys are DER-encoded (`SPKI` public, `PKCS8` private). Base64-encode them to pass as the `secret` string.
275
78
 
276
79
  ```typescript
277
- const ecdh = ECDH.withAlgorithm();
80
+ import { RSA } from '@venizia/ignis-helpers';
278
81
 
279
- // 1. Both parties generate key pairs
280
- const alice = await ecdh.generateKeyPair();
281
- const bob = await ecdh.generateKeyPair();
282
-
283
- // 2. Exchange public keys (over any channel -- they're safe to share)
284
- const alicePubForBob = await ecdh.importPublicKey({ rawKeyB64: alice.publicKeyB64 });
285
- const bobPubForAlice = await ecdh.importPublicKey({ rawKeyB64: bob.publicKeyB64 });
286
-
287
- // 3. Initiator derives AES key (generates a random salt)
288
- const { key: aliceKey, salt } = await ecdh.deriveAESKey({
289
- privateKey: alice.keyPair.privateKey,
290
- peerPublicKey: bobPubForAlice,
291
- });
292
-
293
- // 4. Responder derives the SAME AES key using the initiator's salt
294
- const { key: bobKey } = await ecdh.deriveAESKey({
295
- privateKey: bob.keyPair.privateKey,
296
- peerPublicKey: alicePubForBob,
297
- salt, // Must use the same salt for keys to match
298
- });
299
-
300
- // 5. Alice encrypts -> Bob decrypts (or vice versa)
301
- const encrypted = await ecdh.encrypt({ message: 'Hello Bob!', secret: aliceKey });
302
- const decrypted = await ecdh.decrypt({ message: encrypted, secret: bobKey });
303
- // => 'Hello Bob!'
304
- ```
305
-
306
- > [!IMPORTANT]
307
- > Both parties **must** use the same salt for `deriveAESKey` to produce matching keys. The initiator omits the `salt` parameter (a random 32-byte salt is generated), then shares the returned `salt` string with the responder.
308
-
309
- #### Key Generation and Import
310
-
311
- ```typescript
312
- // Generate a key pair
313
- const { keyPair, publicKeyB64 } = await ecdh.generateKeyPair();
314
- // keyPair.publicKey -- CryptoKey (exported as raw base64 via publicKeyB64)
315
- // keyPair.privateKey -- CryptoKey (non-extractable)
316
- // publicKeyB64 -- base64 encoded raw public key (65 bytes for P-256)
317
-
318
- // Import a peer's base64-encoded public key
319
- const peerKey = await ecdh.importPublicKey({ rawKeyB64: peerPublicKeyB64 });
320
- ```
321
-
322
- #### AES Key Derivation
323
-
324
- The derived key uses **HKDF** (HMAC-based Key Derivation Function) with SHA-256 to produce an AES-256-GCM key from the ECDH shared secret. A random 32-byte salt is generated if not provided.
325
-
326
- ```typescript
327
- // Initiator: omit salt (random salt is generated)
328
- const { key: aesKey, salt } = await ecdh.deriveAESKey({
329
- privateKey: myKeyPair.privateKey,
330
- peerPublicKey: importedPeerPublicKey,
331
- });
332
- // aesKey -- CryptoKey for AES-256-GCM (non-extractable, encrypt + decrypt)
333
- // salt -- base64 encoded 32-byte salt (share with peer)
334
-
335
- // Responder: provide the initiator's salt
336
- const { key: peerAesKey } = await ecdh.deriveAESKey({
337
- privateKey: peerKeyPair.privateKey,
338
- peerPublicKey: importedMyPublicKey,
339
- salt, // Same salt -> same derived key
340
- });
341
- ```
342
-
343
- #### `deriveAESKey` Options
344
-
345
- | Option | Type | Default | Description |
346
- |--------|------|---------|-------------|
347
- | `privateKey` | `CryptoKey` | -- | Your ECDH private key from `generateKeyPair()` |
348
- | `peerPublicKey` | `CryptoKey` | -- | Peer's public key from `importPublicKey()` |
349
- | `salt` | `string` | Random 32 bytes | Base64-encoded salt for HKDF. Omit to generate a new random salt. |
350
-
351
- #### Additional Authenticated Data (AAD)
352
-
353
- ECDH encrypt/decrypt supports **Additional Authenticated Data** via the `opts.additionalData` parameter. AAD is authenticated but not encrypted -- it binds the ciphertext to a context (e.g., channel ID, session ID) so the same ciphertext cannot be replayed in a different context.
354
-
355
- ```typescript
356
- // Encrypt with AAD
357
- const encrypted = await ecdh.encrypt({
358
- message: 'context-bound message',
359
- secret: sharedKey,
360
- opts: { additionalData: 'channel-123' },
361
- });
362
-
363
- // Decrypt must provide the SAME AAD
364
- const decrypted = await ecdh.decrypt({
365
- message: encrypted,
366
- secret: sharedKey,
367
- opts: { additionalData: 'channel-123' },
368
- });
369
-
370
- // Decrypt with wrong/missing AAD throws
371
- await ecdh.decrypt({ message: encrypted, secret: sharedKey });
372
- // => throws (AAD mismatch)
373
- ```
374
-
375
- #### Encrypted Payload Format
376
-
377
- ```typescript
378
- interface IECDHEncryptedPayload {
379
- iv: string; // base64 encoded 12-byte IV
380
- ct: string; // base64 encoded ciphertext + GCM auth tag (128-bit)
381
- }
382
- ```
383
-
384
- #### Security Properties
385
-
386
- | Property | Guarantee |
387
- |----------|-----------|
388
- | **Confidentiality** | AES-256-GCM encryption |
389
- | **Integrity** | GCM authentication tag -- tampered ciphertext is detected |
390
- | **Forward secrecy** | Ephemeral key pairs -- compromising one session doesn't compromise others |
391
- | **Key isolation** | HKDF info parameter separates key derivation contexts |
392
- | **Context binding** | AAD (`additionalData`) prevents cross-context replay |
393
-
394
- ### Hashing
395
-
396
- Standalone `hash` utility function for creating hashes (e.g., for data integrity checks or HMAC signatures).
397
-
398
- ```typescript
399
- import { hash } from '@venizia/ignis-helpers';
400
-
401
- // MD5 Hash
402
- const md5Hash = hash('some text', { algorithm: 'MD5', outputType: 'hex' });
403
-
404
- // SHA256 HMAC (secret is required for SHA256)
405
- const sha256Hash = hash('some text', {
406
- algorithm: 'SHA256',
407
- secret: 'a-secret-key',
408
- outputType: 'hex',
409
- });
410
- ```
411
-
412
- > [!WARNING]
413
- > `SHA256` mode uses HMAC and **requires** the `secret` parameter. If `secret` is omitted, the function returns the original text unchanged (no hash is computed). `MD5` mode does not use a secret.
414
-
415
- #### `hash` Function Signature
416
-
417
- ```typescript
418
- function hash(
419
- text: string,
420
- options: {
421
- algorithm: 'SHA256' | 'MD5';
422
- secret?: string;
423
- outputType: C.BinaryToTextEncoding; // 'hex' | 'base64' | 'base64url'
424
- },
425
- ): string;
426
- ```
427
-
428
- | Option | Type | Required | Description |
429
- |--------|------|----------|-------------|
430
- | `algorithm` | `'SHA256' \| 'MD5'` | Yes | Hashing algorithm to use |
431
- | `secret` | `string` | Only for SHA256 | HMAC secret key. Required for SHA256, ignored for MD5. |
432
- | `outputType` | `'hex' \| 'base64' \| 'base64url'` | Yes | Output encoding of the hash digest |
433
-
434
- ## API Summary
435
-
436
- | Method | Class | Returns | Description |
437
- |--------|-------|---------|-------------|
438
- | `AES.withAlgorithm(algorithm)` | AES | `AES` | Create AES instance with CBC or GCM mode |
439
- | `encrypt(opts)` | AES | `string` | Encrypt a string message |
440
- | `decrypt(opts)` | AES | `string` | Decrypt a ciphertext string |
441
- | `encryptFile(opts)` | AES | `string` | Encrypt file contents to string |
442
- | `decryptFile(opts)` | AES | `string` | Decrypt file contents to string |
443
- | `RSA.withAlgorithm()` | RSA | `RSA` | Create RSA instance |
444
- | `generateDERKeyPair(opts?)` | RSA | `{ publicKey: Buffer, privateKey: Buffer }` | Generate DER-format key pair |
445
- | `encrypt(opts)` | RSA | `string` | Encrypt with public key |
446
- | `decrypt(opts)` | RSA | `string` | Decrypt with private key |
447
- | `ECDH.withAlgorithm(opts?)` | ECDH | `ECDH` | Create ECDH instance with optional HKDF info |
448
- | `generateKeyPair()` | ECDH | `Promise<{ keyPair: CryptoKeyPair, publicKeyB64: string }>` | Generate P-256 key pair |
449
- | `importPublicKey(opts)` | ECDH | `Promise<CryptoKey>` | Import peer's base64 public key |
450
- | `deriveAESKey(opts)` | ECDH | `Promise<{ key: CryptoKey, salt: string }>` | Derive AES-256-GCM key via HKDF |
451
- | `encrypt(opts)` | ECDH | `Promise<IECDHEncryptedPayload>` | Encrypt with derived AES key |
452
- | `decrypt(opts)` | ECDH | `Promise<string>` | Decrypt with derived AES key |
453
- | `hash(text, options)` | _(function)_ | `string` | MD5 or SHA256 HMAC hash |
454
-
455
- ## Troubleshooting
456
-
457
- ### "[validateAlgorithmName] Invalid algorithm name | algorithm: undefined"
458
-
459
- **Cause:** An empty or undefined `algorithm` string was passed to the constructor (or `withAlgorithm()`).
460
-
461
- **Fix:** Provide a valid algorithm name:
82
+ const rsa = RSA.withAlgorithm();
83
+ const { publicKey, privateKey } = rsa.generateDERKeyPair(); // default 2048-bit modulus
462
84
 
463
- ```typescript
464
- const aes = AES.withAlgorithm('aes-256-gcm'); // Not undefined or empty
465
- const rsa = RSA.withAlgorithm(); // No parameter needed
466
- const ecdh = ECDH.withAlgorithm(); // No parameter needed
85
+ const encrypted = rsa.encrypt({ message: 'secret data', secret: publicKey.toString('base64') });
86
+ const decrypted = rsa.decrypt({ message: encrypted, secret: privateKey.toString('base64') });
87
+ // => 'secret data'
467
88
  ```
468
89
 
469
- ### "[ECDH.fromBase64] Invalid base64 input"
470
-
471
- **Cause:** A value passed to an ECDH method (public key, salt, or encrypted payload) is not valid base64. The string must have a length divisible by 4 and contain only characters `A-Za-z0-9+/=`.
90
+ ### Fail soft instead of throwing
472
91
 
473
- **Fix:** Ensure all base64 strings are passed as-is from the methods that produced them (`publicKeyB64`, `salt`, `iv`, `ct`). Do not trim, re-encode, or modify these values.
474
-
475
- ### "Unsupported state or unable to authenticate data"
476
-
477
- **Cause:** The ciphertext or auth tag was modified in transit, or you are decrypting GCM ciphertext with a CBC instance (or vice versa). CBC and GCM produce incompatible ciphertext formats.
478
-
479
- **Fix:** Ensure the same algorithm mode is used for both encrypt and decrypt:
92
+ Pass `opts: { doThrow: false }` to get the original message back on error instead of an exception. Use this when a decryption failure should be a fallback path, not a crash.
480
93
 
481
94
  ```typescript
482
- // Both must use the same mode
483
- const aes = AES.withAlgorithm('aes-256-gcm');
484
- const encrypted = aes.encrypt({ message, secret });
485
- const decrypted = aes.decrypt({ message: encrypted, secret }); // same instance or same mode
95
+ const result = rsa.encrypt({ message: 'test', secret: 'invalid-key', opts: { doThrow: false } });
96
+ // result === 'test' (original message, no throw)
486
97
  ```
487
98
 
488
- ### ECDH decrypt throws even though both sides used each other's public keys
99
+ ### Derive a shared session key with ECDH
489
100
 
490
- **Cause:** Each call to `deriveAESKey` without a `salt` parameter generates a new random 32-byte salt. If both sides generate their own salt, they derive different AES keys.
491
-
492
- **Fix:** The initiator calls `deriveAESKey` without `salt` (generates one), then sends the returned `salt` string to the responder. The responder passes that `salt` into their `deriveAESKey` call.
101
+ `ECDH` is async (Web Crypto) and needs a `deriveAESKey()` step before either side can encrypt. See the [Full reference](/extensions/helpers/crypto/reference#ecdh-key-exchange) for the complete key-exchange flow, salt handling, and additional authenticated data (AAD).
493
102
 
494
103
  ```typescript
495
- // Initiator
496
- const { key: aliceKey, salt } = await ecdh.deriveAESKey({
497
- privateKey: alice.keyPair.privateKey,
498
- peerPublicKey: bobPub,
499
- });
500
- // Send `salt` to responder
501
-
502
- // Responder
503
- const { key: bobKey } = await ecdh.deriveAESKey({
504
- privateKey: bob.keyPair.privateKey,
505
- peerPublicKey: alicePub,
506
- salt, // <-- use initiator's salt
507
- });
508
- ```
509
-
510
- ### SHA256 hash returns the original text instead of a hash
104
+ import { ECDH } from '@venizia/ignis-helpers';
511
105
 
512
- **Cause:** The `SHA256` algorithm uses `createHmac` internally, which requires a `secret` parameter. When `secret` is `undefined`, the function short-circuits and returns the original text.
513
-
514
- **Fix:** Always provide a `secret` when using `SHA256`:
515
-
516
- ```typescript
517
- const hashed = hash('text', {
518
- algorithm: 'SHA256',
519
- secret: 'my-hmac-key',
520
- outputType: 'hex',
521
- });
106
+ const ecdh = ECDH.withAlgorithm();
107
+ const alice = await ecdh.generateKeyPair();
108
+ const bob = await ecdh.generateKeyPair();
522
109
  ```
523
110
 
524
- ## See Also
525
-
526
- - **Related Concepts:**
527
- - [Services](/guides/core-concepts/services) -- Password hashing in user services
111
+ ## See also
528
112
 
529
- - **Other Helpers:**
530
- - [Helpers Index](../index) -- All available helpers
113
+ - [Full reference](/extensions/helpers/crypto/reference) - every option, the ECDH flow, `hash()`, and troubleshooting
114
+ - [Crypto Utility](/references/utilities/crypto) - the standalone `hash()` function for MD5/HMAC-SHA256
115
+ - [Authentication Component](/extensions/components/authentication/) - JWT and password verification
116
+ - [Helpers Overview](/extensions/helpers/) - all available helpers
117
+ - [Security Guidelines](/best-practices/security-guidelines) - cryptographic best practices
531
118
 
532
- - **References:**
533
- - [Crypto Utility](/references/utilities/crypto) -- Pure crypto utilities
534
- - [Authentication Component](/extensions/components/authentication/) -- JWT and password verification
119
+ **Files:**
535
120
 
536
- - **Best Practices:**
537
- - [Security Guidelines](/best-practices/security-guidelines) -- Cryptographic best practices
121
+ - [`packages/helpers/src/modules/crypto/algorithms/base.algorithm.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/crypto/algorithms/base.algorithm.ts) - `AbstractCryptoAlgorithm`, `BaseCryptoAlgorithm`
122
+ - [`packages/helpers/src/modules/crypto/algorithms/aes.algorithm.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/crypto/algorithms/aes.algorithm.ts) - `AES`
123
+ - [`packages/helpers/src/modules/crypto/algorithms/rsa.algorithm.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/crypto/algorithms/rsa.algorithm.ts) - `RSA`
124
+ - [`packages/helpers/src/modules/crypto/algorithms/ecdh.algorithm.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/crypto/algorithms/ecdh.algorithm.ts) - `ECDH`