@aurabx/jmix-js 0.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 (73) hide show
  1. package/.ai/security.md +184 -0
  2. package/.eslintrc.cjs +23 -0
  3. package/.idea/jmix-ts.iml +13 -0
  4. package/.idea/modules.xml +8 -0
  5. package/.idea/vcs.xml +6 -0
  6. package/.prettierrc +9 -0
  7. package/README.md +335 -0
  8. package/WARP.md +143 -0
  9. package/dist/JmixBuilder.d.ts +82 -0
  10. package/dist/JmixBuilder.d.ts.map +1 -0
  11. package/dist/JmixBuilder.js +353 -0
  12. package/dist/JmixBuilder.js.map +1 -0
  13. package/dist/crypto/PayloadDecryptor.d.ts +12 -0
  14. package/dist/crypto/PayloadDecryptor.d.ts.map +1 -0
  15. package/dist/crypto/PayloadDecryptor.js +39 -0
  16. package/dist/crypto/PayloadDecryptor.js.map +1 -0
  17. package/dist/crypto/PayloadEncryptor.d.ts +21 -0
  18. package/dist/crypto/PayloadEncryptor.d.ts.map +1 -0
  19. package/dist/crypto/PayloadEncryptor.js +79 -0
  20. package/dist/crypto/PayloadEncryptor.js.map +1 -0
  21. package/dist/demo-decrypt-existing.js +27 -0
  22. package/dist/demo-decrypt.js +39 -0
  23. package/dist/demo-no-validation.js +95 -0
  24. package/dist/demo-package-encrypted.js +34 -0
  25. package/dist/demo-package.js +28 -0
  26. package/dist/demo-verify-hash.js +30 -0
  27. package/dist/demo.js +73 -0
  28. package/dist/dicom/DicomProcessor.d.ts +40 -0
  29. package/dist/dicom/DicomProcessor.d.ts.map +1 -0
  30. package/dist/dicom/DicomProcessor.js +231 -0
  31. package/dist/dicom/DicomProcessor.js.map +1 -0
  32. package/dist/index.d.ts +6 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +8 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/types/index.d.ts +165 -0
  37. package/dist/types/index.d.ts.map +1 -0
  38. package/dist/types/index.js +31 -0
  39. package/dist/types/index.js.map +1 -0
  40. package/dist/validation/SchemaValidator.d.ts +25 -0
  41. package/dist/validation/SchemaValidator.d.ts.map +1 -0
  42. package/dist/validation/SchemaValidator.js +125 -0
  43. package/dist/validation/SchemaValidator.js.map +1 -0
  44. package/jest.config.json +20 -0
  45. package/package.json +74 -0
  46. package/samples/sample_config.json +52 -0
  47. package/samples/study_1/series_1/CT.1.1.dcm +0 -0
  48. package/samples/study_1/series_1/CT.1.2.dcm +0 -0
  49. package/samples/study_1/series_1/CT.1.3.dcm +0 -0
  50. package/samples/study_1/series_1/CT.1.4.dcm +0 -0
  51. package/samples/study_1/series_1/CT.1.5.dcm +0 -0
  52. package/samples/study_1/series_2/CT.2.1.dcm +0 -0
  53. package/samples/study_1/series_2/CT.2.2.dcm +0 -0
  54. package/samples/study_1/series_2/CT.2.3.dcm +0 -0
  55. package/samples/study_1/series_2/CT.2.4.dcm +0 -0
  56. package/samples/study_1/series_2/CT.2.5.dcm +0 -0
  57. package/samples/study_1/series_3/CT.3.1.dcm +0 -0
  58. package/samples/study_1/series_3/CT.3.2.dcm +0 -0
  59. package/samples/study_1/series_3/CT.3.3.dcm +0 -0
  60. package/samples/study_1/series_3/CT.3.4.dcm +0 -0
  61. package/samples/study_1/series_3/CT.3.5.dcm +0 -0
  62. package/src/JmixBuilder.ts +572 -0
  63. package/src/crypto/PayloadDecryptor.ts +45 -0
  64. package/src/crypto/PayloadEncryptor.ts +97 -0
  65. package/src/dicom/DicomProcessor.ts +262 -0
  66. package/src/index.ts +30 -0
  67. package/src/types/daikon.d.ts +1 -0
  68. package/src/types/index.ts +201 -0
  69. package/src/validation/SchemaValidator.ts +156 -0
  70. package/test-encrypt.js +10 -0
  71. package/tests/JmixBuilder.test.ts +170 -0
  72. package/tests/SchemaValidator.test.ts +196 -0
  73. package/tsconfig.json +21 -0
