carlos / platform Public

hostfiles: resource bounds on tenant-writable trees (gate before region HA activation) Issue open

hostfiles has no resource bounds on tenant-writable trees, and region HA relocation will be its first production caller on that ground.

region-ha-1b-next-build's release flush and install now reach hostfiles on nested instance directories the tenant can write. The relocation reads and writes were hardened there and reviewed by Opus and Astra:

  • symlinks, FIFOs, oversized files and vanished directories are refused;
  • paths are capped at 4095 bytes;
  • the walk is cancellable.

Astra's confirming review (docs/superpowers/plans/CODEX-REVIEW-REGION-HA-TENANT-DIR-2.md on that branch) found two gaps that were already in main's hostfiles:

  1. Restore's writeInside calls root.RemoveAll(x) with no bound (restore.go ~409). If the manifest names a shallow file x and the tenant has left a deep directory tree at x, Go's RemoveAll recurses holding one descriptor per level, with no ctx check. That can exhaust descriptors or stretch the install out while the host guard is held.
  2. The walk's memory is unbounded by directory width (walkunix.go:32, dirunix.go:74). d.names() calls Readdirnames(-1) and sorts every name before any cap or cancellation check. A tenant can leave enough empty files in one directory to exhaust the agent's memory. Ancestor name lists also stay allocated during recursion.

Ask. Bound and make cancellable the deletion traversal. Read directories in bounded batches against a total traversal budget.

This is a gate before CARLOS_REGION_HA is switched on anywhere. It is not a merge blocker, because relocation is dormant. Opus also noted that agent memory grows with a tree's file count up to the inode count. That is inherent to publishing a full manifest, but the budget above would cap it too.

A third bound, found while making the path cap consistent (region-ha-1b-next-build 400a3302). Restore's time grows with the cube of tree depth. At the 4095-byte path cap that allows about 2048 components, which comes to roughly an hour of one core, spent while the install holds the host guard. A top-down cached traversal would make it linear. That's a change to main's Restore, so it belongs with the two bounds above, and it's another gate before activation.