MemoFSMemoFS
Self-Hosting

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/server

Starting the Server

# Boot the server (defaults to port 8787, in-memory store, auth off)
PORT=8787 memofs-server

Health 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

VariableDescriptionDefault
PORTHTTP listen port.8787
MEMOFS_SERVER_TOKENBearer token for authentication. Setting this automatically enables auth.undefined
MEMOFS_SERVER_REQUIRE_AUTHSet to "true" to enforce bearer token validation even without MEMOFS_SERVER_TOKEN."false"
MEMOFS_PROJECT_IDProject 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): 100 concurrent TCP connections.
  • Payload Limit (MAX_BODY_BYTES): 1,048,576 bytes (1MB). Oversized payloads immediately receive 413 Payload Too Large.
  • Graceful Shutdown: Handles SIGTERM and SIGINT signals, 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:

  1. Always Set a Bearer Token: Set MEMOFS_SERVER_TOKEN="secret-token". Clients must provide the header Authorization: Bearer secret-token.
  2. Restrict CORS Origins: Specify allowedOrigins: ["https://yourdomain.com"] to reject unauthorized cross-origin browser requests.
  3. 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.

On this page