mem-port

Deployment

Running mem-port somewhere other than your own machine.

Nothing on this page is required to use mem-port. The daemon is built to run on localhost, where it needs no database, no accounts, and no configuration at all — that is still the default and still the recommended way to use it. This page is for the other case: putting mem-port on a host that more than one person, or more than one machine, can reach.

A networked database becomes required

The default embedded database writes to local disk. That is wrong anywhere the filesystem is ephemeral or more than one instance runs: you lose data when a container is replaced, and replicas silently diverge. Point mem-port at hosted SurrealDB or at Postgres instead.

The loopback boundary goes away

On 127.0.0.1 the operating system decides who can reach the daemon. On any other interface nothing does, so authentication switches on automatically with the exposure. A deployment is closed by default rather than depending on whoever wrote the deploy config.

Authentication follows exposure

There is no flag to remember. Auth is off on loopback, where the OS is already the boundary, and required on any other interface, where there is none. MEM_PORT_AUTH overrides it in both directions — set it to develop against the real thing locally, or to turn auth off when something in front is already authenticating.

Because a deployment binds a real interface, it needs a bootstrap admin or the daemon refuses to start rather than coming up and rejecting every login. The supplied manifests require the password instead of defaulting it, on the reasoning that a shipped default admin password is exactly the kind of thing that survives into production.

Then sign in at /admin to create workspaces, add users, issue their API keys and grant access. A workspace is one isolated knowledge graph, and its name is what clients send as library-id.

Bootstrap admin
MEM_PORT_ADMIN_USER=admin
MEM_PORT_ADMIN_PASSWORD=<a real secret, from Secret Manager>

Used only while no admin exists — leaving it set cannot reset a password or resurrect a deleted account.

  • Clients send two headers and nothing else about them changes: Authorization: Bearer <the user's key>, and library-id: <a workspace they were granted>.
  • API keys are shown once and stored only as a hash. Revoke and reissue when one needs rotating.
  • Being an admin is not data access. Admins decide who may reach what, which is a different power from reading it, so a stolen admin password exposes the account model rather than every knowledge graph. An admin who wants a workspace grants it to themselves, visibly.
  • The panel serves its own documentation at /admin/docs, with copy-pasteable client configuration for the URL the admin actually reached it on, and a read-only graph explorer per workspace — the quickest way to check whether a newly connected client is writing anything.
  • Nothing rate-limits admin logins yet.

Pick a storage engine

mem-port ships two drivers behind one storage contract. Nothing above that contract can tell them apart, so this is an operational choice rather than a product one — pick the database you would rather operate.

  1. Embedded SurrealDB

    The default. Right for a personal daemon on one machine, and wrong for anything deployed.

    Graph storage and vector search in the same process, writing to a file under the data directory. Nothing to install and nothing to run alongside it. It is the only option here that loses data on an ephemeral filesystem, which is why a deployment needs one of the other two.

    • No external service
    • Single instance only
  2. Hosted SurrealDB

    Right if you want the same engine the embedded default uses, operated as a server.

    Surreal Cloud, or SurrealDB on a VM. Each library-id gets its own database inside the configured namespace. All three constraints below are checked at startup, so a misconfiguration fails once with an explanation rather than on every tool call.

    Terminal
    MEM_PORT_DB_URL=wss://your-instance.surreal.cloud \
    MEM_PORT_DB_USER=root \
    MEM_PORT_DB_PASS=... \
      mem-port serve
    • SurrealDB 3.0.0 or newer — sessions and transactions are both 3.0 server-side features, and mem-port needs both: a forked session per library-id for tenancy, and a transaction for import_library. A 2.x server connects fine and then fails on every request.
    • ws:// or wss:// only — the HTTP engine supports neither feature at any version, so an http(s):// URL is rejected outright.
    • A root- or namespace-level user — mem-port creates a database per library-id and defines its schema on first use, which a database-scoped user cannot do.
  3. Postgres + pgvector

    Right if you already operate Postgres, or want a managed service with backup and replication tooling you already know.

    Each workspace gets its own Postgres schema, so isolation is structural rather than a WHERE clause nobody must forget. On GCP this usually means Cloud SQL, which is less work than running SurrealDB on a VM.

    Terminal
    npm install pg                      # optional dependency, only for this driver
    MEM_PORT_DB_URL=postgres://user:pass@host:5432/memport mem-port serve
    • The pg package is an optional dependency, imported lazily — install it only for this driver.
    • pgvector is required and checked at connect time, because every search mem-port offers is a cosine similarity over an embedding. mem-port attempts CREATE EXTENSION itself, which works on most managed services where it is available but not yet enabled.

The two drivers are interchangeable, and that is enforced rather than claimed: one test seeds the same fixture through both engines and asserts every read tool returns byte-identical output. Not covered by it yet: a real managed Postgres, as opposed to the pgvector Docker image, and the admin path against Postgres rather than SurrealDB.

Where to run it

