Found while reading the app, not while fixing anything. The policy editor is the
only one that costs an ssh session today.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Closes the TODO item about orphaned/duplicated VNC servers across sidecar restarts. The diagnosis
there was correct and outlived the move off x11vnc, because the shape of the bug is in the lifecycle,
not the server: the running desktop lives in module state, a sidecar restart forgets it while the
process keeps running, and waitForPort accepted ANY listener on 5900 as proof of a healthy start.
The next start would then spawn a server that could not bind the port, see the ORPHAN listening, and
report success — leaving the platform convinced it had started a desktop the browser was not
looking at.
Two changes. reclaimPort frees the port before spawning: TERM whatever holds it, KILL after two
seconds. waitForPort now also fails when the process we spawned has exited, so a foreign listener
cannot be mistaken for our own.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
First pass of the documentation audit. Every doc was read against what the code actually
does now; this commit fixes the ones worth keeping and deletes the ones that were only
describing a past.
Corrected:
- CLAUDE.md — said seven WebSocket providers (there are eight, and terminal/vault are byte
relays now, not translating bridges), listed channels/ as "Telegram / WhatsApp / Discord
bridges" (they are gone; what remains is how /chat drives an agent turn), missed
officer-wallet in the PM2 list and notify/ in the layout, and described the per-account
email SQLite stores without saying they are the sidecar's and that nothing in the platform
opens them. Further Reading pointed at four files that no longer exist and missed the four
newest.
- docs/working-on-officer.md — PM2 list was four sidecars short, and it still explained the
officer-claude rename as news. Replaced with the thing a reader actually needs: which
process to restart for which change, and why restarting officer no longer costs you a
terminal or an agent session.
- TODO.md — the "dead username plumbing" item was mostly resolved by deleting the channels,
and two email items pointed at api/email/email-db.ts, which is sidecar/email/store.ts now.
- AGENTS.md — trailing paragraph listed the design notes being deleted here.
- MUSIC_API.md — playlists were entirely undocumented: seven endpoints the phone app has no
reference for. Added from the sidecar's own contract.
- docs/jobs-unification.md — phases 1-3 shipped, so it now says so at the top. Phase 4 (push
notifications) is the only reason the file still exists, and email sync is explicitly no
longer part of it.
Deleted, all superseded rather than merely old:
- PHONE_APP.md — a February plan for apps that now exist, with their own repo and README.
- MARKETING_WEBSITE.md — a plan for a site this repo does not contain.
- SECURITY_AUDIT.md + SECURITY_FIXES.md — a February audit of a codebase since restructured;
it still cites queue/handlers, which is now empty.
- docs/DOCKERIZATION_PLAN.md — cites pty-sidecar, whatsapp and projects, all deleted.
- SETUP_GUIDE.md — documents systemd units and setup scripts replaced by PM2 and `bun setup`.
Not harmless: /etc/systemd/system/officer-pty-sidecar.service is still enabled on this host,
pointing at a `monorepo/` directory that no longer exists, and has been failing to start
ever since. That guide is how it got there.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
officer-transmission is a new pm2 peer that owns the transmission rpc
connection and exposes a curated /_officer/* contract instead of proxying
raw rpc. it absorbs the three quirks callers otherwise have to know about:
the 409 x-transmission-session-id handshake, failures returned as
{"result": "..."} inside http 200, and basic auth where an empty username
must send no header at all.
/transmission is the ui, on the workspace/panel framework: a filter nav and
three sections (torrents, stats, settings). the torrent list is virtualised
with 30 available columns, multi-select, and a right-click menu; the detail
pane covers general, files as a real tree, peers and trackers. filters and
the open torrent live in the url, so a filtered view is a link.
phase 1 goal was parity with _references/transmission-web. follow-up work is
recorded in TODO.md.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Remove the multi-user provisioning leftovers (the provision-existing-users.sh
migration was already deleted in the prior commit):
- gut provisionVncEnv from provision.ts (per-user startxfce4 virtual desktop, dead
since the switch to mirroring :0 — vnc-manager.ts self-provisions its own passwd)
- drop the Pi `.pi/agent/sessions` seed and the now-orphaned `run` helper
Wire provisioning into bootstrapHandler: the super admin (first user) is created via
createUser, which never called provisionUserEnvironment — only the invite/verify flow
did. So the single user's DATA_PATH/<email> was never provisioned up front. Now it is.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
the file browser sends msg.cwd relative to the user's home and it went straight to
Bun.spawn, which resolves it against the server's cwd. when that directory does not
exist posix_spawn reports ENOENT naming the binary rather than the directory, so
"Convert Audio" failed with the misleading "posix_spawn 'bash'".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
the hand-installed systemd unit kept a parallel xfce session on :1 alive and
respawned it on every kill, independently of the platform. obsolete now that the
desktop panel mirrors :0; disabled, but the unit file is still on disk.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- the sandbox already drops privileges to the OS user (id -un), so pi-bridge was
the only code path that switched to a per-user unix account
- the four accounts it targeted (andrepadez, john-wick, fedra, miguelbenoliel)
are vestigial: created by scripts/provision-existing-users.sh, with no home
dirs, no files, no processes. the only live account is pastilhas@officer.dev,
which maps to the service user, so isServiceUser was always true and the
sudo -u branch could never fire
- add TODO.md tracking the leftover username plumbing, the useradd script, known
bugs (task-executor relative cwd, bootstrap pi EEXIST), and the ufw 9010 rule
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>