From c85c55006de773d5b2e61ff6617eaf3fa8b05a83 Mon Sep 17 00:00:00 2001 From: profit Date: Wed, 22 Apr 2026 04:15:58 -0500 Subject: [PATCH] ops: systemd units for auditor + context7 bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Promotes two previously manual-start Bun services to systemd so they survive restarts + run continuously. - ops/systemd/lakehouse-auditor.service — polls Gitea every 90s, runs 4 audit checks per PR head SHA, posts commit status + review comment. Runs as root to match existing lakehouse-* service conventions on this host; can read /home/profit/.git-credentials (0600 profit:profit). - ops/systemd/lakehouse-context7-bridge.service — HTTP wrapper on :3900 for Phase 45 doc-drift detection. Decoupled from gateway; runs independently. - ops/systemd/install.sh — idempotent installer (copy → daemon-reload → enable --now). Prints post-install active/enabled status. - ops/systemd/README.md — run/stop/logs/pause docs. Pause control stays per-service (bot.paused / auditor.paused files at repo root). Not wired to branch protection yet — the auditor's commit status is currently advisory, not enforcing. Flip via Gitea branch_protections API when confident. --- ops/systemd/README.md | 57 ++++++++++++++++++ ops/systemd/install.sh | 59 +++++++++++++++++++ ops/systemd/lakehouse-auditor.service | 34 +++++++++++ ops/systemd/lakehouse-context7-bridge.service | 23 ++++++++ 4 files changed, 173 insertions(+) create mode 100644 ops/systemd/README.md create mode 100755 ops/systemd/install.sh create mode 100644 ops/systemd/lakehouse-auditor.service create mode 100644 ops/systemd/lakehouse-context7-bridge.service diff --git a/ops/systemd/README.md b/ops/systemd/README.md new file mode 100644 index 0000000..4004b37 --- /dev/null +++ b/ops/systemd/README.md @@ -0,0 +1,57 @@ +# Lakehouse systemd units + +Service definitions for long-running Lakehouse sidecars that aren't the +Rust gateway itself. The gateway has its own pre-existing unit +(`lakehouse.service`) that was configured at initial deploy time and +isn't tracked here. + +## Units + +| File | Service | Port | Purpose | +|---|---|---|---| +| `lakehouse-auditor.service` | `lakehouse-auditor` | n/a | Polls Gitea for open PRs, runs four checks (static / dynamic / inference / KB query), posts commit-status + review comment. Hard-blocks merges when claims aren't backed. | +| `lakehouse-context7-bridge.service` | `lakehouse-context7-bridge` | `:3900` | HTTP wrapper around context7's public API for Phase 45 doc-drift detection. | + +## Install + +```bash +sudo bash ops/systemd/install.sh +``` + +Idempotent. Copies units to `/etc/systemd/system/`, reloads, enables + (re)starts both services. + +## Operate + +```bash +# Status +systemctl status lakehouse-auditor +systemctl status lakehouse-context7-bridge + +# Live logs +journalctl -u lakehouse-auditor -f + +# Restart +systemctl restart lakehouse-auditor + +# Stop (won't restart until enable + start again) +systemctl stop lakehouse-auditor +``` + +## Pause the auditor without stopping + +```bash +touch /home/profit/lakehouse/auditor.paused # skip cycles until removed +rm /home/profit/lakehouse/auditor.paused # resume +``` + +## Env toggles on the auditor (edit the unit file, `systemctl daemon-reload`, restart) + +``` +LH_AUDITOR_RUN_DYNAMIC=1 # include the hybrid fixture on every audit + # default off — fixture mutates live playbook state +LH_AUDITOR_SKIP_INFERENCE=1 # skip cloud inference for fast/cheap runs +``` + +## Why both services run as root + +To match the existing `lakehouse.service` + `mcp-server` + `observer` conventions on this host. Hardening to a dedicated unprivileged user is a follow-up: would need PATH adjustment for `bun`, credential file accessibility (the auditor reads `/home/profit/.git-credentials` which is `0600 profit:profit` — root reads fine, a non-profit non-root user wouldn't). diff --git a/ops/systemd/install.sh b/ops/systemd/install.sh new file mode 100755 index 0000000..6ffb521 --- /dev/null +++ b/ops/systemd/install.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +# Install the lakehouse-auditor + lakehouse-context7-bridge systemd units. +# Idempotent: re-running just reloads + restarts. +# +# Usage (as root): +# bash ops/systemd/install.sh +# +# What it does: +# 1. Copies *.service to /etc/systemd/system/ +# 2. systemctl daemon-reload +# 3. systemctl enable --now both services +# 4. Prints post-install status + +set -euo pipefail + +UNIT_DIR="$(dirname "$(readlink -f "$0")")" +TARGET_DIR=/etc/systemd/system + +UNITS=( + lakehouse-auditor.service + lakehouse-context7-bridge.service +) + +if [[ $EUID -ne 0 ]]; then + echo "install.sh: must run as root (writes to $TARGET_DIR)" >&2 + exit 1 +fi + +for unit in "${UNITS[@]}"; do + src="$UNIT_DIR/$unit" + dst="$TARGET_DIR/$unit" + if [[ ! -f "$src" ]]; then + echo "install.sh: missing source $src" >&2 + exit 1 + fi + echo "→ copy $unit" + install -m 0644 "$src" "$dst" +done + +echo "→ systemctl daemon-reload" +systemctl daemon-reload + +for unit in "${UNITS[@]}"; do + echo "→ enable + (re)start $unit" + systemctl enable "$unit" >/dev/null + systemctl restart "$unit" +done + +echo "" +echo "─── post-install status ───" +for unit in "${UNITS[@]}"; do + active=$(systemctl is-active "$unit" 2>/dev/null || true) + enabled=$(systemctl is-enabled "$unit" 2>/dev/null || true) + printf " %-40s active=%s enabled=%s\n" "$unit" "$active" "$enabled" +done +echo "" +echo "Live logs: journalctl -u lakehouse-auditor.service -f" +echo "Pause: touch /home/profit/lakehouse/auditor.paused" +echo "Resume: rm /home/profit/lakehouse/auditor.paused" diff --git a/ops/systemd/lakehouse-auditor.service b/ops/systemd/lakehouse-auditor.service new file mode 100644 index 0000000..81b6b7f --- /dev/null +++ b/ops/systemd/lakehouse-auditor.service @@ -0,0 +1,34 @@ +[Unit] +Description=Lakehouse Claim Auditor — polls Gitea for open PRs + hard-blocks placeholder merges +Documentation=file:///home/profit/lakehouse/auditor/README.md +After=network.target lakehouse.service +Wants=lakehouse.service + +[Service] +Type=simple +WorkingDirectory=/home/profit/lakehouse +# Runs as root to match the other lakehouse-* services on this host +# (gateway, mcp-server, observer). The auditor reads the git PAT out +# of /home/profit/.git-credentials which is 0600 profit:profit — +# root can read it, which is why the service runs as root. +# Alternative: run as `profit` and ensure bun binary is on PATH; +# left for a follow-up hardening PR. +ExecStart=/home/profit/.bun/bin/bun run /home/profit/lakehouse/auditor/index.ts +Restart=on-failure +RestartSec=30 +# Stop responds to SIGTERM cleanly — no in-flight cycle survives a +# restart; the poller is idempotent so a mid-cycle restart just +# re-audits from state.json on next start. +KillSignal=SIGTERM +TimeoutStopSec=10 + +# Optional env toggles documented in auditor/index.ts: +# LH_AUDITOR_RUN_DYNAMIC=1 — include hybrid fixture on every audit +# (default off — mutates live playbook) +# LH_AUDITOR_SKIP_INFERENCE=1 — skip cloud inference for fast runs + +# Pause file — operator can `touch /home/profit/lakehouse/auditor.paused` +# to skip the next cycle without stopping the service. + +[Install] +WantedBy=multi-user.target diff --git a/ops/systemd/lakehouse-context7-bridge.service b/ops/systemd/lakehouse-context7-bridge.service new file mode 100644 index 0000000..cb3ecf6 --- /dev/null +++ b/ops/systemd/lakehouse-context7-bridge.service @@ -0,0 +1,23 @@ +[Unit] +Description=Lakehouse context7 HTTP bridge — doc-version lookups for Phase 45 drift detection +Documentation=file:///home/profit/lakehouse/mcp-server/context7_bridge.ts +After=network.target +# No hard dependency on gateway — the bridge is self-contained and +# talks to context7.com directly. Gateway calls INTO the bridge +# during drift checks but they're decoupled. + +[Service] +Type=simple +WorkingDirectory=/home/profit/lakehouse +ExecStart=/home/profit/.bun/bin/bun run /home/profit/lakehouse/mcp-server/context7_bridge.ts +Restart=on-failure +RestartSec=15 +# Default port :3900 (overridable via CONTEXT7_BRIDGE_PORT env). +# If the user moves the bridge to a different port, update the +# LH_BRIDGE_URL env on lakehouse.service + auditor.service too. +Environment=CONTEXT7_BRIDGE_PORT=3900 +KillSignal=SIGTERM +TimeoutStopSec=5 + +[Install] +WantedBy=multi-user.target