Skip to main content
Version: v0.9.0a2
Operator

Deployment & Installation

6 min readFirst-time operatorv0.9.0a1

What this page covers

How to install the Stigmem reference node. Docker Compose is the supported deployment path in v0.9.0a1 for both local development and single-host production. For development against source, see Running without Docker.

Audience: node operators installing Stigmem for the first time, or migrating an existing bare-metal install to Docker.

Operator handbook

For the narrative Docker Compose runbook, see Operating Stigmem โ†’ Deploy runbooks.

Other deployment surfaces are deferred

Fly.io, Helm/Kubernetes, systemd, PaaS, and Grafana recipes have been moved to experimental/deploy-*/ per ADR-002. They remain available as starting points but are unsupported until they pass the ADR-008 reintroduction gates. See Features โ†’ Deployment for the full disposition.

Container image tags โ€” which one should I pull?โ€‹

The reference node image is published to GHCR at ghcr.io/eidetic-labs/stigmem-node. Several tag flavours are published in parallel.

Tag
Stability
Use when
:0.9.0aN / :0.9.0bN / :1.0.0rcN / :1.0.0
immutable
Release-pinned evaluation or controlled internal workloads. Never reassigned after publish. Reproducible builds, audit-traceable deployments, change-control gates. Do not treat alpha tags as production-stable.
:0.9.0-alpha.N / :0.9.0-beta.N / :1.0.0-rc.N
immutable
Same artefact in semver-strict spelling. Use whichever your tooling prefers.
:latest
rolling (release tags)
Quickstart, eval, demos. You accept that the image will silently advance when a new release ships.
:edge
rolling (every main push)
Tracking tip-of-trunk between releases. CI-tested but not release-gated.
:<short-sha>
immutable (90 days)
Forensics, rollback to a specific commit, reproducing a CI failure.
@sha256:<digest>
tamper-evident
Supply-chain-conscious deployments. Fixes the content, not the label. Required for hardened production once the release line is stable.

Quick rule of thumb

  • Trying things out โ†’ :latest
  • Controlled internal workload โ†’ :0.9.0aN (your current release)
  • Auditable deployment โ†’ @sha256:<digest> of the version tag you intend to run
Verifying what :latest resolves to today
docker pull ghcr.io/eidetic-labs/stigmem-node:latest
docker inspect --format '{{.RepoDigests}}' ghcr.io/eidetic-labs/stigmem-node:latest

The printed digest is what's actually running. Pin to that digest in your Compose / Helm chart for a tamper-evident production deployment.

Image retentionโ€‹

Retained forever

:latest; all version tags (:0.9.0aN, :0.9.0bN, :1.0.0rcN, :1.0.0); :edge.

Retained 90 days

Short-SHA tags and their orphan Sigstore signature artefacts.

Operationally enforced by .github/workflows/ghcr-retention.yml.

Prerequisitesโ€‹

Tool
Minimum
Notes
Docker
โ‰ฅ 24
โ€”
Docker Compose (v2)
โ‰ฅ 2.20
โ€”
Git
any recent
โ€”

No Python or Node.js installation is required for the Docker path.

One-click Docker Compose installโ€‹

git clone https://github.com/eidetic-labs/stigmem
cd stigmem
docker compose up --build -d

This builds the reference node image once and starts node-a and node-b:

Container
Host port
Key endpoints
node-a
8765
/healthz ยท /docs ยท /.well-known/stigmem
node-b
8766
/healthz ยท /docs ยท /.well-known/stigmem

Verify both are healthy:

curl -s http://localhost:8765/healthz # โ†’ {"status":"ok"}
curl -s http://localhost:8766/healthz # โ†’ {"status":"ok"}

Then follow the Quickstart to complete the federation handshake and assert your first fact.

Persist your keypairsโ€‹

By default, each node auto-generates an Ed25519 keypair on first start and stores it in node_meta. To survive container recreation, supply your own:

python3 -c "
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
import base64, os
priv = Ed25519PrivateKey.generate()
priv_bytes = priv.private_bytes_raw()
pub_bytes = priv.public_key().public_bytes_raw()
print('STIGMEM_FEDERATION_PRIVKEY=' + base64.urlsafe_b64encode(priv_bytes).decode())
print('STIGMEM_FEDERATION_PUBKEY=' + base64.urlsafe_b64encode(pub_bytes).decode())
"

Add those two variables to the environment: block of your docker-compose.yml.

Stop and clean upโ€‹

docker compose down # stop containers; preserve data volumes
docker compose down -v # stop and delete all data

Multi-node topology (soak / staging)โ€‹

For 4-node full-mesh federation with shorter pull intervals:

# Generate keypairs for all four nodes
cd infra && python soak/keys.py > soak/.env

# Start the cluster
docker compose -f infra/docker-compose.soak.yml --env-file infra/soak/.env up --build -d

