Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ and vice versa.
`db export|import`, `import-yaml` ([#108](https://github.com/CryptoJones/FlatlineRoundtable/issues/108)) — shipped in PR #123
- [x] Run from the store: `load_config` reads the DB, `fetch_keys` reads secrets,
YAML retired ([#109](https://github.com/CryptoJones/FlatlineRoundtable/issues/109)) — shipped in PR #124
- [ ] `db backup|restore` and the telesto restic job ([#110](https://github.com/CryptoJones/FlatlineRoundtable/issues/110)) — on feat/sqlite-store-backup
- [ ] Fleet rollout notes and the Python 3.11 floor ([#111](https://github.com/CryptoJones/FlatlineRoundtable/issues/111))
- [x] `db backup|restore` and the telesto restic job ([#110](https://github.com/CryptoJones/FlatlineRoundtable/issues/110)) — shipped in PR #125
- [ ] Fleet rollout notes and the Python 3.11 floor ([#111](https://github.com/CryptoJones/FlatlineRoundtable/issues/111)) — on docs/sqlite-store-fleet-rollout

### Epic: run the roundtable from inside Orca — local web UI, no fork ([#115](https://github.com/CryptoJones/FlatlineRoundtable/issues/115)) — DONE 2026-10-03

Expand Down
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,61 @@ roundtable secrets check --config /tmp/drill/roundtable.db
roundtable --config /tmp/drill/roundtable.db --list
```

## Fleet

Each host has its own store and its own DB key. makemake is the system of
record: its roster is exported and imported elsewhere, and each host sets its
own secrets under the same names (epic #105, decisions 2 and 6). Nothing is
shared live, so a host that is down or behind never blocks another.

On makemake, after any roster change worth sharing:

```sh
roundtable db export --out ~/roster.json # 0600, never holds a secret value
scp ~/roster.json pluto:roster.json
```

On the other host (pluto shown; its default `python3` is 3.9, with 3.11 beside it):

```sh
git -C ~/source/repos/FlatlineRoundtable pull --ff-only
python3.11 -m pip install --user cryptography
cd ~/source/repos/FlatlineRoundtable && PYTHON=python3.11 ./install.sh
roundtable db init # this host's own DB key, in its pass
roundtable db import ~/roster.json --replace
# Secrets: same names as makemake, this host's values. pluto has one shared
# OpenRouter key, so every openrouter/agent/* name gets that one value.
for n in $(roundtable lanes list | cut -f7 | sed -n 's/^key=//p' | grep '^openrouter/agent/' | sort -u); do
pass show openrouter/api-key | roundtable secrets set "$n" --stdin
done
pass show nvidia/api-key | roundtable secrets set nvidia/api-key --stdin
roundtable secrets check # every name: present
roundtable --list
```

`PYTHON=python3.11 ./install.sh` installs `roundtable` as a two-line wrapper
that execs that interpreter, so the host's default `python3` is left alone.

A host can keep its own value for a host-specific setting, such as a CLI path
or a wrapper. Set it with `lanes edit` after the import, with a `--reason`.
The next `db import --replace` from makemake puts makemake's value back, so
re-apply it after each import. On pluto that is GLaDOS, which reads its brief
through `~/.local/bin/grok-stdin`, because Linux caps one argument at 128 KB:

```sh
roundtable lanes edit GLaDOS --set command=/home/akclark/.local/bin/grok-stdin --set stdin=true \
--set 'args=["--no-auto-update","--disable-web-search","--no-subagents","--no-plan","--tools","","--max-turns","1","--output-format","plain","-m","grok-4.6"]' \
--reason "pluto: Linux caps one argv at 128KB; grok-stdin passes --prompt-file"
roundtable globals set concurrency=16 # pluto's own capacity
```

A CLI lane also needs its CLI signed in on that host (`grok login --device-code`,
`codex login`, `claude`); the store holds only API-key secrets.

After the move, a host's old YAML files are archived into `backups/` beside the
store (so `scripts/backup-telesto.sh` ships them) and removed, so nobody edits a
file that is no longer read.

## Cost

**`harness` decides cost, not `model`.**
Expand Down
30 changes: 24 additions & 6 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,20 @@ TARGET="${HOME}/.claude/skills/flatline-roundtable"
BIN_DIR="${HOME}/.local/bin"
BIN_LINK="${BIN_DIR}/roundtable"
STORE="${XDG_DATA_HOME:-${HOME}/.local/share}/flatline-roundtable/roundtable.db"
# PYTHON=python3.11 ./install.sh for a host whose default python3 is too old
# (pluto: 3.9 by default, 3.11 beside it). roundtable is then installed as a
# two-line wrapper that execs that interpreter, rather than a symlink that
# would run under the old default.
PYTHON="${PYTHON:-python3}"
WRAPPER_MARK="# flatline-roundtable install.sh wrapper"
OLD_YAML="${HOME}/.config/flatline-roundtable/FlatlineRoundtable.yaml"

die() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
log() { printf '%s\n' "$*"; }

uninstall() {
for link in "${TARGET}" "${BIN_LINK}"; do
if [[ -L "${link}" ]]; then
if [[ -L "${link}" ]] || { [[ -f "${link}" ]] && grep -qF "${WRAPPER_MARK}" "${link}"; }; then
rm "${link}"; log "Removed ${link}"
elif [[ -e "${link}" ]]; then
# Never delete something we did not create.
Expand All @@ -35,10 +41,11 @@ uninstall() {
[[ -x "${SCRIPT_DIR}/roundtable" ]] || die "Missing or non-executable ${SCRIPT_DIR}/roundtable."
# The floor is 3.11 (the store code and CI assume it). pluto's default python3
# is 3.9 with 3.11 installed beside it, so name the fix rather than just failing.
python3 -c 'import sys; sys.exit(0 if sys.version_info >= (3, 11) else 1)' 2>/dev/null || \
die "Python 3.11+ required; python3 is $(python3 -V 2>&1). Put python3.11 first on PATH."
python3 -c 'import cryptography' 2>/dev/null || \
die "\`cryptography\` not installed — python3 -m pip install --user cryptography (the store needs it)"
command -v "${PYTHON}" >/dev/null || die "${PYTHON} not found"
"${PYTHON}" -c 'import sys; sys.exit(0 if sys.version_info >= (3, 11) else 1)' 2>/dev/null || \
die "Python 3.11+ required; ${PYTHON} is $("${PYTHON}" -V 2>&1). Rerun with PYTHON=python3.11 ./install.sh"
"${PYTHON}" -c 'import cryptography' 2>/dev/null || \
die "\`cryptography\` not installed — ${PYTHON} -m pip install --user cryptography (the store needs it)"

# `pass` holds the store's DB key, which unlocks every lane secret. This is a
# warning rather than a hard failure because a roster of only `cli` / `acp`
Expand Down Expand Up @@ -87,7 +94,16 @@ check_pass

mkdir -p "${HOME}/.claude/skills" "${BIN_DIR}"
ln -sfn "${SKILL_SRC}" "${TARGET}"; log "Skill -> ${TARGET}"
ln -sfn "${SCRIPT_DIR}/roundtable" "${BIN_LINK}"; log "Binary -> ${BIN_LINK}"
if [[ "${PYTHON}" == python3 ]]; then
ln -sfn "${SCRIPT_DIR}/roundtable" "${BIN_LINK}"; log "Binary -> ${BIN_LINK}"
else
[[ -e "${BIN_LINK}" && ! -L "${BIN_LINK}" ]] && ! grep -qF "${WRAPPER_MARK}" "${BIN_LINK}" && \
die "${BIN_LINK} exists and is not ours — refusing to overwrite it."
rm -f "${BIN_LINK}"
printf '#!/bin/sh\n%s\nexec '"'"'%s'"'"' '"'"'%s'"'"' "$@"\n' "${WRAPPER_MARK}" \
"$(command -v "${PYTHON}")" "${SCRIPT_DIR}/roundtable" > "${BIN_LINK}"
chmod 755 "${BIN_LINK}"; log "Binary -> ${BIN_LINK} (wrapper: $(command -v "${PYTHON}"))"
fi

if [[ -f "${STORE}" ]]; then
log "Store found at ${STORE} — left untouched."
Expand All @@ -98,6 +114,8 @@ else
if [[ -f "${OLD_YAML}" ]]; then
log " roundtable import-yaml ${OLD_YAML} --dry-run # needs PyYAML, once"
log " roundtable import-yaml ${OLD_YAML} --pull-secrets"
log " (or, on a host other than makemake: db import makemake's export —"
log " see the README's Fleet section)"
else
log " roundtable db import ${SCRIPT_DIR}/examples/roster.example.json"
fi
Expand Down
Loading