Files
mrcharles c810942239 Open Terminal fleet for all accounts + OpenWebUI UAT pass (#610)
open-terminal 0.11.34 bare-metal (human-authorized host install) with a
systemd template unit: reachableceo on tailscale-only :30000 (cwd
~/projects) and all 8 other accounts on :30001-30008, each with its own
key kept out of ps in per-user TOMLs, all users added to the docker group.
Keys live in ~/.creds + /etc/ukrrs/open-terminal, never in this repo.

UAT: throwaway OpenWebUI v0.11.1 (matched to the human's Cloudron prod)
driven purely over its HTTP API against the beta gateway lane - connection
verify/config PASS, direct exec as uid 1001 PASS, and the chat round trip
PASS: the model emitted run_command, we executed it through OpenWebUI's
terminal proxy (server holds the key), and the final answer named 9/9 real
project directories; earlier negative runs prove the test catches
hallucinated output. Ops note OPEN-TERMINAL.md documents production wiring
(prod openwebui container v0.3.10 is too old and needs an upgrade first).

Also tonight: agent-stack relaunchers removed (6-, backups kept) so no
screen/crush sessions auto-start on reboot; 22:00 night-profile flip
observed live; docs synced for the earlier teardown.

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
[#610]
2026-08-31 22:50:53 -05:00

210 lines
8.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# setup-rceo-stack.sh — per-account PMO/Work stack spawner for the
# reachableceo stack on ULTIX-STREAMING. ROOT-run, idempotent, PROTOCOL v2
# (headless dispatch, founder gate, doorbell-only stuff).
#
# Coordinate files are generated ONCE and never overwritten afterwards
# (they are founder/agent-owned); a file still carrying __TEMPLATE__
# placeholders is debris from a failed run and gets regenerated.
#
# Usage: setup-rceo-stack.sh <USER> <FOUNDER_ACCT> <SRC_CRUSH> [PREFIX]
# PREFIX defaults to USER. Screens: <PREFIX>-PMO / <PREFIX>-Work.
set -euo pipefail
USR="${1:?usage: setup-rceo-stack.sh <USER> <FOUNDER_ACCT> <SRC_CRUSH> [PREFIX]}"
FOUNDER_ACCT="${2:?founder account for screen acl}"
SRC_CRUSH="${3:?source dir with crush.json/providers.json}"
PREFIX="${4:-$USR}"
CWD="/home/${USR}"
HOME_U="$CWD"
PMO_SCREEN="${PREFIX}-PMO"
WORKSCREEN="${PREFIX}-Work"
id "$USR" >/dev/null 2>&1 || { echo "no such user: $USR"; exit 1; }
if ! id -nG "$USR" | tr ' ' '\n' | grep -qx docker; then
usermod -aG docker "$USR"
echo "groups: added docker"
fi
rc="$HOME_U/.screenrc"
[[ -f "$rc" ]] && cp -a "$rc" "${rc}.bak-$(date +%Y%m%d-%H%M%S)"
touch "$rc"
grep -q '^multiuser on' "$rc" || printf 'multiuser on\n' >> "$rc"
grep -q "^acladd ${FOUNDER_ACCT}" "$rc" || printf 'acladd %s\n' "$FOUNDER_ACCT" >> "$rc"
grep -q '^defscrollback 10000' "$rc" || printf 'defscrollback 10000\n' >> "$rc"
chown "${USR}:${USR}" "$rc"
chmod 644 "$rc"
echo "screenrc: merged, never overwritten (backup kept if pre-existing)"
DST_CRUSH="$HOME_U/.local/share/crush"
mkdir -p "$DST_CRUSH"
for f in crush.json providers.json; do
if [[ ! -f "$DST_CRUSH/$f" && -f "$SRC_CRUSH/$f" ]]; then
cp "$SRC_CRUSH/$f" "$DST_CRUSH/$f"
chown "${USR}:${USR}" "$DST_CRUSH/$f"
chmod 600 "$DST_CRUSH/$f"
echo "crush: copied $f"
fi
done
CC="$HOME_U/.coordinate"
mkdir -p "$CC"/{inbox-work,inbox-pmo,archive,prompts,logs}
fresh() {
[[ ! -f "$1" ]] || grep -qE '__(USR|PREFIX|WORKSCREEN|FOUNDER|DATE)__' "$1"
}
sub() {
sed -i -e "s/__USR__/${USR}/g" -e "s/__PREFIX__/${PREFIX}/g" \
-e "s/__WORKSCREEN__/${WORKSCREEN}/g" \
-e "s/__FOUNDER__/${FOUNDER_ACCT}/g" \
-e "s/__DATE__/$(date +%F)/g" "$1"
}
if fresh "$CC/PROTOCOL.md"; then
cat > "$CC/PROTOCOL.md" <<'EOF'
# __USR__ .coordinate — PMO/Work scratch channel
Local scratch under $HOME: not git-tracked, not durable. Durable artifacts
(tickets/code/docs/decisions) go to the systems of record the founder
attaches to this stack; until then they stay in ~/.coordinate.
Layout:
- inbox-work/ PMO writes TASK-<yyyymmdd>-<HHMM>-<slug>.md (CANCEL- to drop).
- inbox-pmo/ Work writes REPORT-*.md, QUESTION-*.md, BLOCKED-*.md.
- archive/ handled items; prune >30 days.
- BOARD.md roster + standing state. log.md append-only journal
(date -Is | WHO | what). prompts/ intros. logs/ worker output.
TASK fields: Objective / Context / Constraints / Deliverables / Priority.
Turn-start checklist (both agents, every turn): read BOARD.md, scan your
inbox, tail log.md.
Rules:
- PMO plans, prioritizes, writes TASKs, reports to the founder on drop-in.
- FOUNDER GATE: PMO never dispatches without explicit founder approval of
that TASK. Draft -> present -> founder says go -> dispatch. Every time.
- Work executes in priority order; one turn = do work, file REPORT, archive
the TASK. Never self-assign scope (QUESTION instead).
- Empty inbox when pinged: say so, stop. No watchers/polling loops.
- No sudo for agents; root needs go BLOCKED -> PMO -> founder.
- Never kill or attach to the other agent's screen.
Dispatch (headless; never screen-stuff task text; ping success-gated):
screen -dmS work-turn bash -c 'crush run --quiet --session <WORK_TURN_SID> \
"$(cat ~/.coordinate/inbox-work/TASK-*.md)" > ~/.coordinate/logs/work-turn.log 2>&1 \
&& screen -S __PREFIX__-PMO -X stuff "Work turn done OK - read inbox-pmo.\r" \
|| screen -S __PREFIX__-PMO -X stuff "Work turn FAILED - read log.\r"'
First dispatch (or after a kill orphans the lineage): omit --session, then
capture id via `crush session list --json` into BOARD.md.
screen -X stuff is DOORBELL-ONLY (short fixed lines to an idle screen): a
stuffed line can answer an open dialog; a dead pty eats it silently.
Founder contract: founder observes Work read-only; founder writes in PMO;
Work turns come only from PMO dispatch.
EOF
sub "$CC/PROTOCOL.md"
fi
if fresh "$CC/BOARD.md"; then
cat > "$CC/BOARD.md" <<'EOF'
# Board — __USR__
- PMO (screen __PREFIX__-PMO): manager. Founder liaison, planning, TASKs.
- Work (screen __WORKSCREEN__): IC. Executes PMO headless dispatches; files
REPORT/QUESTION/BLOCKED in inbox-pmo.
- Founder (__FOUNDER__): authority + sudo. Observes Work read-only; writes
only in PMO; approves every dispatch (founder gate).
Current focus: (PMO maintains this line)
Session ids: PMO interactive=(), Work interactive=(), Work turn lineage=().
EOF
sub "$CC/BOARD.md"
fi
if [[ ! -f "$CC/log.md" ]]; then
printf '%s | founder-channel | .coordinate created\n' "$(date -Is)" > "$CC/log.md"
fi
if fresh "$CC/prompts/INTRO-PMO.md"; then
cat > "$CC/prompts/INTRO-PMO.md" <<'EOF'
INTRODUCTION (from founder, __DATE__)
You are __USR__-PMO — manager of this stack. Direct report:
__USR__-Work (screen __WORKSCREEN__), a crush instance as the same user.
The founder (__FOUNDER__) interacts with you day-to-day and observes Work
READ-ONLY; all founder writes happen here with you.
Read ~/.coordinate/PROTOCOL.md and follow it exactly: TASK files to
inbox-work/, consume REPORT/QUESTION/BLOCKED from inbox-pmo/, maintain
BOARD.md + log.md. Dispatch = headless crush-run wrappers (PROTOCOL);
never dispatch without the FOUNDER GATE. Monitor headless turns by reading
logs/, never by attaching.
CAPABILITIES: no sudo (root needs go to the founder). Headless workers:
env HOME=/home/__USR__ TERM=xterm-256color screen -dmS <slug> bash -c \
'crush run --quiet "$(cat /path/to/prompt.md)"' \
> /home/__USR__/.coordinate/logs/<slug>.log 2>&1
Use crush sub-agent tooling instead of long sequential exploration.
YOUR MISSION: <<<FOUNDER: one paragraph — what this stack is for>>>
YOUR SYSTEMS OF RECORD: <<<FOUNDER: where durable work lands for this stack>>>
TOKEN DISCIPLINE: terse (<=5 line replies); cite file paths; batch reads;
never paste large files; findings go to ~/.coordinate files.
NOW: read PROTOCOL.md, append intro line to log.md, set BOARD.md
current-focus, greet the founder in 5 lines max. Then wait.
EOF
sub "$CC/prompts/INTRO-PMO.md"
fi
if fresh "$CC/prompts/INTRO-WORK.md"; then
cat > "$CC/prompts/INTRO-WORK.md" <<'EOF'
INTRODUCTION (from founder, __DATE__)
You are __USR__-Work (screen __WORKSCREEN__) — executor of this stack,
running ~99% headless. Humans rarely talk to you directly. __USR__-PMO is
your manager: turns arrive ONLY as headless crush-run TASK files from
PMO (~/.coordinate/inbox-work/). Founder (__FOUNDER__) is final authority,
read-only observer.
YOUR LOOP — every turn, before anything else:
1. PROTOCOL checklist: BOARD.md, inbox-work/ (P1 first; CANCEL drops),
log.md tail.
2. Execute TASKs; obey Constraints; deliver Deliverables. Parallelizable
subtasks may go to headless workers (logs/<slug>.log; verify before
reporting done).
3. File REPORT/QUESTION/BLOCKED in inbox-pmo/, archive the TASK, append
log line. Chat replies are one-liners; substance goes in files.
RULES: never self-assign scope (QUESTION instead). Empty inbox when pinged
= one line, stop. No watchers/polling loops. No sudo — root needs go
BLOCKED to PMO. Never kill the other session's screens. If PROTOCOL.md is
missing, say so and wait — do not recreate.
NOW: read PROTOCOL.md, append your online line to log.md, post
inbox-pmo/REPORT-ready.md ("__USR__-Work online, awaiting first TASK").
One chat line only. Then wait.
EOF
sub "$CC/prompts/INTRO-WORK.md"
fi
chown -R "${USR}:${USR}" "$CC" "$DST_CRUSH"
spawn() {
local s="$1"
if compgen -G "/run/screen/S-${USR}/.*.${s}" > /dev/null; then
echo "already running: $s"
else
runuser -u "$USR" -- env HOME="$HOME_U" TERM=xterm-256color \
screen -dmS "$s" bash -c "cd ${CWD} && exec crush --yolo"
echo "spawned: $s"
fi
}
spawn "$PMO_SCREEN"
spawn "$WORKSCREEN"
echo "OK: stack for ${USR} (cwd=${CWD}, pmo=${PMO_SCREEN}, work=${WORKSCREEN})"