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.shinstead (runhorizon_aios_switch.py initonce);aios switch <name>then repoints every shell without editing the system profile. Seesystem/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_ROOTis 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.jsonfor 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.jsonis 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 |