Skip to content

[Bug]: t3 status suggests --no-lan while a host override keeps LAN access on #35676

Closed

Description

@coygeek

Summary

When an older background service has an explicit host override but no separate local-network setting, its status output recommends a control that cannot disable network access. Following the advice leaves LAN access available and may needlessly restart the service, although the follow-up command warns about the override. A focused command run with a disposable service fixture reproduced the conflicting output.

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. Save the script below as repro.sh and run sh repro.sh <checkout> against the stated commit, with the checkout's dependencies installed.
  2. The script runs the real statusCommand using the existing test layers and a mock installed service. Its control has no host entry and requires the older-install note; the failing case adds T3CODE_HOST=0.0.0.0 and requires the note to disappear.

The run reproduced the failure once out of one attempt. The script prints tool versions and one final RESULT line. Exit 1 means the defect was reproduced, 0 means the expected behavior held, and 2 means setup or the control was inconclusive. It removes its unique temporary test file and scratch directory on exit.

Reproduction script: repro.sh
#!/bin/sh
set -eu

if [ "$#" -ne 1 ]; then
  printf '%s\n' 'usage: repro.sh CHECKOUT' 'RESULT: inconclusive'
  exit 2
fi
checkout=$1
test_file=$checkout/apps/server/src/cli/status.s08-03.test.ts
if [ ! -f "$checkout/apps/server/src/cli/up.ts" ] || [ -e "$test_file" ]; then
  printf '%s\n' 'missing checkout or temporary test path already exists' 'RESULT: inconclusive'
  exit 2
fi
scratch=$(mktemp -d)
cleanup() { rm -f "$test_file"; rm -rf "$scratch"; }
trap cleanup EXIT HUP INT TERM
mkdir -p "$scratch/home"
export HOME="$scratch/home" TMPDIR="$scratch" T3CODE_HOME="$scratch/t3"
printf 'node: %s\n' "$(node --version)"
printf 'vp: %s\n' "$(vp --version | sed -n '1p')"
printf 'platform: %s\n' "$(uname -s -m)"

cat > "$test_file" <<'TEST'
import * as NodeCrypto from "@effect/platform-node/NodeCrypto";
import * as NodeServices from "@effect/platform-node/NodeServices";
import { expect, it } from "@effect/vitest";
import * as HostProcess from "@t3tools/shared/HostProcess";
import * as NetService from "@t3tools/shared/Net";
import * as ConfigProvider from "effect/ConfigProvider";
import * as Effect from "effect/Effect";
import * as FileSystem from "effect/FileSystem";
import * as Layer from "effect/Layer";
import * as TestConsole from "effect/testing/TestConsole";
import { Command } from "effect/cli";
import { afterEach, vi } from "vite-plus/test";
import packageJson from "../../package.json" with { type: "json" };
import * as BootService from "../cloud/bootService.ts";
import { statusCommand } from "./up.ts";

it.layer(Layer.mergeAll(NodeServices.layer, NodeCrypto.layer, NetService.layer))("status with an installed service", (it) => {
  afterEach(() => vi.restoreAllMocks());
  it.effect("does not offer --no-lan while T3CODE_HOST overrides it", () =>
    Effect.gen(function* () {
      const fs = yield* FileSystem.FileSystem;
      const baseDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-status-s08-03-" });
      const status = {
        supported: true,
        installed: true,
        current: true,
        unitPath: "example/t3code.service",
        logPath: "example/boot-service.log",
      } as const;
      vi.spyOn(BootService, "layer").mockReturnValue(
        Layer.succeed(BootService.BootService, BootService.BootService.of({
          status: Effect.succeed(status),
          withStopped: (operation) => operation,
          restart: Effect.succeed("restarted" as const),
          install: () => Effect.die("unused"),
          uninstall: Effect.succeed(false),
        })),
      );
      const runStatus = () =>
        Command.runWith(statusCommand, { version: packageJson.version })(["--base-dir", baseDir]).pipe(
          Effect.provideService(HostProcess.Environment, {}),
          Effect.andThen(TestConsole.logLines),
          Effect.map((lines) => lines.map(String).join("\n")),
          Effect.provide(Layer.mergeAll(
            ConfigProvider.layer(ConfigProvider.fromEnv({ env: {} })),
            Layer.fresh(TestConsole.layer),
          )),
        );
      yield* fs.writeFileString(`${baseDir}/service.env`, "");
      const control = yield* runStatus();
      expect(control).toContain("Older installs keep LAN access on. Turn it off: `t3 up --no-lan`.");
      yield* fs.writeFileString(`${baseDir}/service.env`, "T3CODE_HOST=0.0.0.0\n");
      const overridden = yield* runStatus();
      expect(overridden).toContain("Local network  overridden · T3CODE_HOST=0.0.0.0");
      process.stdout.write(`STATUS_WITH_HOST: ${overridden.replaceAll("\n", " | ")}\n`);
      expect(overridden).not.toContain("Turn it off: `t3 up --no-lan`");
    }),
  );
});
TEST

