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
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.