Node.js Server
Deploying @memofs/server on Node.js with the memofs-server CLI, custom node:http servers, Express, or Fastify.
@memofs/server provides a turnkey Node.js runtime for deploying MemoFS as a standalone background service or containerized workload (Docker, Fly.io, Railway, Render, VPS).
1. Using the memofs-server CLI Binary
The package includes an executable binary memofs-server that boots a hardened node:http server out of the box.
Installation
npm install -g @memofs/serverStarting the Server
# Boot the server (defaults to port 8787, in-memory store, auth off)
PORT=8787 memofs-serverHealth Check
Verify the server is running:
curl http://127.0.0.1:8787/health
# {"ok":true,"name":"memofs-server","version":"0.1.0"}Environment Variables
| Variable | Description | Default |
|---|---|---|
PORT | HTTP listen port. | 8787 |
MEMOFS_SERVER_TOKEN | Bearer token for authentication. Setting this automatically enables auth. | undefined |
MEMOFS_SERVER_REQUIRE_AUTH | Set to "true" to enforce bearer token validation even without MEMOFS_SERVER_TOKEN. | "false" |
MEMOFS_PROJECT_ID | Project workspace ID scoping the runtime. | "self-host" |
Production Hardening & DoS Protections
The memofs-server binary comes pre-configured with defensive network timeouts and resource caps:
- Request Timeout (
server.requestTimeout):30,000ms(30s) to terminate slowloris hangs. - Headers Timeout (
server.headersTimeout):65,000ms(65s) to guard against slow header trickles. - Connection Limit (
server.maxConnections):100concurrent TCP connections. - Payload Limit (
MAX_BODY_BYTES):1,048,576bytes (1MB). Oversized payloads immediately receive413 Payload Too Large. - Graceful Shutdown: Handles
SIGTERMandSIGINTsignals, draining existing connections before exiting.
2. Programmatic Node.js Deployment (node:http)
To inject persistent storage adapters (e.g. SQLite, PostgreSQL, S3, or R2) or AI providers (OpenAI, Voyage AI), instantiate createHostedRuntime and route incoming requests through handleRuntimeRequest:
import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
import { createHostedRuntime, handleRuntimeRequest } from "@memofs/server";
import { createNodeFsMemoryStore } from "@memofs/core/node-fs";
import { createOpenAIEmbedder } from "@memofs/adapter-openai";
import { createVoyageReranker } from "@memofs/adapter-voyage";
// 1. Assemble the MemoFS runtime
const memofs = createHostedRuntime({
store: createNodeFsMemoryStore({ rootDir: "./data" }),
projectId: process.env.MEMOFS_PROJECT_ID ?? "prod-workspace",
embedder: createOpenAIEmbedder({
apiKey: process.env.OPENAI_API_KEY,
}),
reranker: createVoyageReranker({
apiKey: process.env.VOYAGE_API_KEY,
}),
});
// 2. Simple project mutex for the Concurrency Gate
const projectLock = new Map<string, Promise<unknown>>();
const concurrencyLayer = {
acquire: async <T>(projectId: string, fn: () => Promise<T>): Promise<T> => {
const prev = projectLock.get(projectId) ?? Promise.resolve();
let resolveCurrent!: () => void;
const current = new Promise<void>((r) => { resolveCurrent = r; });
projectLock.set(projectId, current);
try {
await prev;
return await fn();
} finally {
resolveCurrent();
}
},
};
// 3. Create HTTP server and bridge to handleRuntimeRequest
const server = createServer(async (req: IncomingMessage, res: ServerResponse) => {
try {
const method = req.method ?? "GET";
const host = req.headers.host ?? "localhost:8787";
const url = new URL(req.url ?? "/", `http://${host}`);
// Read body buffer
const chunks: Buffer[] = [];
for await (const chunk of req) {
chunks.push(chunk as Buffer);
}
const body = Buffer.concat(chunks);
// Build Web Request
const headers = new Headers();
for (const [key, value] of Object.entries(req.headers)) {
if (Array.isArray(value)) {
for (const v of value) headers.append(key, v);
} else if (value !== undefined) {
headers.set(key, value);
}
}
const webRequest = new Request(url.toString(), {
method,
headers,
body: method === "GET" || method === "HEAD" ? undefined : body,
});
// Process with framework-free HTTP core
const webResponse = await handleRuntimeRequest(webRequest, {
runtime: memofs,
concurrencyLayer,
requireAuth: true,
bearerToken: process.env.MEMOFS_SERVER_TOKEN,
allowedOrigins: ["https://app.example.com"],
});
// Write back response
res.statusCode = webResponse.status;
webResponse.headers.forEach((val, key) => {
res.setHeader(key, val);
});
const responseBuffer = Buffer.from(await webResponse.arrayBuffer());
res.end(responseBuffer);
} catch (error) {
console.error("[server] Uncaught request error:", error);
if (!res.headersSent) {
res.writeHead(500, { "Content-Type": "text/plain" });
}
res.end("Internal Server Error");
}
});
server.listen(8787, () => {
console.log("MemoFS server listening on http://0.0.0.0:8787");
});3. Securing Your Deployment
When exposing @memofs/server on public interfaces:
- Always Set a Bearer Token: Set
MEMOFS_SERVER_TOKEN="secret-token". Clients must provide the headerAuthorization: Bearer secret-token. - Restrict CORS Origins: Specify
allowedOrigins: ["https://yourdomain.com"]to reject unauthorized cross-origin browser requests. - Private Networks / VPCs: If running inside an internal Kubernetes cluster, Docker network, or behind a reverse proxy (e.g. NGINX, Cloudflare Tunnel), disable auth by setting
requireAuth: false.