Skip to content

Deployment

Daniel Babjak edited this page Apr 8, 2026 · 2 revisions

Deployment

This page is the production install + ops guide. For first-time setup walkthrough see docs/SETUP_LOCAL.md in the repo. For the daily-operation playbook see Operator Handbook.

Self-host first. Agent Life Space is meant to live on a server you own. There is no managed/SaaS deployment, no cloud broker, no shared state. The whole runtime fits in a single Python process plus Docker for the build/sandbox.


Requirements

Minimum Recommended
OS Ubuntu 22.04+ (other modern Linux works; macOS for dev) Ubuntu 24.04 LTS
Python 3.11 3.12
RAM 4 GB 8 GB+ (semantic embedding model adds ~1.5 GB)
CPU 2 cores 4+ cores
Disk 10 GB free for SQLite + logs 50 GB+ if you'll run long-running build jobs
Docker required (sandbox + build isolation) latest stable
Telegram a bot token from @BotFather + your numeric user ID
LLM Claude Max subscription or Anthropic API key both, so you can flip via runtime LLM control

Install

git clone https://github.com/B2JK-Industry/Agent_Life_Space.git
cd Agent_Life_Space
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
pip install sentence-transformers   # ~470 MB model download

The pip install -e . resolves a small pyproject.toml. We deliberately keep transitive deps minimal (no APScheduler, no Celery, no Redis, no Kubernetes).


Configuration: .env

Copy .env.example to .env (gitignored) and replace every value. Never copy values from someone else's setup. Generate fresh secrets locally.

Required

# Runtime paths
AGENT_PROJECT_ROOT=/absolute/path/to/Agent_Life_Space
AGENT_DATA_DIR=/absolute/path/to/Agent_Life_Space/.agent_runtime
AGENT_PIDFILE_PATH=/absolute/path/to/Agent_Life_Space/.agent-life-space.pid

# Telegram
TELEGRAM_BOT_TOKEN=<from-@BotFather>
TELEGRAM_USER_ID=<your-numeric-id>     # comma-separated if multiple owners

# Vault
AGENT_VAULT_KEY=<generate-fresh>       # python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"

# HTTP API
AGENT_API_KEY=<generate-fresh>         # python -c "import secrets; print(f'agent_api_{secrets.token_urlsafe(24)}')"

Identity (recommended)

AGENT_NAME=MyAgent
AGENT_SERVER_NAME=my-server
AGENT_OWNER_NAME=YourFirstName
AGENT_OWNER_FULL_NAME="Your Full Name"
AGENT_DEFAULT_LANGUAGE=en              # ISO code; operator language always wins per-message

If you leave AGENT_OWNER_NAME and AGENT_OWNER_FULL_NAME empty, the agent will learn the owner profile from the first authorized Telegram message.

LLM backend

Pick one:

# Option A: Claude Code CLI on the same host (uses your Claude Max subscription, $0/call)
LLM_BACKEND=cli
# CLAUDE_CODE_OAUTH_TOKEN=<optional, if your CLI session needs it>
# Option B: direct API
LLM_BACKEND=api
LLM_PROVIDER=anthropic
ANTHROPIC_API_KEY=sk-ant-...
# Option C: OpenAI-compatible (real OpenAI, Ollama, vLLM, LM Studio, ...)
LLM_BACKEND=api
LLM_PROVIDER=openai
OPENAI_API_KEY=sk-...
OPENAI_BASE_URL=http://localhost:11434/v1   # for Ollama

You can flip between backends at runtime without restart — see Runtime LLM Control.

Security

# Sandbox-only mode (default; opt-in to host file access by setting "0")
AGENT_SANDBOX_ONLY=1

# Headless CLI auto-approve — REQUIRED on servers without an interactive operator
# 1 = always pass --dangerously-skip-permissions to the Claude CLI
# 0 = never (interactive desktop only)
# empty = auto-detect TTY (also works for daemon mode)
AGENT_CLI_AUTO_APPROVE=1

Without AGENT_CLI_AUTO_APPROVE=1 (or empty for auto-detect), every LLM tool-use call from the CLI backend on a daemon host will block forever waiting for an operator who can never click "Allow".

Tiered logging

AGENT_LOG_TIERED=1                     # default; 0 falls back to legacy single-file logger
AGENT_LOG_LONG_RETENTION_HOURS=720     # default 30 days
AGENT_LOG_SHORT_RETENTION_HOURS=6      # default 6 hours

Both halves of the system (the rotating handler at boot + the cron prune sweep at runtime) read from the same env var. See Tiered logging for the full contract.

Optional

# GitHub
GITHUB_TOKEN=<your-github-pat>         # used by the gateway for GitHub API calls

# Obolos / external provider gateway
AGENT_OBOLOS_API_BASE_URL=
AGENT_OBOLOS_WALLET_ADDRESS=
AGENT_OBOLOS_AUTH_TOKEN=

# CI release-readiness
# AGENT_RELEASE_READINESS_SKIP_LLM_PROBE=1   # set this in CI workflows; never set in production

First boot

source .venv/bin/activate
.venv/bin/python -m agent --setup-doctor   # config audit
.venv/bin/python -m agent                  # foreground run

The setup doctor walks every required env var, every directory, every module, the vault, the LLM backend, and tells you exactly what's missing. Run it before every install — it catches 90% of "agent won't start" issues before they happen.


Production: systemd

Create /etc/systemd/system/agent-life-space.service:

[Unit]
Description=Agent Life Space
After=network-online.target docker.service
Wants=network-online.target

