anvilsign in

collin/anvil

1#!/usr/bin/env bash
2# (Re)start the anvil container on hagrid from an ALREADY-LOADED image.
3#
4# Build and ship the image first with deploy/build.sh on a capable machine
5# (the VPS can't compile it). This script only runs docker — no build — so it's
6# safe on the low-RAM box. Standalone: needs only docker + the loaded image.
7set -euo pipefail
8
9IMAGE="${ANVIL_IMAGE:-anvil:latest}"
10NETWORK="${ANVIL_NETWORK:-hagrid}"
11SSH_PORT="${ANVIL_SSH_PORT:-22}"
12# Publish SSH on the droplet's DEFAULT public IPv4 only. The reserved IP
13# (137.184.249.48) is reached via the anchor IP 10.15.0.6, where the host's
14# own sshd listens — binding a specific IP here keeps the two off each other.
15SSH_BIND_IP="${ANVIL_SSH_BIND_IP:-165.232.162.167}"
16# NO DOCKER SOCKET. anvil does not execute CI any more -- runners dial in and
17# run jobs on their own daemons (docs/remote-runners.md), so the container has
18# no reason to reach Docker at all. Dropping the mount removes what used to be
19# a root-equivalent hold on the host from the internet-facing process.
20#
21# The one thing this gives up is agent sessions, which still drive Docker
22# locally (crates/anvil-agent). They are off in deploy/anvil.toml and off by
23# default, so nothing here regresses. Set ANVIL_DOCKER_SOCK=/var/run/docker.sock
24# to put the mount back if you turn them on -- and re-read docs/untrusted-mode.md
25# before you do.
26DOCKER_SOCK="${ANVIL_DOCKER_SOCK:-}"
27
28DOCKER_ARGS=()
29if [[ -n "$DOCKER_SOCK" ]]; then
30 DOCKER_ARGS=(-v "${DOCKER_SOCK}:/var/run/docker.sock"
31 --group-add "$(stat -c '%g' "$DOCKER_SOCK")")
32 echo "==> WARNING: mounting ${DOCKER_SOCK} (root-equivalent on this host)"
33fi
34
35# Single sign-on's client secret, if this instance uses one (docs/oidc.md).
36#
37# Read from a file on the host by default, because deploy/deploy.sh pipes this
38# script over ssh (`ssh host 'bash -s' < run.sh`) and no environment travels
39# with it — an env var alone would silently vanish on exactly the path that
40# matters. ANVIL_OIDC_CLIENT_SECRET still wins when running this by hand.
41#
42# Passed only when non-empty: an empty value would override the baked config
43# with "no secret" and turn a confidential client into a public one.
44OIDC_SECRET_FILE="${ANVIL_OIDC_SECRET_FILE:-$HOME/.config/anvil/oidc-client-secret}"
45OIDC_SECRET="${ANVIL_OIDC_CLIENT_SECRET:-}"
46if [[ -z "$OIDC_SECRET" && -r "$OIDC_SECRET_FILE" ]]; then
47 OIDC_SECRET="$(tr -d '[:space:]' <"$OIDC_SECRET_FILE")"
48fi
49
50OIDC_ENV=()
51if [[ -n "$OIDC_SECRET" ]]; then
52 OIDC_ENV=(-e "ANVIL_OIDC_CLIENT_SECRET=${OIDC_SECRET}")
53 echo "==> single sign-on: client secret loaded"
54else
55 echo "==> single sign-on: no client secret found (${OIDC_SECRET_FILE})"
56fi
57
58docker rm -f anvil 2>/dev/null || true
59docker run -d \
60 --name anvil \
61 --network "$NETWORK" \
62 --restart unless-stopped \
63 -p "${SSH_BIND_IP}:${SSH_PORT}:2222" \
64 -v anvil-data:/data \
65 "${DOCKER_ARGS[@]}" \
66 "${OIDC_ENV[@]}" \
67 "$IMAGE"
68
69echo "==> anvil (re)started from $IMAGE (web: anvil:3000 via Caddy, ssh: ${SSH_BIND_IP}:${SSH_PORT})"
70echo "==> CI needs a runner: anvil-worker --url https://anvil.richardscollin.com --token ..."