Skip to content

Networking

Tailscale mesh, SSH keys, VNC, domains, DNS, and Cloudflare Workers.

Tailscale Mesh

All hosts connected via Tailscale (100.x.x.x). See hosts.md for full IP table.

ACL Tags: - tag:sandbox — Applied to agent hosts. Can only receive inbound connections, cannot initiate to other Tailscale nodes (except allowed services). Note: On VPS, causes node key expiration — do NOT apply without updating ACL policy first.

Exit Node: Pi4-01 (100.73.159.126) configured as exit node.

SSH Keys

All keys stored in ~/.ssh/ on the MacBook Air M4:

Key File Target Host User
id_ed25519 Default key —
dell-stig-1 Dell k3s server claude@100.95.212.93
mac-executor Mac Mini M4 (iBuild-E) claude@100.92.170.124
rpi-claude Pi4-03 (Pi-E) claude@100.107.48.17
rpi-pi4-01 Pi4-01 (exit node) —
rpi-pi4-02 Pi4-02 (spare; former Review-E host) claude@100.77.12.75
vps-srv1099021 VPS (Volt-E) root@100.111.142.118

VNC Endpoints

Host Purpose Notes
Volt-E (VPS) claude login OAuth refresh Required periodically
~~Review-E (Pi4-02)~~ Migrated to GCP k3s No longer needs Pi VNC OAuth refresh

Pi-E does NOT need VNC — OAuth tokens pushed from M4 via LaunchAgent.

Cloudflare Domains

Domain Purpose Key Services
dashecorp.com Shared domain for apps without own domain Privacy policies, support pages, App Store links
invotek.no Company website Corporate site
stigjohnny.no Consulting profile site (zone in a separate personal Cloudflare account) Pages project stigjohnny-no on www, source dashecorp/stigjohnny-no; M365 mail (post@stigjohnny.no, its own tenant)
nutrie.app Nutri-E app domain App website, API workers
cutiefeedback.com Cuti-E platform SDK endpoints, dashboard

Cloudflare Workers (32 total)

Production Workers

Worker Domain Purpose
cutie-worker cutiefeedback.com Cuti-E platform API
cutie-worker-sandbox — Cuti-E sandbox environment
deadline-tracker-worker — Deadline tracking (cron: daily 07:00 UTC), D1 backend
email-inbox — Email handler (email + fetch triggers)
infra-monitor — Infrastructure monitoring (cron: every 15 min), KV backend
invotek-account-deletion invotek.no GDPR account deletion endpoint
invotek-privacy-policy invotek.no Privacy policy endpoint
reward-e-website — Reward-E production website
reward-e-staging — Reward-E staging website

Nutri-E Workers

Worker Variant Purpose
nutrie-apple-webhook-worker-* v2, v3, sandbox-v2, sandbox-v3 Apple Server Notifications
nutrie-dsld-worker-* v2, v3, sandbox-v2, sandbox-v3 DSLD supplement data API
nutrie-openai-worker-* production, v2, v3, sandbox, sandbox-v2, sandbox-v3 OpenAI proxy
nutrie-stats-rss — Stats RSS feed

Other

Worker Notes
astar-tracker Astar tracker (astar-db D1 backend)
infra-registry-redirect Redirect to the infra-registry Pages site (infra-registry.pages.dev)
rig-cockpit-worker Rig Cockpit API (rig-cockpit + rtd-social D1; fetch + scheduled + queue). Since 2026-10-03 also the rig's webhook router, see Rig webhook router
rig-router Rig request router (OpenTofu-deployed)
tripletex-proxy Tripletex (accounting) API proxy
site-85a189d3 Created 2026-03-14 — verify purpose

