# JLINC NodeJS SDK The JLINC NodeJS SDK serves as a centralized vehicle for leveraging all aspects of the core JLINC protocol. This library will enable you to build client/server applications that leverage built-in secure identity management, agreement signing, and data exchange provided via the protocol. Details of the JLINC protocol, schema, and context can be found at: https://protocol.jlinc.io/. ## Installation ```bash npm install @jlinc/sdk ``` ## Sample usage The below code sample is a demonstration of the JLINC NodeJS SDK in action. As data moves through the system, it is cryptographically signed with a unique key for each element in the system, and zero-knowledge audit records are delivered to the JLINC Archive Server. ```js const { jlincInit, jlincProduceEvent, jlincProcessEvent, jlincProduceAgreement, jlincProcessAgreement, } = require("@jlinc/sdk"); async function main() { const config = { dataStoreApiUrl: process.env.JLINC_DATA_STORE_API_URL ?? "http://localhost:9090", dataStoreApiKey: process.env.JLINC_DATA_STORE_API_KEY, archiveApiUrl: process.env.JLINC_ARCHIVE_API_URL ?? "http://localhost:9090", archiveApiKey: process.env.JLINC_ARCHIVE_API_KEY, defaultAgreementId: "00000000-0000-0000-0000-000000000000", systemPrefix: process.env.JLINC_SYSTEM_PREFIX ?? "TestJlinc", debug: false, } jlincInit(config); try { let data; let r; // Define participants in this agreement and data exchange const entity1 = `EntityOne`; const entity2 = `EntityTwo`; console.log(`\nAction: Produce (create and sign) agreement`) data = { uri: 'http://localhost:9090/agreements/05c93a30cc699cfaee9185f23a5f68ffb12305acce1ca4b9444758fc4d594828', validRoles: ['provider', 'user'], purposes: ['testing'], caveats: ['production'], requiredSigners: [entity1], signer: entity1, role: 'provider', } r = await jlincProduceAgreement(data) if (r) { if (printResults) { console.log(`Result:`) console.log(`---------------------------------------------`) console.log(JSON.stringify(r, null, 2)) console.log(`---------------------------------------------`) } if (r.created && r.processed) { console.log('PASS') } else { throw new Error('FAIL') } } const agreementId = r.created.agreementId console.log(`\nAction: Process (cross sign) agreement`) data = { agreementId, signer: entity2, role: 'user', } r = await jlincProcessAgreement(data) if (r) { if (printResults) { console.log(`Result:`) console.log(`---------------------------------------------`) console.log(JSON.stringify(r, null, 2)) console.log(`---------------------------------------------`) } if (r.auditData && r.auditData.audit.agreementId === agreementId) { console.log('PASS') } else { throw new Error('FAIL') } } console.log(`\nAction: Produce (create and sign) event for data to be sent`) data = { from: entity1, to: entity2, agreementId, payload: { data: "testing", }, } r = await jlincProduceEvent(data) if (r) { if (printResults) { console.log(`Result:`) console.log(`---------------------------------------------`) console.log(JSON.stringify(r, null, 2)) console.log(`---------------------------------------------`) } if (r.created && r.processed && r.created.agreementId === agreementId) { console.log('PASS') } else { throw new Error('FAIL') } } const eventId = r.created.eventId; console.log(`\nAction: Process (sign) by the receiver after data is sent`) data = { to: entity2, eventId, } r = await jlincProcessEvent(data) if (r) { console.log(`Result:`) if (printResults) { console.log(`---------------------------------------------`) console.log(JSON.stringify(r, null, 2)) console.log(`---------------------------------------------`) } if (r.auditData && r.auditData.audit.eventId === eventId) { console.log('PASS') } else { throw new Error('FAIL') } } console.log(`\nAction: Produce (create and sign) event for data to be sent with successful auth`) data = { from: entity1, to: entity2, auth: { subject: { type: "user", id: "tester", }, action: { name: "read", }, resource: { type: "data", id: "1234", properties: { ownerID: "tester@test.com", } } }, payload: { data: "testing", }, } r = await jlincProduceEvent(data) if (r) { if (printResults) { console.log(`Result:`) console.log(`---------------------------------------------`) console.log(JSON.stringify(r, null, 2)) console.log(`---------------------------------------------`) } if (r.created && r.processed) { console.log('PASS') } else { throw new Error('FAIL') } } console.log(`\nAction: Produce (create and sign) event for data to be sent with failed auth`) data = { from: entity1, to: entity2, auth: { subject: { type: "user", id: "tester", }, action: { name: "write", }, resource: { type: "data", id: "1234", properties: { ownerID: "tester@test.com", } } }, payload: { data: "testing", }, } r = await jlincProduceEvent(data) if (r) { if (printResults) { console.log(`Result:`) console.log(`---------------------------------------------`) console.log(JSON.stringify(r, null, 2)) console.log(`---------------------------------------------`) } if (r.decision != undefined && r.decision == false) { console.log('PASS') } else { throw new Error('FAIL') } } } catch (err) { console.error("[*] ERROR:", err); } } main() ``` ## API Reference ### `jlincInit(data: JLINCConfig)` Call `jlincInit` before using any other method. The configuration object supports the following properties: | Key | Type | Default | Description | |----------|------|---------|-------------| | `dataStoreApiUrl` | `string` | `http://localhost:9090` | Base URL for the data store API | | `dataStoreApiKey` | `string` | | Bearer token for authenticating API requests | | `archiveApiUrl` | `string` | `http://localhost:9090` | URL for the archive service | | `archiveApiKey` | `string` | | API key for the archive service | | `systemPrefix` | `string` | `my-system` | *(Optional)* Prefix applied to all entity short names | | `domain` | `string` | Server Specified | *(Optional)* Default domain for entities. If not specified, it is auto-resolved on first call | | `defaultAgreementId` | `string` | `00000000-0000-0000-0000-000000000000` | *(Optional)* Default agreement ID for events | | `debug` | `boolean` | `false` | *(Optional)* Enables verbose console logging for requests/responses | ### `jlincProduceAgreement(data: JLINCProduceAgreementData)` Creates and publishes a new agreement. **Parameters:** | Key | Type | Description | |-----|------|-------------| | `requiredSigners` | `Array` | List of entities required to sign | | `signer` | `string` | The entity producing/signing this request | | `uri` | `string` | Resource URI the agreement applies to | | `purposes` | `Array` | Allowed purposes under the agreement | | `caveats` | `Array` | Prohibitions under the agreement | | `validRoles` | `Array` | Allowed roles for the agreement | | `role` | `string` | The role being signing the agreement | | `auth` | `object` | *(Optional)* AuthZEN authentication data | ### `jlincProcessAgreement(data: JLINCProcessAgreementData)` Signs an existing agreement. **Parameters:** | Key | Type | Description | |-----|------|-------------| | `agreementId` | `string` | The ID of the agreement to process | | `signer` | `string` | The entity signing the agreement | | `role` | `string` | The role being signed on behalf of | | `auth` | `object` | *(Optional)* AuthZEN authentication data | ### `jlincProduceEvent(data: JLINCProduceEventData)` Creates and publishes a new event. **Parameters:** | Key | Type | Description | |-----|------|-------------| | `from` | `string` | The entity sending and signing the data | | `to` | `string` | The entity to receive the data | | `agreementId` | `string` | *(Optional)* Agreement ID. Falls back to `defaultAgreementId` if omitted | | `payload` | `any` | The data payload to send | | `auth` | `object` | *(Optional)* AuthZEN authentication data forwarded to the API | ### `jlincProcessEvent(data: JLINCProcessEventData)` Signs an existing agreement. **Parameters:** | Key | Type | Description | |-----|------|-------------| | `to` | `string` | The entity receiving and signing the data | | `eventId` | `string` | The ID of the event to process | | `auth` | `object` | *(Optional)* AuthZEN authentication data |