Basic Usage
This chapter covers fundamental JACS operations in Node.js, including agent initialization, document creation, signing, and verification.
v0.7.0: Async-First API
All NAPI operations now return Promises by default. Sync variants are available with a Sync suffix, following the Node.js convention (like fs.readFile vs fs.readFileSync).
// Async (default, recommended)
await agent.load('./jacs.config.json');
const doc = await agent.createDocument(JSON.stringify(content));
// Sync (blocks event loop)
agent.loadSync('./jacs.config.json');
const doc = agent.createDocumentSync(JSON.stringify(content));
Initializing an Agent
Create and Load Agent
import { JacsAgent } from '@hai.ai/jacs';
// Create a new agent instance
const agent = new JacsAgent();
// Load configuration from file (async)
await agent.load('./jacs.config.json');
// Or use sync variant
agent.loadSync('./jacs.config.json');
Configuration File
Create jacs.config.json:
{
"$schema": "https://hai.ai/schemas/jacs.config.schema.json",
"jacs_data_directory": "./jacs_data",
"jacs_key_directory": "./jacs_keys",
"jacs_default_storage": "fs",
"jacs_agent_key_algorithm": "ring-Ed25519",
"jacs_agent_id_and_version": "agent-uuid:version-uuid"
}
Creating Documents
Basic Document Creation
import { JacsAgent } from '@hai.ai/jacs';
const agent = new JacsAgent();
await agent.load('./jacs.config.json');
// Create a document from JSON
const documentData = {
title: "Project Proposal",
content: "Quarterly development plan",
budget: 50000
};
const signedDocument = await agent.createDocument(JSON.stringify(documentData));
console.log('Signed document:', signedDocument);
With Custom Schema
Validate against a custom JSON Schema:
const signedDocument = await agent.createDocument(
JSON.stringify(documentData),
'./schemas/proposal.schema.json' // custom schema path
);
With Output File
const signedDocument = await agent.createDocument(
JSON.stringify(documentData),
null, // no custom schema
'./output/proposal.json' // output filename
);
Without Saving
const signedDocument = await agent.createDocument(
JSON.stringify(documentData),
null, // no custom schema
null, // no output filename
true // noSave = true
);
With Attachments
const signedDocument = await agent.createDocument(
JSON.stringify(documentData),
null, // no custom schema
null, // no output filename
false, // save the document
'./attachments/report.pdf', // attachment path
true // embed files
);
Verifying Documents
Verify Document Signature
// Verify a document's signature and hash
const isValid = await agent.verifyDocument(signedDocumentJson);
console.log('Document valid:', isValid);
Verify Specific Signature Field
// Verify with a custom signature field
const isValid = await agent.verifySignature(
signedDocumentJson,
'jacsSignature' // signature field name
);
Updating Documents
Update Existing Document
// Original document key format: "id:version"
const documentKey = 'doc-uuid:version-uuid';
// Modified document content
const updatedData = {
jacsId: 'doc-uuid',
jacsVersion: 'version-uuid',
title: "Updated Proposal",
content: "Revised quarterly plan",
budget: 75000
};
const updatedDocument = await agent.updateDocument(
documentKey,
JSON.stringify(updatedData)
);
console.log('Updated document:', updatedDocument);
Update with New Attachments
const updatedDocument = await agent.updateDocument(
documentKey,
JSON.stringify(updatedData),
['./new-report.pdf'], // new attachments
true // embed files
);
Signing and Verification
Sign Arbitrary Data
// Sign any string data
const signature = await agent.signString('Important message to sign');
console.log('Signature:', signature);
Verify Arbitrary Data
// Verify a signature on string data
const isValid = await agent.verifyString(
'Important message to sign', // original data
signatureBase64, // base64 signature
publicKeyBuffer, // public key as Buffer
'ring-Ed25519' // algorithm
);
Working with Agreements
The methods in this section use the legacy jacsAgreement sidecar on an existing signed document. For new standalone consent workflows, prefer Agreement v2 through createAgreementV2(), signAgreementV2(), and verifyAgreementV2().
Create an Agreement
// Add agreement requiring multiple agent signatures
const documentWithAgreement = await agent.createAgreement(
signedDocumentJson,
['agent1-uuid', 'agent2-uuid'], // required signers
'Do you agree to these terms?', // question
'Q1 2024 service contract', // context
'jacsAgreement' // field name
);
Sign an Agreement
// Sign the agreement as the current agent
const signedAgreement = await agent.signAgreement(
documentWithAgreementJson,
'jacsAgreement' // agreement field name
);
Check Agreement Status
// Check which agents have signed
const status = await agent.checkAgreement(
documentWithAgreementJson,
'jacsAgreement'
);
console.log('Agreement status:', JSON.parse(status));
Agent Operations
Verify Agent
// Verify the loaded agent's signature
const isValid = await agent.verifyAgent();
console.log('Agent valid:', isValid);
Update Agent
// Update agent document
const updatedAgentJson = await agent.updateAgent(JSON.stringify({
jacsId: 'agent-uuid',
jacsVersion: 'version-uuid',
name: 'Updated Agent Name',
description: 'Updated description'
}));
Sign External Agent
// Sign another agent's document with registration signature
const signedAgentJson = await agent.signAgent(
externalAgentJson,
publicKeyBuffer,
'ring-Ed25519'
);
Request/Response Signing
These methods remain synchronous (V8-thread-only, no Sync suffix):
Sign a Request
// Sign request parameters as a JACS document
const signedRequest = agent.signRequest({
method: 'GET',
path: '/api/resource',
timestamp: new Date().toISOString(),
body: { query: 'data' }
});
Verify a Response
// Verify a signed response
const result = agent.verifyResponse(signedResponseJson);
console.log('Response valid:', result);
// Verify and get signer's agent ID
const resultWithId = agent.verifyResponseWithAgentId(signedResponseJson);
console.log('Signer ID:', resultWithId);
Utility Functions
Hash String
import { hashString } from '@hai.ai/jacs';
// SHA-256 hash of a string
const hash = hashString('data to hash');
console.log('Hash:', hash);
Create Configuration
import { createConfig } from '@hai.ai/jacs';
// Programmatically create a config JSON string
const configJson = createConfig(
undefined, // jacs_use_security
'./jacs_data', // jacs_data_directory
'./jacs_keys', // jacs_key_directory
undefined, // private key filename
undefined, // public key filename
'ring-Ed25519', // key algorithm
undefined, // private key password
undefined, // agent id and version
'fs' // default storage
);
console.log('Config:', configJson);
Error Handling
import { JacsAgent } from '@hai.ai/jacs';
const agent = new JacsAgent();
try {
await agent.load('./jacs.config.json');
} catch (error) {
console.error('Failed to load agent:', error.message);
}
try {
const doc = await agent.createDocument(JSON.stringify({ data: 'test' }));
console.log('Document created');
} catch (error) {
console.error('Failed to create document:', error.message);
}
try {
const isValid = await agent.verifyDocument(invalidJson);
} catch (error) {
console.error('Verification failed:', error.message);
}
Complete Example
import { JacsAgent, hashString } from '@hai.ai/jacs';
async function main() {
// Initialize agent
const agent = new JacsAgent();
await agent.load('./jacs.config.json');
// Create a generic proposal document
const proposal = {
title: 'Project Proposal',
description: 'Q1 development plan',
budget: 50000
};
const signedProposal = await agent.createDocument(JSON.stringify(proposal));
console.log('Document created');
// Verify the document
if (await agent.verifyDocument(signedProposal)) {
console.log('Document signature valid');
}
// Create agreement for proposal approval
const proposalWithAgreement = await agent.createAgreement(
signedProposal,
['manager-uuid', 'developer-uuid'],
'Do you approve this proposal?'
);
// Sign the agreement
const signedAgreement = await agent.signAgreement(proposalWithAgreement);
console.log('Agreement signed');
// Check agreement status
const status = await agent.checkAgreement(signedAgreement);
console.log('Status:', status);
// Hash some data for reference
const proposalHash = hashString(signedProposal);
console.log('Document hash:', proposalHash);
}
main().catch(console.error);
Next Steps
- MCP Integration - Model Context Protocol support
- HTTP Server - Create HTTP APIs
- Express Middleware - Express.js integration
- API Reference - Complete API documentation