Skip to content

Horizon AIOS — Server / Headless Deployment

The server deployment runs the AIOS on a remote or always-on machine without a desktop session. Brains run as OS user accounts, scheduled via cron / systemd (Linux) or Task Scheduler (Windows). Access is via SSH. The audit log is the primary operational interface — there is no UI, no sounds, no statusline.

Status: Not yet end-to-end verified. See tested_configurations.md.


What "Server" Means Here

  • No interactive desktop session. The harness (Claude Code CLI) is invoked over SSH or by a daemon.
  • Brains run unattended as scheduled tasks or services under their own OS user accounts.
  • No sound device — hook commands that play audio will fail or silently no-op. Disable them or suppress errors.
  • No statusline — harness UI features (context bar, git display) do not render in a non-interactive session.
  • The audit log ($HORIZON_LOGS/horizon_aios_monitor/) is the operational view: tail it to observe what agents are doing.

Prerequisites

  • Linux (Ubuntu 22.04+ recommended) or Windows Server.
  • Git, Python 3.8+, your AI harness CLI installed.
  • SSH server configured and accessible.
  • Administrator / sudo access for brain provisioning.
  • Optional: systemd (Linux) or Task Scheduler (Windows) for daemon-mode brains.

Setup

Same bootstrap as desktop — run over SSH. Onboarding is the single secure entry point: it always creates the AIOS OS groups and applies the ACL model (no separate hardening step). Run as root / admin so those steps succeed.

git clone <your-aios-repo-url> /opt/aios   # or your preferred path

# Linux / macOS
sudo bash /opt/aios/horizon_system/sbin/bootstrap.sh

# Windows Server (PowerShell, as admin)
& C:\aios\horizon_system\sbin\bootstrap.ps1

Server profile — enroll no humans

Onboarding asks whether the machine is primarily a server or an active-use workstation, and creates the AIOS-managed OS group horizon_humans ("Horizon.AIOS Actual Humans") on every install. On a headless server, choose the server profile: the group stays empty and only the owner, SYSTEM, and Administrators can write to the AIOS tree. On Windows, onboarding breaks inheritance at $HORIZON_ROOT and re-grants only those principals, so broad inherited write grants (e.g. Authenticated Users) are removed — an empty horizon_humans grants nobody, reducing a bare server to admin-only write.

For unattended / IaC runs, declare the profile explicitly (interactive prompts would hang):

bash bootstrap.sh --profile server        # no humans enrolled
bash bootstrap.sh --yes                   # accept detected defaults

If specific humans do operate this server (see "Multi-Operator Server Pattern" below), use the workstation profile and enroll them by name or SID (cloud / Azure AD accounts are SIDs), or enroll one later without re-onboarding:

bash bootstrap.sh --profile workstation --humans operator-1 --humans operator-2
bash bootstrap.sh --add-human operator-3

