Files
sencho/backend
Anso 2d9e4d0480 fix(mesh): derive override services from compose file and surface cross-node dispatch (#1017)
* fix(mesh): derive override services from compose file and surface cross-node dispatch

Three fixes for issues found during Phase B end-to-end verification:

1. Override generator no longer queries Dockerode for services. Deploys
   removed all containers in the project right before composeArgs ran,
   so docker.listContainers({label: project=...}) returned [] and the
   generator wrote services: {} which broke meshed user containers.
   New helper getDeclaredStackServiceNames reads the local stack's
   compose file via FileSystemService + YAML.parse and returns the
   top-level services keys. Used from ensureStackOverride and
   applyLocalOverride. Defensive fallback: when the read returns []
   AND a previous override on disk has a non-empty services map,
   keep the file and emit mesh.override.preserved.

2. Boot regen race against the pilot tunnel. regenerateAllOverrides
   ran ~1s before the first pilot tunnel-up event, so pushes to
   pilot-mode nodes failed with "no proxy target". MeshService.start
   now also runs regenerateOverridesForNode(nodeId) inside the
   tunnel-up listener, covering both the boot-race window and
   mid-runtime tunnel reconnects. Idempotent.

3. openCrossNode emitted no activity log when a cross-node dial got
   stuck waiting for tcp_open_ack. The forwarder accepted the TCP
   handshake (so nc -v reported open) but logged nothing between
   accept and close. Now logs route.dispatch on entry and emits
   tunnel.fail after PROBE_TIMEOUT_MS if the agent never acks.
   Timer is cleared on every tcpStream and src lifecycle event.

Tests: 8 new in mesh-service.test.ts cover the helper, the BUG-1
override write path, the defensive preservation fallback, the
tunnel-up regen handler, and both cross-node dispatch logging
behaviors. Full vitest suite green (1998 tests pass).

* fix(mesh): satisfy CodeQL path-injection model in new override-read paths

Two new fs.readFile sites added in the previous commit failed the
CodeQL check on the PR even though both paths were guarded by
isValidStackName + isPathWithinBase. The file's existing
applyLocalOverride pattern uses path.basename(stackName) before the
join (commented as "Recognized by CodeQL's path-injection model");
apply the same sanitizer to the new code paths.

- getDeclaredStackServiceNames: wrap stackName in path.basename when
  building composePath.
- readExistingOverrideServiceNames: take (dir, stackName) instead of
  a pre-built filePath so the sanitizer lives next to the read sink.
- ensureStackOverride: build the override file path with
  path.basename(stackName) for symmetry with applyLocalOverride.

Runtime behavior unchanged (path.basename is a no-op for any name
that passed isValidStackName). Tests still green.
2026-05-09 05:39:33 -04:00
..