// PROPOSED tailnet policy — Phase 2 of docs/security-plan.md. DRAFT for // Kyle's review, 2026-11-01. NOT LIVE. Baseline it replaces: // docs/tailscale-acl.hujson. // // Design: single-user tailnet. Kyle's interactive machines stay UNTAGGED // (they speak as isomk@ → full access via autogroup:members). Tags exist to // constrain the devices that are NOT him-at-a-keyboard: phones/TV, IoT, and // the server fleet. Tagging a device removes its user identity, which is // exactly the point. // // Tag assignments (apply on the Machines page before saving this policy): // tag:server (home) : rift, orion, dixie, butler, sk, talos // tag:cloud (public-facing remote): phobos, deimos (Hetzner colo, web), // fs (devlogs/primers, remote dev), svc // tag:mobile : iphone-15-pro, ipad-pro-11-gen-3, pixel-8a, apple-tv // tag:iot : homeassistant, synology // UNtagged (full access, by design): straylight, titan, callisto, io, // wntrmute (agent host). // // Known flows pre-allowed (expect a first-week breakage pass anyway): // - agents on tagged servers → Mattermost on straylight (serve 443) // - orion VictoriaMetrics → node exporters (*:9100, *:8420) // - server agents → wntrmute Hermes peer API (:8642) // - Syncthing mesh on :22000 for every device incl. tagged // // Unresolved: fs also ADVERTISES exit node — deliberate? (dst // autogroup:internet below covers use; drop the advertisement if unwanted.) { "tagOwners": { "tag:server": ["autogroup:admin"], "tag:cloud": ["autogroup:admin"], "tag:mobile": ["autogroup:admin"], "tag:iot": ["autogroup:admin"], }, "acls": [ // Kyle's own (untagged) devices: full access. NOTE: autogroup:members // = every tailnet member; safe only while this stays a single-user // tailnet. Revisit if anyone is ever invited. {"action": "accept", "src": ["autogroup:members"], "dst": ["*:*"]}, // Exit-node egress for user devices (fs currently advertises). {"action": "accept", "src": ["autogroup:members"], "dst": ["autogroup:internet:*"]}, // Syncthing mesh: every device, both directions, one port. {"action": "accept", "src": ["*"], "dst": ["*:22000"]}, // Server ↔ server ↔ cloud: agents, metrics, compose, DBs. {"action": "accept", "src": ["tag:server", "tag:cloud"], "dst": ["tag:server:*", "tag:cloud:*"]}, // Tagged servers reach the untagged hub hosts only on specific flows: // Mattermost (straylight serve 443 + direct 8065), exporters anywhere. {"action": "accept", "src": ["tag:server", "tag:cloud"], "dst": [ "straylight.scylla-hammerhead.ts.net:443", "straylight.scylla-hammerhead.ts.net:8065", ]}, {"action": "accept", "src": ["tag:server"], "dst": ["*:9100", "*:8420"]}, // Server agents → wntrmute Hermes peer API only (Kyle: outbound + // maybe inbound peer API). {"action": "accept", "src": ["tag:server"], "dst": ["wntrmute.scylla-hammerhead.ts.net:8642"]}, // Phones / tablet / TV: HA is a must; Mattermost, Syncthing, and DSM // nice-to-have. Nothing else — no SSH, no DBs, no exporters. {"action": "accept", "src": ["tag:mobile"], "dst": [ "homeassistant.scylla-hammerhead.ts.net:8123", "straylight.scylla-hammerhead.ts.net:443", "synology.scylla-hammerhead.ts.net:5000", "synology.scylla-hammerhead.ts.net:5001", ]}, // (Syncthing for mobile arrives via the *:22000 mesh rule above.) // tag:iot initiates nothing except the Syncthing mesh above. ], // Tailscale SSH: unchanged default (members → own devices). Once P1 // adopts --ssh fleet-wide, consider narrowing dst to tag:server and // adding a checkPeriod so sessions re-auth instead of persisting. "ssh": [ { "action": "accept", "src": ["autogroup:members"], "dst": ["autogroup:self"], "users": ["autogroup:nonroot", "root"], }, ], "nodeAttrs": [ // Funnel: servers and cloud boxes may publish; user desktops, // phones, and IoT may not. (straylight is untagged by design and so // loses Funnel eligibility — it runs serve, tailnet-only, which // needs no attr.) { "target": ["tag:server", "tag:cloud"], "attr": ["funnel"], }, ], // Run on every save; the console executes these before accepting. "tests": [ { "src": "tag:mobile", "accept": ["homeassistant.scylla-hammerhead.ts.net:8123", "straylight.scylla-hammerhead.ts.net:443", "100.95.252.120:22000"], "deny": ["rift.scylla-hammerhead.ts.net:22", "rift.scylla-hammerhead.ts.net:8000", "dixie.scylla-hammerhead.ts.net:5432", "orion.scylla-hammerhead.ts.net:9100"], }, { "src": "tag:server", "accept": ["dixie.scylla-hammerhead.ts.net:11434", "rift.scylla-hammerhead.ts.net:8000", "straylight.scylla-hammerhead.ts.net:443", "wntrmute.scylla-hammerhead.ts.net:8642"], "deny": ["titan.scylla-hammerhead.ts.net:22"], }, { "src": "tag:iot", "accept": ["synology.scylla-hammerhead.ts.net:22000"], "deny": ["rift.scylla-hammerhead.ts.net:22"], }, ], }