eleven / ocho Public

Clone
git clone https://amadan.net/eleven/ocho

Plain git — no account needed to clone.

Download

Download this file

1#!/usr/bin/env bash
2# Build whatever branch is currently checked out and deploy it to a single
3# instance on the self-hosted router box (versioned, side-by-side deploys —
4# see docs/instance-router.md and CLAUDE.local.md "Deploying"). Health-checked:
5# `lchat deploy` itself verifies /api/version's build id after restarting and
6# auto-rolls back on a mismatch, so a bad build never stays live. Also ensures
7# the instance has its default bot sidecar (Claude/Eleven) running —
8# deterministic-only (no ANTHROPIC_API_KEY) unless you pass one in — so Eleven
9# always welcomes new members and answers slash commands out of the box.
10#
11# Usage: ./deploy/go-router.sh [host]
12# ./deploy/go-router.sh # host defaults to team.elevenmessenger.com
13# ./deploy/go-router.sh test.example.com
14# SKIP_BOT=1 ./deploy/go-router.sh # skip the bot-ensure step
15# ANTHROPIC_API_KEY=sk-ant-... ./deploy/go-router.sh # AI-enabled instead
16#
17# Never deploys the whole fleet (`--all`) — always one explicit host, so this
18# script can never affect an instance other than the one you asked for.
19set -euo pipefail
20
21HOST="${1:-team.elevenmessenger.com}"
22# No user in the default: operator SSH rides the WireGuard tunnel and the
23# username comes from ~/.ssh/config (root SSH is bootstrap-only — see
24# docs/instance-router.md § Hardening). Privileged remote commands go through
25# `sudo -n` (the committed allowlist, deploy/sudoers-operators); root can
26# sudo too, so a bootstrap-era root session still works unchanged.
27BOX="${LCHAT_BOX:-89.167.122.227}"
28SKIP_BOT="${SKIP_BOT:-}"
29ANTHROPIC_API_KEY="${ANTHROPIC_API_KEY:-}"
30
31REPO="$(cd "$(dirname "$0")/.." && pwd)"
32cd "$REPO"
33
34BRANCH="$(git rev-parse --abbrev-ref HEAD)"
35VER="$(git rev-parse --short HEAD)"
36if ! git diff --quiet || ! git diff --cached --quiet; then
37 VER="${VER}-dirty"
38 echo "Warning: working tree has uncommitted changes — building anyway (version tagged '$VER')." >&2
39fi
40
41OUT="$(mktemp -t lchat-linux-amd64.XXXXXX)"
42trap 'rm -f "$OUT"' EXIT
43
44echo "Building branch '$BRANCH' ($VER) for linux/amd64..."
45OUT="$OUT" ./deploy/build-linux.sh "$VER"
46
47# /opt/lchat/incoming is a DIRECTORY now (the auto-updater downloads into it
48# too — see updater.go); upload as a file inside it, staged then removed.
49echo "Uploading to $BOX:/opt/lchat/incoming/lchat-upload..."
50# No sudo: the dir exists group-writable (lchat-operators) after operator
51# setup, and mkdir -p on an existing dir succeeds without write access.
52ssh "$BOX" 'mkdir -p /opt/lchat/incoming'
53scp -q "$OUT" "$BOX:/opt/lchat/incoming/lchat-upload"
54
55echo "Staging on the box..."
56STAGE_OUT="$(ssh "$BOX" 'sudo -n /opt/lchat/lchat stage -file /opt/lchat/incoming/lchat-upload && rm -f /opt/lchat/incoming/lchat-upload')"
57echo " $STAGE_OUT"
58STAGED_VER="$(echo "$STAGE_OUT" | awk '{print $2}')"
59if [ "$STAGED_VER" != "$VER" ]; then
60 echo "go-router: staged version '$STAGED_VER' doesn't match built version '$VER' — aborting" >&2
61 exit 1
62fi
63
64echo "Deploying $STAGED_VER to $HOST (health-checked; auto-rolls back on a build mismatch)..."
65ssh "$BOX" "sudo -n /opt/lchat/lchat deploy '$HOST' --version '$STAGED_VER'"
66
67if [ -n "$SKIP_BOT" ]; then
68 echo "SKIP_BOT set — leaving the bot sidecar alone."
69elif ssh "$BOX" "sudo -n test -f /data/${HOST}-bot.env"; then
70 echo "Bot sidecar already provisioned for $HOST — leaving it alone (add-bot mints fresh, one-time invites, so re-running it would orphan the existing Claude/Eleven identities)."
71else
72 echo "No bot sidecar yet for $HOST — adding one (deterministic-only unless \$ANTHROPIC_API_KEY is set)..."
73 # Work around a known bug in the box's current control binary: add-bot's
74 # MkdirAll leaves the session dir root-owned, but the bot unit runs as the
75 # unprivileged `lchat` user (post-hardening) and can't write into it — every
76 # freshly-added bot crash-loops with EACCES, burning its one-time Claude
77 # invite before anyone can intervene (hit this firsthand adding Eleven here).
78 # Fixed upstream in 8e20692, not yet deployed to /opt/lchat/lchat's control
79 # binary on the box. Pre-creating the dir with the right owner is a no-op
80 # once that binary is updated, so this is safe to leave in permanently.
81 ssh "$BOX" "sudo -n mkdir -p /data/${HOST}-bot && sudo -n chown lchat:lchat /data/${HOST}-bot && sudo -n chmod 750 /data/${HOST}-bot"
82 if [ -n "$ANTHROPIC_API_KEY" ]; then
83 ssh "$BOX" "sudo -n ANTHROPIC_API_KEY='$ANTHROPIC_API_KEY' /opt/lchat/lchat add-bot '$HOST'"
84 else
85 ssh "$BOX" "sudo -n /opt/lchat/lchat add-bot '$HOST'"
86 fi
87fi
88
89echo
90echo "✅ $HOST is now running $STAGED_VER (branch '$BRANCH')"
91echo " Recover the admin link anytime: ssh $BOX sudo -n /opt/lchat/lchat admin-link $HOST"
92echo " Step one instance back: ssh $BOX sudo -n /opt/lchat/lchat rollback $HOST"
93