diff --git a/ci/platform-matrix.json b/ci/platform-matrix.json index 55c4c0d0a7b..9eebe0c60db 100644 --- a/ci/platform-matrix.json +++ b/ci/platform-matrix.json @@ -66,7 +66,7 @@ "prd_priority": "P1", "ci_tested": false, "prerequisites_notes": "Deferred. For evaluation on a qualifying DGX Station GB300, see [Additional Setup for DGX Station](prerequisites/dgx-station-preparation) before the Quickstart.", - "notes": "The PRD marks this platform as P1. Workstation form-factor with NVIDIA GPUs and the same Docker + NVIDIA Container Toolkit + CDI requirements as DGX Spark. Station remains Deferred. For canonical hardware qualification, image requirements, preparation, repair limits, and reboot handoff, see [Prepare DGX Station to Install NemoClaw](../get-started/prerequisites/dgx-station-preparation). On a Station GB300, the installer accepts either a clean generic Ubuntu 24.04 ARM64 image or stock DGX OS `7.2.0`, `7.4.0`, or `7.5.0` when a safe, root-owned `/etc/dgx-release` marker identifies `DGX Server for GALAXY-GB300`. It also recognizes two exact no-OTA factory profiles for bounded qualification. Both require `DGX_PLATFORM=DGX Server for GALAXY-GB300` and require `DGX_OTA_VERSION` and `DGX_OTA_DATE` to be absent. The April 2026 NVIDIA Colossus BaseOS profile requires `DGX_PRETTY_NAME=NVIDIA DGX Server`, `DGX_SWBUILD_VERSION=7.5.0-GB300ws-GB200ws`, and `DGX_SWBUILD_DATE=2026-04-02-08-20-16`; the June 2026 NVIDIA AI Developer Tools profile requires `DGX_PRETTY_NAME=NVIDIA DGX GB300WS`, `DGX_SWBUILD_VERSION=7.5.0`, and `DGX_SWBUILD_DATE=2026-06-16-11-48-10`. The stock DGX OS and AI Developer Tools paths validate the factory stack in place without replacing host packages, enabling or restarting services, generating CDI configuration, or rewriting the Docker runtime. The Colossus BaseOS path preserves the factory kernel, driver, DKMS, Docker, and NVIDIA Container Toolkit packages and prepares only Docker access and packaged CDI. Only the Colossus BaseOS path registers the NVIDIA Docker runtime when the launch probe proves it is missing, with restoration of the prior daemon configuration if registration fails. Each path validates the factory GB300 driver and volatile ECC state, Docker and containerd, Buildx, `nvidia-ctk`, CDI, and GPU visibility through both CDI and `--gpus all` before it proceeds. Unknown DGX OS versions, unmatched no-OTA factory images, unsafe release markers, and other Station generations stop before host preparation. The installer offers express install with the pinned `nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B-NVFP4` recipe, including an approximately 352 GB model download, without follow-up provider, model, policy, or sandbox-name choices. On Station GB300 only, direct-GPU sandbox policies add the exact read-only BDF directory for each host-discovered NVIDIA display-class PCI device, plus only existing CPU, memory, NUMA topology, and NVIDIA module initialization paths; they do not add `/sys`, the PCI parent subtree, or sysfs write access. One physical DGX OS `7.5.0` GB300 validation completed with released OpenShell `0.0.85`, local Nemotron Ultra serving, sandbox `cuInit(0)`, and a Hermes write/read file-tool task. In that validation, reads of `/sys/fs/cgroup/cgroup.controllers` and `/sys/class/net/lo/address` remained denied. DGX Station remains Deferred pending repeat qualification from a clean host and CI coverage. Pass `--station-deepseek` to use `deepseek-ai/DeepSeek-V4-Flash` while retaining the one-confirmation express flow. The flag requires an interactive terminal, and `/dev/tty` must be available when the installer runs through `curl | bash`. For headless setup on a qualifying Station, select `NEMOCLAW_PROVIDER=install-vllm` and `NEMOCLAW_VLLM_MODEL=deepseek-v4-flash` instead. Direct managed-vLLM onboarding still defaults to `deepseek-ai/DeepSeek-V4-Flash` when no model override is set." + "notes": "The PRD marks this platform as P1. Workstation form-factor with NVIDIA GPUs and the same Docker + NVIDIA Container Toolkit + CDI requirements as DGX Spark. Station remains Deferred. For canonical hardware qualification, image requirements, preparation, repair limits, reboot handoff, and the explicit temporary metadata override, see [Prepare DGX Station to Install NemoClaw](../get-started/prerequisites/dgx-station-preparation). On a Station GB300, the installer accepts either a clean generic Ubuntu 24.04 ARM64 image or stock DGX OS `7.2.0`, `7.4.0`, or `7.5.0` when a safe, root-owned `/etc/dgx-release` marker identifies `DGX Server for GALAXY-GB300`. It also recognizes two exact no-OTA factory profiles for bounded qualification. Both require `DGX_PLATFORM=DGX Server for GALAXY-GB300` and require `DGX_OTA_VERSION` and `DGX_OTA_DATE` to be absent. The April 2026 NVIDIA Colossus BaseOS profile requires `DGX_PRETTY_NAME=NVIDIA DGX Server`, `DGX_SWBUILD_VERSION=7.5.0-GB300ws-GB200ws`, and `DGX_SWBUILD_DATE=2026-04-02-08-20-16`; the June 2026 NVIDIA AI Developer Tools profile requires `DGX_PRETTY_NAME=NVIDIA DGX GB300WS`, `DGX_SWBUILD_VERSION=7.5.0`, and `DGX_SWBUILD_DATE=2026-06-16-11-48-10`. The stock DGX OS and AI Developer Tools paths validate the factory stack in place without replacing host packages, enabling or restarting services, generating CDI configuration, or rewriting the Docker runtime. The Colossus BaseOS path preserves the factory kernel, driver, DKMS, Docker, and NVIDIA Container Toolkit packages and prepares only Docker access and packaged CDI. Only the Colossus BaseOS path registers the NVIDIA Docker runtime when the launch probe proves it is missing, with restoration of the prior daemon configuration if registration fails. Each path validates the factory GB300 driver and volatile ECC state, Docker and containerd, Buildx, `nvidia-ctk`, CDI, and GPU visibility through both CDI and `--gpus all` before it proceeds. By default, unknown DGX OS versions, unmatched no-OTA factory images, unsafe release markers, and other Station generations stop before host preparation. The installer offers express install with the pinned `nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B-NVFP4` recipe, including an approximately 352 GB model download, without follow-up provider, model, policy, or sandbox-name choices. On Station GB300 only, direct-GPU sandbox policies add the exact read-only BDF directory for each host-discovered NVIDIA display-class PCI device, plus only existing CPU, memory, NUMA topology, and NVIDIA module initialization paths; they do not add `/sys`, the PCI parent subtree, or sysfs write access. One physical DGX OS `7.5.0` GB300 validation completed with released OpenShell `0.0.85`, local Nemotron Ultra serving, sandbox `cuInit(0)`, and a Hermes write/read file-tool task. In that validation, reads of `/sys/fs/cgroup/cgroup.controllers` and `/sys/class/net/lo/address` remained denied. DGX Station remains Deferred pending repeat qualification from a clean host and CI coverage. Pass `--station-deepseek` to use `deepseek-ai/DeepSeek-V4-Flash` while retaining the one-confirmation express flow. The flag requires an interactive terminal, and `/dev/tty` must be available when the installer runs through `curl | bash`. For headless setup on a qualifying Station, select `NEMOCLAW_PROVIDER=install-vllm` and `NEMOCLAW_VLLM_MODEL=deepseek-v4-flash` instead. Direct managed-vLLM onboarding still defaults to `deepseek-ai/DeepSeek-V4-Flash` when no model override is set." }, { "name": "NVIDIA RTX (consumer and Pro workstation GPUs)", diff --git a/docs/get-started/dgx-station-preparation.mdx b/docs/get-started/dgx-station-preparation.mdx index 106b58e47ea..2d6b7c4cb56 100644 --- a/docs/get-started/dgx-station-preparation.mdx +++ b/docs/get-started/dgx-station-preparation.mdx @@ -22,9 +22,29 @@ Each profile requires `DGX_PLATFORM=DGX Server for GALAXY-GB300`, requires both - April 2026 NVIDIA Colossus BaseOS with `DGX_PRETTY_NAME=NVIDIA DGX Server`, `DGX_SWBUILD_VERSION=7.5.0-GB300ws-GB200ws`, and `DGX_SWBUILD_DATE=2026-04-02-08-20-16`. - June 2026 NVIDIA AI Developer Tools with `DGX_PRETTY_NAME=NVIDIA DGX GB300WS`, `DGX_SWBUILD_VERSION=7.5.0`, and `DGX_SWBUILD_DATE=2026-06-16-11-48-10`. -An unknown version, malformed or unsafe marker, unmatched no-OTA factory image, or other Station generation stops before host preparation. +By default, an unknown version, malformed or unsafe marker, unmatched no-OTA factory image, or other Station generation stops before host preparation. On an unqualified system, set `NEMOCLAW_PROVIDER` or `NEMOCLAW_NO_EXPRESS=1` explicitly to continue without Station host automation. + +Use `--force-station-install` only on a genuine DGX Station GB300 when automatic Station detection rejects its DGX release metadata. +The flag handles metadata variants not yet recognized by NemoClaw; it is not a general compatibility bypass. +The flag bypasses only the `/etc/dgx-release` allowlist. +It still requires ARM64 Ubuntu 24.04, DGX Station GB300 hardware identity, exactly one healthy GB300 for inference, zero volatile GB300 ECC errors, adequate storage, healthy required system services, no active agent, inference, or Docker workloads, and working Docker, Buildx, NVIDIA Container Toolkit, CDI, and container GPU-visibility probes. +The override preserves the existing factory driver, packages, and container-runtime configuration; it does not install or repair missing factory components. +It cannot enable Station Express on another Station generation or hardware platform. +This override exists for current genuine Station factory images whose `/etc/dgx-release` metadata omits or varies fields used by the exact automatic allowlist. +Remove the override after those images publish recognized metadata or NemoClaw replaces the exact allowlist with a separately qualified platform-family and runtime-capability check. + +Run the override from an interactive terminal: + +```bash +curl -fsSL https://www.nvidia.com/nemoclaw.sh | bash -s -- --force-station-install +``` + +Add `--station-deepseek` to the same command to select DeepSeek V4 Flash. +The installer records the override in the printed relogin command when Docker-group access requires a new session. + + ## Prepare Generic Ubuntu On the generic Ubuntu path, accepting express install prepares the host with NVIDIA open driver `610.43.02`, Docker CE `29.6.1` with Buildx, and NVIDIA Container Toolkit `1.19.1`. diff --git a/docs/get-started/quickstart.mdx b/docs/get-started/quickstart.mdx index 49f065c48be..b9982ea8c18 100644 --- a/docs/get-started/quickstart.mdx +++ b/docs/get-started/quickstart.mdx @@ -123,7 +123,8 @@ Use these details when your first-run path needs more control. Station accepts the generic Ubuntu 24.04 ARM64 image and stock DGX OS `7.2.0`, `7.4.0`, or `7.5.0` when a safe, root-owned `/etc/dgx-release` marker identifies `DGX Server for GALAXY-GB300`. On a qualifying Station, accepting the prompt selects the pinned `nemotron-3-ultra-550b-a55b` managed-vLLM recipe and completes onboarding without more provider, model, policy, or sandbox-name choices. [Prepare DGX Station to Install NemoClaw](prerequisites/dgx-station-preparation) defines Station qualification, generic Ubuntu preparation, stock DGX OS validation, repair limits, and reboot handoff. - Unknown versions, unsafe release markers, NVIDIA BaseOS images, and other Station generations stop before host preparation; set `NEMOCLAW_PROVIDER` or `NEMOCLAW_NO_EXPRESS=1` to bypass Station host automation. + By default, unknown versions, unsafe release markers, NVIDIA BaseOS images, and other Station generations stop before host preparation; set `NEMOCLAW_PROVIDER` or `NEMOCLAW_NO_EXPRESS=1` to bypass Station host automation. + For an explicit temporary override on genuine Station GB300 hardware with unrecognized release metadata, follow the `--force-station-install` safeguards in the Station preparation guide. One physical DGX OS `7.5.0` GB300 validation completed, but Station remains Deferred pending repeat clean-host qualification and CI coverage. Pass `--station-deepseek` to use DeepSeek V4 Flash for a Station demo instead; the flag selects the interactive prompt and requires terminal access. Refer to [Platform Support](../reference/platform-support) and [Choose an Inference Provider](../inference/learn-and-choose/choose-inference-provider) for the current platform behavior. @@ -200,6 +201,7 @@ Use these details when your first-run path needs more control. One physical DGX OS `7.5.0` GB300 validation completed, but Station remains Deferred pending repeat clean-host qualification and CI coverage. To select DeepSeek V4 Flash while retaining the one-confirmation Station express flow, run `curl -fsSL https://www.nvidia.com/nemoclaw.sh | bash -s -- --station-deepseek`. The `--station-deepseek` flag requires an interactive terminal; in a `curl | bash` pipeline, `/dev/tty` must be available. + The temporary `--force-station-install` flag has the same terminal requirement and bypasses only DGX release-metadata qualification on genuine Station GB300 hardware. Without terminal access, the installer stops before it installs Docker or build dependencies instead of ignoring the flag. For a headless or CI install on a qualifying DGX Station GB300 after host preparation, omit the flag and select the same managed-vLLM recipe explicitly. diff --git a/docs/inference/set-up-vllm.mdx b/docs/inference/set-up-vllm.mdx index 336dfb49fce..f4416b1c991 100644 --- a/docs/inference/set-up-vllm.mdx +++ b/docs/inference/set-up-vllm.mdx @@ -165,7 +165,8 @@ The container publishes port `8000` through Docker, so apply the firewall guidan DGX Station remains Deferred. On DGX Station GB300, NemoClaw can prepare a clean generic Ubuntu 24.04 ARM64 host for the reviewed Station path. -For stock DGX OS `7.2.0`, `7.4.0`, or `7.5.0`, NemoClaw instead requires the supported release marker and validates the factory GPU runtime in place. +By default, stock DGX OS `7.2.0`, `7.4.0`, or `7.5.0` requires the supported release marker before NemoClaw validates the factory GPU runtime in place. +For the explicit temporary metadata override, follow [Prepare DGX Station to Install NemoClaw](../../get-started/prerequisites/dgx-station-preparation). On Station GB300 only, a direct GPU sandbox receives the exact read-only BDF directory for each host-discovered NVIDIA display-class PCI device, plus the existing CPU, memory, NUMA topology, and NVIDIA module initialization paths. The policy does not grant `/sys`, the PCI parent subtree, or sysfs write access. In the physical DGX OS `7.5.0` validation, this exact-BDF policy passed sandbox `cuInit(0)` while reads of `/sys/fs/cgroup/cgroup.controllers` and `/sys/class/net/lo/address` remained denied. diff --git a/docs/reference/platform-support.mdx b/docs/reference/platform-support.mdx index 080baa8e9fb..7238a30c291 100644 --- a/docs/reference/platform-support.mdx +++ b/docs/reference/platform-support.mdx @@ -79,7 +79,7 @@ For install requirements and the shorter setup-oriented platform view, refer to | OS | Container runtime | Status | PRD priority | CI | Notes | |----|-------------------|--------|--------------|----|-------| | DGX OS (Spark) | Docker | Tested | P1 | Yes | Use the standard installer and `$$nemoclaw onboard`. For an end-to-end walkthrough with local inference, see the [NVIDIA Spark playbook](https://build.nvidia.com/spark/nemoclaw). | -| DGX OS (Station) | Docker | Deferred | P1 | No | The PRD marks this platform as P1. Workstation form-factor with NVIDIA GPUs and the same Docker + NVIDIA Container Toolkit + CDI requirements as DGX Spark. Station remains Deferred. For canonical hardware qualification, image requirements, preparation, repair limits, and reboot handoff, see [Prepare DGX Station to Install NemoClaw](../get-started/prerequisites/dgx-station-preparation). On a Station GB300, the installer accepts either a clean generic Ubuntu 24.04 ARM64 image or stock DGX OS `7.2.0`, `7.4.0`, or `7.5.0` when a safe, root-owned `/etc/dgx-release` marker identifies `DGX Server for GALAXY-GB300`. It also recognizes two exact no-OTA factory profiles for bounded qualification. Both require `DGX_PLATFORM=DGX Server for GALAXY-GB300` and require `DGX_OTA_VERSION` and `DGX_OTA_DATE` to be absent. The April 2026 NVIDIA Colossus BaseOS profile requires `DGX_PRETTY_NAME=NVIDIA DGX Server`, `DGX_SWBUILD_VERSION=7.5.0-GB300ws-GB200ws`, and `DGX_SWBUILD_DATE=2026-04-02-08-20-16`; the June 2026 NVIDIA AI Developer Tools profile requires `DGX_PRETTY_NAME=NVIDIA DGX GB300WS`, `DGX_SWBUILD_VERSION=7.5.0`, and `DGX_SWBUILD_DATE=2026-06-16-11-48-10`. The stock DGX OS and AI Developer Tools paths validate the factory stack in place without replacing host packages, enabling or restarting services, generating CDI configuration, or rewriting the Docker runtime. The Colossus BaseOS path preserves the factory kernel, driver, DKMS, Docker, and NVIDIA Container Toolkit packages and prepares only Docker access and packaged CDI. Only the Colossus BaseOS path registers the NVIDIA Docker runtime when the launch probe proves it is missing, with restoration of the prior daemon configuration if registration fails. Each path validates the factory GB300 driver and volatile ECC state, Docker and containerd, Buildx, `nvidia-ctk`, CDI, and GPU visibility through both CDI and `--gpus all` before it proceeds. Unknown DGX OS versions, unmatched no-OTA factory images, unsafe release markers, and other Station generations stop before host preparation. The installer offers express install with the pinned `nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B-NVFP4` recipe, including an approximately 352 GB model download, without follow-up provider, model, policy, or sandbox-name choices. On Station GB300 only, direct-GPU sandbox policies add the exact read-only BDF directory for each host-discovered NVIDIA display-class PCI device, plus only existing CPU, memory, NUMA topology, and NVIDIA module initialization paths; they do not add `/sys`, the PCI parent subtree, or sysfs write access. One physical DGX OS `7.5.0` GB300 validation completed with released OpenShell `0.0.85`, local Nemotron Ultra serving, sandbox `cuInit(0)`, and a Hermes write/read file-tool task. In that validation, reads of `/sys/fs/cgroup/cgroup.controllers` and `/sys/class/net/lo/address` remained denied. DGX Station remains Deferred pending repeat qualification from a clean host and CI coverage. Pass `--station-deepseek` to use `deepseek-ai/DeepSeek-V4-Flash` while retaining the one-confirmation express flow. The flag requires an interactive terminal, and `/dev/tty` must be available when the installer runs through `curl \| bash`. For headless setup on a qualifying Station, select `NEMOCLAW_PROVIDER=install-vllm` and `NEMOCLAW_VLLM_MODEL=deepseek-v4-flash` instead. Direct managed-vLLM onboarding still defaults to `deepseek-ai/DeepSeek-V4-Flash` when no model override is set. | +| DGX OS (Station) | Docker | Deferred | P1 | No | The PRD marks this platform as P1. Workstation form-factor with NVIDIA GPUs and the same Docker + NVIDIA Container Toolkit + CDI requirements as DGX Spark. Station remains Deferred. For canonical hardware qualification, image requirements, preparation, repair limits, reboot handoff, and the explicit temporary metadata override, see [Prepare DGX Station to Install NemoClaw](../get-started/prerequisites/dgx-station-preparation). On a Station GB300, the installer accepts either a clean generic Ubuntu 24.04 ARM64 image or stock DGX OS `7.2.0`, `7.4.0`, or `7.5.0` when a safe, root-owned `/etc/dgx-release` marker identifies `DGX Server for GALAXY-GB300`. It also recognizes two exact no-OTA factory profiles for bounded qualification. Both require `DGX_PLATFORM=DGX Server for GALAXY-GB300` and require `DGX_OTA_VERSION` and `DGX_OTA_DATE` to be absent. The April 2026 NVIDIA Colossus BaseOS profile requires `DGX_PRETTY_NAME=NVIDIA DGX Server`, `DGX_SWBUILD_VERSION=7.5.0-GB300ws-GB200ws`, and `DGX_SWBUILD_DATE=2026-04-02-08-20-16`; the June 2026 NVIDIA AI Developer Tools profile requires `DGX_PRETTY_NAME=NVIDIA DGX GB300WS`, `DGX_SWBUILD_VERSION=7.5.0`, and `DGX_SWBUILD_DATE=2026-06-16-11-48-10`. The stock DGX OS and AI Developer Tools paths validate the factory stack in place without replacing host packages, enabling or restarting services, generating CDI configuration, or rewriting the Docker runtime. The Colossus BaseOS path preserves the factory kernel, driver, DKMS, Docker, and NVIDIA Container Toolkit packages and prepares only Docker access and packaged CDI. Only the Colossus BaseOS path registers the NVIDIA Docker runtime when the launch probe proves it is missing, with restoration of the prior daemon configuration if registration fails. Each path validates the factory GB300 driver and volatile ECC state, Docker and containerd, Buildx, `nvidia-ctk`, CDI, and GPU visibility through both CDI and `--gpus all` before it proceeds. By default, unknown DGX OS versions, unmatched no-OTA factory images, unsafe release markers, and other Station generations stop before host preparation. The installer offers express install with the pinned `nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B-NVFP4` recipe, including an approximately 352 GB model download, without follow-up provider, model, policy, or sandbox-name choices. On Station GB300 only, direct-GPU sandbox policies add the exact read-only BDF directory for each host-discovered NVIDIA display-class PCI device, plus only existing CPU, memory, NUMA topology, and NVIDIA module initialization paths; they do not add `/sys`, the PCI parent subtree, or sysfs write access. One physical DGX OS `7.5.0` GB300 validation completed with released OpenShell `0.0.85`, local Nemotron Ultra serving, sandbox `cuInit(0)`, and a Hermes write/read file-tool task. In that validation, reads of `/sys/fs/cgroup/cgroup.controllers` and `/sys/class/net/lo/address` remained denied. DGX Station remains Deferred pending repeat qualification from a clean host and CI coverage. Pass `--station-deepseek` to use `deepseek-ai/DeepSeek-V4-Flash` while retaining the one-confirmation express flow. The flag requires an interactive terminal, and `/dev/tty` must be available when the installer runs through `curl \| bash`. For headless setup on a qualifying Station, select `NEMOCLAW_PROVIDER=install-vllm` and `NEMOCLAW_VLLM_MODEL=deepseek-v4-flash` instead. Direct managed-vLLM onboarding still defaults to `deepseek-ai/DeepSeek-V4-Flash` when no model override is set. | | Linux | Docker | Tested | P0 | Yes | Primary tested path. Ubuntu 24.04 has host-level onboarding validation. A digest-pinned Ubuntu 26.04 userspace lane builds the CLI and runs preflight, installer, and platform contracts on eligible main pushes; Docker-host, AppArmor, Landlock, and live onboarding validation on 26.04 remain pending. Other distros (Ubuntu 22.04, Fedora, Rocky, Alma, NixOS, Arch) may work but are not validated. | | macOS (Apple Silicon) | Colima, Docker Desktop | Tested with limitations | P0 | Yes | Start the container runtime (Colima or Docker Desktop) before running the installer. Homebrew Colima users must install both Colima and the Docker CLI (`brew install colima docker`) before `docker info` can work. Xcode Command Line Tools (`xcode-select --install`) are typically required for Node native modules during install. NemoClaw recommends them but does not enforce them during preflight. | | NVIDIA RTX (consumer and Pro workstation GPUs) | Docker | Deferred | P1 | No | The PRD marks this platform as P1. Covers RTX consumer cards and RTX Pro workstation cards on Linux hosts that meet the generic-Linux-GPU requirements (NVIDIA Container Toolkit + CDI present). The provider menu emits managed vLLM behind `NEMOCLAW_EXPERIMENTAL=1` or `NEMOCLAW_PROVIDER=install-vllm` for this host class today; the end-to-end onboard path on this hardware is not yet validated in CI. | diff --git a/scripts/install.sh b/scripts/install.sh index 74d54083b62..a515cf85ddb 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -748,6 +748,7 @@ usage() { printf " --yes-i-accept-third-party-software Accept the third-party software notice without prompting\n" printf " --fresh Discard any failed/interrupted onboarding session and start over\n" printf " --station-deepseek Use DeepSeek V4 Flash for DGX Station express install (interactive terminal required)\n" + printf " --force-station-install Bypass only the DGX release-metadata allowlist for Station GB300 express install\n" printf " --version, -v Print installer version and exit\n" printf " --help, -h Show this help message and exit\n\n" printf " ${C_DIM}Environment:${C_RESET}\n" @@ -3024,7 +3025,13 @@ detect_express_platform() { generic-ubuntu | supported-dgx-os | supported-colossus-baseos | supported-ai-developer-tools) printf "DGX Station" ;; - *) printf "Unsupported DGX Station OS" ;; + *) + if [ "${FORCE_STATION_INSTALL:-}" = "1" ]; then + printf "DGX Station" + else + printf "Unsupported DGX Station OS" + fi + ;; esac return fi @@ -3076,6 +3083,36 @@ fail_station_deepseek_terminal_required() { error "--station-deepseek selects the DGX Station express prompt, which needs an interactive terminal. Re-run from a terminal (for a curl|bash pipe, /dev/tty must be available), or omit --station-deepseek and configure the install non-interactively." } +fail_force_station_terminal_required() { + error "--force-station-install selects the DGX Station express prompt, which needs an interactive terminal. Re-run from a terminal (for a curl|bash pipe, /dev/tty must be available), or omit --force-station-install." +} + +validate_force_station_install_override() { + local platform="$1" + if [ "${FORCE_STATION_INSTALL:-}" != "1" ]; then + return 0 + fi + if [ "$platform" != "DGX Station" ]; then + error "--force-station-install requires DGX Station GB300 hardware (detected: ${platform:-unsupported platform})." + fi + if [ "${NEMOCLAW_NO_EXPRESS:-}" = "1" ]; then + error "--force-station-install cannot be combined with NEMOCLAW_NO_EXPRESS=1. Remove one override." + fi + if [ "${NON_INTERACTIVE:-}" = "1" ]; then + local trigger_note="" + if [ -n "${NON_INTERACTIVE_SOURCE:-}" ]; then + trigger_note=" (triggered by: ${NON_INTERACTIVE_SOURCE})" + fi + error "--force-station-install selects the DGX Station express prompt and cannot be combined with non-interactive mode${trigger_note}." + fi + if [ -n "${NEMOCLAW_PROVIDER:-}" ]; then + error "--force-station-install conflicts with NEMOCLAW_PROVIDER=${NEMOCLAW_PROVIDER}. Remove the provider override to use Station express install." + fi + if ! express_prompt_can_read_tty; then + fail_force_station_terminal_required + fi +} + validate_station_deepseek_override() { local platform="$1" if [ "${STATION_DEEPSEEK:-}" != "1" ]; then @@ -3129,6 +3166,7 @@ preflight_explicit_express_flags() { local platform platform="$(detect_express_platform)" validate_express_platform_boundary "$platform" + validate_force_station_install_override "$platform" validate_station_deepseek_override "$platform" } @@ -3360,6 +3398,9 @@ station_express_resume_command() { printf 'curl -fsSL https://www.nvidia.com/nemoclaw.sh | NEMOCLAW_INSTALL_TAG=%s NEMOCLAW_AGENT=%s NEMOCLAW_SANDBOX_NAME=%s NEMOCLAW_POLICY_TIER=%s bash' \ "$_STATION_EXPRESS_RESUME_REVISION" "$_STATION_EXPRESS_RESUME_AGENT" \ "$_STATION_EXPRESS_RESUME_SANDBOX" "$_STATION_EXPRESS_RESUME_POLICY_TIER" + if [ "${FORCE_STATION_INSTALL:-}" = "1" ]; then + printf ' -s -- --force-station-install' + fi } clear_station_express_resume() { @@ -3447,8 +3488,12 @@ run_station_host_preparation() { # selected ref before executing this payload. Keep the sibling lookup and # fail-closed check so Station preparation cannot drift from that ref. local helper="${SCRIPT_DIR}/prepare-dgx-station-host.sh" + local -a helper_args=(--apply) [[ -f "$helper" ]] || error "DGX Station host preparation helper is missing: ${helper}" - bash "$helper" --apply + if [ "${FORCE_STATION_INSTALL:-}" = "1" ]; then + helper_args+=(--force-station-install) + fi + bash "$helper" "${helper_args[@]}" } ensure_station_express_host() { @@ -3463,7 +3508,14 @@ ensure_station_express_host() { supported-colossus-baseos) info "Validating the pinned BaseOS package inventory and preparing Docker access and CDI. Host packages and the NVIDIA driver will not be changed." ;; - *) info "Checking pinned DGX Station host prerequisites. Exact matches are reused." ;; + *) + if [ "${FORCE_STATION_INSTALL:-}" = "1" ]; then + warn "Proceeding with explicit --force-station-install intent; DGX release metadata qualification is bypassed, but Station GB300 hardware and factory-runtime health checks remain required." + info "Validating the existing Station GPU and local container runtime. Host packages, the NVIDIA driver, and runtime configuration will not be changed." + else + info "Checking pinned DGX Station host prerequisites. Exact matches are reused." + fi + ;; esac local status=0 run_station_host_preparation || status=$? @@ -3552,7 +3604,11 @@ describe_express_install() { printf " Factory Ubuntu with NVIDIA AI Developer Tools reuses its driver and container stack after local GPU, CDI, Docker, and Buildx validation. It may add this account to the Docker group, but does not install or replace host packages, rewrite the Docker runtime, or require a reboot.\n" ;; *) - printf " Station host setup reuses exact prerequisite versions, applies the reviewed factory DKMS transition when present, installs missing pinned driver, Docker, and NVIDIA Container Toolkit packages, and may require one reboot.\n" + if [ "${FORCE_STATION_INSTALL:-}" = "1" ]; then + printf " Explicit --force-station-install intent bypasses only DGX release-metadata qualification. Setup preserves the existing driver and container stack and proceeds only after Station GB300, GPU, ECC, Docker, Buildx, Toolkit, CDI, and container GPU-visibility checks pass.\n" + else + printf " Station host setup reuses exact prerequisite versions, applies the reviewed factory DKMS transition when present, installs missing pinned driver, Docker, and NVIDIA Container Toolkit packages, and may require one reboot.\n" + fi ;; esac printf " Host setup may add this trusted local account to the docker group, which grants root-equivalent control. This flow is only for trusted single-user development hosts; shared or managed hosts require an organization-approved Docker access path.\n" @@ -3600,6 +3656,7 @@ maybe_offer_express_install() { local platform platform="$(detect_express_platform)" validate_express_platform_boundary "$platform" + validate_force_station_install_override "$platform" validate_station_deepseek_override "$platform" # Not on a platform we have an express recipe for — say nothing. if [ -z "$platform" ]; then @@ -3685,6 +3742,7 @@ main() { ACCEPT_THIRD_PARTY_SOFTWARE="" FRESH="" STATION_DEEPSEEK="" + FORCE_STATION_INSTALL="" for arg in "$@"; do case "$arg" in --non-interactive) @@ -3694,6 +3752,7 @@ main() { --yes-i-accept-third-party-software) ACCEPT_THIRD_PARTY_SOFTWARE=1 ;; --fresh) FRESH=1 ;; --station-deepseek) STATION_DEEPSEEK=1 ;; + --force-station-install) FORCE_STATION_INSTALL=1 ;; --version | -v) local version_suffix version_suffix="$(installer_version_for_display)" @@ -3725,14 +3784,14 @@ main() { # alone clears the preflight below but the install can still partial-fail at # run_onboard with the same TTY error, leaving phases 1/2 on disk anyway. # - # #7008: `--station-deepseek` is the exception — it explicitly selects the - # interactive DGX Station express prompt, so accepting the notice must NOT + # #7008: Station-only prompt flags are the exception — they explicitly select + # the interactive DGX Station express prompt, so accepting the notice must NOT # imply non-interactive there. The two signals are orthogonal: one accepts a - # licence, the other opts into an interactive express flow. Inferring + # license, the other opts into an interactive express flow. Inferring # non-interactive from the notice would make the express flow reject its own - # required flag (validate_station_deepseek_override). + # required flag validation. if [ "${ACCEPT_THIRD_PARTY_SOFTWARE:-}" = "1" ] && [ "${NON_INTERACTIVE:-}" != "1" ] \ - && [ "${STATION_DEEPSEEK:-}" != "1" ]; then + && [ "${STATION_DEEPSEEK:-}" != "1" ] && [ "${FORCE_STATION_INSTALL:-}" != "1" ]; then NON_INTERACTIVE=1 fi diff --git a/scripts/prepare-dgx-station-host.sh b/scripts/prepare-dgx-station-host.sh index d7093894d11..f41b91eb3df 100755 --- a/scripts/prepare-dgx-station-host.sh +++ b/scripts/prepare-dgx-station-host.sh @@ -13,6 +13,7 @@ readonly GB300_PCI_VENDOR="0x10de" readonly GB300_PCI_DEVICE="0x31c2" readonly GB300_PCI_CLASS_PREFIX="0x03" STATION_HOST_PROFILE="generic-ubuntu" +FORCE_STATION_INSTALL=0 # The qualified generic image currently ships this OEM telemetry bootcmd. Its # exception disappears automatically when the file changes or the bootcmd # failure is fixed; update the pin only with a newly audited image. @@ -80,7 +81,7 @@ readonly -a BASEOS_PACKAGE_SPECS=( readonly BASEOS_CLOUD_CFG_SHA256="038ba435093de59f4a21021caf6c921d63344e9aae3b88795ee5b2659f43f437" readonly BASEOS_CLOUD_INIT_UNIT_SHA256="e13dd95a7bfac6407ea1ce45ed6683c0f4e84c791840d305c937d38ae77d9456" readonly BASEOS_FLUENT_BIT_UNIT_SHA256="1854339f563e518894c156d081912595d2d6e175a1ed6692e74e88224b6bad5f" -readonly BASEOS_FLUENT_BIT_CFG_SHA256="bb380bf6103957cdd7440dfba60107b7a3f50db3c0e75c483a6bfdb5e046201c" +readonly BASEOS_FLUENT_BIT_CFG_NORMALIZED_SHA256="ffec8b1bcc628877b9a230c6b26313b5ee6b25c20398580832133dbb15349551" readonly BASEOS_FLUENT_BIT_PARSERS_SHA256="760e6a347874a6cbdc10c6cd21d82d1ee5388c8573ddfaab05ef37904749dbe1" readonly BASEOS_FLUENT_BIT_PLUGINS_SHA256="9d5aad2c1be151b4d35de53a460f9783f98ac3cc815ebc638b0e8489f4ecd577" readonly BASEOS_FWUPD_UNIT_SHA256="835e7c291761c247d3cd5c64652b768c6a7fdc7cc72fea1bf70fc92e4cb3cfd5" @@ -109,6 +110,10 @@ station_pci_devices_path() { printf '%s' /sys/bus/pci/devices } +reboot_required() { + [[ -e /var/run/reboot-required ]] +} + dgx_station_release_file_is_safe() { local path=$1 metadata uid gid mode size [[ -r "$path" && -f "$path" && ! -L "$path" ]] || return 1 @@ -269,11 +274,15 @@ on_error() { usage() { cat <<'EOF' -Usage: prepare-dgx-station-host.sh --check|--apply|--verify +Usage: prepare-dgx-station-host.sh --check|--apply|--verify [--force-station-install] --check Read-only eligibility and current-state report. --apply Install exact prerequisites or finish post-reboot runtime setup. --verify Read-only host verification plus ephemeral GPU container tests. + --force-station-install + Bypass only the DGX release-metadata allowlist. ARM64 Ubuntu 24.04, + Station GB300 hardware, and all factory-runtime health checks still + apply. The existing driver and container runtime are preserved. Exit 10 from --apply means an operator-controlled reboot is required. After the reboot, run --apply once more, followed by --verify. @@ -282,11 +291,22 @@ run --apply again; a reboot is not required. EOF } -is_valid_mode() { - case "${1:-}" in - --check | --apply | --verify | --classify-dgx-release) return 0 ;; - *) return 1 ;; - esac +parse_args() { + local arg + MODE="" + FORCE_STATION_INSTALL=0 + for arg in "$@"; do + case "$arg" in + --check | --apply | --verify | --classify-dgx-release) + [[ -z "$MODE" ]] || return 1 + MODE="$arg" + ;; + --force-station-install) FORCE_STATION_INSTALL=1 ;; + *) return 1 ;; + esac + done + [[ -n "$MODE" ]] || return 1 + [[ "$MODE" != "--classify-dgx-release" || "$FORCE_STATION_INSTALL" == "0" ]] } is_station_gb300_product() { @@ -369,6 +389,21 @@ file_sha256_matches() { [[ "$actual" == "$expected" ]] } +baseos_fluent_bit_config_matches() { + local path=$1 expected=$2 actual + root_owned_file_is_not_writable_by_group_or_other "$path" || return 1 + actual="$({ + LC_ALL=C sed -E \ + -e 's/^([[:space:]]*Add Hostname) [A-Za-z0-9][A-Za-z0-9._-]*$/\1 /' \ + -e 's/^([[:space:]]*Add MAC) ([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$/\1 /' \ + -e 's/^([[:space:]]*Add IP) ([0-9]{1,3}\.){3}[0-9]{1,3}$/\1 /' \ + "$path" \ + | sha256sum \ + | awk '{print $1}' + } 2>/dev/null)" || return 1 + [[ "$actual" == "$expected" ]] +} + systemd_property_matches() { local unit=$1 property=$2 expected=$3 actual actual="$(systemctl show "$unit" -p "$property" --value 2>/dev/null)" || return 1 @@ -402,7 +437,8 @@ baseos_cloud_init_failure_is_qualified() { baseos_fluent_bit_failure_is_qualified() { baseos_failed_unit_matches fluent-bit.service \ /usr/lib/systemd/system/fluent-bit.service "$BASEOS_FLUENT_BIT_UNIT_SHA256" enabled 1 \ - && file_sha256_matches /etc/fluent-bit/fluent-bit.conf "$BASEOS_FLUENT_BIT_CFG_SHA256" \ + && baseos_fluent_bit_config_matches \ + /etc/fluent-bit/fluent-bit.conf "$BASEOS_FLUENT_BIT_CFG_NORMALIZED_SHA256" \ && file_sha256_matches /etc/fluent-bit/parsers.conf "$BASEOS_FLUENT_BIT_PARSERS_SHA256" \ && file_sha256_matches /etc/fluent-bit/plugins.conf "$BASEOS_FLUENT_BIT_PLUGINS_SHA256" } @@ -556,7 +592,7 @@ verify_baseos_packages() { station_uses_factory_runtime() { case "$STATION_HOST_PROFILE" in - stock-dgx-os | colossus-baseos | ai-developer-tools) return 0 ;; + stock-dgx-os | colossus-baseos | ai-developer-tools | forced-factory-runtime) return 0 ;; *) return 1 ;; esac } @@ -605,7 +641,12 @@ check_platform() { supported-colossus-baseos) STATION_HOST_PROFILE="colossus-baseos" ;; supported-ai-developer-tools) STATION_HOST_PROFILE="ai-developer-tools" ;; *) - fatal "This DGX Station OS image is outside the validated boundary" + if ((FORCE_STATION_INSTALL == 1)); then + STATION_HOST_PROFILE="forced-factory-runtime" + warn "DGX release metadata allowlist bypassed by explicit --force-station-install intent; all hardware and factory-runtime health checks remain required" + else + fatal "This DGX Station OS image is outside the validated boundary" + fi ;; esac info "platform=${product} profile=${STATION_HOST_PROFILE} release=${release_state} os=${PRETTY_NAME} arch=${arch} kernel=$(uname -r)" @@ -842,6 +883,7 @@ common_preflight() { require_command getent require_command grep require_command ps + require_command sed require_command sha256sum require_command ss require_command stat @@ -1451,8 +1493,9 @@ run_apply() { common_preflight if station_uses_factory_runtime; then - [[ ! -e /var/run/reboot-required ]] \ - || fatal "A reboot is pending on the Station factory image; reboot before running Station express install" + if reboot_required; then + fatal "A reboot is pending on the Station factory image; reboot before running Station express install" + fi if [[ "$STATION_HOST_PROFILE" == "colossus-baseos" ]]; then finish_runtime fi @@ -1479,7 +1522,7 @@ run_apply() { require_command readlink require_command sha256sum - if [[ -e /var/run/reboot-required ]]; then + if reboot_required; then if all_packages_exact && ! driver_loaded_exact; then warn "A reboot is required before runtime setup can continue" exit "$REBOOT_REQUIRED_EXIT" @@ -1543,11 +1586,10 @@ run_verify() { } main() { - if (($# != 1)) || ! is_valid_mode "${1:-}"; then + if ! parse_args "$@"; then usage >&2 exit 2 fi - MODE=$1 if [[ "$MODE" == "--classify-dgx-release" ]]; then dgx_station_release_state return 0 diff --git a/test/install-express-prompt.test.ts b/test/install-express-prompt.test.ts index fe0250adb3e..2f6965760b9 100644 --- a/test/install-express-prompt.test.ts +++ b/test/install-express-prompt.test.ts @@ -43,8 +43,9 @@ detect_express_platform() { printf "$EXPRESS_PLATFORM"; } print_banner() { :; } ensure_docker() { :; } ensure_openshell_build_deps() { :; } -# Stop immediately after the real express prompt configures the DeepSeek -# recipe, before setup-jetson.sh or any installation side effect can run. +# Stop immediately after the real Station express prompt configures its recipe, +# before setup-jetson.sh or any installation side effect can run. +classify_dgx_station_release() { printf "%s" "\${EXPRESS_RELEASE_STATE:-generic-ubuntu}"; } bash() { printf "RESULT NON_INTERACTIVE=%s SUDO_MODE=%s PROVIDER=%s MODEL=%s VLLM_MODEL=%s POLICY=%s YES=%s SANDBOX=%s STATION_EXPRESS=%s\\n" \ "\${NON_INTERACTIVE:-}" "\${NEMOCLAW_NON_INTERACTIVE_SUDO_MODE:-}" "\${NEMOCLAW_PROVIDER:-}" "\${NEMOCLAW_MODEL:-}" \ @@ -148,7 +149,11 @@ sys.exit(exit_code) ); } - function detectExpressPlatform(productName: string, releasePath: string) { + function detectExpressPlatform( + productName: string, + releasePath: string, + extraEnv: Record = {}, + ) { return spawnSync( "bash", [ @@ -197,6 +202,7 @@ detect_express_platform ), EXPRESS_PRODUCT_NAME: productName, EXPRESS_DGX_RELEASE_PATH: releasePath, + ...extraEnv, }, }, ); @@ -269,6 +275,19 @@ detect_express_platform ); }); + it("parses and documents the metadata-only Station override", () => { + const result = spawnSync("bash", [INSTALLER_PAYLOAD, "--force-station-install", "--help"], { + cwd: path.join(import.meta.dirname, ".."), + encoding: "utf-8", + }); + const output = `${result.stdout}${result.stderr}`; + + expect(result.status, output).toBe(0); + expect(output).toMatch( + /--force-station-install\s+Bypass only the DGX release-metadata allowlist/, + ); + }); + it("offers express install when curl-piped stdin still has a controlling TTY", () => { const result = runExpressPromptWithTty("y\n", "pipe"); const output = `${result.stdout}${result.stderr}`; @@ -488,6 +507,22 @@ ensure_station_express_host`, }); it.each([ + { + name: "a forced Station install on DGX Spark", + args: ["--force-station-install"], + platform: "DGX Spark", + env: {}, + message: + /--force-station-install requires DGX Station GB300 hardware \(detected: DGX Spark\)/, + }, + { + name: "a forced Station install in non-interactive mode", + args: ["--force-station-install", "--non-interactive"], + platform: "DGX Station", + env: {}, + message: + /--force-station-install selects the DGX Station express prompt and cannot be combined with non-interactive mode \(triggered by: the --non-interactive flag\)/, + }, { name: "a Station-only flag on DGX Spark", args: ["--station-deepseek"], @@ -604,6 +639,46 @@ main "$@" expect(output).not.toMatch(/cannot be combined with non-interactive mode/); }); + it.each<{ + name: string; + extraEnv: Record; + entrypointArgs: string[]; + }>([ + { + name: "environment notice acceptance", + extraEnv: { + NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1", + EXPRESS_RELEASE_STATE: "unsupported-dgx-os", + }, + entrypointArgs: ["--force-station-install"], + }, + { + name: "the CLI notice-acceptance flag", + extraEnv: { EXPRESS_RELEASE_STATE: "unsupported-dgx-os" }, + entrypointArgs: ["--force-station-install", "--yes-i-accept-third-party-software"], + }, + ])("reaches and accepts the forced Station express prompt through main with $name", ({ + extraEnv, + entrypointArgs, + }) => { + const result = runExpressPromptWithTty( + "\n", + "pipe", + "DGX Station", + extraEnv, + "accepted-station-main", + entrypointArgs, + ); + const output = `${result.stdout}${result.stderr}`; + + expect(result.status, output).toBe(0); + expect(output).toMatch(/Explicit --force-station-install intent bypasses only/); + expect(output.match(/Run express install with these settings\?/g)).toHaveLength(1); + expect(output).toMatch(/Using express install for DGX Station/); + expect(output).toMatch(/PROVIDER=install-vllm/); + expect(output).not.toMatch(/cannot be combined with non-interactive mode/); + }); + it("errors instead of silently skipping --station-deepseek when no interactive terminal is available (#7014)", () => { // Python's start_new_session runs main without a controlling terminal, and // stdin is /dev/null — so neither `-t 0` nor /dev/tty is available. This is @@ -824,6 +899,41 @@ detect_express_platform expect(result.stdout).toBe("DGX Station"); }); + it("requires explicit intent before treating unrecognized metadata as Station Express", () => { + const releasePath = path.join( + fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-dgx-release-force-")), + "dgx-release", + ); + fs.writeFileSync( + releasePath, + [ + 'DGX_NAME="DGX Server"', + 'DGX_PRETTY_NAME="NVIDIA DGX GB300WS"', + 'DGX_SWBUILD_DATE="2026-04-02-08-20-16"', + 'DGX_SWBUILD_VERSION="7.5.0-GB300ws-GB200ws"', + 'DGX_PLATFORM="DGX Server for GALAXY-GB300"', + "", + ].join("\n"), + ); + + const rejected = detectExpressPlatform("DGX Station GB300", releasePath); + const forced = detectExpressPlatform("DGX Station GB300", releasePath, { + FORCE_STATION_INSTALL: "1", + }); + + expect(rejected.status, `${rejected.stdout}${rejected.stderr}`).toBe(0); + expect(rejected.stdout).toBe("Unsupported DGX Station OS"); + expect(forced.status, `${forced.stdout}${forced.stderr}`).toBe(0); + expect(forced.stdout).toBe("DGX Station"); + }); + + it("does not let the metadata override impersonate Station GB300 hardware", () => { + const result = detectExpressPlatform("DGX Spark", "", { FORCE_STATION_INSTALL: "1" }); + + expect(result.status, `${result.stdout}${result.stderr}`).toBe(0); + expect(result.stdout).toBe("DGX Spark"); + }); + it.each([ ["unreviewed version", stockDgxRelease("7.6.0")], ["wrong DGX platform", stockDgxRelease("7.5.0", "DGX Server for GALAXY-GB200")], diff --git a/test/install-station-dgx-os.test.ts b/test/install-station-dgx-os.test.ts index 745ffc7950f..8b31d3c6655 100644 --- a/test/install-station-dgx-os.test.ts +++ b/test/install-station-dgx-os.test.ts @@ -6,10 +6,12 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { describe, expect, it } from "vitest"; -import { TEST_SYSTEM_PATH } from "./helpers/installer-sourced-env"; +import { INSTALLER_PAYLOAD, TEST_SYSTEM_PATH } from "./helpers/installer-sourced-env"; const REPO_ROOT = path.resolve(import.meta.dirname, ".."); const STATION_PREPARE = path.join(REPO_ROOT, "scripts", "prepare-dgx-station-host.sh"); +const STATION_REVISION = "a".repeat(40); +const STATION_GENERATION = "0123456789abcdef0123456789abcdef"; function runSourced(script: string, body: string, extraEnv: Record = {}) { const home = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-station-dgx-os-")); @@ -29,7 +31,7 @@ function runSourced(script: string, body: string, extraEnv: Record { + const forced = runSourced( + STATION_PREPARE, + ` +printf 'ID=ubuntu\nVERSION_ID="24.04"\nPRETTY_NAME="Ubuntu 24.04"\n' >"$HOME/os-release" +printf 'NVIDIA DGX Station GB300\n' >"$HOME/product-name" +uname() { printf 'aarch64\n'; } +station_os_release_path() { printf '%s' "$HOME/os-release"; } +station_product_name_path() { printf '%s' "$HOME/product-name"; } +dgx_station_release_path() { printf '%s' "$HOME/dgx-release"; } +dgx_station_release_state() { printf 'unsupported-dgx-os'; } +FORCE_STATION_INSTALL=1 +check_platform +printf 'PROFILE=%s\n' "$STATION_HOST_PROFILE" +`, + ); + + expect(forced.result.status, forced.output).toBe(0); + expect(forced.output).toContain("release metadata allowlist bypassed"); + expect(forced.output).toContain("PROFILE=forced-factory-runtime"); + + const unforced = runSourced( + STATION_PREPARE, + ` +printf 'ID=ubuntu\nVERSION_ID="24.04"\nPRETTY_NAME="Ubuntu 24.04"\n' >"$HOME/os-release" +printf 'NVIDIA DGX Station GB300\n' >"$HOME/product-name" +uname() { printf 'aarch64\n'; } +station_os_release_path() { printf '%s' "$HOME/os-release"; } +station_product_name_path() { printf '%s' "$HOME/product-name"; } +dgx_station_release_path() { printf '%s' "$HOME/dgx-release"; } +dgx_station_release_state() { printf 'unsupported-dgx-os'; } +check_platform +`, + ); + + expect(unforced.result.status, unforced.output).not.toBe(0); + expect(unforced.output).toContain("outside the validated boundary"); + }); + + it("parses the metadata override only alongside a preparation mode", () => { + const accepted = runSourced( + STATION_PREPARE, + ` +parse_args --apply --force-station-install +printf 'MODE=%s FORCE=%s\n' "$MODE" "$FORCE_STATION_INSTALL" +`, + ); + const classifier = runSourced( + STATION_PREPARE, + "parse_args --classify-dgx-release --force-station-install", + ); + + expect(accepted.result.status, accepted.output).toBe(0); + expect(accepted.output).toContain("MODE=--apply FORCE=1"); + expect(classifier.result.status, classifier.output).not.toBe(0); + }); + + it("does not let explicit metadata intent bypass architecture validation", () => { + const { result, output } = runSourced( + STATION_PREPARE, + ` +uname() { printf 'x86_64\n'; } +FORCE_STATION_INSTALL=1 +check_platform +`, + ); + + expect(result.status, output).not.toBe(0); + expect(output).toContain("Expected ARM64, found x86_64"); + }); +}); + +describe("DGX Station forced metadata installer handoff", () => { + it("forwards explicit intent only to Station host preparation", () => { + const { home, result, output } = runSourced( + INSTALLER_PAYLOAD, + ` +SCRIPT_DIR="$HOME" +touch "$SCRIPT_DIR/prepare-dgx-station-host.sh" +bash() { printf 'HELPER_ARGS=%s\n' "$*"; } +FORCE_STATION_INSTALL=1 +run_station_host_preparation +`, + ); + + expect(result.status, output).toBe(0); + expect(output).toContain( + `HELPER_ARGS=${path.join(home, "prepare-dgx-station-host.sh")} --apply --force-station-install`, + ); + }); + + it("preserves explicit intent in the relogin command", () => { + const { result, output } = runSourced( + INSTALLER_PAYLOAD, + ` +_SELECTED_EXPRESS_PLATFORM='DGX Station' +NEMOCLAW_VLLM_MODEL='deepseek-v4-flash' +FORCE_STATION_INSTALL=1 +station_installer_revision() { printf '${STATION_REVISION}'; } +station_express_resume_generation() { printf '${STATION_GENERATION}'; } +run_station_host_preparation() { return 11; } +ensure_station_express_host +`, + ); + + expect(result.status, output).toBe(11); + expect(output).toContain("bash -s -- --force-station-install"); + }); }); describe("DGX Station stock DGX OS runtime validation", () => { + it("preserves packages and the runtime for a forced metadata profile", () => { + const { result, output } = runSourced( + STATION_PREPARE, + ` +require_command() { + [[ "$1" == "sudo" ]] || { printf 'UNEXPECTED_REQUIREMENT %s\n' "$1"; return 1; } +} +acquire_sudo() { :; } +common_preflight() { STATION_HOST_PROFILE=forced-factory-runtime; } +reboot_required() { return 1; } +verify_dgx_os_runtime_sudo() { printf 'FACTORY_RUNTIME_VALIDATED\n'; } +ensure_docker_group() { printf 'DOCKER_GROUP_PRESENT\n'; } +install_packages() { printf 'PACKAGE_MUTATION\n'; return 1; } +finish_runtime() { printf 'RUNTIME_MUTATION\n'; return 1; } +run_apply +`, + ); + + expect(result.status, output).toBe(0); + expect(output).toContain("FACTORY_RUNTIME_VALIDATED"); + expect(output).toContain("APPLY_RESULT=COMPLETE"); + expect(output).not.toContain("PACKAGE_MUTATION"); + expect(output).not.toContain("RUNTIME_MUTATION"); + expect(output).not.toContain("UNEXPECTED_REQUIREMENT"); + }); + it("requires the exact qualified BaseOS package inventory", () => { const exact = runSourced( STATION_PREPARE, @@ -480,6 +617,77 @@ baseos_failed_unit_matches cloud-init.service /usr/lib/systemd/system/cloud-init expect(drifted.result.status, drifted.output).not.toBe(0); }); + it("qualifies the BaseOS Fluent Bit template independently of host identity", () => { + const configDir = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-fluent-bit-")); + const config = path.join(configDir, "fluent-bit.conf"); + fs.writeFileSync( + config, + [ + "[FILTER]", + " Name modify", + " Match *", + " Add Hostname station-a", + " Add MAC A4:A6:8D:00:00:01", + " Add IP 10.88.4.21", + "", + ].join("\n"), + ); + const normalized = spawnSync( + "bash", + [ + "--noprofile", + "--norc", + "-c", + String.raw`sed -E -e 's/^([[:space:]]*Add Hostname) [A-Za-z0-9][A-Za-z0-9._-]*$/\1 /' -e 's/^([[:space:]]*Add MAC) ([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$/\1 /' -e 's/^([[:space:]]*Add IP) ([0-9]{1,3}\.){3}[0-9]{1,3}$/\1 /' "$CONFIG" | sha256sum | awk '{print $1}'`, + ], + { encoding: "utf-8", env: { ...process.env, CONFIG: config } }, + ).stdout.trim(); + + const exact = runSourced( + STATION_PREPARE, + ` +root_owned_file_is_not_writable_by_group_or_other() { return 0; } +baseos_fluent_bit_config_matches "$FLUENT_BIT_CONFIG" "$EXPECTED_SHA" +`, + { + EXPECTED_SHA: normalized, + FLUENT_BIT_CONFIG: config, + PATH: process.env.PATH ?? TEST_SYSTEM_PATH, + }, + ); + expect(exact.result.status, exact.output).toBe(0); + + fs.writeFileSync(config, fs.readFileSync(config, "utf-8").replace("station-a", "station-b")); + const differentHost = runSourced( + STATION_PREPARE, + ` +root_owned_file_is_not_writable_by_group_or_other() { return 0; } +baseos_fluent_bit_config_matches "$FLUENT_BIT_CONFIG" "$EXPECTED_SHA" +`, + { + EXPECTED_SHA: normalized, + FLUENT_BIT_CONFIG: config, + PATH: process.env.PATH ?? TEST_SYSTEM_PATH, + }, + ); + expect(differentHost.result.status, differentHost.output).toBe(0); + + fs.writeFileSync(config, fs.readFileSync(config, "utf-8").replace("Match *", "Match changed")); + const changedTemplate = runSourced( + STATION_PREPARE, + ` +root_owned_file_is_not_writable_by_group_or_other() { return 0; } +baseos_fluent_bit_config_matches "$FLUENT_BIT_CONFIG" "$EXPECTED_SHA" +`, + { + EXPECTED_SHA: normalized, + FLUENT_BIT_CONFIG: config, + PATH: process.env.PATH ?? TEST_SYSTEM_PATH, + }, + ); + expect(changedTemplate.result.status, changedTemplate.output).not.toBe(0); + }); + it("keeps stock DGX OS out of the generic package mutation path", () => { const { result, output } = runSourced( STATION_PREPARE, diff --git a/test/station-doc-ownership.test.ts b/test/station-doc-ownership.test.ts index ebd3ed02c77..4b358eef518 100644 --- a/test/station-doc-ownership.test.ts +++ b/test/station-doc-ownership.test.ts @@ -15,6 +15,8 @@ const STATION_PREPARATION = path.join( "dgx-station-preparation.mdx", ); const STATION_QUICKSTART = path.join(REPO_ROOT, "docs", "get-started", "quickstart.mdx"); +const PLATFORM_SUPPORT = path.join(REPO_ROOT, "docs", "reference", "platform-support.mdx"); +const VLLM_SETUP = path.join(REPO_ROOT, "docs", "inference", "set-up-vllm.mdx"); const WINDOWS_PREPARATION = path.join(REPO_ROOT, "docs", "get-started", "windows-preparation.mdx"); const DOCS_INDEX = path.join(REPO_ROOT, "docs", "index.yml"); @@ -24,6 +26,8 @@ describe("DGX Station documentation ownership", () => { const prerequisites = fs.readFileSync(PREREQUISITES, "utf-8"); const stationPreparation = fs.readFileSync(STATION_PREPARATION, "utf-8"); const quickstart = fs.readFileSync(STATION_QUICKSTART, "utf-8"); + const platformSupport = fs.readFileSync(PLATFORM_SUPPORT, "utf-8"); + const vllmSetup = fs.readFileSync(VLLM_SETUP, "utf-8"); const pinnedValues = [ "DRIVER_VERSION", "DOCKER_VERSION", @@ -47,6 +51,12 @@ describe("DGX Station documentation ownership", () => { } expect(stationPreparation).toContain("DGX Server for GALAXY-GB300"); expect(quickstart).toContain("DGX Server for GALAXY-GB300"); + expect(stationPreparation).toContain("--force-station-install"); + expect(stationPreparation).toContain("metadata omits or varies fields"); + expect(stationPreparation).toContain("Remove the override after"); + expect(quickstart).toContain("--force-station-install"); + expect(platformSupport).toContain("explicit temporary metadata override"); + expect(vllmSetup).toContain("explicit temporary metadata override"); expect(stationPreparation).toMatch(/(?:DGX )?Station(?: remains|'s) Deferred/); expect(stationPreparation).toContain("One physical DGX OS `7.5.0` GB300 validation completed"); expect(stationPreparation).toContain("[Platform Support](../../reference/platform-support)");