Members of horizon_humans get Full control of the AIOS tree and Read/Write on brains/ (near-admins who maintain the brains/apps; brain-to-brain isolation is preserved separately by ownership + each brain's private group), and are isolated from each other on projects/ (traverse-only parent, owner-only projects/<user> children). The chosen profile and enrolled humans are recorded in the gitignored marker $HORIZON_ROOT/.horizon_aios_deployment.json.

Bootstrap already registers the full HORIZON_* set for all users and cron jobs — no manual step needed on a fresh install: bootstrap.sh SECTION 7 writes /etc/profile.d/horizon_aios.sh (login-shell scope) and additionally runs horizon_aios_env_scope.py, which upserts /etc/environment (PAM-read: login, su -, sshd, and PAM-aware cron — this is what actually reaches this project's own cron-scheduled maintenance/monitor jobs) and a systemd system.conf.d drop-in. Root is required for both; a non-root bootstrap degrades to a user-scope fallback and warns loudly at install time (see file_structure_invariants.md §1.1) — if that happened, either re-run bootstrap as root, or hand-add the vars to the system profile yourself:

# Linux, manual fallback only (normally unnecessary — see above):
export HORIZON_ROOT=/opt/aios
export HORIZON_SYSTEM=$HORIZON_ROOT/horizon_system
export HORIZON_BIN=$HORIZON_SYSTEM/bin
export HORIZON_ETC=$HORIZON_SYSTEM/ai_os_etc
export HORIZON_DOCS=$HORIZON_SYSTEM/documentation
export HORIZON_SOUNDS=$HORIZON_SYSTEM/sounds
export HORIZON_LOGS=$HORIZON_SYSTEM/logs

Note: System-scope registration is a deliberate fit for a server pinned to a single AIOS (so the vars are available to all users and cron jobs). On a desktop or any machine that uses the AIOS switcher, prefer sourcing ~/.horizon/active_env.sh instead (run horizon_aios_switch.py init once); aios switch <name> then repoints every shell without editing the system profile. See system/aios_switching.md.

Disabling sounds on server

In ~/.claude/settings.json, set hook commands to no-ops or remove the Stop, PermissionRequest, and StopFailure sound hooks entirely. The log hook (writing to $HORIZON_LOGS) should remain active.


Provisioning Brains

Same as desktop — run as root/admin:

sudo python $HORIZON_SYSTEM/sbin/horizon_aios_create_brain.py brain-name

This creates the OS user account, directory, and permissions. On server, brains run as daemon processes rather than interactive sessions.


Running Brains as Daemons

Linux — systemd service unit

Create /etc/systemd/system/brain-BRAINNAME.service:

[Unit]
Description=Horizon AIOS Brain — BRAINNAME
After=network.target

[Service]
Type=simple
User=BRAINNAME
Group=BRAINNAME
WorkingDirectory=/opt/aios/brains/BRAINNAME
Environment=HORIZON_ROOT=/opt/aios
Environment=HORIZON_SYSTEM=/opt/aios/horizon_system
Environment=ANTHROPIC_API_KEY=<inject from OS credential store or wrapper script>
ExecStart=/usr/local/bin/claude --dangerously-skip-permissions
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target

Enable and start:

systemctl daemon-reload
systemctl enable brain-BRAINNAME
systemctl start brain-BRAINNAME

Windows Server — Task Scheduler (sketch)

Create a scheduled task that runs as the brain's OS user account: - Action: claude.exe --dangerously-skip-permissions - Run as: brain OS user account - Working directory: C:\aios\brains\BRAINNAME - Trigger: At startup, or on schedule

Use schtasks /create or the Task Scheduler GUI. Store the brain's API key in Windows Credential Manager and inject it via a wrapper script.


Log Monitoring as the Operational Interface

The audit log is the primary way to observe what a brain is doing on a server:

# Watch live monitor events (file access, writes, creates)
tail -f $HORIZON_LOGS/horizon_aios_monitor/*.jsonl

# Watch hook events
tail -f $HORIZON_LOGS/hooks/*.log

# Watch brain-specific output
tail -f $HORIZON_LOGS/brains/BRAINNAME/*.log

Pipe to jq for structured queries:

tail -f $HORIZON_LOGS/horizon_aios_monitor/*.jsonl | jq 'select(.type == "created")'

Ensure horizon_aios_monitor.py is running as the administrative context (not as a brain user):

python $HORIZON_SYSTEM/sbin/horizon_aios_monitor.py &

Multi-Operator Server Pattern

The AIOS model assumes one human operator per AIOS owner account. The owner's ~/.claude/settings.json (global settings, hooks, statusLine) is per OS home directory. If two humans — for example, a primary admin and a co-worker — need to use the same AIOS installation on a shared server, follow this pattern:

Do not share an OS account. Each human operator needs their own OS user account on the server.

Each person runs bootstrap under their own account:

# As operator-1 (with sudo)
sudo bash /opt/aios/horizon_system/sbin/bootstrap.sh

# As operator-2 (with sudo, in a separate SSH session)
sudo bash /opt/aios/horizon_system/sbin/bootstrap.sh

Bootstrap installs each person's personal ~/.claude/settings.json from the shared AIOS template. The devroot .claude/settings.json (permissions only, committed to the repo) is shared across all operators — that is intentional. Per-operator state (hooks, statusLine, global settings) lives in each account's own ~/.claude/.

Each operator who needs day-to-day write access to the tree must be enrolled in horizon_humans — pass them at onboarding (--profile workstation --humans <name|sid> ...) or add them later with bootstrap --add-human <name|sid>. Enrolled humans get Full control of the AIOS tree and Read/Write on brains/ (near-admins who maintain the brains/apps), and are instead isolated from each other per-user on projects/.

Brains are not the right mechanism for human co-workers. Brains are isolated AI agent accounts. Human operators need their own OS accounts with their own Claude Code sessions, their own SSH keys, and their own shell environments.

$HORIZON_ROOT repository — two valid models:

  • Shared storage: $HORIZON_ROOT is mounted at the same path for all operators. Each operator's ~/.claude/ is their own (different home directory), so settings stay separate. All operators read from the same committed .claude/settings.json for devroot-scoped permissions.
  • Separate clones: Each operator clones the AIOS repo to their own home directory or a path they control. The devroot .claude/settings.json is replicated from the repo in each clone. Updates require each operator to pull independently (or the sync schedule handles it).

Either model works. Shared storage is simpler for keeping all operators on the same AIOS version; separate clones give each operator independent upgrade control.


Server vs. Desktop

Concern Server Desktop
Session type Headless / SSH Interactive desktop app
Brains Daemons (systemd / Task Scheduler) OS users, launched interactively
Sounds Not available — disable hooks Native, first-class
Statusline Not rendered Renders in harness UI
Audit log Primary operational view Secondary (UI is primary)
Setup complexity Bootstrap + system profile + daemon config Bootstrap only
Always-on Natural (server stays up) Requires leaving machine on