NevoFlux
Headless (Docker)

Headless (Docker)

Run NevoFlux Agent as a containerized service — a headless browser driven over HTTP (task API, OpenAI / Anthropic-compatible APIs, MCP, A2A) or from a phone, with no GUI. Added in agent v0.3.10; updated for v0.3.16.

NevoFlux Agent can run headless, inside a container, with no desktop. The official nevoflux/agent image packages the daemon, a headless browser, and the built-in knowledge base. You hand it a natural-language task; the agent drives a real browser (automation + computer use + shell/fs tools) and returns the result.

"Headless" is the deployment mode, not a reduced feature set. A headless task can do everything a desktop session can — browse, use tools, run skills, and read/write the knowledge base. The only difference is there is no visible window.

This section arrived in nevoflux-agent v0.3.10. Since v0.3.13 the image is published to Docker Hub on every release. Since then the container has grown several ways in:

Way inWhat it isSince
Task APIPOST /tasks, status, SSE events, follow-up turns, per-turn usagev0.3.10
OpenAI & Anthropic APIsChat Completions (streaming), Responses, Messages, /v1/models; per-model script backendsv0.3.14 / v0.3.16
MCP endpoint & admin APIBuilt-in browser / computer tools, Python script tools, token-protected deploy APIv0.3.14
A2AAgent2Agent v0.3 and v1.0 — be called by other agents, and call themv0.3.16
Remote control (container)Drive the container from a phone; no HTTP at allv0.3.14

And the packaging:

  • Docker image — pull or build it, its build args, run modes, browser engine, and live-view (VNC).
  • Docker Compose — the shipped docker-compose.yml, fully annotated.

Quick start

# 1. pull the official image (amd64 + arm64; published since v0.3.13)
docker pull nevoflux/agent:0.3.16          # or :latest

# 2. run the long-running service (needs your provider key at runtime)
docker run --rm -p 8080:8080 -e ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" nevoflux/agent:0.3.16

# 3. submit a browser task
curl -s localhost:8080/tasks -H 'Content-Type: application/json' \
  -d '{"task":"open example.com and report the title","mode":"browser"}'
# → {"id":"task-0"}
curl -s localhost:8080/tasks/task-0
# → {"id":"task-0","status":"succeeded","output":"The title is Example Domain.", ...}

The same port also answers OpenAI and Anthropic SDKs and A2A clients — with no authentication on any of them, so publish it only to callers you trust (see Two run models).

Need a custom build (extra packs, a pinned agent/browser pair, unreleased binaries)? See Docker image → Build it yourself.

Prefer Compose? See Docker Compose — it packages the same run with all the hardening flags pre-applied.

What's in the image

ComponentDetail
Daemonnevoflux-agent (downloaded from the agent release), the core processing server
Browserthe headless Gecko build (downloaded from the browser release); on amd64, an in-process engine serves browser tools first since v0.3.15
DisplayXvfb virtual X display (:99) — the Linux browser needs a display
Knowledge baseGBrain installed and enabled by default (see Knowledge Base); NEVOFLUX_BRAIN_ENABLED=0 turns it off at run time
Base imagedebian:bookworm-slim, runs as a non-root user (nevo, uid 10001), tini as PID 1
EmbeddingsONNX Runtime + the e5 model bundled — search runs fully offline (HF_HUB_OFFLINE=1)

Two run models — pick by trust

Security in headless mode depends on container granularity, because the container is the sandbox — the product does not build its own. Shell / fs tools run inside the daemon process, so the boundary is the container, plus its network egress policy and the absence of host credential mounts.

ModelCommandUse for
Service mode--daemon --headless --http-addr 0.0.0.0:8080Trusted tasks. One long-running daemon serves many tasks over time. This is the verified, fully-wired path.
One container per taska fresh container per task, torn down afterUntrusted tasks. Isolation per task; nothing survives between tasks.

Why granularity matters. Because bash / run_command execute in the daemon process, a long-lived container that serves many untrusted tasks re-opens cross-task poisoning (one task can leave state that affects the next). For untrusted workloads, give each task its own disposable container.

The hard boundary is set outside the app:

  • Network egress policy — allowlist the LLM API + your task domains (an egress proxy or a firewalled network / k8s NetworkPolicy).
  • No host credential mounts — mount only /work (results) and read-only base-profiles. Prefer injecting the API key via a front proxy so a prompt-injected agent can't read it from the environment.
  • Who can reach the ports. The task API, the OpenAI / Anthropic endpoints and /mcp have no authentication; A2A has an optional bearer token; only the admin API requires one. Treat network reachability as the access control.

In-process defenses (per-task tool allowlist, fs-sandbox, SENSITIVE_PATHS, domain checks) are defense-in-depth layers, not the boundary.

Status of the one-shot path — still open as of v0.3.16. The shipped deploy assets include an oneshot Compose service and a run --task … one-shot form, but that CLI subcommand is not wired into the agent binary yet (re-checked against v0.3.16) — only the --daemon --headless service mode is. Until it lands, get true per-task isolation by running a fresh service container per task (start it, submit one task, tear the container down), or use session mode with end_session for trusted sequential flows. See the Compose page for details.

Kubernetes

For untrusted workloads at scale, run one Job per task: restartPolicy: Never, activeDeadlineSeconds = your wall-clock cap, a securityContext with runAsNonRoot / readOnlyRootFilesystem / seccompProfile: RuntimeDefault / drop: ["ALL"], emptyDir for /work and the data dir, a NetworkPolicy for egress, and base-profiles as a read-only volume. Concurrency and warm capacity are the platform's job — the product runs exactly one browser per task.

On this page