Files
platform/TODO.md
T

7.0 KiB

TODO

Deferred work. Context: Officer is collapsing from multi-tenant / open-source-ready to a single-user platform. Treat multi-tenant indirection as accidental complexity, not a requirement.

Single-user cleanup

  • Remove dead username plumbing. pi-bridge.spawnPi no longer uses username — it always runs as the service user. But the handlers still compute toShellUsername(...) and thread it through sendAndAwaitspawnOptions, where nothing reads it. send-claude-code declares username (lines 8, 36) without using it. Typechecks clean today (excess-property checks don't fire on variables), so it silently looks load-bearing. Touches: channels/{whatsapp,telegram,discord}/handler.ts, channels/send-and-await.ts, channels/send-claude-code.ts. After this, toShellUsername has one caller left (provision.tsgenerateClaudeSettings) and may be inlinable.

  • Delete or gut scripts/provision-existing-users.sh. Line ~100 runs sudo useradd -d "$HOME_DIR" -s /bin/zsh -M "$shell_user". This is what created the four vestigial Unix accounts (andrepadez, john-wick, fedra, miguelbenoliel) that cluttered the lightdm greeter. Nothing in the server creates Unix users; if this script runs again they come back.

  • Collapse the rest of the multi-tenant machinery. Candidates, in rough order of payoff: roles (Super Admin/Member), the sandboxed-vs-unsandboxed path split, per-email home dirs under dev-data/{email}/home, per-user server state (dock, user apps, AppRegistry keyed by email), and auth (passkeys-per-origin, JWT signin, unmounted PasskeyGate).

Email

  • Gmail-style search operators (done 2026-07-24, 0e5b91e). parseEmailQuery + searchEmails in api/email/email-db.ts parse: from:/to:/subject:/body: → FTS5 column filters; has:attachment, is:unread/is:read, label:X, before:/after:YYYY-MM-DD → SQL WHERE on emails; quoted values → exact phrases; free text → prefix-AND full-text; unknown op:val falls back to free text. Structured-filters-only queries skip the FTS join. Validated on the real 18k-mail DB. Search box placeholder hints at operators.

    • OR (done 2026-07-24, 85d38fb). Query splits on top-level uppercase OR into branches; each branch is a self-contained condition (id IN (FTS subquery) + its SQL filters) and branches are OR'd — so OR works across full-text and structured filters. Verified on the 18k DB (from:github OR from:deepgram = 90+7 = 97 exactly).
    • Remaining: parenthesised grouping (nesting (a OR b) c), in:sent/inbox/spam/trash (folder scope), and relative dates (newer_than:7d). Grouping needs a recursive parser.
    • Group search results into threads too. Folder views group by conversation (ee11c94), but /email/search still returns one row per message. Apply the same thread_id collapse to the FTS result set so search matches Gmail's grouped results.
  • Conversation threading (done 2026-07-24, ee11c94). thread_id column on emails: header-based for new mail (id = sha1(Message-Id), so References[0] hashes to the root's id — computeThreadId in email-db.ts), subject+counterpart backfill for already-synced mail. /messages collapses to one row per thread (window fn) with count/unread; /thread/:id + /thread/:id/read; reader renders a collapsible stack. Verified on synthetic + 18k real DB.

    • Upgrade old mail to exact threading. One-time full re-fetch to capture References for already-synced mail — replaces the subject+counterpart fallback (which can over-merge recurring same-subject mail from one sender). Heavy/network-bound; opt-in.
    • Store the RFC Message-Id header on ingest so replies can set a real In-Reply-To (currently reply threading leans on subject/participants; id is a local hash, not the header).
  • Compose/reply/send (done 2026-07-24). Gmail SMTP send with contacts autocomplete, rich contenteditable body (inline images at the caret via paste/drag-drop, Bcc), single close button.

  • busy_timeout on the email db (done 2026-07-24, 9527e0b). API + email sidecar share emails.db; wait out a concurrent writer instead of 500-ing with "database is locked".

Known bugs

  • bootstrap.ts runs npm install -g for Pi on every boot. findPiPackageDir checks stale paths and an outdated package name, so installPi always fires and floods the logs with EEXIST noise. Should detect @earendil-works/pi-coding-agent at the real npm prefix.

  • VNC mirror can orphan/duplicate x11vnc across sidecar restarts. vnc-manager.startSession tracks the running mirror in module-level state and skips spawning only if that pid is alive. On a vnc-sidecar restart the state resets to null while the old x11vnc keeps running orphaned; the next startSession spawns a second one. Worse, waitForPort treats any listener on 5900 as success, so the platform can believe it started a mirror that is actually a stale process — desyncing into multiple contending x11vnc instances on :0 (suspected cause of click-lag on 2026-07-16, cleared by killing all but one). Fix: before spawning, kill any existing x11vnc -display :0 / free port 5900 (an ExecStartPre-style cleanup, like the old officer-vnc.service unit did with vncserver -kill).

Infra (alpha)

  • Delete /etc/systemd/system/officer-vnc.service. Hand-installed unit (not in this repo) that ran vncserver :1 -geometry 1920x1080 -localhost yes -fg with Restart=on-failure, enabled at boot — it kept a whole parallel XFCE session alive on :1 (283 MB, 166 tasks) independently of the platform, and silently respawned it whenever the display was killed. Obsolete now that the Desktop panel mirrors :0 via x11vnc. Disabled 2026-07-15 (systemctl disable --now), but the unit file is still on disk. Nothing in the codebase recreates it and nothing documents it, so delete the file rather than leave a mystery service one systemctl enable away from returning.

  • ufw blocks port 9010 on the LAN. Default deny incoming; only 22/tcp, 80/tcp, 443/tcp, and everything on tailscale0 are allowed — so http://192.168.47.196:9010 is unreachable from the LAN while localhost and Tailscale work. Fix: sudo ufw allow from 192.168.47.0/24 to any port 9010 proto tcp

Backburner

  • Music tagging feature (/music, Mp3tag-inspired). v1 was scoped as a two-panel workspace — file browser left, table right, single "Load" context-menu item flattening audio files into the table. Discarded before completion; would need GET /file-browser/flatten-audio, a useFilesAPI.flattenAudio method, and the Music app panel rebuilt from scratch.