eleven / messenger Public

Clone
git clone https://amadan.net/eleven/messenger

Plain git — no account needed to clone.

Download

Download this file

1<!doctype html>
2<html lang="en">
3<head>
4<meta charset="utf-8" />
5<meta name="viewport" content="width=device-width, initial-scale=1" />
6<title>Everything that runs on a box — Running the fleet</title>
7<link rel="stylesheet" href="guide.css" />
8<script src="guide.js" defer></script>
9</head>
10<body data-page="processes.html">
11<div class="frame">
12<nav class="side"></nav>
13<main class="content">
14<div class="wrap">
15
16<header class="top">
17 <p class="eyebrow"><span class="robot">&#129302;</span> Running the fleet &middot; Part 03 of 10</p>
18 <h1>Everything that runs on a box</h1>
19 <p class="thesis">This page is a complete census of the processes on a fleet box: <strong>what each one is, how it is started, how it is configured, how it is supervised, and how you check on it.</strong> It also maps the whole filesystem layout, because every process is defined by the files it reads and writes.</p>
20</header>
21
22<div class="sysmap" data-highlight="router instances services control updater"></div>
23<p class="sysmap-caption">You are here: everything inside the &ldquo;one box&rdquo; zone. Each highlighted node is one or more of the processes catalogued below.</p>
24
25<h2 id="systemd-primer">A one-minute systemd primer</h2>
26<p>Every long-running process on a box is supervised by <strong>systemd</strong>, the standard Linux service manager. Three ideas are enough to follow everything on this page:</p>
27<ul>
28 <li>A <strong>unit</strong> is a small text file in <code>/etc/systemd/system/</code> describing one service: what command to run, as which user, with what restrictions, and what to do if it exits. All of this system&rsquo;s unit files are written for you by <code>lchat bootstrap</code> (described in <a href="deploys.html#bootstrap">part 04</a>); their sources are version-controlled in the repository under <code>deploy/systemd/</code>.</li>
29 <li>A <strong>template unit</strong> has an <code>@</code> in its name and is instantiated per name after the <code>@</code>. So <code>lchat@.service</code> is a template, and <code>lchat@chat-a.example.com.service</code> is the concrete service for one instance — systemd substitutes the part after <code>@</code> (written <code>%i</code> inside the unit file) wherever it appears. This one mechanism is how a single unit file serves any number of instances.</li>
30 <li>The verbs you will use constantly: <code>systemctl status &lt;unit&gt;</code> (is it running, since when, last log lines), <code>systemctl restart &lt;unit&gt;</code>, <code>systemctl enable --now &lt;unit&gt;</code> (start it and also start it at every boot), <code>systemctl disable --now &lt;unit&gt;</code> (stop it and stop starting it at boot), and <code>journalctl -u &lt;unit&gt;</code> (its full log).</li>
31</ul>
32<p>Nearly every unit here sets <code>Restart=always</code>, meaning systemd restarts the process within a second or two if it ever exits. Crash recovery is therefore automatic; your job is to notice <em>repeated</em> crashing (see <a href="operations.html#reading-logs">part 08</a>), not to restart things by hand.</p>
33
34<h2 id="census">The census: every process, at a glance</h2>
35<div class="tablewrap">
36<table>
37 <tr><th>systemd unit</th><th>What it is</th><th>Command it runs</th><th>Runs as</th></tr>
38 <tr><td><code>lchat-router.service</code></td><td>The <a href="router.html">router</a>: TLS + routing; hosts the <a href="hibernation.html">activator</a> and <a href="mesh.html">mesh</a> when configured.</td><td><code>/opt/lchat/lchat router --registry /data/registry.db --acme-cache /data/acme</code></td><td><code>lchat</code></td></tr>
39 <tr><td><code>lchat@&lt;host&gt;.service</code></td><td>One chat space instance (template unit; one per host).</td><td><code>/opt/lchat/live/&lt;host&gt; --socket /run/lchat/&lt;host&gt;.sock --db /data/&lt;host&gt;.db</code></td><td><code>lchat</code></td></tr>
40 <tr><td><code>lchat-bot@&lt;host&gt;.service</code></td><td>The optional bot sidecar for one instance (template unit).</td><td><code>/opt/lchat/live/&lt;host&gt; bot run</code></td><td><code>lchat</code></td></tr>
41 <tr><td><code>lchat-hooks@&lt;host&gt;.service</code></td><td>The optional webhook-ingest sidecar for one instance (template unit): serves <code>in.&lt;host&gt;</code> behind the router, turning inbound webhook HTTP into encrypted events. Stood up by <code>lchat add-hooks &lt;host&gt;</code>. <span class="status partial">built, not yet deployed</span></td><td><code>/opt/lchat/live/&lt;host&gt; hooks run</code></td><td><code>lchat</code></td></tr>
42 <tr><td><code>lchat-home.service</code></td><td>The home server: the anonymous, passkey-gated backup store (a separate binary from the same repository).</td><td><code>/opt/lchat/home --addr /run/lchat/home.sock --db /data/home.db</code></td><td><code>lchat</code></td></tr>
43 <tr><td><code>lchat-push.service</code></td><td>The push relay: the central Apple push-notification signer (separate binary).</td><td><code>/opt/lchat/push --addr /run/lchat/push.sock --db /data/push.db</code></td><td><code>lchat</code></td></tr>
44 <tr><td><code>lchat-links.service</code></td><td>The <code>11ch.at</code> short-link redirector for message permalinks (issue #905) — unlike home/push, a subcommand of the main <code>lchat</code> binary, not a separate one; holds no database (an in-memory, TTL-bounded verification cache only). Runs on the <strong>one</strong> box declared the short-link apex (<code>bootstrap --links-apex</code>); written and disabled everywhere else. <span class="status partial">built, not yet deployed</span></td><td><code>/opt/lchat/lchat links run --socket /run/lchat/links.sock</code></td><td><code>lchat</code></td></tr>
45 <tr><td><code>lchat-control.service</code></td><td>The control daemon: the root-only local API that creates and deletes instances. See <a href="spaces.html#controld">part 05</a>.</td><td><code>/opt/lchat/lchat controld</code></td><td>root (deliberately)</td></tr>
46 <tr><td><code>lchat-cloud.service</code></td><td>The cloud provisioner: the public, unprivileged self-serve space-creation service. See <a href="spaces.html#cloud">part 05</a>.</td><td><code>/opt/lchat/lchat cloud --socket /run/lchat/cloud.sock --db /data/cloud.db</code></td><td><code>lchat</code></td></tr>
47 <tr><td><code>lchat-update.timer</code> + <code>.service</code></td><td>The hourly auto-updater (a timer that fires a one-shot pass). See <a href="deploys.html#auto-updates">part 04</a>.</td><td><code>/opt/lchat/lchat update run</code></td><td>root (deliberately)</td></tr>
48 <tr><td><code>lchat-litestream.service</code></td><td>The fleet-backup replicator: streams every database on the box to the backup bucket, continuously. See <a href="operations.html#backups">part 08</a>.</td><td><code>litestream replicate</code> (a separate, stock Litestream binary with config <code>/etc/litestream.yml</code>)</td><td><code>lchat</code></td></tr>
49 <tr><td><code>lchat-tunnel.service</code></td><td>An optional try-it tunnel service for self-hosters; not part of normal fleet operation and not covered further in this guide.</td><td><code>/opt/lchat/lchat tunnel serve</code></td><td><code>lchat</code></td></tr>
50</table>
51</div>
52<p>One process is deliberately <em>absent</em> from this census when hibernation is in use: a hibernating instance has <strong>no systemd unit running at all</strong>. Its process, when awake, is a direct child of the router, invisible to <code>systemctl</code> but visible in the process tree — <a href="hibernation.html#children">part 06</a> explains how to see and manage those.</p>
53
54<h2 id="see-everything">Seeing everything at once</h2>
55<figure class="cmd">
56<figcaption>Run on the box — three views of what is running</figcaption>
57<pre><code># 1. Every lchat-related unit systemd knows about, with its state:
58systemctl list-units 'lchat*'
59<span class="out">UNIT LOAD ACTIVE SUB DESCRIPTION
60lchat-router.service loaded active running lchat router (TLS terminator + host router)
61lchat@chat-a.example.com.service loaded active running lchat instance chat-a.example.com
62lchat-bot@chat-a.example.com.service loaded active running lchat bot sidecar for chat-a.example.com
63lchat-control.service loaded active running lchat control daemon (local provisioning API)
64lchat-update.timer loaded active waiting hourly lchat release-channel update check</span>
65
66# 2. The fleet view from lchat itself — every registered host, whether systemd-managed
67# or hibernating, with status and version:
68lchat instances
69<span class="out">HOST KIND STATUS VERSION HIBERNATE SOCKET
70chat-a.example.com instance ready 1b4bd808 no /run/lchat/chat-a.example.com.sock
71chat-b.example.com instance ready 1b4bd808 yes /run/lchat/chat-b.example.com.sock
72home.example.com service ready - no /run/lchat/home.sock</span>
73
74# 3. Which unix sockets are actually bound right now (a hibernating host that is
75# asleep has a registry row but no bound socket):
76ss -xlH | grep /run/lchat</code></pre>
77</figure>
78<p>These three views answer different questions, and it is worth internalising the difference. <code>systemctl</code> shows <em>supervised processes</em>. <code>lchat instances</code> shows <em>the registry</em> — the intended state of the box, including hosts that are asleep on purpose. <code>ss</code> shows <em>reality at this instant</em> — which sockets have a live listener. When the three disagree, the disagreement is itself the diagnostic: a <code>ready</code> registry row with no bound socket and no unit is a sleeping hibernator (normal); a running unit with no bound socket is a process failing to start (investigate its journal).</p>
79
80<h2 id="filesystem">The filesystem layout: what lives where</h2>
81<p>Every process above is defined by files in three directories. This layout is worth memorising, because nearly every operational question (&ldquo;where is this instance&rsquo;s data?&rdquo;, &ldquo;where is its admin token?&rdquo;, &ldquo;which binary is it running?&rdquo;) is answered by a path.</p>
82<h3 id="opt"><code>/opt/lchat/</code> — the software</h3>
83<div class="tablewrap">
84<table>
85 <tr><th>Path</th><th>Contents</th></tr>
86 <tr><td><code>/opt/lchat/lchat</code></td><td>The control binary: what the router, control daemon, updater, and your own command-line invocations run.</td></tr>
87 <tr><td><code>/opt/lchat/versions/&lt;ver&gt;/lchat</code></td><td>The <strong>version store</strong>: every staged build, one directory per build identifier (a short git commit hash). Builds accumulate here and are garbage-collected when nothing references them. See <a href="deploys.html#version-store">part 04</a>.</td></tr>
88 <tr><td><code>/opt/lchat/live/&lt;host&gt;</code></td><td>One symbolic link per instance, pointing into the version store. <strong>This is the single most important path on the box</strong>: each instance&rsquo;s unit runs <code>/opt/lchat/live/&lt;host&gt;</code>, so <em>which build an instance runs is decided entirely by where its link points</em>. Deploying is repointing a link; rollback is repointing it back.</td></tr>
89 <tr><td><code>/opt/lchat/incoming/</code></td><td>A staging area for uploaded and downloaded binaries before they are verified and moved into the version store.</td></tr>
90 <tr><td><code>/opt/lchat/.deploy.lock</code></td><td>A lock file that makes deploys, rollbacks, and updater passes take turns instead of interleaving. If two are attempted at once, the second fails immediately with a clear message.</td></tr>
91 <tr><td><code>/opt/lchat/manifest-stamp</code></td><td>The timestamp of the newest release manifest the box has ever accepted — the auto-updater refuses anything older, so the release feed cannot be replayed backwards. See <a href="deploys.html#signing">part 04</a>.</td></tr>
92 <tr><td><code>/opt/lchat/home</code>, <code>/opt/lchat/push</code></td><td>The sibling-service binaries.</td></tr>
93</table>
94</div>
95<h3 id="data"><code>/data/</code> — the state</h3>
96<div class="tablewrap">
97<table>
98 <tr><th>Path</th><th>Contents</th></tr>
99 <tr><td><code>/data/&lt;host&gt;.db</code> (+ <code>-wal</code>, <code>-shm</code>)</td><td>One instance&rsquo;s entire SQLite database. The <code>-wal</code> and <code>-shm</code> files are SQLite&rsquo;s write-ahead log machinery and travel with the database. Contents are end-to-end ciphertext plus routing metadata.</td></tr>
100 <tr><td><code>/data/&lt;host&gt;.env</code></td><td>The instance&rsquo;s <strong>environment file</strong> (mode 0600): its configuration as <code>KEY=value</code> lines, read by its unit at start. Critically, it holds <code>LCHAT_ADMIN_TOKEN</code> — the database stores only a hash of the admin token, so <strong>this file is the only recoverable copy</strong>. <code>lchat admin-link &lt;host&gt;</code> reads it for you.</td></tr>
101 <tr><td><code>/data/&lt;host&gt;-blobs/</code></td><td>The instance&rsquo;s encrypted attachment files (photos, videos), content-addressed, stored outside the database to keep the database small.</td></tr>
102 <tr><td><code>/data/&lt;host&gt;.vapid</code></td><td>The instance&rsquo;s web-push signing key. Kept out of the database on purpose (so database replicas in object storage carry no private key). If you ever move an instance between boxes, copy this file too, or every browser push subscription silently breaks — see <a href="operations.html#migration">part 08</a>.</td></tr>
103 <tr><td><code>/data/&lt;host&gt;-bot.env</code> + <code>/data/&lt;host&gt;-bot/</code></td><td>The bot sidecar&rsquo;s environment file and its persisted bot identities (session files). The existence of <code>&lt;host&gt;-bot.env</code> is, literally, what &ldquo;this instance has a sidecar&rdquo; means to the system.</td></tr>
104 <tr><td><code>/data/registry.db</code></td><td>The <a href="router.html#the-registry">registry</a> — the box&rsquo;s routing table and instance ledger.</td></tr>
105 <tr><td><code>/data/acme/</code></td><td>The TLS certificate cache (private keys inside — see <a href="router.html#tls">part 02</a>).</td></tr>
106 <tr><td><code>/data/router.env</code>, <code>control.env</code>, <code>cloud.env</code>, <code>home.env</code>, <code>push.env</code>, <code>update.env</code></td><td>Per-service environment files, one per unit, same <code>KEY=value</code> format. Each unit declares its own; most are optional.</td></tr>
107 <tr><td><code>/data/home.db</code>, <code>push.db</code>, <code>cloud.db</code></td><td>The sibling services&rsquo; own databases.</td></tr>
108 <tr><td><code>/data/trash/&lt;host&gt;-&lt;timestamp&gt;/</code></td><td>Archived data of deleted spaces. Deletion in this system <strong>archives, never destroys</strong> — see <a href="spaces.html#removing">part 05</a>.</td></tr>
109</table>
110</div>
111<h3 id="run"><code>/run/lchat/</code> — the sockets</h3>
112<p>A memory-backed directory (recreated at every boot) holding the unix sockets: <code>&lt;host&gt;.sock</code> for each awake instance, plus <code>home.sock</code>, <code>push.sock</code>, <code>cloud.sock</code>, and <code>control.sock</code>. Sockets are transient by nature; nothing here needs backing up.</p>
113<p>Two more paths complete the picture: <code>/etc/lchat/control.token</code> (a root-only secret gating the control daemon — <a href="spaces.html#controld">part 05</a>) and <code>/etc/litestream.yml</code> (the fleet-backup configuration — <a href="operations.html#backups">part 08</a>).</p>
114
115<h2 id="instances">The instance process, in detail</h2>
116<p>Each chat space runs as one instance of the template unit <code>lchat@.service</code>. Reading its key lines teaches you most of the process model:</p>
117<figure class="cmd">
118<figcaption>The heart of /etc/systemd/system/lchat@.service (%i = the host)</figcaption>
119<pre><code>EnvironmentFile=-/data/%i.env
120ExecStart=/opt/lchat/live/%i --socket /run/lchat/%i.sock --db /data/%i.db
121Restart=always
122RestartSec=1
123User=lchat
124MemoryMax=512M</code></pre>
125</figure>
126<ul>
127 <li><strong>The host is the identity.</strong> The name after <code>@</code> flows into every path: the binary link, the socket, the database, the environment file. Knowing a host, you can find everything about it.</li>
128 <li><strong>Configuration is the environment file.</strong> The instance reads ordinary environment variables (<code>LCHAT_ADMIN_TOKEN</code>, <code>LCHAT_PUSH_RELAY_URL</code>, and so on); the unit points them at <code>/data/&lt;host&gt;.env</code>. Changing an instance&rsquo;s configuration means editing that file and running <code>systemctl restart lchat@&lt;host&gt;</code>.</li>
129 <li><strong>The version is the symlink.</strong> <code>ExecStart</code> runs <code>/opt/lchat/live/&lt;host&gt;</code>, so a deploy changes the link and restarts the unit — nothing about the unit itself changes. <code>readlink /opt/lchat/live/&lt;host&gt;</code> tells you exactly which build an instance runs.</li>
130 <li><strong>Resource limits are per instance.</strong> <code>MemoryMax=512M</code> means one runaway instance is killed by the kernel before it can starve the others — isolation is enforced, not hoped for.</li>
131 <li><strong>Graceful shutdown is built in.</strong> On being told to stop, the instance finishes in-flight requests for up to ten seconds and closes its WebSocket connections cleanly (clients auto-reconnect), then exits. Combined with the router&rsquo;s <a href="router.html#restart-tolerance">thirteen-second dial retry</a>, this is why restarts do not drop requests.</li>
132</ul>
133<figure class="cmd">
134<figcaption>Run on the box — the routine per-instance checks</figcaption>
135<pre><code>systemctl status lchat@chat-a.example.com # running? since when? recent log lines
136journalctl -u lchat@chat-a.example.com -n 200 # its full journal (add -f to follow)
137readlink /opt/lchat/live/chat-a.example.com # which build does it run?
138<span class="out">/opt/lchat/versions/1b4bd808/lchat</span>
139curl -s --unix-socket /run/lchat/chat-a.example.com.sock http://localhost/api/version
140<span class="out">{"version":"a41c9f2","build":"1b4bd808"}</span> # answers even if DNS/TLS are broken — isolates a problem to the instance itself</code></pre>
141</figure>
142
143<h2 id="security-model">The security posture every unit shares</h2>
144<p>All the units are heavily sandboxed with systemd&rsquo;s confinement features: they run as the unprivileged system user <code>lchat</code>, see a read-only filesystem except <code>/data</code> and <code>/run/lchat</code>, cannot gain new privileges, cannot load kernel modules, and are memory-capped. Two deliberate exceptions run as root: the <strong>control daemon</strong> and the <strong>auto-updater</strong>, because their whole job is to do the two privileged things (start and stop units via <code>systemctl</code>, and write the version store). Both are reachable only locally — the control daemon on a local socket gated by a root-only token, the updater only via its timer. The router is the one unit with a special capability (<code>CAP_NET_BIND_SERVICE</code>) so it can bind ports 80 and 443 <em>without</em> being root.</p>
145<div class="plain">
146 <span class="label">In plain words</span>
147 <p>Everything runs as a locked-down, unprivileged user except the two processes whose job is administration itself, and those two never talk to the internet. If an instance is somehow compromised, the sandbox means it cannot read other instances&rsquo; secrets, cannot touch the software store, and cannot escalate.</p>
148</div>
149
150<h2 id="sibling-services">The sibling services: home, push, and links</h2>
151<p>Three services deploy next to the messenger but are not chat instances, and the router treats them as plain <code>service</code> routes — names it forwards traffic to, whose lifecycle it does not manage:</p>
152<ul>
153 <li><strong>The home server</strong> (<code>lchat-home.service</code>) is the account-recovery backup store: members&rsquo; encrypted key backups, gated by passkeys. Its one operational constraint is that <strong>its hostname must never change</strong> — the hostname is part of the passkey security model (the WebAuthn &ldquo;relying party&rdquo; origin), and changing it orphans every passkey. Its environment file <code>/data/home.env</code> is <em>required</em> (no leading <code>-</code> in the unit) because a misconfigured home server is worse than a stopped one.</li>
154 <li><strong>The push relay</strong> (<code>lchat-push.service</code>) holds the one Apple push-notification signing key for the whole fleet, so that individual instances never do. Instances authenticate to it with per-instance revocable tokens. Its environment file <code>/data/push.env</code> is likewise required.</li>
155 <li><strong>The links redirector</strong> (<code>lchat-links.service</code>, <span class="status partial">built, not yet deployed</span>) fronts <code>11ch.at</code>, the shared short-domain for message permalinks (issue #905 — <code>linkscmd.go</code>): a bare request to the apex bounces to <code>elevenmessenger.com</code>, and <code>/l/&lt;host&gt;</code> live-checks the target (an SSRF-guarded fetch of its own <code>/api/version</code>, cached in memory) before redirecting. Unlike home and push it is a <em>subcommand</em> of the main <code>lchat</code> binary rather than a separate one, and it holds no database at all — a restart just re-populates its verification cache from scratch, which is cheap because entries are looked up lazily. Exactly <em>one</em> box in a fleet is the short-link apex, and only that box runs it: <code>lchat bootstrap --links-apex &lt;host&gt;</code> starts the redirector and registers the host as a route pointing at its socket. Everywhere else the unit is written and left disabled, so no box carries a process it has no use for. The route is then the source of truth — later bootstraps read the registry and converge to it, so the apex stays up across re-runs and every other box stays quiet.</li>
156</ul>
157<p>All three are registered with the router by <code>lchat add-service &lt;host&gt; --socket &lt;path&gt;</code>, which simply adds a <code>service</code> row to the registry after checking the backend answers. Deploying home or push is manual and rare: copy the new binary over the old one and <code>systemctl restart</code> the unit (<a href="deploys.html#sibling-deploys">part 04</a>); links, being a subcommand of the main binary, picks up a new build whenever <code>/opt/lchat/lchat</code> is replaced <em>and its unit is restarted</em> — which both <code>lchat bootstrap</code> and the hourly auto-updater do for you (bootstrap restarts rather than <code>enable --now</code>s precisely so an update re-execs the new binary instead of leaving the old process running; the updater bounces it alongside <code>lchat-control</code> and <code>lchat-cloud</code>, the other units that exec the control binary directly).</p>
158
159<h2 id="bot-sidecars">Bot sidecars: a member that runs on a server</h2>
160<p>An instance can have a <strong>bot sidecar</strong>: a process hosting the built-in helper personas (Claude and Eleven). Architecturally, a bot is an ordinary end-to-end encrypted <em>member</em> of the chat whose &ldquo;device&rdquo; happens to be a server process: it holds its own private keys (in the session files under <code>/data/&lt;host&gt;-bot/</code>) and connects to its instance as a WebSocket <em>client</em>, over the instance&rsquo;s public hostname, exactly like a browser would. It serves no inbound traffic at all — no socket, no port, no route.</p>
161<div class="why">
162 <span class="label">Why it is this way</span>
163 <p>The product&rsquo;s core rule is that the server process never holds a usable decryption key. A bot must decrypt the threads it participates in, so its keys must live in a <em>different</em> process — one the operator can place anywhere (the same box, a different machine, behind a home router), because it only ever dials out. Compromising the server must not yield bot keys; this separation is what guarantees that.</p>
164</div>
165<p>Operationally: <code>lchat add-bot &lt;host&gt;</code> provisions the identities and writes <code>/data/&lt;host&gt;-bot.env</code>; the unit <code>lchat-bot@&lt;host&gt;.service</code> runs <code>/opt/lchat/live/&lt;host&gt; bot run</code> — note that it execs <em>the same versioned symlink as its instance</em>, so the bot always runs the same build as its app, by construction, and every deploy restarts it onto the matching version. Give it intelligence by adding <code>ANTHROPIC_API_KEY=&hellip;</code> to its environment file and restarting; without a key the bots still run with deterministic (non-AI) behaviour.</p>
166<p>One subtlety worth knowing even if you never touch it: because the sidecar dials its instance&rsquo;s <em>public</em> URL, the box must resolve its own hostnames to itself. Every hosted hostname is pinned to <code>127.0.0.1</code> in <code>/etc/hosts</code>, so on-box clients reach the local router directly instead of going out to the internet and back. Cloud images that regenerate <code>/etc/hosts</code> on boot have that behaviour disabled. If a sidecar logs connection failures after a migration, stale DNS or a missing hosts-pin is the first thing to check.</p>
167<p>On a <em>hibernating</em> instance the sidecar unit is disabled and the activator co-spawns the sidecar only while the instance is awake — that story is told in <a href="hibernation.html#sidecar-co-spawn">part 06</a>.</p>
168
169<h2 id="config-changes">How to change any process&rsquo;s configuration</h2>
170<p>The pattern is identical everywhere, and worth stating once as a rule: <strong>configuration lives in an environment file under <code>/data/</code>, and takes effect on restart.</strong></p>
171<ol class="procedure">
172 <li><p class="step-title">Edit the environment file for the process.</p>
173 <p>For an instance: <code>/data/&lt;host&gt;.env</code>. For the router: <code>/data/router.env</code>. For a sidecar: <code>/data/&lt;host&gt;-bot.env</code>. And so on per the <a href="#data">table above</a>. Keep the file&rsquo;s ownership and 0600 mode — it may hold secrets.</p></li>
174 <li><p class="step-title">Restart (or reload) the corresponding unit.</p>
175 <figure class="cmd">
176 <figcaption>Run on the box</figcaption>
177 <pre><code>systemctl restart lchat@chat-a.example.com # instance config change
178systemctl restart lchat-router # router config change (or reload, for route changes only)</code></pre>
179 </figure></li>
180 <li><p class="step-title">Confirm the process came back healthy.</p>
181 <figure class="cmd">
182 <figcaption>Run on the box</figcaption>
183 <pre><code>systemctl status lchat@chat-a.example.com
184curl -s https://chat-a.example.com/api/version</code></pre>
185 </figure></li>
186</ol>
187<div class="warn">
188 <span class="label">One exception</span>
189 <p>A <em>hibernating</em> instance has no unit to restart. Its environment file is read fresh by the activator at every wake, so an edit takes effect at the next wake with no action from you — or force the matter by making any request to the host. See <a href="hibernation.html#children">part 06</a>.</p>
190</div>
191
192</div>
193</main>
194</div>
195</body>
196</html>
197