Your first encrypted note
Create a private catalog, add a note, and update it with ordinary JavaScript. The SDK handles encryption, chain advancement and transport.
Before you start
Use Node.js 22 or later, or a modern browser with Web Crypto and WebSocket support. You also need a compatible ULDA Server endpoint. Follow the Server Quick Start to run one locally.
Install the SDK in your application:
npm install ulda-auth@1.1.0
Use ES modules: an .mjs file in Node.js, or your application's module-aware bundler. TypeScript declarations are included.
Connect and create a note
This example connects to the local development server. Pass a password from your application's input flow.
import { Ulda } from 'ulda-auth';
export async function createNote(password) {
const ulda = new Ulda('http://127.0.0.1:3000', {
allowInsecureLocalhost: true,
});
try {
await ulda.ready;
const masterId = await ulda.register(password);
await ulda.add('notes', { text: 'Hello, ULDA' });
await ulda.data.notes.text.update('Updated on the client');
return {
masterId,
note: ulda.value('notes'),
};
} finally {
await ulda.close();
}
}
Call createNote(password) with a nonempty password. The returned note.text is 'Updated on the client'. Save the returned masterId through your application so the user can open this catalog again.
register() creates a master: an encrypted catalog containing the names, IDs and secrets of its content resources. add() creates a content resource and adds it to that catalog.
For a deployed server, construct the SDK with your HTTPS endpoint and omit the local-development option:
const ulda = new Ulda('https://your-ulda-server.example');
The constructor starts the connection. ready waits for the server handshake; register() or connect() opens a catalog. The password is not sent to the server.
Read and update JSON
With an open catalog, read a verified local copy:
const note = ulda.value('notes');
Local reads do not contact the server. Editing that copy does not save anything. Use an explicit update:
await ulda.data.notes.text.update('Pick up coffee');
Await the returned promise before treating a write as complete. Direct assignment to the SDK reference tree is not supported.
Open it again
Create a new SDK instance and use the saved ID with the same password. This also works on another device that can reach your server.
import { Ulda } from 'ulda-auth';
export async function openNote(endpoint, masterId, password) {
const ulda = new Ulda(endpoint);
try {
await ulda.ready;
await ulda.connect(masterId, password);
return ulda.value('notes');
} finally {
await ulda.close();
}
}
This function expects an HTTPS endpoint. For local testing, use the loopback constructor options from the first example. Keep masterId as a string; resource IDs can exceed JavaScript's safe integer range.
Closing an instance ends its session and releases its working state. It does not delete the stored resources.
Keep a client synchronized
The functions above close after one operation. In an interactive application, keep an instance open while its view is active:
// Attach to an already open instance.
const unsubscribe = ulda.on('change', () => {
if (ulda.status === 'open') {
renderNotes(ulda.value('notes'));
}
});
// When the view is disposed:
unsubscribe();
await ulda.close();
renderNotes is your application's rendering function. The callback reads the SDK's verified local state; the event itself contains IDs, not plaintext. Use each write's promise for its completion rather than relying on a change event.
Handle a failed write
Inspect error.code and show a useful state to the user. A lost acknowledgement can produce UNKNOWN_OUTCOME: the server may already have committed the write. Do not automatically repeat it.
The SDK can restore a disconnected session with bounded reconnect attempts, but it does not provide a persistent offline queue, automatic write replay or conflict merging. See conflicts and recovery before adding retry behavior.
Continue with working with data and events and synchronization to build an interactive application.