@venizia/ignis-docs 0.2.0 → 0.2.1-0

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 (142) 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 +22 -11
  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 +26 -2
  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 +6 -2
  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 +182 -93
  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 +107 -322
  26. package/content/extensions/components/authentication/api.md +454 -603
  27. package/content/extensions/components/authentication/errors.md +121 -498
  28. package/content/extensions/components/authentication/index.md +88 -801
  29. package/content/extensions/components/authentication/usage.md +207 -956
  30. package/content/extensions/components/authorization/api.md +736 -656
  31. package/content/extensions/components/authorization/errors.md +168 -206
  32. package/content/extensions/components/authorization/index.md +82 -797
  33. package/content/extensions/components/authorization/usage.md +194 -527
  34. package/content/extensions/components/health-check.md +71 -243
  35. package/content/extensions/components/mail/api.md +504 -287
  36. package/content/extensions/components/mail/errors.md +73 -61
  37. package/content/extensions/components/mail/index.md +96 -467
  38. package/content/extensions/components/mail/usage.md +130 -172
  39. package/content/extensions/components/request-tracker.md +66 -173
  40. package/content/extensions/components/socket-io/api.md +195 -13
  41. package/content/extensions/components/socket-io/errors.md +3 -3
  42. package/content/extensions/components/socket-io/index.md +50 -337
  43. package/content/extensions/components/socket-io/usage.md +143 -26
  44. package/content/extensions/components/static-asset/api.md +410 -142
  45. package/content/extensions/components/static-asset/errors.md +110 -53
  46. package/content/extensions/components/static-asset/index.md +79 -608
  47. package/content/extensions/components/static-asset/usage.md +180 -300
  48. package/content/extensions/components/websocket/api.md +275 -399
  49. package/content/extensions/components/websocket/errors.md +47 -56
  50. package/content/extensions/components/websocket/index.md +74 -407
  51. package/content/extensions/components/websocket/usage.md +110 -341
  52. package/content/extensions/helpers/cron/index.md +51 -160
  53. package/content/extensions/helpers/crypto/index.md +62 -483
  54. package/content/extensions/helpers/crypto/reference.md +456 -0
  55. package/content/extensions/helpers/env/index.md +60 -178
  56. package/content/extensions/helpers/error/index.md +221 -207
  57. package/content/extensions/helpers/inversion/index.md +65 -556
  58. package/content/extensions/helpers/inversion/reference.md +522 -0
  59. package/content/extensions/helpers/kafka/admin.md +20 -1
  60. package/content/extensions/helpers/kafka/compile-binary.md +41 -35
  61. package/content/extensions/helpers/kafka/consumer.md +54 -20
  62. package/content/extensions/helpers/kafka/examples.md +21 -16
  63. package/content/extensions/helpers/kafka/index.md +80 -610
  64. package/content/extensions/helpers/kafka/producer.md +134 -5
  65. package/content/extensions/helpers/kafka/schema-registry.md +45 -70
  66. package/content/extensions/helpers/logger/hf-logger.md +193 -0
  67. package/content/extensions/helpers/logger/index.md +64 -563
  68. package/content/extensions/helpers/logger/pino.md +85 -0
  69. package/content/extensions/helpers/logger/reference.md +746 -0
  70. package/content/extensions/helpers/network/api.md +241 -195
  71. package/content/extensions/helpers/network/index.md +72 -530
  72. package/content/extensions/helpers/queue/index.md +72 -900
  73. package/content/extensions/helpers/queue/reference.md +467 -0
  74. package/content/extensions/helpers/redis/index.md +73 -645
  75. package/content/extensions/helpers/redis/reference.md +727 -0
  76. package/content/extensions/helpers/secrets/index.md +66 -0
  77. package/content/extensions/helpers/socket-io/api.md +305 -203
  78. package/content/extensions/helpers/socket-io/index.md +66 -432
  79. package/content/extensions/helpers/storage/api.md +564 -462
  80. package/content/extensions/helpers/storage/index.md +77 -573
  81. package/content/extensions/helpers/types/index.md +66 -499
  82. package/content/extensions/helpers/types/reference.md +650 -0
  83. package/content/extensions/helpers/uid/index.md +58 -227
  84. package/content/extensions/helpers/websocket/api.md +329 -216
  85. package/content/extensions/helpers/websocket/index.md +65 -503
  86. package/content/extensions/helpers/worker-thread/index.md +58 -396
  87. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  88. package/content/guides/core-concepts/persistent/models.md +1 -1
  89. package/content/guides/core-concepts/persistent/search-typesense.md +3 -3
  90. package/content/guides/core-concepts/persistent/transactions.md +1 -1
  91. package/content/guides/core-concepts/secrets-vault.md +177 -0
  92. package/content/guides/core-concepts/services.md +1 -1
  93. package/content/guides/migrations/redis-helpers-migration.md +1 -1
  94. package/content/guides/migrations/unified-connectors-migration.md +2 -2
  95. package/content/guides/tutorials/ecommerce-api.md +3 -8
  96. package/content/references/base/application.md +1 -1
  97. package/content/references/base/connectors.md +79 -136
  98. package/content/references/base/datasources-reference.md +599 -0
  99. package/content/references/base/datasources.md +84 -444
  100. package/content/references/base/dependency-injection.md +17 -39
  101. package/content/references/base/filter-system/application-usage.md +69 -121
  102. package/content/references/base/filter-system/array-operators.md +12 -0
  103. package/content/references/base/filter-system/comparison-operators.md +12 -0
  104. package/content/references/base/filter-system/default-filter.md +136 -348
  105. package/content/references/base/filter-system/fields-order-pagination.md +38 -16
  106. package/content/references/base/filter-system/index.md +106 -257
  107. package/content/references/base/filter-system/json-filtering.md +12 -2
  108. package/content/references/base/filter-system/list-operators.md +16 -2
  109. package/content/references/base/filter-system/logical-operators.md +13 -0
  110. package/content/references/base/filter-system/null-operators.md +13 -0
  111. package/content/references/base/filter-system/pattern-matching.md +12 -0
  112. package/content/references/base/filter-system/quick-reference.md +11 -2
  113. package/content/references/base/filter-system/range-operators.md +12 -0
  114. package/content/references/base/filter-system/tips.md +70 -133
  115. package/content/references/base/filter-system/use-cases.md +156 -233
  116. package/content/references/base/middlewares.md +35 -21
  117. package/content/references/base/models-reference.md +886 -0
  118. package/content/references/base/models.md +80 -1452
  119. package/content/references/base/repositories/advanced.md +156 -192
  120. package/content/references/base/repositories/index.md +77 -650
  121. package/content/references/base/repositories/mixins.md +22 -18
  122. package/content/references/base/repositories/relations.md +123 -171
  123. package/content/references/base/repositories/soft-deletable.md +58 -56
  124. package/content/references/base/secrets.md +263 -0
  125. package/content/references/base/services.md +2 -2
  126. package/content/references/configuration/environment-variables.md +48 -4
  127. package/content/references/configuration/index.md +49 -31
  128. package/content/references/quick-reference.md +3 -16
  129. package/content/references/utilities/crypto.md +35 -76
  130. package/content/references/utilities/date.md +33 -73
  131. package/content/references/utilities/index.md +1 -1
  132. package/content/references/utilities/jsx-reference.md +298 -0
  133. package/content/references/utilities/jsx.md +82 -525
  134. package/content/references/utilities/module.md +29 -62
  135. package/content/references/utilities/parse.md +34 -64
  136. package/content/references/utilities/performance.md +33 -58
  137. package/content/references/utilities/promise.md +28 -62
  138. package/content/references/utilities/request.md +57 -218
  139. package/content/references/utilities/schema.md +43 -137
  140. package/content/references/utilities/statuses-reference.md +361 -0
  141. package/content/references/utilities/statuses.md +63 -667
  142. package/package.json +8 -8