cd "$checkout"
set +e
vp test run apps/server/src/cli/status.s08-03.test.ts > "$scratch/test.log" 2>&1
test_status=$?
set -e
sed -n '/^STATUS_WITH_HOST:/p; /^ Test Files/p; /^      Tests/p' "$scratch/test.log"
if [ "$test_status" -eq 0 ]; then
  printf '%s\n' 'RESULT: expected behavior'
  exit 0
fi
if grep -q 'AssertionError.*Turn it off: `t3 up --no-lan`' "$scratch/test.log" && grep -q 'STATUS_WITH_HOST:' "$scratch/test.log"; then
  printf '%s\n' 'RESULT: defect reproduced'
  exit 1
fi
printf '%s\n' 'RESULT: inconclusive'
exit 2

Expected behavior

With T3CODE_HOST set, status should not claim the inherited LAN setting determines reachability or recommend t3 up --no-lan as a way to close LAN access. The docs/user/background-service.md#L45-L58 says any host value replaces that setting; the docs/user/managed-policy.md#L227-L232 permits t3 up --no-lan for an old service only when service.env has no host value.

Actual behavior

With a mocked installed service for the selected home and service.env containing T3CODE_HOST=0.0.0.0 but no T3CODE_LAN, a run of t3 status --base-dir <temporary-home> printed both Local network overridden · T3CODE_HOST=0.0.0.0 and the note “Older installs keep LAN access on. Turn it off: t3 up --no-lan.” The command handler reaches the formatter through apps/server/src/cli/up.ts#L514-L545; the formatter shows the override in its row but adds the older-install note based only on the missing LAN entry (apps/server/src/cli/up.ts#L159-L180).

Impact and scope

Impact: P2 - Following the actionable status hint can leave the service bound to 0.0.0.0 while the user expects LAN access to be off; t3 up --no-lan can also restart an installed service and interrupt running agent turns without changing this reachability.

The tested input is an installed service whose environment file has a host override and no LAN entry. The displayed contradiction was deterministic in the focused run. The actual binding and the restart effect were traced in source rather than exercised against a live service: explicit host takes precedence over LAN in apps/server/src/cli/config.ts#L423-L443, while t3 up writes the LAN choice, warns about the host override, and may restart the service (apps/server/src/cli/up.ts#L288-L295, apps/server/src/cli/up.ts#L321-L347, apps/server/src/cli/up.ts#L396-L425). To close LAN access, set the host to a loopback address in service.env and restart, as the managed-policy guide describes. Reach across other service managers was not run.

Earliest failure stage

Output or presentation

Analysis or suspected cause

Source trace: readSetup sets lanFromOldDefault when the installed service has no T3CODE_LAN entry, independently of the T3CODE_HOST entry (apps/server/src/cli/up.ts#L63-L92). The status row checks the host first, but the note checks only lanFromOldDefault (apps/server/src/cli/up.ts#L159-L177).

Additional context

Open PR #31878 changes the printed command to preserve --base-dir; its described scope and patch do not change when this older-install note appears. The same conditional remains on upstream main after the stated commit, based on the current apps/server/src/cli/up.ts#L159-L180 contents and file commit history.

Evidence provenance: observed focused command output and static source traces. Local verification: reproduced once in one attempt. Reproduction completeness: complete for the status output; a live service bind and restart were not exercised. Restoration check: the reproduction script exits 0 when the host-override row remains and the --no-lan note is absent.

Impact

Minor bug or occasional failure

Version or commit

main @ c00a373

Environment

Node.js v26.12.1, vite-plus CLI v1.5.1, macOS arm64; in-process service fixture without a running background service.

Logs or stack traces

node: v26.12.1
vp: vp v1.5.1
platform: Darwin arm64
STATUS_WITH_HOST: This machine |   Service        installed · t3@0.1.9 |   Local network  overridden · T3CODE_HOST=0.0.0.0 |  | Older installs keep LAN access on. Turn it off: `t3 up --no-lan`. |  | Change access: `t3 up`.
 Test Files  1 failed (1)
      Tests  1 failed (1)
RESULT: defect reproduced

Screenshots, recordings, or supporting files

No response

Workaround

Set the host to a loopback address in service.env and restart, as described in docs/user/managed-policy.md#L227-L232.

References

Merged PR #30900 corrected the managed-policy documentation to say t3 up --no-lan applies to old background services only when service.env has no host value. The status hint still appears with an explicit host override.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions