Installation & Runtime Setup

What to install, which environment variables to set, and how to start every process.

Hard prerequisites

Python dependencies

Pinned in requirements.txt at the repo root. Headline packages:

PackageUsed by
fastapi, uvicornAPI + lifespan
httpx[socks], PySocks, curl-cffi, brotlicffiLLM client, web fetcher, Tor passthrough
chromadb, sentence-transformers, tiktokenVector memory and embeddings
sqlalchemy, psycopg2-binaryPostgres admin tool
dockerSandbox
PyMuPDF, pypdf, beautifulsoup4Document ingest, vision OCR fallback
duckduckgo-searchWeb search
transformers, torch, sentencepiece, huggingface-hubTokeniser, embeddings, image-gen server
apschedulerScheduled background tasks
qwen-agent, soundfileOptional Qwen ReAct bridge (core/agent_qwen.pytools/qwen_bridge.py). soundfile is a transitive dep of qwen-agent (its utils/utils.py does import soundfile at module load); pinned explicitly so fresh installs don't hit a cryptic ModuleNotFoundError when the Qwen variant is used. On Linux also install the system package libsndfile1 (apt: sudo apt-get install libsndfile1); macOS / Windows wheels ship libsndfile bundled. The default agent path (ghost_agent.main) does NOT need qwen_bridge.
slack-bolt, slack-sdkSlack interface
dspy-aiPrompt optimisation (ghost_agent.optim, scripts/run_gepa.py). _require_dspy() surfaces a clear install error if the transitive chain (litellm / gepa / asyncer) is broken.
black, pylint, pytest, pytest-asyncioDev tooling

Environment variables

The CLI reads arguments first, then falls back to environment variables. See CLI Reference for argument-by-argument detail.

VariableDefaultEffect
GHOST_API_KEYghost-secret-123Value matched against the X-Ghost-Key header on every authed route. Mandatory for interface/server.py; agent itself ships a default but you should override.
GHOST_HOME~/ghost_llamacppBase directory; agent creates sandbox/ and system/memory/ beneath it.
GHOST_MODELqwen-3.6-35b-a3Default model name advertised on Ollama-compatibility routes.
GHOST_SANDBOX_DIRderived from GHOST_HOMEHost directory bind-mounted into the container at /workspace.
GHOST_DEFAULT_DBpostgresql://ghost@127.0.0.1:5432/agentDSN used by postgres_admin tool when no override given.
GHOST_SANDBOX_MEM4gMemory cap for the sandbox container.
GHOST_SANDBOX_CPU_QUOTA200000CPU quota in microseconds (200000 µs / 100000 µs period ≈ 2 vCPU).
GHOST_INTERFACE_STREAM_CAP52428800 (50 MB)Per-task stream buffer cap inside interface/server.py.
TOR_PROXYunset (but required)socks5h://127.0.0.1:9050. All outbound HTTP from the agent is routed through this SOCKS proxy; identity rotation uses the companion Tor control port. Leaving it unset causes outbound tools to refuse to fire — fail-closed by design.
SLACK_BOT_TOKEN / SLACK_APP_TOKENunsetSlack bot bot/app credentials. Required by interface/externals/slack_bot/main.py.
PI_VOICE_URLhttp://raspberrypi.local:8000Base URL for the TTS/STT server reached by server.py.

Filesystem layout at runtime

$GHOST_HOME/
├── sandbox/                      # bind-mounted into container as /workspace
│   ├── acquired_skills/          # generated tool .py files
│   │   ├── <name>.py             # (name strictly validated — see tools/acquired_skills)
│   │   ├── retired/              # auto-archived degraded skills
│   │   └── skills_registry.json
│   └── ... user/agent files
├── trajectories/                 # Stage-1 distill log (day-partitioned JSONL)
│   └── YYYY-MM-DD/
│       └── session-<sid>.jsonl   # redacted turn-level traces
└── system/
    ├── ghost-agent.log
    ├── eval/
    │   └── baseline.json         # frozen SuiteResult (scripts/eval_baseline.py)
    ├── optim/
    │   └── <signature>.json      # tuned GEPA instructions (scripts/run_gepa.py)
    └── memory/
        ├── chroma.sqlite3        # ChromaDB persistent store
        ├── knowledge_graph.db    # Graph triplets (SQLite)
        ├── episodic_memory.db    # Episodes (SQLite)
        ├── projects.db           # Project / task / artifact / event log
        ├── memory_journal.json   # Short-term work queue
        ├── user_profile.json     # Profile facts
        ├── skills_playbook.json  # Lessons (verified + utility-ranked)
        ├── adaptive_threshold.json
        ├── contradiction_log.json
        ├── self_play_frontier.json + .lock
        ├── library_index.json    # Names of ingested documents
        └── scratchpad.db         # LRU + TTL working memory

