a host can drop a core sidecar it does not use

`OFFICER_DISABLED_SIDECARS` in .env, comma separated, filtered out of the
generated ecosystem file.

core processes are a fixed array because the platform's own features depend on
them. that is still true — what is not true is that every host runs every
feature. fofos is a 1.9 GB machine serving a website, and officer-opencode sat on
it costing 456 MB for a harness nobody there uses.

it froze hard twice today, and the tell is what is MISSING from the journal: no
OOM kill, no panic, no shutdown — the log simply stops mid-session and resumes at
the manual reboot. that is memory exhaustion with no headroom left to kill
anything cleanly, on a box with 1.9 GB and a 2 GB swapfile that was already 440
MB into swap one minute after boot.

.env rather than a database setting, and that is the load-bearing part: this file
is regenerated during a plugin install, so a host that dropped a process needs it
to stay dropped across one. per-host is also exactly the scope of the decision.

not validated. naming `officer` or `officer-pty` will break that host, and which
processes are safe to drop depends on what the host is for — which this script
cannot know and should not guess.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
This commit is contained in:
2026-08-28 23:02:52 +01:00
co-authored by Claude Opus 5
parent f4685eeafe
commit 4e0a065f0e
+26 -1
View File
@@ -60,9 +60,34 @@ function coreProcesses(): Pm2App[] {
const [name, script, args] = line.split('|'); const [name, script, args] = line.split('|');
if (!name || !script) return fail(`unreadable CORE_PROCESSES entry: "${line}"`); if (!name || !script) return fail(`unreadable CORE_PROCESSES entry: "${line}"`);
return { name, script, args, cwd: PLATFORM_DIR, watch: false }; return { name, script, args, cwd: PLATFORM_DIR, watch: false };
}); })
.filter((app) => !DISABLED.has(app.name));
} }
/*
Core processes this host does not want, from `OFFICER_DISABLED_SIDECARS` in `.env`.
A core process is core because the platform's own features depend on it — which is why the list is a
fixed array and not a setting. But "the platform depends on it" is not "every host runs it": fofos is a
1.9 GB machine serving a website, and `officer-opencode` sat there costing 456 MB of that for a harness
nobody on it uses. It froze hard twice, with no OOM line in the journal — which is what memory
exhaustion looks like when there is not enough headroom left to kill something cleanly.
Deliberately NOT a database setting. This file is regenerated during a plugin install, so a host that
has dropped a process needs it to stay dropped across one. `.env` is read at generation time and is
per-host, which is exactly the scope of the decision.
Naming something the platform genuinely needs will break that host — `officer` itself, or
`officer-pty` if anyone uses a terminal. Not validated, because which processes are safe to drop
depends on what the host is for, and this script cannot know that.
*/
const DISABLED = new Set(
(process.env.OFFICER_DISABLED_SIDECARS ?? '')
.split(',')
.map((entry) => entry.trim())
.filter(Boolean),
);
/** Installed AND enabled AND actually ships a sidecar — all three, or it gets no entry. */ /** Installed AND enabled AND actually ships a sidecar — all three, or it gets no entry. */
async function pluginEntries(): Promise<Pm2App[]> { async function pluginEntries(): Promise<Pm2App[]> {
const [installs, { plugins }] = await Promise.all([listPluginInstalls(), discoverPlugins()]); const [installs, { plugins }] = await Promise.all([listPluginInstalls(), discoverPlugins()]);