Skip to main content

Expose the bus over the tailnet

There are two ways. Both keep traffic inside the tailnet. Funnel is never used.

Tailscale serve (default)​

serve stays on 127.0.0.1:47910. Tailscale terminates HTTPS on port 8443 and forwards to it.

  1. Enable HTTPS certificates for the tailnet (admin console, DNS page, "Enable HTTPS"). This publishes the machine name in public Certificate Transparency logs. If you do not want that, use the direct bind below.
  2. Check it works:
    tailscale cert <bus-host>.<tailnet>.ts.net
    It writes a .crt and a private .key into the current directory. tailscale serve does not need them; delete them. Do not rely on tailscale status --json .Self.CertDomains: on a macOS client it stayed null after a certificate was issued.
  3. Expose:
    tailscale serve --bg --https=8443 --accept-app-caps catesworks.com/cap/session-bus http://127.0.0.1:47910
  4. Base URL for clients: https://<bus-host>.<tailnet>.ts.net:8443.

Undo with tailscale serve --https=8443 off. Do not use tailscale serve reset; it clears every serve configuration on the host.

Direct tailnet bind​

BUS_HOST=$(tailscale ip -4) BUS_PORT=47910 node dist/cli.js serve
  • No certificates needed. Base URL: http://<bus-host>.<tailnet>.ts.net:47910.
  • The bind guard accepts the address only if tailscale ip reports it. If tailscale is not on the PATH, serve refuses to start (exit 2).
  • Tailscale identity headers are not logged in this mode.

ACL grant​

Grant the client machines access to the bus port. Save the current policy version first; rollback is restoring it.

{
"grants": [
{
"src": ["tag:<client-tag>"],
"dst": ["tag:<bus-host-tag>"],
"ip": ["tcp:8443"]
}
]
}
  • For the direct bind, grant tcp:47910 instead.
  • autogroup:member works as a source if the clients are user devices rather than tagged ones.
  • Optionally add the app capability catesworks.com/cap/session-bus. The bus only logs it; it does not use it for authorization.
  • Add a policy test for the grant if your tailnet uses them.

Check from a client​

curl -s -o /dev/null -w '%{http_code}\n' -X POST https://<bus-host>.<tailnet>.ts.net:8443/mcp
# 401

401 means the request reached the bus and was refused for lack of a token. A timeout means the ACL or serve configuration is wrong.