| 1 | # The tenant listener: systemd owns the unix socket at |
| 2 | # /run/carlos/apps/<host>.sock and passes the bound fd to carlos-app@<host> |
| 3 | # (sd_listen_fds — the app sees LISTEN_FDS=1 and serves on fd 3). |
| 4 | # |
| 5 | # Why socket activation instead of the app binding a path itself: it is the |
| 6 | # simplest design that gets the PERMISSIONS right. The DynamicUser tenant |
| 7 | # owns the listening process, but the file is created by root and owned by |
| 8 | # carlos with mode 0600 — so the edge (user carlos) can connect and no |
| 9 | # tenant can reach another tenant's socket. This closes the loopback |
| 10 | # cross-talk gap that TCP Addr routes have. The alternatives all leak: a |
| 11 | # tenant-bound socket is owned by an ephemeral uid the edge can't connect |
| 12 | # to; umask/chmod games make it world-connectable; a shared group doesn't |
| 13 | # exist for DynamicUser without inventing static accounts. Here the tenant |
| 14 | # never needs path access at all — the fd arrives already bound, and the |
| 15 | # socket survives service restarts, so the edge's unix dial never even sees |
| 16 | # the rebind window. |
| 17 | # |
| 18 | # It is also the wake-on-connect half of hibernation: with the service |
| 19 | # stopped (the activator's idle timeout), a connection here starts it — |
| 20 | # including while the agent itself is down. The activator's edge-mediated |
| 21 | # wake (systemctl start + HTTP health wait) remains the primary path |
| 22 | # because it is health-gated: a broken boot becomes a clean 503 at the |
| 23 | # edge, not a connect parked forever in the listen backlog. |
| 24 | # |
| 25 | # Enable per tenant, alongside the route: |
| 26 | # systemctl enable --now carlos-app@<host>.socket |
| 27 | # Apps that cannot accept an inherited listener yet keep a TCP Addr route |
| 28 | # and NO socket unit — the documented fallback (with the cross-talk caveat |
| 29 | # above, until the app learns LISTEN_FDS). |
| 30 | |
| 31 | [Unit] |
| 32 | Description=CARLOS app %i socket |
| 33 | |
| 34 | [Socket] |
| 35 | ListenStream=/run/carlos/apps/%i.sock |
| 36 | SocketUser=carlos |
| 37 | SocketGroup=carlos |
| 38 | SocketMode=0600 |
| 39 | |
| 40 | [Install] |
| 41 | WantedBy=sockets.target |
| 42 | |