@@ -0,0 +1,456 @@
1
+ ---
2
+ title: Crypto - Full Reference
3
+ description: Complete reference for AES, RSA, ECDH, the shared algorithm base classes, and every option
4
+ difficulty: intermediate
5
+ ---
6
+
7
+ # Crypto - Full Reference
8
+
9
+ Exhaustive reference for `AES`, `RSA`, `ECDH`, the shared `AbstractCryptoAlgorithm`/`BaseCryptoAlgorithm` base classes, and the standalone `hash()` utility. For a readable introduction and the most common tasks, start with the [Crypto overview](/extensions/helpers/crypto/).
10
+
11
+ **Files:**
12
+
13
+ - [`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`
14
+ - [`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`, `AESAlgorithmType`
15
+ - [`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`, `RSAAlgorithmType`
16
+ - [`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`, `ECDHAlgorithmType`, `IECDHEncryptedPayload`, `IECDHExtraOptions`
17
+ - [`packages/helpers/src/modules/crypto/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/crypto/common/types.ts) - `ICryptoAlgorithm`
18
+ - [`packages/helpers/src/modules/crypto/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/crypto/common/constants.ts) - `DEFAULT_CIPHER_BITS`, `DEFAULT_PAD_END`
19
+ - [`packages/helpers/src/utilities/crypto.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/utilities/crypto.utility.ts) - `hash()`
20
+
21
+ ## Quick Reference
22
+
23
+ | Class | Extends | Secret type | Async | Runtime API |
24
+ |-------|---------|--------------|-------|-------------|
25
+ | `AES` | `BaseCryptoAlgorithm` | `string` | No | Node `node:crypto` |
26
+ | `RSA` | `BaseCryptoAlgorithm` | `string` (base64 DER key) | No | Node `node:crypto` |
27
+ | `ECDH` | `AbstractCryptoAlgorithm` | `CryptoKey` | Yes | Web Crypto (`crypto.subtle`) |
28
+ | `hash()` | _(standalone function)_ | `string` (HMAC secret, SHA256 only) | No | Node `node:crypto` |
29
+
30
+ ### Import paths
31
+
32
+ ```typescript
33
+ // Algorithm classes
34
+ import { AES, RSA, ECDH } from '@venizia/ignis-helpers';
35
+
36
+ // Hash utility function
37
+ import { hash } from '@venizia/ignis-helpers';
38
+
39
+ // Types
40
+ import type {
41
+ AESAlgorithmType,
42
+ RSAAlgorithmType,
43
+ ECDHAlgorithmType,
44
+ IECDHEncryptedPayload,
45
+ IECDHExtraOptions,
46
+ ICryptoAlgorithm,
47
+ } from '@venizia/ignis-helpers';
48
+ ```
49
+
50
+ All of the above resolve through the root `@venizia/ignis-helpers` barrel, which re-exports `./modules` (and therefore `./modules/crypto`) and `./utilities` in full.
51
+
52
+ ## Type Hierarchy
53
+
54
+ `Source ->` [`base.algorithm.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/crypto/algorithms/base.algorithm.ts)
55
+
56
+ ```
57
+ ICryptoAlgorithm (interface)
58
+ └── AbstractCryptoAlgorithm (extends BaseHelper)
59
+ ├── BaseCryptoAlgorithm (adds normalizeSecretKey, getAlgorithmKeySize)
60
+ │ ├── AES
61
+ │ └── RSA
62
+ └── ECDH (uses CryptoKey objects, not string secrets)
63
+ ```
64
+
65
+ `ICryptoAlgorithm` carries 7 generic type parameters - the algorithm name literal, encrypt/decrypt input and output types, the secret key type, and an extra-options type:
66
+
67
+ ```typescript
68
+ interface ICryptoAlgorithm<
69
+ AlgorithmNameType extends string,
70
+ EncryptInputType = unknown,
71
+ DecryptInputType = unknown,
72
+ SecretKeyType = unknown,
73
+ EncryptReturnType = unknown,
74
+ DecryptReturnType = unknown,
75
+ ExtraOptions = unknown,
76
+ > {
77
+ algorithm: AlgorithmNameType;
78
+ encrypt(opts: { message: EncryptInputType; secret: SecretKeyType; opts?: ExtraOptions }): EncryptReturnType;
79
+ decrypt(opts: { message: DecryptInputType; secret: SecretKeyType; opts?: ExtraOptions }): DecryptReturnType;
80
+ }
81
+ ```
82
+
83
+ `AbstractCryptoAlgorithm` extends `BaseHelper` and declares `encrypt`/`decrypt` as abstract; it adds no behavior of its own. `BaseCryptoAlgorithm` is the concrete base for string-secret algorithms:
84
+
85
+ | Member | Signature | Description |
86
+ |--------|-----------|--------------|
87
+ | constructor | `(opts: { scope: string; algorithm: AlgorithmType })` | Sets `this.algorithm`, calls `validateAlgorithmName` |
88
+ | `validateAlgorithmName` | `(opts: { algorithm: AlgorithmType }) => void` | Throws if `algorithm` is empty/falsy |
89
+ | `normalizeSecretKey` | `(opts: { secret: string; length: number; padEnd?: string }) => string` | Truncates to `length` or right-pads with `padEnd` (default `'0'` - the digit character, since `DEFAULT_PAD_END` is `(0x00).toString()`, not a null byte) |
90
+ | `getAlgorithmKeySize` | `() => number` | Parses the bit size out of `this.algorithm` (e.g. `256` from `'aes-256-gcm'`), divides by 8 for byte length |
91
+
92
+ `ECDH` extends `AbstractCryptoAlgorithm` directly - it does not inherit `normalizeSecretKey` or `getAlgorithmKeySize`, since its secrets are `CryptoKey` objects, not strings.
93
+
94
+ ## AES
95
+
96
+ `Source ->` [`aes.algorithm.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/crypto/algorithms/aes.algorithm.ts)
97
+
98
+ ```typescript
99
+ type AESAlgorithmType = 'aes-256-cbc' | 'aes-256-gcm';
100
+
101
+ const aes = AES.withAlgorithm('aes-256-gcm'); // or 'aes-256-cbc'
102
+ ```
103
+
104
+ | Mode | Authenticated | Notes |
105
+ |------|----------------|-------|
106
+ | `aes-256-cbc` | No | Plain block cipher, no tamper detection |
107
+ | `aes-256-gcm` | Yes | Ciphertext includes a 16-byte GCM auth tag; tampering throws on decrypt |
108
+
109
+ ### `encrypt`
110
+
111
+ ```typescript
112
+ encrypt(opts: { message: string; secret: string; opts?: IAESExtraOptions }): string
113
+ ```
114
+
115
+ | Option (`opts.opts`) | Type | Default | Description |
116
+ |-----------------------|------|---------|-------------|
117
+ | `iv` | `Buffer` | `crypto.randomBytes(16)` | Initialization vector |
118
+ | `inputEncoding` | `crypto.Encoding` | `'utf-8'` | Encoding of `message` |
119
+ | `outputEncoding` | `crypto.Encoding` | `'base64'` | Encoding of the returned ciphertext |
120
+ | `doThrow` | `boolean` | `true` | If `false`, returns the original `message` instead of throwing on error |
121
+
122
+ The secret is normalized via `normalizeSecretKey` to the algorithm's key size (32 bytes for both modes) before being used as the cipher key. The output layout is `IV [+ GCM auth tag] + ciphertext`, concatenated and encoded with `outputEncoding`.
123
+
124
+ ```typescript
125
+ import C from 'node:crypto';
126
+
127
+ const encrypted = aes.encrypt({
128
+ message: 'hello',
129
+ secret: 'my-secret',
130
+ opts: {
131
+ iv: C.randomBytes(16),
132
+ inputEncoding: 'utf-8',
133
+ outputEncoding: 'hex',
134
+ doThrow: false,
135
+ },
136
+ });
137
+ ```
138
+
139
+ ### `decrypt`
140
+
141
+ ```typescript
142
+ decrypt(opts: { message: string; secret: string; opts?: IAESExtraOptions }): string
143
+ ```
144
+
145
+ | Option (`opts.opts`) | Type | Default | Description |
146
+ |-----------------------|------|---------|-------------|
147
+ | `iv` | `Buffer` | Extracted from the first 16 bytes of the decoded ciphertext | Initialization vector |
148
+ | `inputEncoding` | `crypto.Encoding` | `'base64'` | Encoding of `message` |
149
+ | `outputEncoding` | `crypto.Encoding` | `'utf-8'` | Encoding of the returned plaintext |
150
+ | `doThrow` | `boolean` | `true` | If `false`, returns the original `message` instead of throwing on error |
151
+
152
+ For `aes-256-gcm`, the next 16 bytes after the IV are read as the auth tag and passed to `setAuthTag` before the remaining bytes are treated as ciphertext.
153
+
154
+ > [!WARNING]
155
+ > Decrypting `aes-256-gcm` ciphertext with an `aes-256-cbc` instance (or vice versa) throws `Unsupported state or unable to authenticate data` - the two modes produce incompatible byte layouts. Always encrypt and decrypt with the same algorithm mode.
156
+
157
+ ### `encryptFile` / `decryptFile`
158
+
159
+ ```typescript
160
+ encryptFile(opts: { absolutePath: string; secret: string }): string
161
+ decryptFile(opts: { absolutePath: string; secret: string }): string
162
+ ```
163
+
164
+ Both read the file synchronously via `fs.readFileSync`, decode it as UTF-8, then call `encrypt`/`decrypt` on the string content using default extra-options. If `absolutePath` is empty or falsy, both return `''` without touching the filesystem.
165
+
166
+ ```typescript
167
+ const encrypted = aes.encryptFile({ absolutePath: '/path/to/config.json', secret: 'my-secret' });
168
+ const decrypted = aes.decryptFile({ absolutePath: '/path/to/config.json.enc', secret: 'my-secret' });
169
+ ```
170
+
171
+ ## RSA
172
+
173
+ `Source ->` [`rsa.algorithm.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/crypto/algorithms/rsa.algorithm.ts)
174
+
175
+ ```typescript
176
+ type RSAAlgorithmType = 'rsa';
177
+
178
+ const rsa = RSA.withAlgorithm(); // no parameters - always algorithm 'rsa'
179
+ ```
180
+
181
+ ### `generateDERKeyPair`
182
+
183
+ ```typescript
184
+ generateDERKeyPair(opts?: { modulus: number }): { publicKey: Buffer; privateKey: Buffer }
185
+ ```
186
+
187
+ | Option | Type | Default | Description |
188
+ |--------|------|---------|-------------|
189
+ | `modulus` | `number` | `2048` | RSA modulus length in bits, passed to `crypto.generateKeyPairSync` |
190
+
191
+ `publicKey` is exported as `{ type: 'spki', format: 'der' }`; `privateKey` as `{ type: 'pkcs8', format: 'der' }`. Both are raw `Buffer`s - base64-encode them (`.toString('base64')`) to pass as the `secret` string to `encrypt`/`decrypt`.
192
+
193
+ ### `encrypt`
194
+
195
+ ```typescript
196
+ encrypt(opts: { message: string; secret: string; opts?: IRSAExtraOptions }): string
197
+ ```
198
+
199
+ `secret` is the base64-encoded public key (SPKI/DER). Internally builds a public key via `crypto.createPublicKey({ format: 'der', type: 'spki' })` and calls `crypto.publicEncrypt`.
200
+
201
+ | Option (`opts.opts`) | Type | Default | Description |
202
+ |-----------------------|------|---------|-------------|
203
+ | `inputEncoding.key` | `crypto.Encoding` | `'base64'` | Encoding of the `secret` key buffer |
204
+ | `inputEncoding.message` | `crypto.Encoding` | `'utf-8'` | Encoding of `message` |
205
+ | `outputEncoding` | `crypto.Encoding` | `'base64'` | Encoding of the returned ciphertext |
206
+ | `doThrow` | `boolean` | `true` | If `false`, returns the original `message` instead of throwing on error |
207
+
208
+ ### `decrypt`
209
+
210
+ ```typescript
211
+ decrypt(opts: { message: string; secret: string; opts?: IRSAExtraOptions }): string
212
+ ```
213
+
214
+ `secret` is the base64-encoded private key (PKCS8/DER). Internally builds a private key via `crypto.createPrivateKey({ format: 'der', type: 'pkcs8' })` and calls `crypto.privateDecrypt`.
215
+
216
+ | Option (`opts.opts`) | Type | Default | Description |
217
+ |-----------------------|------|---------|-------------|
218
+ | `inputEncoding.key` | `crypto.Encoding` | `'base64'` | Encoding of the `secret` key buffer |
219
+ | `inputEncoding.message` | `crypto.Encoding` | `'base64'` | Encoding of `message` |
220
+ | `outputEncoding` | `crypto.Encoding` | `'utf-8'` | Encoding of the returned plaintext |
221
+ | `doThrow` | `boolean` | `true` | If `false`, returns the original `message` instead of throwing on error |
222
+
223
+ ```typescript
224
+ const rsa = RSA.withAlgorithm();
225
+ const { publicKey, privateKey } = rsa.generateDERKeyPair({ modulus: 4096 });
226
+
227
+ const encrypted = rsa.encrypt({
228
+ message: 'hello',
229
+ secret: publicKey.toString('base64'),
230
+ opts: { outputEncoding: 'hex', doThrow: false },
231
+ });
232
+
233
+ const decrypted = rsa.decrypt({
234
+ message: encrypted,
235
+ secret: privateKey.toString('base64'),
236
+ opts: { inputEncoding: { key: 'base64', message: 'hex' } },
237
+ });
238
+ ```
239
+
240
+ ## ECDH Key Exchange
241
+
242
+ `Source ->` [`ecdh.algorithm.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/crypto/algorithms/ecdh.algorithm.ts)
243
+
244
+ `ECDH` implements ECDH P-256 key exchange with HKDF-derived AES-256-GCM session encryption, entirely on the Web Crypto API (`crypto.subtle`). Every method is `async`.
245
+
246
+ ```typescript
247
+ const ecdh = ECDH.withAlgorithm(); // curve is always 'ecdh-p256'
248
+ const ecdhCustom = ECDH.withAlgorithm({ hkdfInfo: 'my-app-session-keys' });
249
+ ```
250
+
251
+ | Constructor option | Type | Default | Description |
252
+ |---------------------|------|---------|-------------|
253
+ | `algorithm` | `'ecdh-p256'` | `'ecdh-p256'` | Accepted but not branched on - the curve is always P-256 |
254
+ | `hkdfInfo` | `string` | `'ignis-ecdh-p256-aes-256-gcm-v1'` | HKDF info string, UTF-8 encoded, used to isolate key derivation between application contexts |
255
+
256
+ Different `hkdfInfo` values produce **incompatible** derived keys from the same ECDH shared secret.
257
+
258
+ ### Key generation and import
259
+
260
+ | Method | Signature | Description |
261
+ |--------|-----------|--------------|
262
+ | `generateKeyPair` | `() => Promise<{ keyPair: CryptoKeyPair; publicKeyB64: string }>` | `deriveBits`-only key pair (`extractable: false`); `publicKeyB64` is the raw exported public key, base64-encoded (65 bytes for P-256) |
263
+ | `importPublicKey` | `(opts: { rawKeyB64: string }) => Promise<CryptoKey>` | Imports a peer's raw base64 public key |
264
+
265
+ ```typescript
266
+ const { keyPair, publicKeyB64 } = await ecdh.generateKeyPair();
267
+ const peerKey = await ecdh.importPublicKey({ rawKeyB64: peerPublicKeyB64 });
268
+ ```
269
+
270
+ ### `deriveAESKey`
271
+
272
+ ```typescript
273
+ deriveAESKey(opts: {
274
+ privateKey: CryptoKey;
275
+ peerPublicKey: CryptoKey;
276
+ salt?: string;
277
+ }): Promise<{ key: CryptoKey; salt: string }>
278
+ ```
279
+
280
+ Derives shared bits via ECDH (`deriveBits`, 256 bits), imports them as an HKDF key, then derives a non-extractable AES-256-GCM `CryptoKey` via HKDF-SHA256 using `salt` and the instance's `hkdfInfo`.
281
+
282
+ | Option | Type | Default | Description |
283
+ |--------|------|---------|-------------|
284
+ | `privateKey` | `CryptoKey` | - | Caller's ECDH private key from `generateKeyPair()` |
285
+ | `peerPublicKey` | `CryptoKey` | - | Peer's public key from `importPublicKey()` |
286
+ | `salt` | `string` | A random 32-byte salt is generated | Base64-encoded HKDF salt. Omit to generate a new random one |
287
+
288
+ > [!IMPORTANT]
289
+ > Both parties must use the **same salt** to derive matching keys. The initiator omits `salt` (a random one is generated and returned); the responder must pass that returned `salt` back into their own `deriveAESKey` call. If both sides generate their own salt, they derive different, non-matching keys.
290
+
291
+ ### `encrypt` / `decrypt`
292
+
293
+ ```typescript
294
+ encrypt(opts: { message: string; secret: CryptoKey; opts?: IECDHExtraOptions }): Promise<IECDHEncryptedPayload>
295
+ decrypt(opts: { message: IECDHEncryptedPayload; secret: CryptoKey; opts?: IECDHExtraOptions }): Promise<string>
296
+ ```
297
+
298
+ `secret` is the `CryptoKey` returned by `deriveAESKey`. Uses AES-GCM with a random 12-byte IV per call and a 128-bit auth tag.
299
+
300
+ ```typescript
301
+ interface IECDHEncryptedPayload {
302
+ iv: string; // base64 encoded 12-byte IV
303
+ ct: string; // base64 encoded ciphertext + 128-bit GCM auth tag
304
+ }
305
+
306
+ interface IECDHExtraOptions {
307
+ additionalData?: string;
308
+ }
309
+ ```
310
+
311
+ `opts.additionalData` (AAD) is authenticated but not encrypted - it binds the ciphertext to a context (channel ID, session ID) so the ciphertext cannot be replayed in a different context. Decrypt must supply the exact same `additionalData`; a mismatch (including an omitted value where one was used to encrypt) throws.
312
+
313
+ ### Complete flow
314
+
315
+ ```typescript
316
+ const ecdh = ECDH.withAlgorithm();
317
+
318
+ // 1. Both parties generate key pairs
319
+ const alice = await ecdh.generateKeyPair();
320
+ const bob = await ecdh.generateKeyPair();
321
+
322
+ // 2. Exchange public keys (safe to share over any channel)
323
+ const alicePubForBob = await ecdh.importPublicKey({ rawKeyB64: alice.publicKeyB64 });
324
+ const bobPubForAlice = await ecdh.importPublicKey({ rawKeyB64: bob.publicKeyB64 });
325
+
326
+ // 3. Initiator derives the AES key (generates a random salt)
327
+ const { key: aliceKey, salt } = await ecdh.deriveAESKey({
328
+ privateKey: alice.keyPair.privateKey,
329
+ peerPublicKey: bobPubForAlice,
330
+ });
331
+
332
+ // 4. Responder derives the SAME AES key using the initiator's salt
333
+ const { key: bobKey } = await ecdh.deriveAESKey({
334
+ privateKey: bob.keyPair.privateKey,
335
+ peerPublicKey: alicePubForBob,
336
+ salt,
337
+ });
338
+
339
+ // 5. Alice encrypts, Bob decrypts (or vice versa)
340
+ const encrypted = await ecdh.encrypt({ message: 'Hello Bob!', secret: aliceKey });
341
+ const decrypted = await ecdh.decrypt({ message: encrypted, secret: bobKey });
342
+ // => 'Hello Bob!'
343
+ ```
344
+
345
+ ### Security properties
346
+
347
+ | Property | Guarantee |
348
+ |----------|-----------|
349
+ | Confidentiality | AES-256-GCM encryption |
350
+ | Integrity | GCM authentication tag - tampered ciphertext is detected on decrypt |
351
+ | Forward secrecy | Ephemeral, non-extractable key pairs - compromising one session does not compromise others |
352
+ | Key isolation | `hkdfInfo` separates key derivation across application contexts |
353
+ | Context binding | `additionalData` (AAD) prevents cross-context replay |
354
+
355
+ ## Hashing
356
+
357
+ `Source ->` [`crypto.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/utilities/crypto.utility.ts)
358
+
359
+ `hash()` is a standalone function - not a class, not part of the `AES`/`RSA`/`ECDH` hierarchy - exported from `packages/helpers/src/utilities`, not `modules/crypto`. It is documented in full on the [Crypto Utility reference](/references/utilities/crypto).
360
+
361
+ ```typescript
362
+ function hash(
363
+ text: string,
364
+ options: {
365
+ algorithm: 'SHA256' | 'MD5';
366
+ secret?: string;
367
+ outputType: C.BinaryToTextEncoding; // 'hex' | 'base64' | 'base64url' | 'latin1'
368
+ },
369
+ ): string;
370
+ ```
371
+
372
+ | Option | Type | Required | Description |
373
+ |--------|------|----------|-------------|
374
+ | `algorithm` | `'SHA256' \| 'MD5'` | Yes | Hashing algorithm |
375
+ | `secret` | `string` | Only for `SHA256` | HMAC secret key; ignored for `MD5` |
376
+ | `outputType` | `'hex' \| 'base64' \| 'base64url' \| 'latin1'` | Yes | Digest output encoding |
377
+
378
+ > [!WARNING]
379
+ > `SHA256` uses `createHmac` and **requires** `secret`. If `secret` is `undefined`, the function short-circuits and returns `text` unchanged - no hash is computed, no error is thrown. `MD5` never needs a secret and always hashes.
380
+
381
+ ```typescript
382
+ import { hash } from '@venizia/ignis-helpers';
383
+
384
+ const md5Hash = hash('some text', { algorithm: 'MD5', outputType: 'hex' });
385
+ const sha256Hmac = hash('some text', { algorithm: 'SHA256', secret: 'a-secret-key', outputType: 'hex' });
386
+ ```
387
+
388
+ ## API Summary
389
+
390
+ | Method | Class | Returns | Description |
391
+ |--------|-------|---------|-------------|
392
+ | `AES.withAlgorithm(algorithm)` | `AES` | `AES` | Create an AES instance with CBC or GCM mode |
393
+ | `encrypt(opts)` | `AES` | `string` | Encrypt a string message |
394
+ | `decrypt(opts)` | `AES` | `string` | Decrypt a ciphertext string |
395
+ | `encryptFile(opts)` | `AES` | `string` | Encrypt file contents to a string |
396
+ | `decryptFile(opts)` | `AES` | `string` | Decrypt file contents to a string |
397
+ | `RSA.withAlgorithm()` | `RSA` | `RSA` | Create an RSA instance |
398
+ | `generateDERKeyPair(opts?)` | `RSA` | `{ publicKey: Buffer; privateKey: Buffer }` | Generate a DER-format key pair |
399
+ | `encrypt(opts)` | `RSA` | `string` | Encrypt with a public key |
400
+ | `decrypt(opts)` | `RSA` | `string` | Decrypt with a private key |
401
+ | `ECDH.withAlgorithm(opts?)` | `ECDH` | `ECDH` | Create an ECDH instance with optional `hkdfInfo` |
402
+ | `generateKeyPair()` | `ECDH` | `Promise<{ keyPair: CryptoKeyPair; publicKeyB64: string }>` | Generate a P-256 key pair |
403
+ | `importPublicKey(opts)` | `ECDH` | `Promise<CryptoKey>` | Import a peer's base64 public key |
404
+ | `deriveAESKey(opts)` | `ECDH` | `Promise<{ key: CryptoKey; salt: string }>` | Derive an AES-256-GCM key via HKDF |
405
+ | `encrypt(opts)` | `ECDH` | `Promise<IECDHEncryptedPayload>` | Encrypt with a derived AES key |
406
+ | `decrypt(opts)` | `ECDH` | `Promise<string>` | Decrypt with a derived AES key |
407
+ | `hash(text, options)` | _(function)_ | `string` | MD5 digest or HMAC-SHA256 |
408
+
409
+ ## Troubleshooting
410
+
411
+ ### "[validateAlgorithmName] Invalid algorithm name | algorithm: undefined"
412
+
413
+ **Cause:** An empty or undefined `algorithm` was passed to the constructor (or `withAlgorithm()`).
414
+
415
+ **Fix:**
416
+
417
+ ```typescript
418
+ const aes = AES.withAlgorithm('aes-256-gcm'); // not undefined or empty
419
+ const rsa = RSA.withAlgorithm(); // no parameter needed
420
+ const ecdh = ECDH.withAlgorithm(); // no parameter needed
421
+ ```
422
+
423
+ ### "[ECDH.fromBase64] Invalid base64 input"
424
+
425
+ **Cause:** A value passed to an ECDH method (public key, salt, IV, or ciphertext) is not valid base64 - length not divisible by 4, or characters outside `A-Za-z0-9+/=`.
426
+
427
+ **Fix:** Pass base64 strings through exactly as produced by the methods that generated them (`publicKeyB64`, `salt`, `iv`, `ct`) - do not trim, re-encode, or modify them.
428
+
429
+ ### "Unsupported state or unable to authenticate data"
430
+
431
+ **Cause:** The ciphertext or auth tag was modified in transit, or you decrypted `aes-256-gcm` ciphertext with a `aes-256-cbc` instance (or vice versa) - the two modes produce incompatible byte layouts.
432
+
433
+ **Fix:** Use the same algorithm mode for both encrypt and decrypt.
434
+
435
+ ### ECDH decrypt throws even though both sides imported each other's public keys
436
+
437
+ **Cause:** Each `deriveAESKey` call without a `salt` generates a new random 32-byte salt. If both sides generate their own, they derive different AES keys.
438
+
439
+ **Fix:** The initiator calls `deriveAESKey` without `salt` and sends the returned `salt` to the responder; the responder passes that exact `salt` into their own `deriveAESKey` call.
440
+
441
+ ### SHA256 hash returns the original text instead of a hash
442
+
443
+ **Cause:** `hash()` with `algorithm: 'SHA256'` requires `secret`; when `secret` is `undefined` it short-circuits and returns `text` unchanged.
444
+
445
+ **Fix:**
446
+
447
+ ```typescript
448
+ const hashed = hash('text', { algorithm: 'SHA256', secret: 'my-hmac-key', outputType: 'hex' });
449
+ ```
450
+
451
+ ## See also
452
+
453
+ - [Crypto overview](/extensions/helpers/crypto/) - introduction and the most common tasks
454
+ - [Crypto Utility](/references/utilities/crypto) - full `hash()` reference
455
+ - [Authentication Component](/extensions/components/authentication/) - JWT and password verification
456
+ - [Security Guidelines](/best-practices/security-guidelines) - cryptographic best practices