feat(infra): expose FABRIC dashboard over Tailscale with TLS

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 <noreply@anthropic.com>
This commit is contained in:
jedarden 2026-04-23 22:05:39 -04:00
parent bcebfb55c0
commit 19450d3047
3 changed files with 118 additions and 3 deletions

57
CLAUDE.md Normal file
View file

@ -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 |

View file

@ -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 <FABRIC_AUTH_TOKEN>`
- 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'`.

View file

@ -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"