@@ -0,0 +1,184 @@
1
+ # JMIX Envelope Security White Paper
2
+
3
+ Christopher Skene
4
+ 8th June 2025
5
+ DRAFT - WORK IN PROGRESS
6
+
7
+ ## Abstract
8
+
9
+ This paper presents the security encryption architecture for JMIX (JSON Medical Imaging Exchange) Envelopes, supporting secure, peer-to-peer exchange of identifiable imaging data. The model is designed to support interoperability between independent actors and optionally leverage the Aurabox directory and services for enhanced usability, policy enforcement, and auditability, without introducing any dependency on Aurabox for decryption or transmission.
10
+
11
+ ## 1. Introduction
12
+
13
+ Healthcare imaging data exchange faces challenges around security, interoperability, and decentralisation. Traditional centralised exchange models introduce unnecessary dependencies and trust anchors, whereas pure peer-to-peer systems can lack usability or governance.
14
+
15
+ JMIX aims to strike a balance: enabling direct, secure, peer-to-peer sharing, while allowing parties who choose to use a trusted directory (e.g. Aurabox) to gain benefits like key discovery, consent validation, and identity anchoring.
16
+
17
+ Some of the features of this security model include:
18
+
19
+ - Forward Secrecy: Ephemeral keys ensure compromise of long-term keys does not expose past data.
20
+ - Authentication: GCM tag ensures ciphertext cannot be tampered with.
21
+ - Minimal Trust: No dependency on Aurabox or any other service for basic operation.
22
+ - Optional Governance: Aurabox can be used to anchor identities, resolve key IDs, or audit transfers
23
+ - Non-repudiation: Proof of sender
24
+
25
+ ## 2. Design Principles
26
+
27
+ Self-contained encryption: All JMIX envelopes must be decryptable using only what is contained inside the envelope, assuming the recipient possesses the correct private key.
28
+
29
+ Ephemeral key use: All encryption must use ephemeral sender keys to provide forward secrecy.
30
+
31
+ Fixed cryptographic stack: All payloads are encrypted using AES-256-GCM with ECDH key agreement and HKDF (SHA-256) for symmetric key derivation.
32
+
33
+ Directory-optional context: Key discovery, identity mapping, and policy evaluation may optionally be facilitated by Aurabox or a compatible directory, but are not required for decryption.
34
+
35
+ ## 3. Cryptographic Foundations
36
+
37
+ Algorithm:
38
+
39
+ - Encryption: AES-256 in Galois/Counter Mode (GCM)
40
+ - Key Exchange: Elliptic Curve Diffie-Hellman (ECDH) over Curve25519
41
+ - Key Derivation: HKDF using SHA-256
42
+
43
+ Each package is encrypted using a one-time symmetric key derived from ECDH, ensuring forward secrecy and resilience.
44
+
45
+ ## 4. Envelope Structure
46
+
47
+ ### a. Encryption (required when the package is encrypted)
48
+
49
+ The Encryption block describes the encryption of the package directory. It is required when the package is encrypted, but not when the package is unencrypted (for example, if the envelope is being used as a download).
50
+
51
+ ```json
52
+ "encryption": {
53
+ "algorithm": "AES-256-GCM",
54
+ "ephemeral_public_key": "<base64>",
55
+ "iv": "<base64>",
56
+ "auth_tag": "<base64>"
57
+ }
58
+ ```
59
+
60
+ These fields are all required for the recipient to decrypt the contents of the package directory.
61
+
62
+ - algorithm: Always AES-256-GCM
63
+ - ephemeral_public_key: The sender's one-time-use ECDH public key.
64
+ - iv: 96-bit AES-GCM nonce.
65
+ - auth_tag: 128-bit authentication tag generated during encryption.
66
+
67
+ ### b. Sender Assertion (optional, but recommended)
68
+
69
+ The Sender Assertion block allows the sender to provide information about themselves, so that the receiver can verify the authenticity of the package. It serves as a mechanism for authenticating and verifying the identity of the sender of a JMIX envelope, especially in decentralised or federated environments.
70
+
71
+ It helps guard against spoofing by cryptographically binding the claimed identity to the encrypted payload.
72
+ Enables trust decisions in the absence of live infrastructure (e.g., offline environments).
73
+ Supports forensic verification: recipients can prove to others who sent the data and that it hasn’t been altered.
74
+
75
+ The sender_assertion block exists to:
76
+
77
+ - Provide verifiable metadata about the sender, such as their claimed identity and the cryptographic evidence that supports that claim.
78
+ - Enable trust and non-repudiation, by binding the sender’s claimed identity to the specific encrypted payload using a signature.
79
+ - Support optional directory-based verification, such as through Aurabox or other compatible directories, without being dependent on them.
80
+
81
+ This is crucial in peer-to-peer settings, where there is no central server to authenticate participants. It allows recipients and third parties (for example, auditors) to validate who sent a JMIX package, even offline. The Sender Assertion is foundational to JMIX's trust and governance model, acting as a decentralised and optionally third-party-verified sender identity proof, ensuring secure and accountable medical imaging exchange.
82
+
83
+ ```json
84
+ {
85
+ "signing_key": {
86
+ "alg": "Ed25519",
87
+ "public_key": "<base64>",
88
+ "fingerprint": "SHA256:<hex>"
89
+ },
90
+ "key_reference": "aurabox://org/clinic-a#key-ed25519",
91
+ "signed_fields": [
92
+ "sender.id",
93
+ "sender.name",
94
+ "signatures.manifest.kid",
95
+ "manifest_hash"
96
+ ],
97
+ "signature": "<base64sig>",
98
+ "expires_at": "2025-07-07T00:00:00Z",
99
+ "directory_attestation": {
100
+ "provider": "aurabox",
101
+ "attestation_signature": "<JWS>",
102
+ "attestation_timestamp": "2025-06-07T14:01:00Z",
103
+ "attestation_public_key": "<base64>"
104
+ }
105
+ }
106
+ ```
107
+
108
+ The file is referenced in the sender block, as follows:
109
+
110
+ ```json
111
+ "sender": {
112
+ "id": "org:au.gov.health.123456",
113
+ "name": "Clinic A",
114
+ "contact": "dicom@clinica.org.au",
115
+ "assertion": {
116
+ "...": "assertion goes here"
117
+ }
118
+ }
119
+ ```
120
+
121
+ While the attestation includes expiry, the receiver is not bound to reject the envelope if the expiry has passed. This is up to receivers to define.
122
+
123
+ ### c. Requester Assertion (optional)
124
+
125
+ The requester assertion follows a similar pattern to the Sender Assertion and allows the receiver to validate the requester. This may be useful to determine that the receiver is receiving information it is allowed to hold, based on the requester's identity.
126
+
127
+ The requester identity should be provided by the requester when requesting the data, either via a JMIX API or some other mechanism. The signature model is identical to the sender's.
128
+
129
+ ```json
130
+ "requester": {
131
+ "name": "Dr. Michael Chen",
132
+ "email": "mchen@university-medical.edu",
133
+ "authentication_level": "directory_verified",
134
+ "assertion": {
135
+ "...": "assertion goes here"
136
+ }
137
+ }
138
+ ```
139
+
140
+ ### Verification Workflow
141
+
142
+ - Fetch signed_fields and corresponding values from the envelope.
143
+ - Canonicalise the field-value structure.
144
+ - Verify the signature using signing_key.public_key.
145
+ - Optional: Validate directory_attestation.attestation_signature using attestation_public_key.
146
+ - Check expires_at if present.
147
+
148
+ ## 5. Modes of Operation
149
+
150
+ | Use Case | Description |
151
+ | --------------------------- | -------------------------------------------------------------------------------------------------------- |
152
+ | Pure P2P | Sender and recipient exchange directly (email, file transfer, etc.). Only envelope metadata is required. |
153
+ | P2P with Aurabox resolution | Recipient or sender uses Aurabox API to discover keys, validate sender IDs, or log consent. |
154
+ | Asymmetric P2P | One user uses Aurabox for discovery, the other operates independently. |
155
+
156
+ ## 6. Encryption Workflow
157
+
158
+ - Sender generates a new ephemeral EC keypair.
159
+ - Shared key is derived via ECDH (sender's ephemeral privkey plus recipient's pubkey).
160
+ - Symmetric key is produced using HKDF with SHA-256.
161
+ - Payload is encrypted with AES-256-GCM using the derived key, random IV, and the tag is retained.
162
+ - Envelope is assembled with the encrypted payload and manifest.json.
163
+
164
+ ## 7. Recipient Key Management and Discovery
165
+
166
+ JMIX assumes that each recipient maintains a stable, long-term asymmetric keypair (for example, Curve25519) for envelope decryption. The public component of this key must be made available to potential senders through one or more of the following mechanisms:
167
+
168
+ - Out-of-Band Exchange: Recipients may distribute their public keys manually via QR codes, shared config files, or email. This is suitable for small, trusted collaborations.
169
+ - Directory Lookup (optional): Aurabox and compatible directories may expose a public key endpoint, for example, aurabox://org/clinic-a#key-ed25519, to facilitate key discovery and resolution.
170
+ - In-Envelope Reference: Senders may embed a key_reference URI in the sender assertion, indicating the expected recipient key. This does not impact decryption, but helps with auditing and debugging.
171
+
172
+ For secure operation, recipients must safeguard their private key material. Rotation or revocation of keys should be managed via external policy or infrastructure and is not currently defined within the JMIX envelope format.
173
+
174
+ ## 8. Deployment Scenarios
175
+
176
+ - Independent Users: Healthcare providers, researchers, or clinicians using custom tooling.
177
+ - Integrated Aurabox Users: Those who opt-in for identity resolution, central consent records, or integration with Aurabox storage and viewing services.
178
+ - Mixed Mode: One peer uses Aurabox to obtain the other’s public key, then sends a self-contained envelope.
179
+
180
+ ## 9. Conclusion
181
+
182
+ The JMIX envelope encryption strategy strikes a balance between secure, verifiable data exchange and ecosystem flexibility. It provides robust default behaviour for independent users while allowing powerful enhancements when Aurabox services are available.
183
+
184
+ This ensures imaging data can be safely exchanged, whether between two doctors, hospitals, or platforms, with confidence in its privacy, integrity, and interoperability.
package/.eslintrc.cjs ADDED
@@ -0,0 +1,23 @@
1
+ module.exports = {
2
+ env: {
3
+ node: true,
4
+ es2022: true,
5
+ jest: true,
6
+ },
7
+ extends: ['eslint:recommended'],
8
+ parserOptions: {
9
+ ecmaVersion: 2022,
10
+ sourceType: 'module',
11
+ },
12
+ rules: {
13
+ 'no-console': 'warn',
14
+ },
15
+ ignorePatterns: [
16
+ 'dist/',
17
+ 'coverage/',
18
+ 'node_modules/',
19
+ '*.js',
20
+ '**/*.d.ts',
21
+ 'src/**/*.ts', // Skip TypeScript files for now
22
+ ],
23
+ };
@@ -0,0 +1,13 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="WEB_MODULE" version="4">
3
+ <component name="NewModuleRootManager">
4
+ <content url="file://$MODULE_DIR$">
5
+ <sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
6
+ <excludeFolder url="file://$MODULE_DIR$/.tmp" />
7
+ <excludeFolder url="file://$MODULE_DIR$/temp" />
8
+ <excludeFolder url="file://$MODULE_DIR$/tmp" />
9
+ </content>
10
+ <orderEntry type="inheritedJdk" />
11
+ <orderEntry type="sourceFolder" forTests="false" />
12
+ </component>
13
+ </module>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/jmix-ts.iml" filepath="$PROJECT_DIR$/.idea/jmix-ts.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
package/.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
5
+ </component>
6
+ </project>
package/.prettierrc ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "semi": true,
3
+ "trailingComma": "es5",
4
+ "singleQuote": true,
5
+ "printWidth": 80,
6
+ "tabWidth": 2,
7
+ "useTabs": false,
8
+ "endOfLine": "lf"
9
+ }
package/README.md ADDED
@@ -0,0 +1,335 @@
1
+ # JMIX TypeScript Library
2
+
3
+ TypeScript implementation of the JMIX (JSON Medical Interchange) format for secure medical data exchange.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install
9
+ npm run build
10
+ ```
11
+
12
+ ## Prerequisites
13
+
14
+ - Node.js 20+ LTS
15
+ - npm
16
+ - TypeScript 5+
17
+
18
+ ## Quick Start
19
+
20
+ ```typescript
21
+ import { JmixBuilder } from 'jmix-ts';
22
+
23
+ // Create a builder
24
+ const builder = new JmixBuilder({
25
+ outputPath: './tmp'
26
+ });
27
+
28
+ // Create or load configuration
29
+ const config = await JmixBuilder.loadConfig('./samples/sample_config.json');
30
+
31
+ // Build envelope from DICOM directory
32
+ const envelope = await builder.buildFromDicom('./samples/study_1', config);
33
+
34
+ // Save to files
35
+ await builder.saveToFiles(envelope, './output');
36
+ ```
37
+
38
+ ## Demo
39
+
40
+ Run the demo to see the library in action:
41
+
42
+ ```bash
43
+ npm run build
44
+ node demo-no-validation.js
45
+ ```
46
+
47
+ This creates a complete JMIX envelope with:
48
+ - `manifest.json` - Security, routing, and patient information
49
+ - `metadata.json` - Clinical data and DICOM metadata
50
+ - `audit.json` - Audit trail and transmission details
51
+ ckaging (folder-based JMIX envelope)
52
+
53
+ Create a proper JMIX folder that includes the original DICOM files under `payload/dicom` and computes a deterministic SHA-256 payload hash stored at `manifest.security.payload_hash`.
54
+
55
+ CLI-like demo:
56
+
57
+ ```sh
58
+ node demo-package.js ./samples/study_1 ./tmp
59
+ ```
60
+
61
+ Programmatic API:
62
+
63
+ ```ts
64
+ import { JmixBuilder } from './dist/index.js';
65
+
66
+ const builder = new JmixBuilder();
67
+ const config = await JmixBuilder.loadConfig('./samples/sample_config.json');
68
+ const packagePath = await builder.packageToDirectory('./samples/study_1', config, './tmp');
69
+ console.log(packagePath); // => ./tmp/<envelope_id>.JMIX
70
+ ```
71
+
72
+ Resulting layout:
73
+
74
+ ```text
75
+ <outputRoot>/<envelope_id>.JMIX/
76
+ ├── manifest.json
77
+ ├── audit.json
78
+ └── payload/
79
+ ├── metadata.json
80
+ └── dicom/
81
+ └── ... (original DICOM files, structure preserved)
82
+ ```
83
+
84
+ ### Encryption (AES-256-GCM with X25519 + HKDF)
85
+
86
+ The library can produce an encrypted JMIX envelope. It tars the plaintext `payload/` directory, encrypts it with AES-256-GCM using a key derived from X25519 ECDH and HKDF-SHA256, then writes `payload.encrypted` and removes the plaintext `payload/`.
87
+
88
+ - Key agreement: Curve25519 (X25519)
89
+ - KDF: HKDF-SHA256 (info = "JMIX-Payload-Encryption")
90
+ - Cipher: AES-256-GCM
91
+ - Manifest fields:
92
+ - `manifest.security.payload_hash` — SHA-256 over the plaintext `payload/` (deterministic, path+newline+bytes per file)
93
+ - `manifest.security.encryption`:
94
+ - `algorithm`: "AES-256-GCM"
95
+ - `ephemeral_public_key`: base64
96
+ - `iv`: base64 (12 bytes)
97
+ - `auth_tag`: base64 (16 bytes)
98
+
99
+ Demo:
100
+
101
+ ```sh
102
+ # recipientPublicKeyBase64 must be a 32-byte Curve25519 key in base64
103
+ node demo-package-encrypted.js <recipientPublicKeyBase64> ./samples/study_1 ./tmp
104
+ ```
105
+
106
+ Programmatic API:
107
+
108
+ ```ts
109
+ import { JmixBuilder } from './dist/index.js';
110
+
111
+ const builder = new JmixBuilder();
112
+ const config = await JmixBuilder.loadConfig('./samples/sample_config.json');
113
+ const packagePath = await builder.packageEncryptedToDirectory(
114
+ './samples/study_1',
115
+ config,
116
+ './tmp',
117
+ '<recipientPublicKeyBase64>'
118
+ );
119
+ console.log(packagePath);
120
+ ```
121
+
122
+ Encrypted layout:
123
+
124
+ ```text
125
+ <outputRoot>/<envelope_id>.JMIX/
126
+ ├── manifest.json # includes security.payload_hash and security.encryption
127
+ ├── audit.json
128
+ └── payload.encrypted # AES-256-GCM encrypted TAR of plaintext payload/
129
+ ```
130
+
131
+ ### Decryption
132
+
133
+ You can decrypt an encrypted JMIX envelope and restore a plaintext `payload/` directory. The payload hash is verified against `manifest.security.payload_hash`.
134
+
135
+ Self-contained demo (encrypt + decrypt with a fresh keypair):
136
+
137
+ ```sh
138
+ node demo-decrypt.js ./samples/study_1 ./tmp
139
+ ```
140
+
141
+ Decrypt an existing folder (requires the matching Curve25519 private key in base64):
142
+
143
+ ```sh
144
+ node demo-decrypt-existing.js ./tmp/<envelope_id>.JMIX <recipientPrivateKeyBase64>
145
+ ```
146
+
147
+ Programmatic API:
148
+
149
+ ```ts
150
+ import { JmixBuilder } from './dist/index.js';
151
+
152
+ const builder = new JmixBuilder();
153
+ const payloadPath = await builder.decryptEnvelope(
154
+ './tmp/<envelope_id>.JMIX',
155
+ '<recipientPrivateKeyBase64>'
156
+ );
157
+ console.log(payloadPath);
158
+ ```
159
+
160
+ ### Verify Payload Hash
161
+
162
+ Verify the payload hash listed in `manifest.security.payload_hash`.
163
+
164
+ - Plaintext payload/: compare directly
165
+ - Encrypted payload.encrypted: requires the recipient private key to decrypt to a temp dir before verifying
166
+
167
+ CLI demo:
168
+
169
+ ```sh
170
+ # Plaintext envelope
171
+ npm run demo:verify:hash -- ./tmp/<envelope_id>.JMIX
172
+
173
+ # Encrypted envelope (requires private key)
174
+ npm run demo:verify:hash -- ./tmp/<envelope_id>.JMIX <recipientPrivateKeyBase64>
175
+ ```
176
+
177
+ Programmatic API:
178
+
179
+ ```ts
180
+ import { JmixBuilder } from './dist/index.js';
181
+
182
+ const builder = new JmixBuilder();
183
+ const result = await builder.verifyPayloadHash(
184
+ './tmp/<envelope_id>.JMIX',
185
+ { recipientPrivateKeyBase64: '<privKey-if-encrypted>' }
186
+ );
187
+ console.log(result.ok, result.expected, result.computed);
188
+ ```
189
+
190
+ ## Scripts
191
+
192
+ ```bash
193
+ # Development
194
+ npm run build # Compile TypeScript
195
+ npm run typecheck # Type checking only
196
+ npm test # Run all tests
197
+ npm run test:watch # Run tests in watch mode
198
+ npm run test:coverage # Generate coverage report
199
+
200
+ # Code Quality
201
+ npm run format # Check code formatting
202
+ npm run format:fix # Fix code formatting
203
+ npm run clean # Clean build artifacts
204
+ ```
205
+
206
+ ## Architecture
207
+
208
+ ```
209
+ src/
210
+ ├── JmixBuilder.ts # Main orchestrator
211
+ ├── types/index.ts # TypeScript interfaces
212
+ ├── validation/ # Ajv schema validation
213
+ ├── dicom/ # DICOM file processing
214
+ ├── crypto/ # AES-256-GCM encryption (future)
215
+ └── errors/ # Error types
216
+
217
+ tests/ # Jest test suite
218
+ samples/ # Sample JSON files
219
+ ```
220
+
221
+ ## Configuration
222
+
223
+ ### Schema Validation
224
+
225
+ ** This is Alpha quality code and has not yet been fully tested **
226
+
227
+ The library uses configurable schema validation:
228
+
229
+ ```typescript
230
+ const builder = new JmixBuilder({
231
+ schemaValidatorOptions: {
232
+ schemaPath: '../jmix/schemas', // Default: ../jmix/schemas
233
+ strictMode: true // Default: true
234
+ }
235
+ });
236
+
237
+ // Or via environment variable
238
+ process.env.JMIX_SCHEMA_PATH = '/path/to/schemas';
239
+ ```
240
+
241
+ ### Output Directory
242
+
243
+ Follows user rules for temp file output:
244
+
245
+ ```typescript
246
+ const builder = new JmixBuilder({
247
+ outputPath: './tmp' // Default: ./tmp (not /tmp)
248
+ });
249
+ ```
250
+
251
+ ## DICOM Processing
252
+
253
+ The library automatically:
254
+
255
+ 1. Recursively scans directories for DICOM files
256
+ 2. Validates files using DICM magic number at byte offset 128
257
+ 3. Extracts metadata (patient info, study details, series information)
258
+ 4. Falls back gracefully to configuration data when DICOM parsing fails
259
+ 5. Supports empty directories for testing
260
+
261
+ ## Schema Validation
262
+
263
+ - **Default**: Looks for schemas in `../jmix/schemas`
264
+ - **Configurable**: Override via constructor or environment variable
265
+ - **Graceful Degradation**: Skips validation when schemas not found
266
+ - **Comprehensive**: Validates manifest, metadata, and audit components
267
+
268
+ ## Testing
269
+
270
+ Run the comprehensive test suite:
271
+
272
+ ```bash
273
+ # All tests
274
+ npm test
275
+
276
+ # Specific test files
277
+ npm test JmixBuilder.test.ts
278
+ npm test SchemaValidator.test.ts
279
+
280
+ # With coverage
281
+ npm run test:coverage
282
+ ```
283
+
284
+ The test suite includes:
285
+ - ✅ JmixBuilder integration tests
286
+ - ✅ Schema validator tests
287
+ - ✅ Type definition tests
288
+ - ✅ DICOM processing tests
289
+ - ✅ Configuration loading tests
290
+ - ✅ File I/O tests
291
+
292
+ All tests use the `/samples` directory for realistic test data and output to `./tmp` for temporary files.
293
+
294
+ ## Security Model
295
+
296
+ Implements the JMIX security whitepaper specifications:
297
+ - **AES-256-GCM** encryption with ephemeral public keys
298
+ - **Base64 encoding** for all cryptographic material (ephemeral_public_key, iv, auth_tag)
299
+ - **Forward secrecy** through ephemeral key usage
300
+ - **Optional governance** via Aurabox directory services
301
+
302
+ See `.ai/security.md` for the complete security model.
303
+
304
+ ## Development
305
+
306
+ The library follows TypeScript best practices:
307
+ - **ESM modules** with .js imports in source
308
+ - **Strict TypeScript** configuration
309
+ - **Comprehensive type definitions** for all JMIX components
310
+ - **Jest testing** with ts-jest for ESM support
311
+ - **Prettier formatting** for consistent code style
312
+
313
+ ## Files Generated
314
+
315
+ A complete JMIX envelope consists of:
316
+
317
+ - **`manifest.json`** - Routing, security classification, patient info, sender/receiver details
318
+ - **`metadata.json`** - Clinical metadata, DICOM study information, custom metadata
319
+ - **`audit.json`** - Audit trail, status, and events timeline
320
+ - **`files.json`** - (Optional) File manifest with hashes and sizes
321
+
322
+ ## Compatibility
323
+
324
+ - **JMIX Specification**: Implements JMIX v1.0 format
325
+ - **Schema Compatibility**: Works with JMIX JSON Schema Draft 2020-12
326
+ - **DICOM Support**: Basic DICOM file detection and metadata extraction
327
+ - **Framework Agnostic**: Pure TypeScript, works with any Node.js framework
328
+
329
+ ## License
330
+
331
+ Matches the licensing of the JMIX specification and related implementations.
332
+
333
+ ---
334
+
335
+ For detailed API documentation, see the TypeScript declarations in `dist/` after building.