Skip to main content

Node.js / server-side

Using the SDK server-side is supported — the package is ESM + CJS, has no browser-only code on its critical path, and the MemoryStorageAdapter runs anywhere Node does.

This guide covers the two things that differ from the browser path: storage selection and the identity requirement.

Storage

Use MemoryStorageAdapter for ephemeral / per-process state, or write a custom adapter backed by filesystem / Redis / Postgres. IndexedDBStorageAdapter is browser-only.

import {
StorageManager,
MemoryStorageAdapter,
} from '@atomicmemory/atomicmemory-sdk/storage';

const adapter = new MemoryStorageAdapter();
await adapter.initialize();

const storage = new StorageManager([adapter]);
await storage.initialize();

Identity: the server-side wrinkle

AtomicMemorySDK.ingest() runs through the capture gate, which consults UserAccountsManager. The SDK assumes a UserAccountsManager is configured; without one, ingest will throw.

On a server, where there's no end-user session and capture policies are usually enforced upstream, the pragmatic options are:

  1. testMode: true. The hardcoded test provider returns permissive defaults — capture allowed, injection allowed. Sufficient for server jobs that already enforce authorization at their own boundary.
  2. A minimal hardcoded identity provider. Configure the user-accounts services with fixed identity + fixed preferences that reflect your server's policy. This is explicit and inspectable.
  3. Skip the facade. If you don't need the gate, use the MemoryProvider directly. This is an advanced path — you lose ContextManager's policy layer — but it's the honest answer for data-pipeline jobs that have no user context.
import { AtomicMemorySDK } from '@atomicmemory/atomicmemory-sdk';

const sdk = new AtomicMemorySDK({
userAccounts: {
identity: {
providerType: 'web2',
apiBaseUrl: 'http://localhost:8787',
testMode: true,
},
preferences: {
providerType: 'web2',
apiBaseUrl: 'http://localhost:8787',
testMode: true,
encryption: { keySource: 'provided', key: new Uint8Array(32) },
},
},
context: {
providers: {
default: 'atomicmemory',
atomicmemory: { apiUrl: process.env.CORE_URL! },
},
},
});

await sdk.initialize();

An Express example

import express from 'express';
import { AtomicMemorySDK } from '@atomicmemory/atomicmemory-sdk';

const sdk = new AtomicMemorySDK({ /* config as above */ });
await sdk.initialize();

const app = express();
app.use(express.json());

app.post('/memory', async (req, res) => {
const { userId, content } = req.body;
await sdk.ingest(
{
mode: 'text',
content,
scope: { user: userId },
provenance: { source: 'api' },
},
'internal',
);
res.status(204).end();
});

app.get('/memory/:userId/search', async (req, res) => {
const page = await sdk.search(
{ query: req.query.q as string, scope: { user: req.params.userId }, limit: 10 },
'internal',
);
res.json(page);
});

app.listen(3000);

Authorization (who can call /memory, for which userId) is your app's responsibility. The SDK's gates are about user consent, not access control.

Next