mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 02:29:33 +01:00
fix: keep foreign probe parsing linear in what it reads (release review)
The parser and classifier run synchronously on the event loop over text a pane, a container or an ssh host wrote, and one process could stall the server for every session: 4 s for a 128 KiB argv of `/pi/` tokens, about a minute for 2 MiB of socket rows. The `cli.js` launcher rule leaves the signature regex for a per-word scan, socket rows dedupe through a Set, the process row regex takes the `s` flag (a bare CR made `$` fail and `\s+` give its spaces back, quadratic in a row), argv and pane command are cut at FOREIGN_MAX_ARGV_CHARS, and one matcher per scan answers each distinct argv once, since every pane of a hostile host can claim the same process tree. Measured on a private tmux server with the same 131 KiB argv: the largest event-loop gap went from 3991 ms to 45 ms. The worst scan the caps allow takes about 80 ms. Socket rows stay uncapped on purpose (see the docs). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2c084966f1
commit
800b3f4658
5 files changed
+187
-24
No files matched your search
@@ -105,6 +105,8 @@ Input arriving during a wake is buffered: a chunk over 4 KB is dropped whole, ne
|
||||
|
||||
**A server that stops answering.** A tmux client never returns from a stopped server, and it hands dups of its stdout to the server, so that server holds our pipe open after the client is dead (measured: SIGTERM ended the client and no EOF came). ⚠️ So a watchdog around `list-panes` cannot work, and the same hold would keep `docker exec` and `ssh` waiting. `buildForeignProbeScript()` pings each socket with `has-session` at `/dev/null` under `FOREIGN_SOCKET_TIMEOUT_SECONDS`, in the script because no signal of ours reaches a client inside a container or across ssh, and only a server that answered gets the real stdout. One that did not prints `CMFH` and costs its own rows and a note. The watchdog reports through its own exit status, since a client ended by SIGTERM exits 0. `runForeignProbe()` runs all three transports in their own process group, signals the group at the deadline, destroys its end of the pipe by hand (or one handle leaks per scan) and keeps the rows it read. ⚠️ Sockets have no count cap: in glob order `codeman*` sorts before `default`, and a box with many instances would never reach the sessions this feature is for (212 sockets measured here). The sweep is bounded by time.
|
||||
|
||||
**Text from someone else's process.** The parser and the classifier run synchronously on the event loop over what a pane, a container or an ssh host printed, and the row caps do not help when one row is the whole 2 MiB. ⚠️ So every step stays linear in that text: no regex tail over an argv (`launcherPrefixes()` is a scan), `.*` under the `s` flag so `$` cannot fail and hand `\s+` its spaces back, a `Set` for the socket rows, `FOREIGN_MAX_ARGV_CHARS` on what is kept, and one `createForeignSignatureMatcher()` per scan, since every pane of a hostile host can claim the same process tree and repeat the same rows.
|
||||
|
||||
**One wrapper per target.** `foreignTargetKey()` is the ONE key, read by the adopt route and `adoptedBy`, built from the location, the host identity, the socket and the name: a socket and a name alone collide across containers and hosts. Both read live `Session`s, since the mux record only exists after the first attach.
|
||||
|
||||
**No workspace.** ⚠️ An adopted session keeps its connection on `adopt.remote`/`adopt.docker`, so `session.remote` and `session.docker` are undefined, and its `workingDir` is the neutral `/tmp`. Code that branches on those two reads an in-container or over-ssh adoption as a local session working in `/tmp`. So anything that treats `workingDir` as the session's workspace must also test `session.isAdopted` (`adopt` on a pure view): uploads, the upload GC, git status, git fetch and its scheduler, the image watcher, Read My Mind's git signals, the Discord folder, `last-response`, guarded input and Ralph's `@fix_plan.md` and `@ralph_prompt.md` routes all do. The file surfaces (Files panel, File Viewer and its edit mode) still treat `/tmp` as the workspace, which is harmless: it is world-writable anyway, and adoption is admin-only in multi-user mode. A wrapper reports `statusLineExporter: 'remote'`, which is also what keeps keep-warm off it.
|
||||
|
||||
Reference in new issue
Block a user