[Service]
Type=simple
User=youruser
Group=youruser
WorkingDirectory=/home/youruser/Agent_Life_Space
EnvironmentFile=/home/youruser/Agent_Life_Space/.env
ExecStart=/home/youruser/Agent_Life_Space/.venv/bin/python -m agent
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal

# Hardening
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/home/youruser/Agent_Life_Space
PrivateTmp=true
ProtectHome=read-only
ProtectKernelTunables=true
ProtectControlGroups=true
LockPersonality=true
RestrictRealtime=true

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable agent-life-space
sudo systemctl start agent-life-space
sudo journalctl -u agent-life-space -f

The service file uses systemd's hardening directives (NoNewPrivileges, ProtectSystem=strict, etc.) to lock down what the agent process can touch even if Python somehow escapes the sandbox.

User-level systemd (no root)

If you don't have root:

mkdir -p ~/.config/systemd/user
cp agent-life-space.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable agent-life-space
systemctl --user start agent-life-space
loginctl enable-linger youruser   # so it survives logout

Cloudflare tunnel (optional)

For external access to the HTTP API and dashboard without opening a port on your firewall:

# Install cloudflared (Ubuntu)
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb \
  -o /tmp/cloudflared.deb
sudo dpkg -i /tmp/cloudflared.deb

# Quick tunnel (URL changes on every restart)
cloudflared tunnel --url http://localhost:8420

For a stable URL, register a named tunnel with cloudflared tunnel login + cloudflared tunnel create.

The repo ships a scripts/tunnel-watchdog.sh that:

  1. Polls every 2 minutes
  2. Restarts the tunnel if it died
  3. Sends the new URL to your Telegram on restart

Crontab entry:

*/2 * * * * /home/youruser/Agent_Life_Space/scripts/tunnel-watchdog.sh >> /tmp/tunnel-watchdog.log 2>&1

Server hardening

# Firewall — block everything except SSH and HTTPS (if you really expose the API)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow from 127.0.0.1 to any port 8420   # local-only API
sudo ufw enable

# SSH brute-force protection
sudo apt install -y fail2ban

# Disable services you don't need on a headless box
sudo systemctl disable --now fwupd udisks2 packagekit upower multipathd

# Laptop lid: don't suspend if you run on a closed laptop
sudo sed -i 's/#HandleLidSwitch=.*/HandleLidSwitch=ignore/' /etc/systemd/logind.conf
sudo systemctl restart systemd-logind

# Docker group (so the agent can spawn sandbox containers)
sudo apt install -y docker.io
sudo usermod -aG docker youruser
# log out + back in, or run: sg docker -c "id"

The HTTP API binds to 127.0.0.1:8420 by default. Only the Cloudflare tunnel sees it from the outside, and the tunnel adds Cloudflare's WAF + DDoS in front.


Verify the install

# Setup audit
.venv/bin/python -m agent --setup-doctor | jq .

# Liveness
curl http://127.0.0.1:8420/api/health
# -> {"health":"ok"}

# Authenticated status
curl -H "Authorization: Bearer $AGENT_API_KEY" http://127.0.0.1:8420/api/operator/status | jq .

# Telegram smoke test
# Send "/status" to your bot. You should get a status reply within ~5 seconds.

# Run the test suite
.venv/bin/python -m pytest tests/ -q
# -> 1762 passed, 4 skipped

# Architecture invariants (no hardcoded paths, no duplicate persona, sandbox default = "1")
.venv/bin/python -m pytest tests/test_architecture_invariants.py -v

If any of these fail, check the Troubleshooting page.


Upgrades

cd ~/Agent_Life_Space
git fetch
git checkout v1.X.Y                # or git pull origin main for bleeding edge
source .venv/bin/activate
pip install -e .                   # in case dependencies changed

# Check the CHANGELOG for migration notes
less CHANGELOG.md

# Reboot
sudo systemctl restart agent-life-space
sudo journalctl -u agent-life-space -f

The agent migrates legacy state automatically:

  • Vault v1 → v2 (single-file format) on first open. No operator action.
  • AGENT_DATA_DIR legacy detection on fresh checkouts. No operator action.
  • AGENT_LOG_LONG_RETENTION_DAYS_HOURS with deprecation warning. Update .env at your convenience.

If a release breaks compatibility, the CHANGELOG entry calls it out under Migration Notes explicitly.


Backups

The agent state lives in three places:

  1. <AGENT_DATA_DIR>/ — all SQLite databases (memory, finance, build, review, control plane, approvals, ...). Back up the whole directory.
  2. <AGENT_PROJECT_ROOT>/agent/vault/secrets.enc — the encrypted vault. Back up this single file.
  3. .env — your master key, API key, telegram token. Store in a real password manager (1Password, Bitwarden, KeePassXC, pass). Without .env you cannot decrypt the vault, so the backup of secrets.enc alone is useless.

Suggested backup command (cron daily):

0 3 * * * tar czf /backup/agent-$(date +\%Y\%m\%d).tar.gz \
  /home/youruser/Agent_Life_Space/.agent_runtime \
  /home/youruser/Agent_Life_Space/agent/vault/secrets.enc \
  && find /backup -name 'agent-*.tar.gz' -mtime +30 -delete

The find ... -mtime +30 -delete keeps a 30-day rolling window on disk; ship the daily tarball to off-site storage if you need longer retention.


Things deployment does NOT include

  • No Kubernetes manifests. This is a single-process self-host agent. If you really want to run it in K8s, write your own Deployment + PVC.
  • No Helm chart. Same reason.
  • No managed offering. There is no agentlifespace.com. The whole point is sovereignty.
  • No telemetry endpoint. The agent does not phone home. Never has.
  • No automatic upgrades. You pull, you read the CHANGELOG, you reboot. We will never push code to your box.

Clone this wiki locally