clank-together/deploy/install.sh
Clank Together 836610da8f Clank Together: coordinator, sandboxed verifier and agent client
Coordinator (accounts with approval, API tokens, tasks with leases, sandboxed MSVC verification against
the original exe, peer review by three agents for non-identical code, human review, serial merge queue),
the `td` contributor CLI with an MCP server for agents, pinned Ghidra integration, deployment (Caddy,
systemd, nftables, Forgejo) and the wowdecomp 15890 migration tools.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RNwtXPYqFu3oKaaXwYwNWW
2026-10-18 01:29:16 +02:00

95 lines
3.6 KiB
Bash

#!/bin/sh
# Installs or updates the Clank Together coordinator on Debian 13. Run as root
# from the unpacked source directory: sh deploy/install.sh <public-hostname>
set -eu
HOST="${1:?usage: install.sh <public-hostname>}"
NODE_VERSION=v22.21.0
SRC=$(cd "$(dirname "$0")/.." && pwd)
APP=/opt/togetherdecomp
DATA=/var/lib/togetherdecomp
echo "== packages"
export DEBIAN_FRONTEND=noninteractive
apt-get update -q
apt-get install -y -q git curl ca-certificates nftables caddy docker.io xz-utils >/dev/null
echo "== node $NODE_VERSION"
if [ "$(/opt/node/bin/node --version 2>/dev/null || true)" != "$NODE_VERSION" ]; then
tmp=$(mktemp -d)
curl -fsSL "https://nodejs.org/dist/$NODE_VERSION/node-$NODE_VERSION-linux-x64.tar.xz" -o "$tmp/node.tar.xz"
curl -fsSL "https://nodejs.org/dist/$NODE_VERSION/SHASUMS256.txt" -o "$tmp/SHASUMS256.txt"
(cd "$tmp" && grep " node-$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sed 's/node-.*/node.tar.xz/' | sha256sum -c -)
rm -rf /opt/node && mkdir -p /opt/node
tar -xJf "$tmp/node.tar.xz" -C /opt/node --strip-components=1
rm -rf "$tmp"
fi
echo "== docker sandbox settings"
mkdir -p /etc/docker
cat > /etc/docker/daemon.json <<'EOF'
{
"userns-remap": "default",
"no-new-privileges": true,
"icc": false,
"live-restore": true,
"log-driver": "local"
}
EOF
systemctl restart docker
echo "== service user and code"
id togetherdecomp >/dev/null 2>&1 || useradd --system --home-dir "$DATA" --shell /usr/sbin/nologin togetherdecomp
usermod -aG docker togetherdecomp
rm -rf "$APP.new" && mkdir -p "$APP.new"
cp -a "$SRC/src" "$SRC/template" "$SRC/ghidra_scripts" "$SRC/toolchain" "$SRC/package.json" "$APP.new/"
chown -R root:root "$APP.new" && chmod -R go-w "$APP.new"
rm -rf "$APP.old"; [ -d "$APP" ] && mv "$APP" "$APP.old"; mv "$APP.new" "$APP"
mkdir -p "$DATA" && chown togetherdecomp:togetherdecomp "$DATA" && chmod 700 "$DATA"
NODE="/opt/node/bin/node --disable-warning=ExperimentalWarning"
if [ ! -f "$DATA/server.json" ]; then
runuser -u togetherdecomp -- $NODE "$APP/src/server/cli.js" init --data "$DATA"
fi
# Production settings, keeping anything else already configured.
UIDGID="$(id -u togetherdecomp):$(id -g togetherdecomp)"
runuser -u togetherdecomp -- $NODE -e '
const fs = require("fs"); const [file, host, user] = process.argv.slice(1);
const c = JSON.parse(fs.readFileSync(file, "utf8"));
c.listen = { host: "127.0.0.1", port: 8080 };
c.publicUrl = "https://" + host;
c.trustProxy = true;
c.allowUnsandboxedRunners = false;
for (const r of Object.values(c.runners || {})) if (r.type === "docker") r.user = user;
fs.writeFileSync(file, JSON.stringify(c, null, 2) + "\n");
' "$DATA/server.json" "$HOST" "$UIDGID"
echo "== systemd"
install -m 0644 "$SRC/deploy/togetherdecomp.service" /etc/systemd/system/togetherdecomp.service
install -m 0644 "$SRC/deploy/nftables-td.nft" /etc/nftables-td.nft
cat > /etc/systemd/system/td-firewall.service <<'EOF'
[Unit]
Description=Clank Together host firewall
After=network-pre.target docker.service
Wants=network-pre.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/sbin/nft -f /etc/nftables-td.nft
ExecStop=/usr/sbin/nft delete table inet td_filter
[Install]
WantedBy=multi-user.target
EOF
sed "s/saty.dev/$HOST/" "$SRC/deploy/Caddyfile" > /etc/caddy/Caddyfile
caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile >/dev/null
systemctl daemon-reload
systemctl enable --now td-firewall.service
systemctl restart td-firewall.service
systemctl enable togetherdecomp.service caddy.service
systemctl restart togetherdecomp.service caddy.service
sleep 2
systemctl --no-pager --lines=5 status togetherdecomp.service | head -12
echo "== done: https://$HOST"