From 72aba7da32f0c8d9c58863a8c6fe389e0dd36dfb Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Mon, 1 Jun 2026 22:34:50 +0000 Subject: [PATCH] t3code: reconcile per-user t3 instances from /etc/ttyd-user-map Sticky port allocation (3773+), enables t3-serve@, emits /etc/t3-serve/dispatch.json for the dispatch service. systemd timer (OnBootSec+hourly) mirrors the apply-mbps-caps pattern. Co-Authored-By: Claude Opus 4.7 --- scripts/t3-provision-users.service | 7 +++++ scripts/t3-provision-users.sh | 44 ++++++++++++++++++++++++++++++ scripts/t3-provision-users.timer | 10 +++++++ 3 files changed, 61 insertions(+) create mode 100644 scripts/t3-provision-users.service create mode 100644 scripts/t3-provision-users.sh create mode 100644 scripts/t3-provision-users.timer diff --git a/scripts/t3-provision-users.service b/scripts/t3-provision-users.service new file mode 100644 index 00000000..eb81ff0c --- /dev/null +++ b/scripts/t3-provision-users.service @@ -0,0 +1,7 @@ +[Unit] +Description=Reconcile per-user t3 instances from /etc/ttyd-user-map +After=network.target + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/t3-provision-users diff --git a/scripts/t3-provision-users.sh b/scripts/t3-provision-users.sh new file mode 100644 index 00000000..fb51ab4e --- /dev/null +++ b/scripts/t3-provision-users.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# Reconcile per-user t3 instances from /etc/ttyd-user-map. +# Each "authentik_user=os_user" line -> an enabled t3-serve@ on a +# sticky port, plus /etc/t3-serve/dispatch.json (authentik_user -> {os_user,port}) +# consumed by t3-dispatch. +set -euo pipefail +MAP=/etc/ttyd-user-map +ENVDIR=/etc/t3-serve +BASE_PORT=3773 +install -d -m 0755 "$ENVDIR" + +port_of() { grep -oE 'T3_PORT=[0-9]+' "$1" | cut -d= -f2; } + +next_port() { # lowest free port >= BASE_PORT not already assigned + local used p + used=$(grep -hoE 'T3_PORT=[0-9]+' "$ENVDIR"/*.env 2>/dev/null | cut -d= -f2 | sort -n) + p=$BASE_PORT + while echo "$used" | grep -qx "$p"; do p=$((p+1)); done + echo "$p" +} + +declare -A DISPATCH +while IFS='=' read -r ak os; do + [[ -z "${ak// }" || "$ak" =~ ^[[:space:]]*# ]] && continue + ak=$(echo "$ak" | xargs); os=$(echo "$os" | xargs) + [[ -z "$ak" || -z "$os" ]] && continue + if ! id "$os" >/dev/null 2>&1; then + logger -t t3-provision "skip $ak: no OS user $os"; continue + fi + envf="$ENVDIR/$os.env" + [[ -f "$envf" ]] || echo "T3_PORT=$(next_port)" > "$envf" + port=$(port_of "$envf") + systemctl enable --now "t3-serve@$os.service" >/dev/null 2>&1 || true + DISPATCH[$ak]="{\"os_user\":\"$os\",\"port\":$port}" +done < "$MAP" + +tmp=$(mktemp) +{ printf '{'; first=1 + for ak in "${!DISPATCH[@]}"; do + [[ $first -eq 0 ]] && printf ','; first=0 + printf '"%s":%s' "$ak" "${DISPATCH[$ak]}" + done; printf '}\n'; } > "$tmp" +install -m 0644 "$tmp" "$ENVDIR/dispatch.json"; rm -f "$tmp" +logger -t t3-provision "reconcile complete: $(wc -c < "$ENVDIR/dispatch.json") bytes" diff --git a/scripts/t3-provision-users.timer b/scripts/t3-provision-users.timer new file mode 100644 index 00000000..944eb165 --- /dev/null +++ b/scripts/t3-provision-users.timer @@ -0,0 +1,10 @@ +[Unit] +Description=Periodic t3 per-user reconcile + +[Timer] +OnBootSec=2min +OnCalendar=hourly +Persistent=true + +[Install] +WantedBy=timers.target