@aurabx/jmix-js
v0.2.1
Published
TypeScript implementation of JMIX (JSON Medical Interchange) format for secure medical data exchange
Downloads
13
Readme
JMIX TypeScript Library
TypeScript implementation of the JMIX (JSON Medical Interchange) format for secure medical data exchange.
Installation
npm install
npm run buildPrerequisites
- Node.js 20+ LTS
- npm
- TypeScript 5+
Quick Start
import { JmixBuilder } from 'jmix-ts';
// Create a builder
const builder = new JmixBuilder({
outputPath: './tmp'
});
// Create or load configuration
const config = await JmixBuilder.loadConfig('./samples/sample_config.json');
// Build envelope from DICOM directory
const envelope = await builder.buildFromDicom('./samples/study_1', config);
// Save to files
await builder.saveToFiles(envelope, './output');Demo
Run the demo to see the library in action:
npm run build
node demo-no-validation.jsThis creates a complete JMIX envelope with:
manifest.json- Security, routing, and patient informationmetadata.json- Clinical data and DICOM metadataaudit.json- Audit trail and transmission details ckaging (folder-based JMIX envelope)
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.
CLI-like demo:
node demo-package.js ./samples/study_1 ./tmpProgrammatic API:
import { JmixBuilder } from './dist/index.js';
const builder = new JmixBuilder();
const config = await JmixBuilder.loadConfig('./samples/sample_config.json');
const packagePath = await builder.packageToDirectory('./samples/study_1', config, './tmp');
console.log(packagePath); // => ./tmp/<envelope_id>.JMIXResulting layout:
<outputRoot>/<envelope_id>.JMIX/
├── manifest.json
├── audit.json
└── payload/
├── metadata.json
└── dicom/
└── ... (original DICOM files, structure preserved)Encryption (AES-256-GCM with X25519 + HKDF)
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/.
- Key agreement: Curve25519 (X25519)
- KDF: HKDF-SHA256 (info = "JMIX-Payload-Encryption")
- Cipher: AES-256-GCM
- Manifest fields:
manifest.security.payload_hash— SHA-256 over the plaintextpayload/(deterministic, path+newline+bytes per file)manifest.security.encryption:algorithm: "AES-256-GCM"ephemeral_public_key: base64iv: base64 (12 bytes)auth_tag: base64 (16 bytes)
Demo:
# recipientPublicKeyBase64 must be a 32-byte Curve25519 key in base64
node demo-package-encrypted.js <recipientPublicKeyBase64> ./samples/study_1 ./tmpProgrammatic API:
import { JmixBuilder } from './dist/index.js';
const builder = new JmixBuilder();
const config = await JmixBuilder.loadConfig('./samples/sample_config.json');
const packagePath = await builder.packageEncryptedToDirectory(
'./samples/study_1',
config,
'./tmp',
'<recipientPublicKeyBase64>'
);
console.log(packagePath);Encrypted layout:
<outputRoot>/<envelope_id>.JMIX/
├── manifest.json # includes security.payload_hash and security.encryption
├── audit.json
└── payload.encrypted # AES-256-GCM encrypted TAR of plaintext payload/Decryption
You can decrypt an encrypted JMIX envelope and restore a plaintext payload/ directory. The payload hash is verified against manifest.security.payload_hash.
Self-contained demo (encrypt + decrypt with a fresh keypair):
node demo-decrypt.js ./samples/study_1 ./tmpDecrypt an existing folder (requires the matching Curve25519 private key in base64):
node demo-decrypt-existing.js ./tmp/<envelope_id>.JMIX <recipientPrivateKeyBase64>Programmatic API:
import { JmixBuilder } from './dist/index.js';
const builder = new JmixBuilder();
const payloadPath = await builder.decryptEnvelope(
'./tmp/<envelope_id>.JMIX',
'<recipientPrivateKeyBase64>'
);
console.log(payloadPath);Verify Payload Hash
Verify the payload hash listed in manifest.security.payload_hash.
- Plaintext payload/: compare directly
- Encrypted payload.encrypted: requires the recipient private key to decrypt to a temp dir before verifying
CLI demo:
# Plaintext envelope
npm run demo:verify:hash -- ./tmp/<envelope_id>.JMIX
# Encrypted envelope (requires private key)
npm run demo:verify:hash -- ./tmp/<envelope_id>.JMIX <recipientPrivateKeyBase64>Programmatic API:
import { JmixBuilder } from './dist/index.js';
const builder = new JmixBuilder();
const result = await builder.verifyPayloadHash(
'./tmp/<envelope_id>.JMIX',
{ recipientPrivateKeyBase64: '<privKey-if-encrypted>' }
);
console.log(result.ok, result.expected, result.computed);Scripts
# Development
npm run build # Compile TypeScript
npm run typecheck # Type checking only
npm test # Run all tests
npm run test:watch # Run tests in watch mode
npm run test:coverage # Generate coverage report
# Code Quality
npm run format # Check code formatting
npm run format:fix # Fix code formatting
npm run clean # Clean build artifactsArchitecture
src/
├── JmixBuilder.ts # Main orchestrator
├── types/index.ts # TypeScript interfaces
├── validation/ # Ajv schema validation
├── dicom/ # DICOM file processing
├── crypto/ # AES-256-GCM encryption (future)
└── errors/ # Error types
tests/ # Jest test suite
samples/ # Sample JSON filesConfiguration
Schema Validation
** This is Alpha quality code and has not yet been fully tested **
The library uses configurable schema validation:
const builder = new JmixBuilder({
schemaValidatorOptions: {
schemaPath: '../jmix/schemas', // Default: ../jmix/schemas
strictMode: true // Default: true
}
});
// Or via environment variable
process.env.JMIX_SCHEMA_PATH = '/path/to/schemas';Output Directory
Follows user rules for temp file output:
const builder = new JmixBuilder({
outputPath: './tmp' // Default: ./tmp (not /tmp)
});DICOM Processing
The library automatically:
- Recursively scans directories for DICOM files
- Validates files using DICM magic number at byte offset 128
- Extracts metadata (patient info, study details, series information)
- Falls back gracefully to configuration data when DICOM parsing fails
- Supports empty directories for testing
Schema Validation
- Default: Looks for schemas in
../jmix/schemas - Configurable: Override via constructor or environment variable
- Graceful Degradation: Skips validation when schemas not found
- Comprehensive: Validates manifest, metadata, and audit components
Testing
Run the comprehensive test suite:
# All tests
npm test
# Specific test files
npm test JmixBuilder.test.ts
npm test SchemaValidator.test.ts
# With coverage
npm run test:coverageThe test suite includes:
- ✅ JmixBuilder integration tests
- ✅ Schema validator tests
- ✅ Type definition tests
- ✅ DICOM processing tests
- ✅ Configuration loading tests
- ✅ File I/O tests
All tests use the /samples directory for realistic test data and output to ./tmp for temporary files.
Security Model
Implements the JMIX security whitepaper specifications:
- AES-256-GCM encryption with ephemeral public keys
- Base64 encoding for all cryptographic material (ephemeral_public_key, iv, auth_tag)
- Forward secrecy through ephemeral key usage
- Optional governance via Aurabox directory services
See .ai/security.md for the complete security model.
Development
The library follows TypeScript best practices:
- ESM modules with .js imports in source
- Strict TypeScript configuration
- Comprehensive type definitions for all JMIX components
- Jest testing with ts-jest for ESM support
- Prettier formatting for consistent code style
Files Generated
A complete JMIX envelope consists of:
manifest.json- Routing, security classification, patient info, sender/receiver detailsmetadata.json- Clinical metadata, DICOM study information, custom metadataaudit.json- Audit trail, status, and events timelinefiles.json- (Optional) File manifest with hashes and sizes
Compatibility
- JMIX Specification: Implements JMIX v1.0 format
- Schema Compatibility: Works with JMIX JSON Schema Draft 2020-12
- DICOM Support: Basic DICOM file detection and metadata extraction
- Framework Agnostic: Pure TypeScript, works with any Node.js framework
License
Matches the licensing of the JMIX specification and related implementations.
For detailed API documentation, see the TypeScript declarations in dist/ after building.
