Running Fallen-8
One command brings up the whole thing: engine, REST API, F8 Studio, the model sidecar behind semantic traversal and Studio’s natural-language assist, the document and NLP sidecars behind the semantic layer, the MCP server that AI agents connect to, and the full observability stack. Every feature is on and nothing asks for authentication. That is the default and it just works. This doc then covers every other way to run it and every knob you can turn.
The one command
Section titled “The one command”npm run env:upnpm run env:upThis runs docker compose up for the whole environment: the base compose file plus the
observability and split-topology overlays, the ingestion and nlp profiles, and the GPU
overlay when a GPU is detected. Eight containers publish a host port:
| Service | URL | What it is |
|---|---|---|
| F8 Studio (UI) | http://localhost:8081 | The browser UI, in its own container (F8_UI_PORT) |
| F8 REST API | http://localhost:8080 | The engine plus the REST surface: the data plane (F8_PORT) |
| Model sidecar (Ollama) | http://localhost:11434 | Serves bge-m3 for embeddings and the assist models for NL assist |
| MCP server | http://localhost:8090 | The AI-agent surface, anonymous and read-only in this environment (F8_MCP_PORT) |
| Document conversion (docling) | http://localhost:5001 | Converts PDF, Office and HTML uploads for ingestion (F8_DOCLING_PORT) |
| NLP enrichment (spaCy) | http://localhost:8100 | Named entities and key terms per chunk, folded into the entity graph (F8_NLP_PORT) |
| Observability (Grafana) | http://localhost:3000 | The single pane for metrics, traces and logs (F8_GRAFANA_PORT) |
| OTLP ingest | localhost:4317 (gRPC) / :4318 (HTTP) | Where Fallen-8 instances outside this compose push telemetry |
Prometheus, Tempo and Loki run too, reachable only inside the f8-net network; Grafana is the way
in. The engine is the only caller of docling and the NLP sidecar, so their ports are published for
inspection, not for you to call. env:up prints the first three plus Grafana and the OTLP endpoint.
Stop and inspect it as one unit, never individual containers:
npm run env:down # stop everything; the data + model volumes persistnpm run env:logs # follow all logsnpm run env:status # health of the whole environmentFrom the published images
Section titled “From the published images”env:up builds the first-party images locally. Every release tag also publishes them to
GHCR (ghcr.io/cosh/fallen-8-core plus -api, -studio, -mcp, -integrations, -ollama,
-nlp), so the same environment runs without building anything:
npm run env:up:published # newest release (latest)F8_IMAGE_TAG=0.0.28 npm run env:up:published # pin a release for reproducible deploymentsImage tags mirror the git release tags; latest always points at the newest release. GPU
auto-detection works exactly like env:up (Ollama gets the device, a runtime-only handoff);
the one difference is the NLP sidecar, which stays on the published CPU tier because its
transformer tier only exists as a local build. And for just Fallen-8 itself (engine + REST
API + F8 Studio in one container), no sidecars:
docker run -d --name fallen8 -p 8080:8080 -v f8-data:/data ghcr.io/cosh/fallen-8-core:latestThe environment is open (no API key) and has text-in embeddings on (semantic search and GraphRAG work out of the box). Dynamic C# code execution is always on: there is no switch for it, because compiled filter and cost fragments are Fallen-8’s query model. They run in-process with full trust, so the API key is the only boundary. Set one before exposing the service off-box, and harden the MCP server separately (see Security).
First start pulls models
Section titled “First start pulls models”On the first env:up the sidecar pulls its models into the f8-ollama-models volume:
bge-m3 (embeddings), phi4-mini (base), phi4-f8-mini (the delegate-assist fine-tune,
Studio’s default), and phi4-f8 (the larger GPU-only fine-tune, ~9 GB: set
F8_PULL_PHI4F8=0 to skip it on a CPU-only or disk-constrained host). That is over 10 GB
with phi4-f8, so first start can take a while: the API and Studio are up immediately, and
assist starts answering once the mini pull finishes (the bigger model finishes later in the
background). Later starts reuse the cached volume and need no network. Watch progress with
npm run env:logs. The docling image is a separate ~4.4 GB pull on that same first start
(F8_INGESTION=false skips it).
Offline or want to skip the wait? Pre-seed the volume once from a machine with internet, which needs only Docker and no host Ollama:
bash scripts/ensure-models.sh # assist models into the f8-ollama-models volumeThe pre-seed covers the same set the container would pull: phi4-mini, phi4-f8-mini, phi4-f8
unless F8_PULL_PHI4F8=0, and bge-m3 unless F8_EMBEDDINGS=false. So an environment seeded here
and started offline has a model behind every capability it advertises.
If a pull fails the sidecar still comes up with whatever is present (it does not crash-loop); fix the network and re-run. The 404-in-Studio case is in Troubleshooting.
Environment variables (compose)
Section titled “Environment variables (compose)”Set these before npm run env:up. In PowerShell use $env:NAME = "value"; … (it stays set
for the session; Remove-Item Env:NAME clears it) and chain with ;, not &&.
| Variable | Default | Effect |
|---|---|---|
F8_PORT |
8080 |
Host port for the REST API, the data plane (the container always listens on 8080) |
F8_UI_PORT |
8081 |
Host port for the F8 Studio container; the data plane allow-lists exactly this origin for CORS |
F8_API_KEY |
(unset) | Set it and the entire data plane requires the key; unset = open. The MCP bridge receives it for its own downstream calls but keeps its own caller auth (F8_MCP_AUTH_MODE) (Security) |
F8_EMBEDDINGS |
true |
Text-in embeddings via the sidecar’s bge-m3; false disables the provider (bring-your-own-vector still works) |
F8_CHAT |
true |
The chat gateway (POST /chat to the sidecar’s assist model), the default NL-assist path. false answers 403 (custom browser-direct backends still work) |
F8_INGESTION |
true |
Document ingestion. false skips the docling sidecar (a ~4.4 GB image) and answers 403 on the document endpoints, including txt/md |
F8_NLP |
true |
NLP enrichment. false skips the spaCy sidecar; ingestion still writes Document and Chunk vertices, just without the entity graph |
F8_INTEGRATIONS |
true |
The integrations runtime, on its own profile and with no host port. false skips the sidecar and the four /integrations/* routes then refuse the capability |
F8_INTEGRATIONS_ALLOWED_HOSTS |
(unset) | Comma-separated hosts a run holding a credential may contact, enforced as the request leaves. Unset means no restriction, and env:up says so: a source address arrives in a job from whoever can reach the API |
F8_INTEGRATIONS_SELF_SIGNED_HOSTS |
(unset) | Comma-separated hosts whose TLS certificate is not validated. The one place this feature reduces trust, and what a UniFi console or Fronius inverter on a private address needs to be reachable at all |
F8_GPU |
(auto) | 1 forces the NVIDIA GPU, 0 forces CPU-only; unset auto-detects |
F8_IMAGE_TAG |
latest |
Which published image version env:up:published runs (mirrors the release tags, e.g. 0.0.28); ignored by the building env:up |
F8_PULL_PHI4F8 |
1 |
Also pulls the larger GPU-only phi4-f8 assist model (~9 GB); set 0 to skip it (CPU-only / disk-constrained hosts) |
F8_DELEGATE_REPO |
stoic_hellman_728/phi4-f8-mini |
Source for the default assist fine-tune (tagged locally as phi4-f8-mini); F8_PHI4F8_REPO is the same for phi4-f8 |
F8_MCP_AUTH_MODE |
None |
Caller auth for the MCP server. Anything reachable off-box wants StaticToken plus F8_MCP_TOKEN |
F8_MCP_ENABLE_WRITE / F8_MCP_ENABLE_ADMIN / F8_MCP_ENABLE_CODE |
false |
Opt individual MCP tool tiers in; agents get read-only tools by default |
F8_TENANT_ID / F8_INSTANCE_ID |
local / f8-local |
This instance’s fleet identity in the dashboards (F8_TENANT_NAME and F8_INSTANCE_NAME set the display names) |
F8_GRAFANA_PASSWORD |
admin |
Locks the Grafana admin account; anonymous editor access is on by default for the trusted network |
Every published port has an override: F8_PORT (8080), F8_UI_PORT (8081), F8_MCP_PORT (8090),
F8_GRAFANA_PORT (3000), F8_DOCLING_PORT (5001), F8_NLP_PORT (8100), F8_OTLP_GRPC_PORT (4317)
and F8_OTLP_HTTP_PORT (4318). Ollama’s 11434 is fixed. F8_DOCLING_IMAGE pins the docling image,
which is deliberately not :latest so its document schema cannot drift under you.
Examples:
F8_API_KEY=change-me npm run env:up # secured (every request needs the key)F8_PORT=9090 npm run env:up # REST API on :9090F8_INGESTION=false npm run env:up # skip the docling + NLP sidecars entirely$env:F8_API_KEY = "change-me"; npm run env:up$env:F8_PORT = "9090"; npm run env:up$env:F8_INGESTION = "false"; npm run env:upTopology: a decoupled UI by default
Section titled “Topology: a decoupled UI by default”npm run env:up runs F8 Studio as its own container (the split topology): the data plane serves
the REST API at :8080, and a separate f8-studio nginx container serves the UI at
http://localhost:${F8_UI_PORT} (default 8081), talking to the API cross-origin. This is applied by
docker-compose.split.yml layered over the base compose file: it re-tasks the data plane to serve
REST only, wires the UI origin into Fallen8__Security__AllowedCorsOrigins, and adds the f8-studio
service.
The all-in-one (UI baked into the API container, both on :8080) still ships as one image and
runs from the base compose file with no overlay. Pass the two profiles yourself, because a bare
docker compose up starts neither sidecar while the base file still declares both capabilities on
(document uploads would be limited to txt/md and no entity graph would be built):
docker compose --profile ingestion --profile nlp up -d --buildWithout the observability overlay there is also nothing behind the base file’s
Fallen8__Observability__Otlp__Endpoint, so the exporter has no collector to push to: add
-f docker-compose.observability.yml (what env:up does) or set that variable to empty. The
runtime config.js seam, the image, and the CORS rules are in
Standalone F8 Studio.
Running the engine + API without Docker
Section titled “Running the engine + API without Docker”A bare run needs only the .NET 10 SDK. It starts in the Development environment, which is the only environment that serves the OpenAPI document and Scalar reference:
dotnet run --project fallen-8-core-apiApp# → http://localhost:5000 (OpenAPI + Scalar reference are served here in Development)dotnet run --project fallen-8-core-apiAppThis is engine plus REST only. None of the sidecars are there, so the capabilities that depend on them default to off: the embedding provider, the chat gateway behind Studio’s NL assist, document ingestion, and NLP enrichment all answer 403 until you enable them and point them at a backend of your own (txt/md ingestion needs only the flag, since docling converts binary formats only). Bring-your-own-vector, analytics, paths, indexes and every other feature work regardless. The MCP server is its own deployable and is likewise absent from a bare run (how to run it).
With F8 Studio built in
Section titled “With F8 Studio built in”Studio is a React SPA served by the API app from its wwwroot. Build it in, then run:
npm run install:ui # oncenpm run build:apiapp # build the SPA into the apiApp's wwwrootdotnet run --project fallen-8-core-apiApp# open http://localhost:5000To develop the UI against a running API with hot reload, use npm run dev (Vite dev server)
instead of build:apiapp. Debugging the whole stack in VS Code is covered in
Debugging in VS Code.
Configuration keys
Section titled “Configuration keys”The compose variables above map onto the app’s configuration, which you can also set
directly (via appsettings.json, environment variables with the Fallen8__Section__Key
form, or the command line). The defaults:
| Key | Default | Owner doc |
|---|---|---|
Fallen8:Durability:StorageDirectory |
app base dir (/data in the container) |
Save games |
Fallen8:Durability:Volatile |
false |
Save games |
Fallen8:Durability:SaveOnShutdown |
true |
Save games |
Fallen8:Metadata:Directory |
<app base>/metadata |
Save games |
Fallen8:Namespaces:MaxNamespaces |
10000 |
Namespaces |
Fallen8:Namespaces:LoadOnStartup |
true (per-namespace overridable) |
Namespaces |
Fallen8:Namespaces:StartupLoadMode |
Catalog (All, DefaultOnly) |
Namespaces |
Fallen8:Security:ApiKey |
null (open) |
Security |
Fallen8:Security:EnableDynamicPluginLoading |
true (per-namespace overridable) |
Plugin registration |
Fallen8:Security:AllowedCorsOrigins |
[] (deny all cross-origin) |
Standalone F8 Studio |
Fallen8:Security:BenchmarkMaxIterations |
10000 |
Benchmark |
Fallen8:Embedding:Enabled |
false (bare run) |
Semantic traversal |
Fallen8:Chat:Enabled |
false (bare run) |
F8 Studio |
Fallen8:Ingestion:Enabled |
false (bare run) |
Semantic layer |
Fallen8:Nlp:Enabled |
false (bare run) |
Semantic layer |
Fallen8:ChangeFeed:Enabled |
true |
Change feed |
Fallen8:BulkIO:ImportBatchSize |
10000 |
Bulk import/export |
Fallen8:Analytics:DefaultTimeBudgetSeconds |
30 (max 300) |
Graph analytics |
Fallen8:Observability:Prometheus:Enabled |
false |
Observability |
Fallen8:Observability:Otlp:Endpoint |
null (no push) |
Observability |
Volatile=true disables all disk writes (no WAL, no checkpoints): the fastest way to run a
throwaway instance for a demo or a test.
Fallen8__Namespaces__StartupLoadMode=All overrides every per-namespace exclusion for a boot, which
is the way back from an exclusion you regret without hand-editing the catalog; DefaultOnly loads
nothing but default, for when the selection itself is what is broken
(namespaces).
These are the defaults a bare run gets. The compose environment overrides several of them:
Metadata:Directory becomes /data/metadata (it does not follow StorageDirectory, so both are
set explicitly to keep graph data and the save-game registry on one volume), and Otlp:Endpoint
points at the in-network collector, so metrics, traces and logs export from the first boot without
you enabling anything.
GPU acceleration
Section titled “GPU acceleration”npm run env:up and npm run env:up:published auto-detect an NVIDIA GPU (nvidia-smi
present) and hand it to the containers that can use one. Ollama runs assist and embedding
inference accelerated in both modes (a runtime-only handoff, docker-compose.gpu.yml). When
building locally, the NLP sidecar additionally swaps to the en_core_web_trf transformer
model on the device instead of en_core_web_lg on the CPU (docker-compose.gpu-nlp.yml).
Otherwise everything runs CPU-only. The transformer model is baked in at build time, so
flipping F8_GPU rebuilds the nlp image (transformer weights plus the CUDA extras): the
first env:up after the switch is slow, and env:up:published always stays on the CPU NLP
tier. Force detection with F8_GPU:
F8_GPU=0 npm run env:up # CPU-only even if a GPU is presentF8_GPU=1 npm run env:up # require the GPU (fails to create the container if unavailable)The GPU reaches the container through the NVIDIA Container Toolkit, installed where the
Docker engine runs. On Docker Desktop (WSL2 backend) install the current NVIDIA driver on
Windows and confirm nvidia-smi works inside WSL: the backend then exposes the GPU
automatically. For Docker running natively in a Linux distro install
nvidia-container-toolkit and sudo nvidia-ctk runtime configure --runtime=docker. Verify
the GPU reaches a container before env:up:
docker run --rm --gpus all --entrypoint nvidia-smi ollama/ollama:latest # should list your GPUIf that fails the GPU is not exposed to Docker; the stack still runs CPU-only with F8_GPU=0.
AMD GPUs are not covered by this compose: Ollama would need the ollama/ollama:rocm image, and the
NLP sidecar’s transformer tier assumes CUDA, so an AMD host also stays on the CPU en_core_web_lg
model.
See also
Section titled “See also”- Architecture: how the containers, engine and sidecars fit together
- Security: the API key you set at launch
- Studio: the browser UI this serves
- MCP server: the agent surface on
:8090and how to secure it - Observability: the Grafana pane and the fleet identity knobs
- Save games: where durable data lives and how startup loads it
- REST API: OpenAPI/Scalar (Development only) and the endpoint map
- Troubleshooting: first-start model pulls, GPU, and other snags