From 39c8d25952f1c618e7dc2212034589d66b26c541 Mon Sep 17 00:00:00 2001 From: Apurv Kumaria Date: Sun, 26 Jul 2026 00:27:21 -0700 Subject: [PATCH 1/2] test(e2e): make Hermes rebuild credentialless Signed-off-by: Apurv Kumaria --- test/e2e/live/rebuild-hermes.test.ts | 15 ++++++++++----- test/e2e/support/rebuild-hermes-env.test.ts | 4 ++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/test/e2e/live/rebuild-hermes.test.ts b/test/e2e/live/rebuild-hermes.test.ts index 7b2a55917b..bab1c8f7d5 100644 --- a/test/e2e/live/rebuild-hermes.test.ts +++ b/test/e2e/live/rebuild-hermes.test.ts @@ -654,7 +654,7 @@ test(STALE_BASE_REBUILD "phase 1 current Hermes base resolution plus immutable old Hermes base fixture", "openshell provider create/update and sandbox create/exec/list", "curated local ~/.nemoclaw registry and onboard-session rebuild metadata", - "real nemoclaw rebuild --yes --verbose", + "real nemoclaw rebuild --yes --verbose without host inference credentials", "Hermes .env/config.yaml messaging placeholder preservation", "backup credential leak scan under ~/.nemoclaw/rebuild-backups", ], @@ -1208,12 +1208,16 @@ test(STALE_BASE_REBUILD } progress.phase("rebuild the Hermes sandbox"); + const rebuildEnv = testEnv(undefined, { + NEMOCLAW_REBUILD_VERBOSE: "1", + ...baseReusePlan?.childEnv, + }); + expect(rebuildEnv).not.toHaveProperty("NVIDIA_INFERENCE_API_KEY"); + expect(rebuildEnv).not.toHaveProperty("COMPATIBLE_API_KEY"); + expect(rebuildEnv).not.toHaveProperty("NVIDIA_API_KEY"); const rebuild = await host.nemoclaw([SANDBOX_NAME, "rebuild", "--yes", "--verbose"], { artifactName: "phase-6-nemoclaw-rebuild-hermes", - env: testEnv(apiKey, { - NEMOCLAW_REBUILD_VERBOSE: "1", - ...baseReusePlan?.childEnv, - }), + env: rebuildEnv, redactionValues, timeoutMs: REBUILD_TIMEOUT_MS, captureLimitBytes: LONG_COMMAND_CAPTURE_LIMIT_BYTES, @@ -1225,6 +1229,7 @@ test(STALE_BASE_REBUILD expect(rebuildOutput).toContain(`nemoclaw ${SANDBOX_NAME} gateway-token --quiet`); expect(rebuildOutput).toContain(`Using Hermes Agent base image: ${phase1BaseResolution.ref}`); expect(rebuildOutput).not.toContain("Rebuilding Hermes Agent base image"); + expect(rebuildOutput).not.toMatch(/provider credential not found/i); await waitForSandboxReady(host, apiKey, "phase-6-post-rebuild"); const backupPathText = rebuildOutput.match(/^\s*Backup:\s+(.+)$/mu)?.[1]?.trim(); diff --git a/test/e2e/support/rebuild-hermes-env.test.ts b/test/e2e/support/rebuild-hermes-env.test.ts index a4be2f646f..9d036b039f 100644 --- a/test/e2e/support/rebuild-hermes-env.test.ts +++ b/test/e2e/support/rebuild-hermes-env.test.ts @@ -83,16 +83,20 @@ describe("rebuild-Hermes base reuse", () => { HOME: process.env.HOME, PATH: process.env.PATH, BUILDX_BUILDER: "external-builder", + COMPATIBLE_API_KEY: "must-not-reach-child", NEMOCLAW_ACCEPT_DEV_UNVERIFIED_INSTALL: "1", NEMOCLAW_OPENSHELL_CHANNEL: "dev", NVIDIA_API_KEY: "must-not-reach-child", + NVIDIA_INFERENCE_API_KEY: "must-not-reach-child", }, {}, ); expect(childEnv.NEMOCLAW_ACCEPT_DEV_UNVERIFIED_INSTALL).toBe("1"); expect(childEnv.NEMOCLAW_OPENSHELL_CHANNEL).toBe("dev"); + expect(childEnv.COMPATIBLE_API_KEY).toBeUndefined(); expect(childEnv.NVIDIA_API_KEY).toBeUndefined(); + expect(childEnv.NVIDIA_INFERENCE_API_KEY).toBeUndefined(); expect(childEnv.BUILDX_BUILDER).toBeUndefined(); }); }); From 6b306be128632b7726b3b1e2ee418cd05ddf043c Mon Sep 17 00:00:00 2001 From: Apurv Kumaria Date: Sun, 26 Jul 2026 00:31:31 -0700 Subject: [PATCH 2/2] perf(e2e): remove duplicate Hermes Discord rebuild Signed-off-by: Apurv Kumaria --- test/e2e/live/hermes-discord.test.ts | 52 +++------------------------- 1 file changed, 5 insertions(+), 47 deletions(-) diff --git a/test/e2e/live/hermes-discord.test.ts b/test/e2e/live/hermes-discord.test.ts index b99d40faf9..a3b61082b6 100644 --- a/test/e2e/live/hermes-discord.test.ts +++ b/test/e2e/live/hermes-discord.test.ts @@ -356,7 +356,7 @@ async function rawTokenSurfaceProbe( }); } -test("hermes-discord: Hermes Discord schema, credential isolation, native gateway rewrite, and rebuild credential reuse", { +test("hermes-discord: Hermes Discord schema, credential isolation, and native gateway rewrite", { timeout: HERMES_DISCORD_TEST_TIMEOUT_MS, meta: { e2ePhases: [ @@ -366,7 +366,6 @@ test("hermes-discord: Hermes Discord schema, credential isolation, native gatewa "validate Discord config and placeholders", "exercise native Discord gateway rewrite", "verify Discord token isolation and REST boundary", - "rebuild without host inference credentials", "finalize Hermes Discord resources", ], }, @@ -378,7 +377,7 @@ test("hermes-discord: Hermes Discord schema, credential isolation, native gatewa await artifacts.target.declare({ id: "hermes-discord", boundary: - "install.sh --non-interactive Hermes sandbox + Discord config + OpenShell provider rewrite + sandbox leak probes + rebuild credential reuse", + "install.sh --non-interactive Hermes sandbox + Discord config + OpenShell provider rewrite + sandbox leak probes", sandboxName: SANDBOX_NAME, discordServerIds: DISCORD_SERVER_IDS, discordAllowedIds: DISCORD_ALLOWED_IDS, @@ -723,46 +722,6 @@ done`, expectExitZero(bridgeResidue, "no local Discord bridge residue probe"); expect(bridgeResidue.stdout.trim()).toBe(""); - progress.phase("rebuild without host inference credentials"); - await bestEffortLifecycleCleanup(() => - host.command("docker", ["rm", "-f", fakeGateway.container], { - artifactName: "phase-8-remove-fake-discord-container-before-rebuild", - env, - redactionValues, - timeoutMs: 60_000, - }), - ); - fs.rmSync(fakeGateway.dir, { recursive: true, force: true }); - await bestEffortLifecycleCleanup(() => - host.command( - "bash", - [ - "-lc", - "sudo rm -rf .tmp/fake-discord.* 2>/dev/null || rm -rf .tmp/fake-discord.* 2>/dev/null || true", - ], - { - artifactName: "phase-8-remove-fake-discord-scratch-before-rebuild", - cwd: REPO_ROOT, - env, - redactionValues, - timeoutMs: 60_000, - }, - ), - ); - - const rebuildEnv = commandEnv(); - delete rebuildEnv.NVIDIA_INFERENCE_API_KEY; - delete rebuildEnv.NVIDIA_INFERENCE_API_KEY; - delete rebuildEnv.COMPATIBLE_API_KEY; - const rebuild = await host.command("nemoclaw", [SANDBOX_NAME, "rebuild", "--yes"], { - artifactName: "phase-8-rebuild-without-inference-env", - env: rebuildEnv, - redactionValues, - timeoutMs: 45 * 60_000, - }); - expectExitZero(rebuild, "Hermes rebuild without NVIDIA_INFERENCE_API_KEY"); - expect(resultText(rebuild)).not.toMatch(/provider credential not found/i); - progress.phase("finalize Hermes Discord resources"); await (async (): Promise => { switch (process.env.NEMOCLAW_E2E_KEEP_SANDBOX) { @@ -771,7 +730,7 @@ done`, default: } const destroy = await host.command("nemoclaw", [SANDBOX_NAME, "destroy", "--yes"], { - artifactName: "phase-9-nemoclaw-destroy", + artifactName: "phase-8-nemoclaw-destroy", env, redactionValues, timeoutMs: 15 * 60_000, @@ -779,7 +738,7 @@ done`, expectExitZero(destroy, "destroy Hermes Discord sandbox"); await bestEffortLifecycleCleanup(() => host.command(host.openshellCommandPath, ["gateway", "destroy", "-g", "nemoclaw"], { - artifactName: "phase-9-openshell-gateway-destroy", + artifactName: "phase-8-openshell-gateway-destroy", env, redactionValues, timeoutMs: 120_000, @@ -792,7 +751,7 @@ done`, `registry="$HOME/.nemoclaw/sandboxes.json"; if [ -f "$registry" ] && grep -Fq ${shellQuote(`"${SANDBOX_NAME}"`)} "$registry"; then echo FOUND; exit 1; else echo ABSENT; fi`, ], { - artifactName: "phase-9-registry-removal-probe", + artifactName: "phase-8-registry-removal-probe", env: sandboxAccessEnv(), redactionValues, timeoutMs: 30_000, @@ -815,7 +774,6 @@ done`, rawTokenAbsentFromConfigEnvProcessAndFilesystem: true, discordRestBoundaryReachedOrSkippedOnTimeout: true, noLocalDiscordBridgeResidue: true, - rebuildReusedGatewayCredentialWithoutInferenceEnv: true, cleanupVerified: process.env.NEMOCLAW_E2E_KEEP_SANDBOX !== "1", }, });