Merge branch 's3-async-copy' into s3-followups

This commit is contained in:
2026-10-09 15:18:57 +07:00
6 changed files with 130 additions and 33 deletions
+3 -1
View File
@@ -574,7 +574,9 @@ a pane really is. Each case below is refused or bounded, with a test in
a whole member file is read only up to a cap (1 MiB for a login or a
settings file, 16 MiB for an index or a workflow journal, 256 MiB for a
SQLite copy, 64 MiB otherwise); a member transcript over 256 MiB is neither
parsed nor snapshotted, and the snapshot keeps a sparse file's holes.
parsed nor snapshotted, and the snapshot keeps a sparse file's holes. The
snapshot copy is asynchronous (1 MiB at a time), so a big one never stalls
the dashboard for other users, and it lands by rename — never half-written.
- **A request that makes a route throw** answers 500 instead of stopping the
server (a member folder of a gone user crashed `POST /api/lanes/worktree`).
- **Who a pane runs as.** A non-admin owns no workspace outside their home
+1 -1
View File
@@ -278,7 +278,7 @@ Review ran 2026-10-08 (four lenses: root/sudo, cross-member, secrets, local-mode
- F39 `execIn` and `write-as` (every sync sudo into a member) stop after 5 s (`MEMBER_EXEC`) — a FIFO `include.path` or a FIFO write target hung git and write-as;
- F40 transcripts and journals are read with `readLines` (chunked, a line over 64 MiB is skipped, never built) — readline crashed the server on a one-line 512 MB sparse file (import-history, sessions, codex-usage);
- F41 `readFileSafe*` read a member's file only up to a cap (`max`; 1 MiB logins/settings, 16 MiB indexes/journals, 256 MiB SQLite copies, 64 MiB default), and transcript-cache skips a member transcript over 256 MiB;
- F42 the transcript snapshot skips a member file over 256 MiB and writes zero runs as holes — a sparse file was copied out as real blocks into the host's disk;
- F42 the transcript snapshot skips a member file over 256 MiB and writes zero runs as holes — a sparse file was copied out as real blocks into the host's disk (follow-up: the copy is asynchronous, 1 MiB per read/write, so a big one no longer blocks the event loop; one copy per snapshot file at a time, written to a temp file and renamed into place);
- F43 an async route's rejection goes to `next(err)` (Express `Layer.handle_request`), and `refuseS3Folder` uses `ownerForRead` — `POST /api/lanes/worktree` with a gone member's folder crashed the server;
- F44 a workspace outside the users root may be owned only by an admin (live role: `assertOwnerFits` at create, assign and every spawn), and demoting/disabling a user ends their host panes — a developer-owned host folder started host-account panes;
- F45 a pane's cwd must be its workspace's account's (`ECWDACCOUNT`, checked before anything is written for the pane);