fix: enforce 1:1 compose path mapping for Pilot agent mounts (#1516)

* fix: enforce 1:1 compose path mapping for Pilot agent mounts

Pilot enrollment now generates validated 1:1 bind mounts so every
agent path maps to a unique compose directory. Persisted agent paths
reconcile during startup to catch drift. Unsafe relative-bind redeploys
are blocked before container removal to prevent path escapes.

- Add composePathMapping utility with strict path validation
- Generate COMPOSE_DIR and validated mounts during Pilot enrollment
- Reconcile persisted agent paths during startup bootstrap
- Block redeploy when a relative-bind mount would escape the compose root
- Default Pilot UI path to /opt/docker/sencho
- Update multi-node and pilot-agent documentation
- Add regression tests for enrollment, bootstrap, compose-service,
  and environment-check paths

* fix: update E2E enrollment regexes for YAML-quoted token values
This commit is contained in:
Anso
2026-06-29 14:35:50 -04:00
committed by GitHub
parent 9ff678a7bb
commit 41dc339c26
14 changed files with 481 additions and 53 deletions
+17
View File
@@ -54,6 +54,21 @@ export function ensurePilotJwtSecret(): boolean {
return true;
}
/** Keep the pilot's persisted local node aligned with its configured mount. */
export function reconcilePilotComposeDir(): boolean {
if (!isPilotMode()) return false;
const configuredDir = process.env.COMPOSE_DIR?.trim();
if (!configuredDir) return false;
const db = DatabaseService.getInstance();
const localNode = db.getDefaultNode();
if (!localNode || localNode.compose_dir === configuredDir) return false;
db.updateNode(localNode.id, { compose_dir: configuredDir });
console.log(`[Startup] pilot-agent: compose directory set to ${configuredDir}`);
return true;
}
function clearSelfContainerNotificationRouting(): void {
const identity = SelfIdentityService.getInstance().getIdentity();
const changed = DatabaseService.getInstance().clearSelfContainerNotificationRouting(
@@ -88,6 +103,8 @@ export async function startServer(server: Server): Promise<void> {
);
}
reconcilePilotComposeDir();
try {
console.log('Running stack migration check...');
const defaultFsService = FileSystemService.getInstance(NodeRegistry.getInstance().getDefaultNodeId());