Nodes run on ports 8765โ€“8768. See the 4-node federation guide for peer wiring, backpressure, and failure injection.

Environment variable referenceโ€‹

Coreโ€‹

Variable
Default
Description
STIGMEM_DB_PATH
stigmem.db
Path to the SQLite database file.
STIGMEM_HOST
0.0.0.0
Bind address.
STIGMEM_PORT
8765
HTTP listen port.
STIGMEM_NODE_URL
http://localhost:8765
Public URL of this node โ€” included in PeerDeclaration payloads.
STIGMEM_LOG_LEVEL
info
debug ยท info ยท warning ยท error.
STIGMEM_AUTH_REQUIRED
true
Every request must carry a valid Bearer token. Set false only for single-operator / local-only installs.

Federationโ€‹

Variable
Default
Description
STIGMEM_FEDERATION_ENABLED
false
Enable the pull-replication loop.
STIGMEM_FEDERATION_PUBKEY
"" (auto)
Base64url Ed25519 public key. Persist this.
STIGMEM_FEDERATION_PRIVKEY
"" (auto)
Base64url Ed25519 private key. Persist this.
STIGMEM_FEDERATION_PULL_INTERVAL_S
30
Seconds between pull cycles. Peer-advertised value takes precedence.
STIGMEM_FEDERATION_PUSH_ENABLED
false
Enable push replication (experimental).
STIGMEM_FEDERATION_NONCE_WINDOW_S
300
How long a nonce is retained to block replays.
STIGMEM_FEDERATION_ALLOW_TEAM
false
Allow team-scoped facts to cross federation boundaries. Audit-logged.

Decay, Attestation, OIDC, Performanceโ€‹

Variable
Default
Description
STIGMEM_DECAY_TTL_SECONDS
0 (disabled)
Default TTL when a sweep runs without an explicit ttl_seconds.
STIGMEM_DECAY_MIN_CONFIDENCE
0.0 (disabled)
Default minimum confidence for decay sweeps.
STIGMEM_ATTESTATION_REQUIRED
false
Require Ed25519 attestation token on every POST /v1/facts. Enable for multi-tenant or public nodes.
STIGMEM_SOURCE_ATTESTATION_MODE
off
Legacy compatibility field. Use the experimental source-attestation plugin plus explicit plugin gates for runtime enforcement.
STIGMEM_OIDC_ENABLED
false
Enable the OIDC โ†’ API key bridge.
STIGMEM_OIDC_ISSUER_URL
""
IdP issuer URL.
STIGMEM_OIDC_AUDIENCE
""
Expected aud claim.
STIGMEM_OIDC_TOKEN_TTL_HOURS
8
Lifetime of issued API keys.
STIGMEM_OIDC_ALLOWED_DOMAINS
"" (any)
Comma-separated allowed email domains.
STIGMEM_ASYNC_JOB_THRESHOLD
100000
Fact count per scope above which decay sweep returns 202 Accepted.

Upgradeโ€‹

Upgrading a Docker Compose installโ€‹

git pull
docker compose up --build -d

Data volumes persist across rebuilds. Database migrations run automatically on startup โ€” there is no manual migration step.

Upgrading from bare-metal to Docker Composeโ€‹

  1. Stop the existing service. If running as a macOS LaunchAgent: bash scripts/service-uninstall.sh. If running manually, stop the process (Ctrl-C or kill the PID).
  2. Locate your database. By default the bare-metal node writes to data/stigmem.db (relative to the repo root). ls -lh data/stigmem.db. If you set STIGMEM_DB_PATH, use that path instead.
  3. Create a Docker volume and copy data in (see snippet below).
  4. Start Docker Compose. docker compose up --build -d. Verify node-a picked up the data: curl -s 'http://localhost:8765/v1/facts?limit=5' | jq .facts
  5. (Optional) Restore your keypair. If you had a stable STIGMEM_FEDERATION_PUBKEY/PRIVKEY, add the same values to docker-compose.yml so existing peers recognize the node.
# Create the named volumes Docker Compose expects
docker volume create stigmem__node-a-data
docker volume create stigmem__node-b-data

# Copy your existing database into node-a's volume
docker run --rm \
-v "$(pwd)/data/stigmem.db:/src/stigmem.db:ro" \
-v "stigmem__node-a-data:/data" \
busybox cp /src/stigmem.db /data/stigmem.db
Volume naming

Docker Compose prefixes volume names with the project name (derived from the directory name). If your repo directory is not named stigmem, replace stigmem__ with <directory-name>_.

Running without Dockerโ€‹

For development against the source directly:

cd stigmem/node
uv sync # install dependencies
uv run python -m stigmem_node # starts on http://localhost:8765

Interactive Swagger UI: http://localhost:8765/docs.

For macOS persistent service (LaunchAgent, no Docker), see Installation.