Boot sequence

From main.py:399-440 and the lifespan async context (main.py:140-398):

  1. Parse CLI flags + env vars.
  2. Make sure GHOST_HOME/sandbox and GHOST_HOME/system/memory exist.
  3. setup_logging() wires colourised structured logs (utils/logging.py).
  4. load_tokenizer() grabs / downloads the Qwen3 tokenizer for token budgeting.
  5. Build GhostContext with Scratchpad, MemoryJournal, SkillMemory, FrontierTracker.
  6. Construct the FastAPI app and attach the lifespan coroutine.
  7. Lifespan startup:
    • LLMClient with foreground + swarm/worker/visual/coding/image_gen pools.
    • DockerSandbox.ensure_running() in a worker thread.
    • ProjectStore always (for user-driven multi-session projects).
    • Memory stores (vector, graph, profile, episodes, contradiction log, adaptive threshold) — skipped under --no-memory.
    • APScheduler for user-defined cron tasks.
    • MemoryBus, Verifier, UncertaintyTracker.
    • Optional MCTSReasoner + HypothesisTester when --deep-reason.
    • GhostAgent + biological watchdog daemon task.
  8. Lifespan shutdown: cancel watchdog, stop scheduler, close LLM client, stop sandbox container (kept around with remove=False for fast resume).
  9. uvicorn.run(app, host, port, log_config=None).

Process commands

# core agent
python -m src.ghost_agent.main \
    --upstream-url "http://127.0.0.1:8080" \
    --host 0.0.0.0 --port 8000 --verbose

# web UI proxy (separate port)
python interface/server.py --agent-log /var/log/ghost-agent.log

# Slack bot (Socket Mode)
SLACK_BOT_TOKEN=xoxb-... SLACK_APP_TOKEN=xapp-... \
  python interface/externals/slack_bot/main.py \
    --log-file /var/log/ghost-slack-bot.err

# Voice server (Jetson / RPi GPU box)
python interface/externals/tts_stt/voice_server.py

# Image gen server (GPU box)
python interface/externals/image_generation/img_gen_server.py
Heads-up: the agent's lifespan stops the sandbox container with remove=False so subsequent restarts resume in seconds. Pass --remove-on-shutdown equivalent only if you need a clean image.

Pre-provisioned sandbox image (optional, recommended)

The runtime sandbox wrapper can provision Chromium + apt deps + the full pip stack on first boot, but this takes 2–3 minutes the first time and can silently rot if committed before --with-deps was added. The repeatable path is to build a proper image:

scripts/build_sandbox_image.sh
# → builds ghost-agent-base:latest (~2 GB first run; ~5 min on warm cache)
# → runs a Chromium launch smoke test at the end

The image bakes apt system packages, the deep-learning pip stack, and playwright install --with-deps chromium at build time — self-play + browser tasks start instantly. The runtime gate checks both the /root/.supercharged.v2 marker AND that the Chromium binary is actually on disk; if the marker is present but the binary is missing (the exact silent-failure mode the v2 bump exists to catch), a full reinstall fires on next ensure_running.

If you skip this step, the fallback inline provisioning still produces a correct image; it just takes longer on the first turn.

Test runner

pytest                                   # full suite (asyncio_mode=auto)
pytest tests/test_agent_planning.py      # single file
pytest -k "memory and not slack"         # filter
black src interface tests
pylint src/ghost_agent