From 19450d30473aef5182fe84fec7fee1a9a17ffd8a Mon Sep 17 00:00:00 2001 From: jedarden Date: Thu, 23 Apr 2026 22:05:39 -0400 Subject: [PATCH] feat(infra): expose FABRIC dashboard over Tailscale with TLS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Configure tailscale serve to proxy https://hetzner-ex44.tail1b1987.ts.net/ to localhost:3000. Tailnet-only — no public internet exposure. - scripts/setup-tailscale-serve.sh: one-time setup script (idempotent) - README.md: add Remote Access section with URL, access model, and setup steps - CLAUDE.md: new project-level reference for service location, URLs, auth model Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 57 ++++++++++++++++++++++++++++++++ README.md | 39 ++++++++++++++++++++-- scripts/setup-tailscale-serve.sh | 25 ++++++++++++++ 3 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 CLAUDE.md create mode 100755 scripts/setup-tailscale-serve.sh diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..0d4a0ac --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,57 @@ +# FABRIC + +## What This Is + +FABRIC is a live dashboard for NEEDLE worker activity — TUI and web modes. + +- **Repo**: `/home/coding/FABRIC` +- **Stack**: TypeScript + Node.js, Express, WebSocket, React frontend (Vite), blessed TUI +- **Log source**: `~/.needle/logs/` (per-worker JSONL files, hot-added via DirectoryTailer) + +## Running Service + +```bash +systemctl --user status fabric-web.service # check status +systemctl --user restart fabric-web.service # restart +``` + +The service runs as `fabric web --port 3000 --source ~/.needle/logs --otlp-http :4318`. +Auth token is loaded from `~/.config/fabric/secrets.env` (`FABRIC_AUTH_TOKEN`). + +## Remote Access + +| URL | Notes | +|-----|-------| +| `http://localhost:3000` | Local only | +| `https://hetzner-ex44.tail1b1987.ts.net/` | Tailscale tailnet, TLS, **no public internet** | + +The Tailscale HTTPS proxy is configured via `tailscale serve --bg http://localhost:3000`. +To re-apply after a reset: `./scripts/setup-tailscale-serve.sh`. + +## Auth Model + +- `FABRIC_AUTH_TOKEN` in `~/.config/fabric/secrets.env` protects all POST endpoints +- GET endpoints (dashboard UI, workers, events read) are open — read-only, no secret data +- Tailscale provides network-level access control (tailnet membership required) + +## Build & Test + +```bash +npm run build # compile TypeScript + Vite frontend +npm test # vitest unit tests +npm run test:e2e # Playwright E2E tests +npx tsc --noEmit # type-check without emitting +``` + +## Key Files + +| Path | Purpose | +|------|---------| +| `src/cli.ts` | Entry point; all CLI commands | +| `src/web/server.ts` | Express HTTP server + WebSocket + auth middleware | +| `src/web/frontend/` | React SPA (Vite build) | +| `src/directoryTailer.ts` | Watches `~/.needle/logs/`, hot-adds new JSONL files | +| `src/store.ts` | In-memory event store + SQLite persistence | +| `scripts/fabric-web.service` | systemd unit file | +| `scripts/setup-tailscale-serve.sh` | One-time Tailscale Serve setup | +| `docs/plan.md` | Full architecture and phase roadmap | diff --git a/README.md b/README.md index 424f997..dd4a956 100644 --- a/README.md +++ b/README.md @@ -230,6 +230,38 @@ The pruner emits `mend.logs_pruned` events to `~/.needle/logs/fabric-mend.jsonl` 17 3 * * * ~/.local/bin/fabric prune ``` +## Remote Access via Tailscale + +The web dashboard is served over HTTPS on the Tailscale tailnet (not the public internet): + +``` +https://hetzner-ex44.tail1b1987.ts.net/ +``` + +**Access model:** +- Available only to devices joined to the `tail1b1987.ts.net` tailnet (laptop, phone, etc.) +- TLS provided by Tailscale's managed certificates — no self-signed cert warnings +- GET requests (dashboard, workers list, event feed) are unauthenticated +- POST requests (`/api/events`, `/api/events/batch`) require `Authorization: Bearer ` +- Not exposed via Tailscale Funnel — no public internet access + +**Setup (one-time):** + +```bash +# Grant operator access + configure HTTPS proxy +./scripts/setup-tailscale-serve.sh + +# Or manually +sudo tailscale set --operator=$USER +tailscale serve --bg http://localhost:3000 +``` + +The serve config persists across reboots. To remove it: + +```bash +tailscale serve --https=443 off +``` + ## Production Deployment FABRIC runs as a user-level systemd service (`fabric-web.service`) with OTLP/HTTP enabled: @@ -242,9 +274,10 @@ systemctl --user status fabric-web.service ss -tlnp | grep 4318 ``` -| Component | Port | Purpose | -|-----------|------|---------| -| Web dashboard | `:3000` | Browser UI + REST API | +| Component | Port/URL | Purpose | +|-----------|----------|---------| +| Web dashboard (local) | `:3000` | Browser UI + REST API | +| Web dashboard (remote) | `https://hetzner-ex44.tail1b1987.ts.net/` | Tailscale HTTPS (tailnet only) | | OTLP/HTTP | `:4318` | NEEDLE metric ingestion | NEEDLE's `otlp_metric_sink` is enabled in `~/.needle/config.yaml`, pushing aggregated token/cost/bead metrics to `http://localhost:4318/v1/metrics`. FABRIC deduplicates these against JSONL-tailed events and writes them to `~/.needle/fabric.db` with `metrics_source='otlp-metric'`. diff --git a/scripts/setup-tailscale-serve.sh b/scripts/setup-tailscale-serve.sh new file mode 100755 index 0000000..cf7315b --- /dev/null +++ b/scripts/setup-tailscale-serve.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# Configure Tailscale Serve to expose FABRIC dashboard over Tailscale with TLS. +# Tailnet-only (not public internet). Run once; config persists across reboots. +# +# Prerequisites: +# - tailscale connected to tail1b1987.ts.net +# - Current user is set as Tailscale operator (handled below) +# - fabric-web.service running (fabric web on :3000) +# +# After setup: https://hetzner-ex44.tail1b1987.ts.net/ + +set -euo pipefail + +# Grant operator access so future serve commands don't need sudo +sudo tailscale set --operator="$USER" + +# Configure HTTPS serve (tailnet-only, not Funnel) +tailscale serve --bg http://localhost:3000 + +echo "" +echo "Tailscale Serve configured." +tailscale serve status +echo "" +echo "Dashboard: https://hetzner-ex44.tail1b1987.ts.net/" +echo "To remove: tailscale serve --https=443 off"