A container image, a Compose stack per database engine, and a Cloud Run service definition live under deployments/. All of them default to closed at the network layer as well as in the daemon — Compose publishes on loopback, Cloud Run uses internal ingress with invoker auth required — on the reasoning that defence in depth is cheap and an exposed admin panel is worth one more lock.

  1. Docker Compose

    The quickest way to run the real thing: a database with a volume, and mem-port pointed at it.

    Two stacks, one per engine. Each brings its database up and serves the admin panel at /admin. Set the bootstrap admin password first or the stack will not start.

    Terminal
    export MEM_PORT_ADMIN_PASSWORD='local-dev-password'
    
    # SurrealDB
    docker compose -f deployments/docker/docker-compose.yml up --build
    
    # or Postgres
    docker compose -f deployments/docker/docker-compose.postgres.yml up --build
    • If a locally installed mem-port already holds 8787, publish elsewhere: MEM_PORT_HOST_PORT=8799 docker compose -f deployments/docker/docker-compose.yml up --build
  2. Cloud Run

    Right for a managed deployment on GCP, with Cloud SQL as the usual database.

    A service definition plus a deploy script. It needs a database it can reach and two secrets — the database password and the bootstrap admin password — before the first deploy. The script builds linux/amd64 explicitly, because an image built on Apple Silicon is arm64 by default and fails to start on Cloud Run with an exec-format error that never mentions architecture. Images are tagged with the git short SHA, so a rollback names an exact build rather than chasing :latest.

    Terminal
    PROJECT_ID=my-project REGION=us-central1 ./deployments/gcp/deploy.sh
    • The values in cloudrun.yaml are chosen, not defaults: containerConcurrency 8, because Node is single threaded and embedding inference is CPU-bound, where the platform default of 80 queues work behind one busy request.
    • memory 1Gi, because the ONNX runtime plus the model does not fit comfortably in 512Mi.
    • CPU throttling off, because CPU is throttled to near zero between requests by default, which can drop an idle WebSocket to SurrealDB.
    • minScale 1, which avoids paying model load and the WebSocket handshake on the first request after an idle period. Set it to 0 to trade cold starts for cost.
Known gap

Treat the manifests as stale after any change to auth, config or packaging

The deployment files have now been wrong twice for the same reason: verified once when written, then silently invalidated by a later change. 1.0.0 and 1.0.1 both shipped Compose files that refused to start, because authentication became required for anything binding a real interface after those files were written and checked. 1.0.2 fixed them. Nothing builds the image or starts the stacks automatically yet, so that class of bug is not closed — it is only fixed for now.

Every environment variable

All 21 variables the daemon reads, grouped as .env.example groups them. Real environment variables always win over a .env file, so a deployed container needs no .env at all — it takes its configuration from the platform.

Server

Where the daemon listens, and where its state lives.

MEM_PORT_PORT
8787
HTTP port. PORT is also honoured, since container platforms inject it
MEM_PORT_HOST
127.0.0.1
Interface to bind. Loopback is a security boundary, not a preference — set 0.0.0.0 only where something else enforces who may connect
MEM_PORT_DATA_DIR
OS app-data dir
Where the embedded database and the cached embedding model live
MEM_PORT_ENV_FILE
./.env
Where to load the env file from. Also settable as mem-port serve --env-file <path>

Database

The driver is normally inferred from the URL scheme; the rest applies to whichever engine that selects.

MEM_PORT_DB_URL
surrealkv://<data-dir>/memport.db
Database URL. surrealkv:// and mem:// are embedded, ws:// and wss:// are hosted SurrealDB, postgres:// is Postgres
MEM_PORT_STORE
inferred from the URL
Which driver to use. Set it only to have a mismatch reported rather than inferred
MEM_PORT_DB_USER / MEM_PORT_DB_PASS
none
Credentials. Required for a hosted SurrealDB server, which must be a root- or namespace-level user
MEM_PORT_DB_TOKEN
none
Bearer token, as an alternative to user/password and mutually exclusive with them
MEM_PORT_DB_NAMESPACE
memport
Namespace holding one database per library-id
MEM_PORT_DB_PREFIX
none
Prefix for tenant database names on a shared cluster, or for the per-workspace schema names under Postgres
MEM_PORT_DB_MAX_SESSIONS
256
Cached per-library SurrealDB sessions before the least recently used is closed. Per instance, so N instances mean the cluster sees N times this
MEM_PORT_DB_POOL_SIZE
10
Postgres connection pool size

Authentication and the admin panel

Unset, these follow the bind. Set them to override it, or to supply the first admin.

MEM_PORT_AUTH
follows MEM_PORT_HOST
off or required. Unset, it is off on loopback and required on any other interface
MEM_PORT_ADMIN_USER
none
Creates the first admin, and only while no admin exists
MEM_PORT_ADMIN_PASSWORD
none
That admin's password. Required rather than defaulted, deliberately
MEM_PORT_SESSION_TTL_HOURS
12
How long an admin panel login lasts
MEM_PORT_PUBLIC_URL
the Host header
The URL clients should be pointed at, shown on the panel's docs page. Set it behind a proxy that rewrites Host, where the header would otherwise give a useless internal address

Embeddings

Semantic search runs on a local ONNX model, with no API key and no external call.

MEM_PORT_EMBEDDING_MODEL
Xenova/all-MiniLM-L6-v2
Local embedding model id (reserved for future use)
MEM_PORT_MODEL_CACHE_DIR
<data-dir>/models
Where the model is cached. In a container, point this at weights baked in at build time so a cold start does not download them

UI

Whether the read tools declare a renderable results panel.

MCP_APPS / MEM_PORT_MCP_APPS
on
Set to 0 to stop the read tools declaring an MCP Apps UI resource

.env.example in the repo is the committed reference, and carries the reasoning behind each default in full.

Everything on this page is copied from mem-port’s own deployment files and configuration reference. Running it locally needs none of it.