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:
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.- 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.
- Skip the facade. If you don't need the gate, use the
MemoryProviderdirectly. This is an advanced path — you loseContextManager'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
- Scopes and identity — the full
UserAccountsManagerstory - Using the atomicmemory backend — production checklist that applies here too