Migration Guide
This guide covers migrating between JACS versions and common migration scenarios.
Version Compatibility
JACS maintains backward compatibility for document verification:
- Documents signed with older versions can be verified with newer versions
- Older JACS versions cannot verify documents using newer cryptographic algorithms
Migrating Node.js from 0.6.x to 0.7.0
Breaking Change: Async-First API
In v0.7.0, all NAPI operations return Promises by default. Sync variants are available with a Sync suffix, following the Node.js convention (like fs.readFile vs fs.readFileSync).
Before (v0.6.x):
const agent = new JacsAgent();
agent.load('./jacs.config.json');
const doc = agent.createDocument(JSON.stringify(content));
const isValid = agent.verifyDocument(doc);
After (v0.7.0, async -- recommended):
const agent = new JacsAgent();
await agent.load('./jacs.config.json');
const doc = await agent.createDocument(JSON.stringify(content));
const isValid = await agent.verifyDocument(doc);
After (v0.7.0, sync -- for scripts/CLI):
const agent = new JacsAgent();
agent.loadSync('./jacs.config.json');
const doc = agent.createDocumentSync(JSON.stringify(content));
const isValid = agent.verifyDocumentSync(doc);
Method Renaming Summary
| v0.6.x | v0.7.0 Async (default) | v0.7.0 Sync |
|---|---|---|
agent.load(path) | await agent.load(path) | agent.loadSync(path) |
agent.createDocument(...) | await agent.createDocument(...) | agent.createDocumentSync(...) |
agent.verifyDocument(doc) | await agent.verifyDocument(doc) | agent.verifyDocumentSync(doc) |
agent.verifyAgent() | await agent.verifyAgent() | agent.verifyAgentSync() |
agent.updateAgent(json) | await agent.updateAgent(json) | agent.updateAgentSync(json) |
agent.updateDocument(...) | await agent.updateDocument(...) | agent.updateDocumentSync(...) |
agent.signString(data) | await agent.signString(data) | agent.signStringSync(data) |
agent.createAgreement(...) | await agent.createAgreement(...) | agent.createAgreementSync(...) |
agent.signAgreement(...) | await agent.signAgreement(...) | agent.signAgreementSync(...) |
agent.checkAgreement(...) | await agent.checkAgreement(...) | agent.checkAgreementSync(...) |
V8-Thread-Only Methods (No Change)
These methods remain synchronous without a Sync suffix because they use V8-thread-only APIs (Env, JsObject):
agent.signRequest(params)-- unchangedagent.verifyResponse(doc)-- unchangedagent.verifyResponseWithAgentId(doc)-- unchanged
Simplified API (Module-Level)
The @hai.ai/jacs/simple module follows the same pattern:
// v0.6.x
await jacs.quickstart({ name: 'my-agent', domain: 'agent.example.com' });
const signed = jacs.signMessage({ action: 'approve' });
// v0.7.0 async (recommended)
await jacs.quickstart({ name: 'my-agent', domain: 'agent.example.com' });
const signed = await jacs.signMessage({ action: 'approve' });
// v0.7.0 sync
jacs.quickstartSync({ name: 'my-agent', domain: 'agent.example.com' });
const signed = jacs.signMessageSync({ action: 'approve' });
Pure Sync Functions (No Change)
These functions do not call NAPI and remain unchanged (no suffix needed):
hashString(),createConfig(),getPublicKey(),isLoaded(),exportAgent(),getAgentInfo()getDnsRecord(),getWellKnownJson(),verifyStandalone()- Trust store:
trustAgent(),listTrustedAgents(),untrustAgent(),isTrusted(),getTrustedAgent()
Migration Steps
- Update dependency:
npm install @hai.ai/jacs@0.7.0 - Add
awaitto all NAPI method calls, or appendSyncto method names - Update test assertions to handle Promises (use
async/awaitin test functions) - V8-thread-only methods (
signRequest,verifyResponse,verifyResponseWithAgentId) need no changes
Migrating from 0.5.1 to 0.5.2
Migration Notes
PBKDF2 Iteration Count: New key encryptions use 600,000 iterations (up from 100,000). Existing encrypted keys are decrypted automatically via fallback. To upgrade existing keys, re-encrypt them:
# Re-generate keys to use the new iteration count
jacs keygen
Deprecated Environment Variables
JACS_USE_SECURITYis nowJACS_ENABLE_FILESYSTEM_QUARANTINE. The old name still works with a deprecation warning.
New Environment Variables
| Variable | Default | Description |
|---|---|---|
JACS_MAX_SIGNATURE_AGE_SECONDS | 0 (no expiration) | Maximum age of valid signatures. Set to a positive value to enable (e.g., 7776000 for 90 days). |
JACS_REQUIRE_EXPLICIT_ALGORITHM | false | When true, reject verification if signingAlgorithm is missing. |
JACS_ENABLE_FILESYSTEM_QUARANTINE | false | Enable filesystem quarantine (replaces JACS_USE_SECURITY). |
Deprecated Method Aliases (0.9.0)
In v0.9.0, several method names were standardized. The old names remain as aliases for backward compatibility but are deprecated and will be removed in 1.0.0 (minimum 2 minor releases after deprecation).
Runtime Deprecation Warnings
Set the JACS_SHOW_DEPRECATIONS=1 environment variable to emit runtime warnings when deprecated methods are called:
export JACS_SHOW_DEPRECATIONS=1
This is recommended during development and CI to identify code that needs updating.
Deprecated Alias Table
| SDK | Deprecated Method | Canonical Replacement | Since | Removal |
|---|---|---|---|---|
| Python (binding) | agent.wrap_a2a_artifact() | agent.sign_artifact() | 0.9.0 | 1.0.0 |
| Python (A2A) | a2a.wrap_artifact_with_provenance() | a2a.sign_artifact() | 0.9.0 | 1.0.0 |
| Node.js (binding) | agent.wrapA2aArtifact() | agent.signArtifact() | 0.9.0 | 1.0.0 |
| Node.js (binding) | agent.wrapA2aArtifactSync() | agent.signArtifactSync() | 0.9.0 | 1.0.0 |
| Node.js (A2A) | a2a.wrapArtifactWithProvenance() | a2a.signArtifact() | 0.9.0 | 1.0.0 |
| Rust (core) | agent.wrap_a2a_artifact() | agent.sign_artifact() | 0.9.0 | 1.0.0 |
| Go | SignA2AArtifact() (simple API) | Uses sign_artifact internally | -- | -- |
All aliases behave identically to their canonical replacements. No behavioral changes are needed when migrating -- only rename the method call.
Migration Examples
Python:
# Before (deprecated)
wrapped = agent.wrap_a2a_artifact(artifact_json, "task")
# After (canonical)
signed = agent.sign_artifact(artifact_json, "task")
Node.js:
// Before (deprecated)
const wrapped = await agent.wrapA2aArtifact(artifactJson, 'task');
// After (canonical)
const signed = await agent.signArtifact(artifactJson, 'task');
Python A2A integration:
# Before (deprecated)
wrapped = a2a.wrap_artifact_with_provenance(artifact, "task")
# After (canonical)
signed = a2a.sign_artifact(artifact, "task")
Node.js A2A integration:
// Before (deprecated)
const wrapped = await a2a.wrapArtifactWithProvenance(artifact, 'task');
// After (canonical)
const signed = await a2a.signArtifact(artifact, 'task');
Module-Level Function Deprecation (Reminder)
Module-level functions (e.g., jacs.load(), jacs.sign_request() in Python; load(), signRequest() in Node.js) were deprecated in earlier releases. Use JacsAgent instance methods instead. See the Python and Node.js API references for the full list.
Migrating from 0.2.x to 0.3.x
Configuration Changes
New Configuration Fields:
{
"observability": {
"logs": { "enabled": true, "level": "info" },
"metrics": { "enabled": false },
"tracing": { "enabled": false }
}
}
Deprecated Fields:
jacs_log_level→ Useobservability.logs.leveljacs_log_file→ Useobservability.logs.destination
Migration Steps
-
Update Configuration:
# Backup current config cp jacs.config.json jacs.config.json.backup # Update to new format # Add observability section if needed -
Update Dependencies:
# Node.js npm install @hai.ai/jacs@latest # Python pip install --upgrade jacs -
Verify Existing Documents:
jacs document verify -d ./jacs_data/documents/
Migrating Storage Backends
Filesystem to AWS S3
-
Create S3 Bucket:
aws s3 mb s3://my-jacs-bucket -
Update Configuration:
{ "jacs_default_storage": "aws", "jacs_data_directory": "s3://my-jacs-bucket/data" } -
Set Environment Variables:
export AWS_ACCESS_KEY_ID="your-key" export AWS_SECRET_ACCESS_KEY="your-secret" export AWS_REGION="us-east-1" -
Migrate Documents:
# Upload existing documents aws s3 sync ./jacs_data/ s3://my-jacs-bucket/data/ -
Verify Migration:
jacs document verify -d s3://my-jacs-bucket/data/documents/
AWS S3 to Filesystem
-
Download Documents:
aws s3 sync s3://my-jacs-bucket/data/ ./jacs_data/ -
Update Configuration:
{ "jacs_default_storage": "fs", "jacs_data_directory": "./jacs_data" } -
Verify Documents:
jacs document verify -d ./jacs_data/documents/
Migrating Cryptographic Algorithms
Ed25519 to Post-Quantum
For increased security, you may want to migrate to post-quantum algorithms.
-
Create New Agent with New Algorithm:
{ "jacs_agent_key_algorithm": "pq-dilithium" }jacs agent create --create-keys true -f new-agent.json -
Update Configuration:
{ "jacs_agent_key_algorithm": "pq-dilithium", "jacs_agent_id_and_version": "new-agent-id:new-version" } -
Re-sign Critical Documents (Optional):
// Re-sign documents with new algorithm const oldDoc = JSON.parse(fs.readFileSync('./old-doc.json')); // Remove old signature fields delete oldDoc.jacsSignature; delete oldDoc.jacsSha256; // Create new signed version const newDoc = await agent.createDocument(JSON.stringify(oldDoc));
Note: Old documents remain valid with old signatures. Re-signing is only needed for documents that require the new algorithm.
Migrating Between Platforms
Node.js to Python
Both platforms use the same document format:
// Node.js - create document
const signedDoc = await agent.createDocument(JSON.stringify(content));
fs.writeFileSync('doc.json', signedDoc);
# Python - verify the same document
with open('doc.json', 'r') as f:
doc_string = f.read()
is_valid = agent.verify_document(doc_string)
Sharing Agents Between Platforms
Agents can be used across platforms by sharing configuration:
-
Export Agent Files:
jacs_keys/ ├── private.pem └── public.pem jacs.config.json -
Use Same Config in Both:
// Node.js await agent.load('./jacs.config.json');# Python agent.load('./jacs.config.json')
Migrating Key Formats
Unencrypted to Encrypted Keys
-
Encrypt Existing Key:
# Backup original cp jacs_keys/private.pem jacs_keys/private.pem.backup # Encrypt with password openssl pkcs8 -topk8 -in jacs_keys/private.pem \ -out jacs_keys/private.pem.enc -v2 aes-256-cbc # Remove unencrypted key rm jacs_keys/private.pem mv jacs_keys/private.pem.enc jacs_keys/private.pem -
Update Configuration:
{ "jacs_agent_private_key_filename": "private.pem" } -
Set Password:
export JACS_PRIVATE_KEY_PASSWORD="your-secure-password"
Database Migration
Adding Database Storage
If migrating from filesystem to include database storage:
-
Create Database Schema:
CREATE TABLE jacs_documents ( id UUID PRIMARY KEY, version_id UUID NOT NULL, document JSONB NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), UNIQUE(id, version_id) ); -
Import Existing Documents:
const fs = require('fs'); const path = require('path'); const { Pool } = require('pg'); const pool = new Pool({ connectionString: process.env.DATABASE_URL }); const docsDir = './jacs_data/documents'; async function importDocuments() { const docDirs = fs.readdirSync(docsDir); for (const docId of docDirs) { const docPath = path.join(docsDir, docId); const versions = fs.readdirSync(docPath); for (const versionFile of versions) { const docString = fs.readFileSync( path.join(docPath, versionFile), 'utf-8' ); const doc = JSON.parse(docString); await pool.query(` INSERT INTO jacs_documents (id, version_id, document) VALUES ($1, $2, $3) ON CONFLICT (id, version_id) DO NOTHING `, [doc.jacsId, doc.jacsVersion, doc]); } } } importDocuments();
MCP Integration Migration
Adding JACS to Existing MCP Server
-
Install JACS:
npm install @hai.ai/jacs -
Wrap Existing Transport:
// Before const transport = new StdioServerTransport(); await server.connect(transport); // After import { createJACSTransportProxy } from '@hai.ai/jacs/mcp'; const baseTransport = new StdioServerTransport(); const secureTransport = createJACSTransportProxy( baseTransport, './jacs.config.json', 'server' ); await server.connect(secureTransport); -
Update Client:
// Client also needs JACS const baseTransport = new StdioClientTransport({ command: 'node', args: ['server.js'] }); const secureTransport = createJACSTransportProxy( baseTransport, './jacs.client.config.json', 'client' ); await client.connect(secureTransport);
HTTP API Migration
Adding JACS to Existing Express API
-
Install Middleware:
npm install @hai.ai/jacs -
Add Middleware to Routes:
import { JACSExpressMiddleware } from '@hai.ai/jacs/http'; // Before app.use('/api', express.json()); // After - for JACS-protected routes app.use('/api/secure', express.text({ type: '*/*' })); app.use('/api/secure', JACSExpressMiddleware({ configPath: './jacs.config.json' })); // Keep non-JACS routes unchanged app.use('/api/public', express.json()); -
Update Route Handlers:
// Before app.post('/api/data', (req, res) => { const payload = req.body; // ... }); // After app.post('/api/secure/data', (req, res) => { const payload = req.jacsPayload; // Verified payload // ... });
Troubleshooting Migration
Common Issues
Documents Not Verifying After Migration:
- Check algorithm compatibility
- Verify keys were copied correctly
- Ensure configuration paths are correct
Key File Errors:
- Verify file permissions (600 for private key)
- Check key format matches algorithm
- Ensure password is set for encrypted keys
Storage Errors After Migration:
- Verify storage backend is accessible
- Check credentials/permissions
- Ensure directory structure is correct
Verification Checklist
After any migration:
-
Verify Configuration:
jacs config read -
Verify Agent:
jacs agent verify -
Verify Sample Document:
jacs document verify -f ./sample-doc.json -
Test Document Creation:
echo '{"test": true}' > test.json jacs document create -f test.json -
Verify Version:
jacs version
Rollback Procedures
If migration fails:
-
Restore Configuration:
cp jacs.config.json.backup jacs.config.json -
Restore Keys:
cp -r jacs_keys.backup/* jacs_keys/ -
Restore Dependencies:
# Node.js npm install @hai.ai/jacs@previous-version # Python pip install jacs==previous-version -
Verify Rollback:
jacs agent verify jacs document verify -d ./jacs_data/documents/
See Also
- Configuration Reference - Configuration options
- Cryptographic Algorithms - Algorithm details
- Storage Backends - Storage options