Download this file
| 1 | [Unit] |
| 2 | Description=lchat instance %i |
| 3 | After=network-online.target |
| 4 | Wants=network-online.target |
| 5 | |
| 6 | [Service] |
| 7 | # add-instance writes /data/<host>.env (0600) with LCHAT_ADMIN_TOKEN + per-instance |
| 8 | # config. The leading '-' makes it optional so a self-provisioning instance still boots. |
| 9 | EnvironmentFile=-/data/%i.env |
| 10 | # Execs a per-instance symlink (/opt/lchat/live/<host> → /opt/lchat/versions/<ver>/ |
| 11 | # lchat) rather than one shared binary, so each instance runs its own version and a |
| 12 | # build can be canaried on one instance before it's promoted. add-instance / |
| 13 | # init-versions create the link; `lchat deploy --version` repoints it. See |
| 14 | # docs/instance-router.md § Versioned deploys. |
| 15 | ExecStart=/opt/lchat/live/%i --socket /run/lchat/%i.sock --db /data/%i.db |
| 16 | Restart=always |
| 17 | RestartSec=1 |
| 18 | |
| 19 | # --- hardening (see docs/instance-router.md § Hardening) --- |
| 20 | User=lchat |
| 21 | Group=lchat |
| 22 | # Binds a unix socket only — no capabilities needed. |
| 23 | CapabilityBoundingSet= |
| 24 | NoNewPrivileges=yes |
| 25 | ProtectSystem=strict |
| 26 | ProtectHome=yes |
| 27 | ReadWritePaths=/data /run/lchat |
| 28 | PrivateTmp=yes |
| 29 | PrivateDevices=yes |
| 30 | ProtectClock=yes |
| 31 | ProtectHostname=yes |
| 32 | ProtectKernelTunables=yes |
| 33 | ProtectKernelModules=yes |
| 34 | ProtectKernelLogs=yes |
| 35 | ProtectControlGroups=yes |
| 36 | ProtectProc=invisible |
| 37 | RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX |
| 38 | RestrictNamespaces=yes |
| 39 | RestrictRealtime=yes |
| 40 | RestrictSUIDSGID=yes |
| 41 | LockPersonality=yes |
| 42 | MemoryDenyWriteExecute=yes |
| 43 | SystemCallFilter=@system-service |
| 44 | SystemCallErrorNumber=EPERM |
| 45 | SystemCallArchitectures=native |
| 46 | MemoryMax=512M |
| 47 | TasksMax=256 |
| 48 | |
| 49 | [Install] |
| 50 | WantedBy=multi-user.target |
| 51 | |