Count note (verified 2026-07-01): the 32 total includes 6 leftover Nutri-E v2/sandbox-v2 workers from the v2→v3 migration (retiring tracked in dashecorp/infra#248). Analytics gotcha: the Workers GraphQL Analytics API counts cron + queue + fetch invocations together, so cron/queue workers (deadline-tracker-worker, infra-monitor, rig-cockpit-worker, cutie-worker) read as "anomalously busy" in raw request counts — not a real anomaly. Cost: ~$5/month (the Workers Paid floor, required by cutie-worker's WebSocketHandler Durable Object).

Cloudflare D1 Databases (8)

Sizes verified 2026-07-01 via mcp__cloudflare__d1_list_databases.

Database ID Size Used By
cutie-sandbox c5ee2ef3-34e5-4e45-b21e-e5a203a7f177 3.8MB cutie-worker-sandbox
cutie-production 3111e614-2200-4c51-b63f-d8d3e46f7503 1.9MB cutie-worker
rtd-social d298499d-abb8-4009-b184-9bd8145617c1 184KB Rig Cockpit /videos + Run the Docs social pipeline
rig-cockpit 1d1ca944-8ba2-4ab0-8bb4-08582b81eb9d 112KB rig-cockpit-worker
astar-db ea4d99db-e206-4d63-a2a4-405ec79ba372 60KB astar-tracker worker
email-inbox 970229bf-a3e1-4a8f-9f9d-73fc14d959da 52KB email-inbox worker
submission-checklist 5019587a-5546-40f6-99c2-b235503a7284 48KB submission-checklist MCP
deadline-tracker f108263d-105e-44eb-94c9-a84e35a69568 36KB deadline-tracker-worker

Cloudflare Pages

~20+ Pages projects on the account (the Cloudflare MCP has no Pages list endpoint — enumerate live with wrangler pages project list or the dashboard; ~22 at the 2026-05-16 audit). Major projects: infra-registry (this registry), rig-research / rig-docs / dashecorp-docs / invotek-docs (docs sites), dashecorp (marketing), cutie-admin-dashboard, nutri-e, cuti-e, ai-accountant, scout. All on the free tier (generous headroom).

Cloudflare KV Namespaces (10)

Namespace ID Used By
SUBSCRIPTIONS 17ffbd7939be4c59984fa8220a34fa2e Nutri-E Apple webhook (production)
SUBSCRIPTIONS_SANDBOX c823bfef717348c982b00c8a310b6e00 Nutri-E Apple webhook (sandbox)
SUBSCRIPTIONS_DELETED b2e12a4d1c1e494396e98c9ef8724b42 Nutri-E deleted subscriptions
SUBSCRIPTIONS_DELETED_SANDBOX ebc6dd3e6fea43a090b24606d291fd93 Nutri-E deleted subs (sandbox)
CACHE_KV a497291bf1ff41a888902c39281f338d Nutri-E DSLD cache (production)
CACHE_SANDBOX 3793746890ca4ba49641cacfddb768da Nutri-E DSLD cache (sandbox)
RATE_LIMIT 56f5968c8a86441aa98bbdee6edf66de Nutri-E rate limiting (production)
RATE_LIMIT_SANDBOX f55e2bf148db4359970f63ac37c572f3 Nutri-E rate limiting (sandbox)
production-DEVICE_ACTIVITY 5a36bf6a3e894a08b15cbec1fa19dba8 Nutri-E device activity
INFRA_MONITOR b67b5e912d914ce3b1ab856a3d8c9fc6 infra-monitor worker

Cloudflare Queues (5)

Queue ID Producers Consumers Used By
cutie-notifications 27bfce8f 1 1 Cuti-E push notifications
cutie-notifications-sandbox bfe8812a 1 1 Cuti-E notifications (sandbox)
cutie-notifications-dlq f0ff7f66 0 0 Dead letter queue
rig-webhooks — — — Rig webhook buffer, 14-day retention (router)
rig-webhooks-dlq — — — Its dead letter queue, 14-day retention

Cloudflare R2

Bucket Detail Since
rig-backups-dell The Dell's off-site backups (rig-gitops#897, see Kubernetes → Dell plan step 4). EU jurisdiction. Object lock and lifecycle are set per prefix, and a catch-all rule keeps everything else 34 days infra#573 (P1a); lifecycle fix infra#577

No tokens yet. The R2 tokens come in P1b, after the operator grants infra CI's Cloudflare token "Account API Tokens: Edit" (O1). The writer token then goes into SOPS.

Email Routing

Address Forwards To Domain
pi-e@dashecorp.com pie.dashecorp@proton.me dashecorp.com (Cloudflare Email Routing)

Tailscale ACL Policy

Source of truth: dashecorp/infra → tailscale/main.tf (managed via OpenTofu, deployed via GitHub Actions terraform.yml)

Current policy uses role-based tags: tag:server, tag:agent, tag:terraform, tag:ci. tag:agent can reach tag:server; tag:server can reach tag:server (and nothing untagged: *:* never covers tagged devices, and an untagged device is not in tag:server); tag:ci, the ephemeral GitHub Actions node, may reach only SSH on the Dell (dell:22, infra#503); the Dell (hosts.dell) may reach the Synology NAS (hosts.nas, the untagged DS412plus) on tcp/2049 only, for its NFSv4.1 volumes (infra#525; before that rule the Dell's 11 NAS mounts hung from 2026-04-13 to 2026-10-01, rig-gitops#882); all members can use the exit node for internet access. Admin has full access to all devices. SSH access limited to admin in accept mode.

Cloudflare Tunnels

Source of truth: dashecorp/infra → cloudflare/dashecorp.com/tunnel.tf (Cloudflare tunnel resources, deployed via OpenTofu).

Every ingress rule is whole-host. There is no path key anywhere in cloudflare_zero_trust_tunnel_cloudflared_config.gcp, so for each hostname the origin receives the full URL space of that port. Path scoping exists only in the Access layer, and only once (the GitHub webhook bypass). Audited 2026-09-11.

Tunnel Domain Target Cloudflare Access
dashecorp-gcp rig-conductor.dashecorp.com http://rig-conductor-api.rig-conductor.svc.cluster.local:8080 Yes — email allow + non_identity service token (the cockpit worker's)
dashecorp-gcp rig-conductor.dashecorp.com/api/webhook/github (same origin) Bypassed by design — HMAC is the only gate. Since 2026-10-03 a Workers route sends this path to rig-cockpit-worker first (router)
dashecorp-gcp flux.dashecorp.com http://weave-gitops.weave-gitops.svc.cluster.local:9001 (Flux GitOps UI) Yes — email allow
dashecorp-gcp rig-assistant.dashecorp.com http://rig-assistant.rig-assistant.svc.cluster.local:8787 No — see below
dashecorp-gcp rig-e.dashecorp.com http://rig-e-rig-agent-runtime.dashecorp.svc.cluster.local:3000 No — see below
dashecorp-dell-v2 rig-conductor-dell.dashecorp.com http://rig-conductor-api.rig-conductor.svc.cluster.local:8080 on the Dell Yes — the rig's service token only

The Dell's connector is live since 2026-10-15 (rig-gitops#1015): one cloudflared pod on the Dell runs dashecorp-dell-v2 (f3b364b4…), a new tunnel made by OpenTofu (infra#655) and fed its token by the phone-approved C4 step (CI/CD). Without a token the hostname answers 403. It is unrelated to the hand-made Dell tunnels removed on 2026-10-02 (below).

GCP's tunnel secret leaked into old CI logs (infra#654): OpenTofu printed a random_id's id, which is its value. Rotating it is pending the operator's decision. Mask [id=...] in every CI-log grep.

cost.dashecorp.com CNAMEs to this tunnel with no matching ingress rule, so it lands on the http_status:404 catch-all. It is Access-protected, so the 404 sits behind a login. Stale, not exploitable; outputs.tf still advertises it.

Rig webhook router (multi-location rig)

Added 2026-10-03 for the multi-location rig (Kubernetes → Dell plan step 4, dashecorp/infra#565):

Object Detail Since
rig-conductor-gcp.dashecorp.com Behind Access, service token only: just the rig_cockpit_worker token is allowed. The router's forward target for gcp (rig-cockpit-worker#306, 2026-10-15) infra#567
Queues rig-webhooks, rig-webhooks-dlq 14-day retention infra#567
Workers route rig-conductor.dashecorp.com/api/webhook/github* → rig-cockpit-worker The router, live since 2026-10-15 14:04:33Z (ROUTING_MODE=live, rig-cockpit-worker#304): HMAC check, queued on rig-webhooks, 202, then forwarded to the tenant location's own hostname infra#569, rig-cockpit-worker#304
Worker secret GITHUB_WEBHOOK_SECRET The router's HMAC check: the shared webhook secret. OpenTofu writes it to rig-cockpit-worker as ROUTER_WEBHOOK_SECRET, and every deploy puts it (Credentials). Shadow logs show hmacValid: true (operator check, 2026-10-15) infra#664, rig-cockpit-worker#302 (2026-10-15)
  • Never set global_fetch_strictly_public on rig-cockpit-worker. Its pass-through fetches rig-conductor.dashecorp.com; with the flag on, that fetch goes back through the route into the Worker itself and loops.
  • Forwards never target the stable host. The queue consumer is not on the route's zone, so a forward to rig-conductor.dashecorp.com would most likely re-enter the router (508). Every location has its own hostname, and a test in rig-cockpit-worker checks it (#305).
  • Route scope: only /api/webhook/github* (and later /api/merge*), never /*. Check that GitHub hook deliveries return 2xx after any route change.
  • Rollback: a PR that sets ROUTING_MODE = "shadow" (the merge deploys it); queued messages still drain; then redeliver failed deliveries from GitHub.

Rig Cockpit GitHub webhooks

Added 2026-10-03 for the Rig Cockpit approvals MVP (rig-cockpit-worker#268). Both paths are on rig-cockpit.dashecorp.com, and both hooks are signed with the same secret (Credentials):

Path Hook Since
/v1/github/waiting rig-gitops workflow_job/workflow_run hook 691622740: runs waiting for approval, shown as the banner and push. Ping returned 200 infra#582
/v1/github/inbox dashecorp/operator-inbox issues hook: the "Waiting for you" list (Operator inbox) infra#585

Neither operator-inbox nor rig-approve has a rig-conductor hook.

Manual / non-Terraform tunnels: the Dell's are gone (2026-10-02). The Dell's only tunnel now is the OpenTofu-managed dashecorp-dell-v2 above. Before that, the Dell ran three hand-made tunnels: - invotek-dell: vcluster.invotek.no (vCluster Platform) and conductor-e.dashecorp.com; - tablez-vcluster: grafana, api and app.invotek.no; - a host tunnel in another account: code.stigjohnny.no (code-server) and argocd.stigjohnny.no.

Their workloads and connectors were removed in the Dell cleanup. The first two tunnels, their 4 invotek.no CNAMEs and their 2 Access apps were adopted into OpenTofu and deleted (dashecorp/infra#548, docs/2026-10-02-dell-cloudflare-cleanup.md there).

Left in place by operator decision ("Leave them for now. But document"): the stigjohnny.no objects, which sit in a Cloudflare account the CI token cannot see (Zero Trust team stigjohnnyno): - the DNS records code.stigjohnny.no and argocd.stigjohnny.no; - the former host tunnel; - two Access apps.

They are inert: the tunnel has no connector, and both hostnames sit behind an Access login. Deleting them needs the operator in that account's dashboard, or a token for it.

Other down tunnels in the main account, not the Dell's and not yet decided: automate-e-dashboard, iclaw-e (iClaw-E retired 2026-09-25), and atl-dashboard below.

Offline / Retired Tunnels

Tunnel Domain Notes
atl-dashboard (DOWN) atl-e.dashecorp.com Offline, removed from Terraform (legacy, namespace broken)
rig-agent-runtime-dashboard book-e.dashecorp.com Retired — no Terraform definition

Cloudflare Email Routing

Address Forwards To Domain
pi-e@dashecorp.com pie.dashecorp@proton.me dashecorp.com
(inbound to email-inbox worker) D1 database email-inbox invotekas.workers.dev

Unauthenticated surfaces (audited 2026-09-11)

Two of the tunnel's four hostnames have no Access application, and both justify it in-comment with "the origin's own token is the gate". That is a per-endpoint claim being used to license a whole-host mapping: it holds for the one route that checks a token and for nothing else on the port.

Surface State Tracking
rig-e.dashecorp.com — GET / and /api/state served the runtime dashboard: log ring, sessions, tool calls, token/cost accounting CLOSED 2026-09-11 13:05Z — /api/state now 404 dashecorp/infra#447
rig-e.dashecorp.com — wss://…/ws accepted an anonymous Upgrade and sent the same dashboard state, then tailed the log ring live CLOSED — the listener split (rig-agent-runtime#704); every Upgrade on the routed port is refused, /ws included rig-agent-runtime#700
SSH tcp:22 open to 0.0.0.0/0 on all three rig nodes, via GCP's auto-created default-allow-ssh PART-CLOSED — allow-ssh-iap added and applied, allow-ssh-admin un-staled and its dead tag fixed; the world-open rule is still there pending part 2 dashecorp/infra#449
valkey on NodePort 30079, auth off (upstream valkey/valkey, since 2026-09-23). It holds assignments:* and merge-gate:*, and its one live off-cluster client is iBuild-E on the Mac Mini OPEN — not internet-reachable; worse than the API half, since writing it means becoming the thing that tells the rig what to do. Auth is still off. RESTORE is now disabled beside the chart-default FLUSHDB/FLUSHALL (rig-gitops#828, the path for CVE-2026-63639 / CVE-2026-25243), which closes the one-shot wipe and the CVE path. Any client can still read and write every stream dashecorp/infra#448
rig-e.dashecorp.com — an Upgrade on the token-gated /api/chat bypassed the token check entirely CLOSED 2026-09-11 11:45Z — rig-agent-runtime#698, deployed via rig-gitops#753; re-probed 11:50Z, answers 400 where it answered 101 at 11:20Z rig-gitops#754
rig-assistant.dashecorp.com — FastAPI's default /docs, /redoc, /openapi.json; an interactive API console on the origin where the operator types the bearer, loading Swagger UI from a floating CDN tag OPEN — lower severity, the /v1/* routes are bearer-gated dashecorp/rig-assistant#18
rig-conductor on NodePort 30080 — the whole API, no authentication of any kind. Access guards the hostname, not this OPEN — verified NOT internet-reachable (no firewall rule, no load balancer, packets dropped). Lateral movement from any pod or tailnet device, not external attack dashecorp/infra#448

The NodePort is the one that matters most once an attacker is already inside — and that qualifier was missing when this was first written. The firewall check (2026-09-11) came back negative: nothing permits 30080 or 30079 from the internet, there is no load balancer, and the ports drop packets from outside. access.tf documents the NodePort as intentional ("internal agents reach the API via Tailscale NodePort, not through this Cloudflare tunnel"), which means Cloudflare Access is decorative against anything already inside. The write paths kept off the cockpit's proxy allowlist were all reachable there unauthenticated. Since 2026-09-24: - Merge needs Review-E's GitHub approval at the head, whoever calls (rig-conductor#2350). - GPU start/stop, force-done, tenant create/delete, overrides and agent deletes carry an operator-token gate (rig-conductor#2369, X-Rig-Operator-Token). It is report-only until OPERATOR_ROUTE_GATE=enforce. - Still open: event injection (/api/events) and Valkey on 30079. Both need a per-agent identity (infra#448).

Two lessons worth carrying to any new surface:

  • An auth check on an HTTP handler does not protect a WebSocket on the same server. Node dispatches an Upgrade to the 'upgrade' event and never to 'request', so method, path and bearer checks written as request handling are simply not on that code path. Scope the socket explicitly.
  • Scoping the leaking path is not the same as closing the leak. Pinning that socket to /ws stopped the token bypass and closed nothing else: wss://…/ws was always a dashboard path, so it still serves the same state to anyone who can reach the port. The path was the symptom; the listener was the bug. Check the fix against the exposure, not against the symptom that revealed it.
  • Reachability is inherited, and that is the dangerous direction. GET /api/state is correct code for a cluster-internal listener. It became an internet-facing leak because a hostname was later pointed at the port it shared, and nothing in the endpoint changed to say so. The durable answer is default-deny on whichever listener is routed, so a new endpoint cannot become public by inheritance — it has to be added to an allowlist on purpose.
  • A case-sensitive pattern is safe for ALLOWING a route and unsafe for EXCLUDING one. An over-strict allowance fails closed and someone notices the 404; an over-strict exclusion fails open and silently, because the upstream is usually more permissive about case than the guard (rig-cockpit-worker#161).

Network Security Layers

  1. Tailscale ACL — tag:sandbox restricts outbound from agent hosts
  2. Docker iptables — DOCKER-USER chain blocks containers to private ranges (Pi-E, Volt-E)
  3. macOS pf — Firewall on Mac Mini M4 blocks outbound to Tailscale/LAN
  4. Host networking — Volt-E uses host networking (Docker bridge incompatible with Tailscale exit node)