Skip to content

feat: add a lean universal developer tooling baseline #121

Open

Description

@eXPerience83

Current implementation status — 2026-10-18 (PR2 candidate). The final 2026-10-06 design-lock supersedes the historical lists below. PR1 #263 is merged and TrueNAS-accepted (47 commands); dual Python is accepted with 3.14.8 default / 3.15.0 project-selected, and #279/#280 are merged. PR2 is implemented in #281 at b516a87848f797533978ffd8c7fcf03108154988, adding exactly Ruff 0.17.1, pytest 9.1.1, pre-commit 4.7.0, Mike Farah yq 4.54.1, shfmt 3.14.1 and actionlint 1.7.12 (53 commands). Exact-head CI passes functional/hardening/project/isolation/notices/SPDX/Trivy gates; measured uncompressed PR2 delta is +70.41 MiB base / +68.06 MiB final, requiring human review of the 50 MiB soft budget. PR2 is not accepted/merged: final CodeRabbit review is quota-blocked, and human code/legal/size review remains. PR3 remains the separate later agent-demand survey; #180 remains separate. No stable release or TrueNAS change is made by this candidate.


Context

Remote Dev already provides a deliberately small cross-project development base: Git/Git LFS/SSH/GitHub CLI, Bash/tmux/ttyd, common file/search/archive utilities, a native build baseline, Python, Node.js/npm, uv, mise and ShellCheck.

The current tool matrix intentionally defers language-specific toolchains, browser automation, infrastructure CLIs, Docker access and project-scoped linters until there is a demonstrated need. That boundary should remain intact.

Recent real use showed a smaller gap: a handful of low-cost, language-agnostic tools are useful before a repository can even install its own dependencies. YAML processing, shell formatting, GitHub Actions validation and basic filesystem/network/TLS diagnostics recur across many unrelated repositories.

This issue defines the universal immutable baseline only. It must not turn the common image into a large general-purpose workstation.

Related architecture/security boundaries: #31, #42, #53, #112.

Goal

Add a small, auditable set of cross-project developer and diagnostic tools to the common immutable image while preserving Remote Dev's current security, reproducibility, legal-inventory and image-size discipline.

A tool belongs in this baseline only when it is useful across many unrelated repositories before project-specific setup, adds no daemon or credential requirement, requires no broader mounts/capabilities, and has a small enough maintenance/size cost to justify permanent inclusion.

Locked scope

Managed through the existing mise lock/provenance path

Add these tools through the existing immutable mise.toml / mise.lock mechanism rather than ad-hoc download scripts:

  • yq — Mike Farah's portable YAML/JSON/XML/TOML processor.
  • shfmt — shell formatter from mvdan/sh.
  • actionlint — static checker for GitHub Actions workflows.

Important: do not use Ubuntu's yq package as a substitute for Mike Farah's yq; the Ubuntu package is a different Python/jq-based implementation.

At implementation time, revalidate the current stable upstream release, official project/repository, supported Linux AMD64/ARM64 artifacts, checksums, provenance/attestations and license for each tool. Do not copy version numbers from this issue as a permanent contract.

Ubuntu APT utilities

Evaluate and add the following from Ubuntu 26.04 using --no-install-recommends unless measurement or dependency review shows an unexpected cost/risk:

  • bzip2 — complete the common source/archive formats already handled by the image.
  • file — identify unknown files/artifacts without repository-specific tooling.
  • lsof — inspect open files/listening processes during debugging.
  • iproute2 — provide modern read-only network diagnostics such as ss, ip addr and ip route.
  • bind9-dnsutils — provide dig/DNS diagnostics.
  • netcat-openbsd — bounded TCP/UDP connectivity/listener diagnostics through nc.
  • openssl — TLS/certificate/key inspection CLI.
  • zstd — support .zst/.tar.zst and remove the current maintenance asymmetry where regenerate-mise-lock.sh may need to install zstd ad hoc to refresh Python legal metadata.
  • gettext-base — provide lightweight template/environment substitution such as envsubst.

APT packages remain covered by the existing Ubuntu package/inventory policy rather than receiving independent third-party release downloaders.

Node package-manager bridge

The selected Node 24 line currently belongs to the range where upstream Corepack is distributed with Node.

  • Verify the exact pinned Node artifact contains the expected corepack CLI.
  • Verify npx remains available with the pinned npm installation.
  • If safe and deterministic, enable Corepack shims for yarn/yarnpkg and pnpm/pnpx without selecting or baking arbitrary global Yarn/pnpm versions.
  • Enabling the shims must not download Yarn/pnpm during image build, container startup or health checks.
  • Project-declared package-manager versions remain project-owned and may be fetched only when the user/project explicitly invokes that package manager.
  • Document that the Corepack assumption must be re-reviewed when Remote Dev migrates beyond the selected Node line; do not treat Corepack as a permanent Node guarantee.

If the exact pinned runtime cannot satisfy those requirements cleanly, keep npm/npx as the guaranteed baseline and document Corepack as deferred rather than adding a second global package-manager installation path.

mise / supply-chain integration

The repository already has a strict mise.toml + mise.lock path for Python, Node and uv. Extend that mechanism instead of creating a parallel installer framework.

  • Add exact version keys for the new mise-managed tools to versions.env.
  • Add exact versions/backends to mise.toml.
  • Regenerate mise.lock for the repository's declared lock platforms.
  • Extend scripts/validate-mise-lock.py and its tests so the accepted tool set remains explicit and fail-closed.
  • Validate the exact backend expected for each tool; do not accept arbitrary registry/backend substitution.
  • Validate approved artifact origins/URLs, checksums and supported architecture mappings.
  • Require/re-verify provenance when the selected backend/upstream provides a supported provenance mechanism.
  • Preserve the existing isolated mise lock regeneration model and MISE_SAFE=1 boundary.
  • Do not enable floating mise registries for the immutable image/lock generation path.
  • Extend check-upstream.yml so stable updates for these selected tool lines are proposed through the same reviewed automation path as the existing managed tools.
  • Keep update changes coherent: versions, lock data, Dockerfile/config references, legal inventory/notices and validation must change in the same PR when applicable.

Legal / third-party inventory

  • Add the mise-managed third-party components to third_party/inventory.json through the existing inventory tooling.
  • Preserve the exact required upstream license/notice material under third_party/components/.
  • Extend inventory/notice generation or validation rather than adding hand-maintained exceptions.
  • Keep Ubuntu APT packages represented by the existing ubuntu-apt inventory boundary.
  • Coordinate any unexpected redistribution, trademark, terms or provenance concern with standing tracker maintenance: recurring third-party license and distribution review #53.

Baseline verification and diagnostics

The documented base should be an executable contract, not only a package list.

  • Extend scripts/base-verify.sh to verify the new mandatory commands.
  • While touching it, compare the current documented universal tool matrix with base-verify.sh and add missing checks for already-promised essential commands where appropriate.
  • Extend remote-dev-doctor with the new universally useful commands for agent/shell roles where their absence would make the image incomplete.
  • Do not make the launcher role depend on developer tooling it does not use.
  • Extend runtime smoke coverage so a built image proves the expected tool commands are available.
  • Verify yq --version, shfmt --version and actionlint --version correspond to the exact locked versions.
  • Add at least one bounded functional smoke check where version output alone would not catch the wrong implementation (especially yq).

Security invariants

This issue must not weaken the outer-container isolation contract in order to make a utility work.

  • No privileged mode.
  • No SYS_ADMIN.
  • No new Linux capabilities.
  • No unconfined seccomp/AppArmor workaround.
  • No Docker/Podman socket.
  • No host-root mount.
  • No broad /root, /home, /opt or /usr/local writable persistence.
  • No daemon or background service added by these tools.
  • No credentials or API keys bundled into the image.
  • No automatic network access from these tools during normal container startup, health checks or doctor execution.

ping/iputils-ping is deliberately not required by this issue. It may be reconsidered only if it is proven useful in the final hardened deployment without adding CAP_NET_RAW, setuid/file-capability exceptions or another security relaxation. The baseline already has curl, dig, nc, ss and ip for ordinary connectivity/DNS diagnostics.

Likewise, do not add tcpdump, strace or similar lower-level diagnostics here if their useful operation conflicts with the supported hardened container boundary.

Image-size / dependency budget

Measure rather than guess.

  • Record the base image size before the change.
  • Record the base image size after the complete candidate baseline.
  • Record the compressed/pulled size delta if the existing build tooling makes it practical.
  • Record relevant APT dependency growth and any unexpectedly large mise artifacts.
  • Treat 50 MiB uncompressed as a soft review threshold for the complete issue: exceeding it is not an automatic failure, but requires explicit re-evaluation of the candidates before merge.
  • Do not keep a marginal tool merely because the host currently has ample disk space; pull/update cost, attack surface, SBOM size and future maintenance matter too.

CI / regression requirements

All existing common-image gates must remain green, including as applicable:

  • repository configuration validation;
  • version-pin / mise-lock validation;
  • AMD64 image build and runtime smoke test;
  • bundled notice validation;
  • SPDX SBOM generation;
  • Trivy scans and the no-fixable-critical gate;
  • launcher isolation and same-image reuse;
  • Codex bundled fallback/runtime behavior;
  • existing TrueNAS/Compose configuration tests.

The lock may continue to contain ARM64 artifacts where that is part of the existing reproducibility contract, but this issue must not claim Remote Dev ARM64 runtime support; real ARM64 publication/support remains tracked by #112.

Documentation

  • Update docs/tool-matrix.md so the immutable baseline reflects the final accepted set.
  • Keep the distinction between bundled universal tools and deferred/on-demand project toolchains explicit.
  • Update README/Spanish documentation only where the user-facing baseline changes require it.
  • Update CHANGELOG.md.
  • Document the Corepack/package-manager behavior precisely if enabled.

Explicit non-goals

Do not expand this issue to include:

  • Docker Engine, Docker-in-Docker, Docker/Podman socket access or privileged container control;
  • Docker CLI/Compose client design — investigate separately if desired, including safe no-daemon and remote-daemon semantics;
  • CMake/Ninja as a permanent default solely to support selected native projects;
  • Go, Rust, Java, Ruby, PHP, Swift, Erlang/Elixir or additional Python/Node runtime lines;
  • pytest, Ruff, mypy, pre-commit or other Python project dependencies;
  • ESLint, Prettier, TypeScript, global Yarn/pnpm versions or other Node project dependencies;
  • kubectl, Helm, Terraform/OpenTofu or cloud-provider CLIs;
  • Playwright, Chromium or other browser/E2E runtimes;
  • PostgreSQL/MySQL/Redis clients;
  • language/framework-specific linters or generators;
  • convenience-only terminal UI replacements such as bat, eza, delta, tree or editor bundles;
  • security/scanning suites already better owned by CI unless a separate cross-project requirement proves otherwise;
  • Gmail/Calendar/Drive/Slack/Notion/Linear or other account Apps/connectors;
  • MCP integrations such as Context7, which remain optional and are tracked separately by tracking: add optional Context7 integration per agent service #33/feat: add optional Context7 integration for the Codex service #94/feat: extend optional Context7 integration to the Antigravity service #95;
  • the future persistent on-demand toolchain mechanism already reserved in the roadmap.

Suggested implementation split

Prefer two reviewable PRs rather than one mixed change if implementation confirms the boundary is clean:

PR 1 — system baseline and diagnostics

feat: complete universal system tooling baseline

Expected scope:

  • APT utility additions;
  • Corepack/npx verification and bounded shim decision;
  • base verifier / doctor / smoke checks;
  • tool-matrix and changelog updates;
  • image-size evidence.

PR 2 — locked developer CLIs

feat: add locked universal developer CLIs

Expected scope:

  • yq, shfmt, actionlint via mise;
  • versions.env, mise.toml, mise.lock;
  • strict lock validator/tests;
  • upstream update automation;
  • legal inventory/notices;
  • functional/version smoke tests;
  • final image-size evidence and docs synchronization.

The final PR split may change if a smaller dependency relationship is demonstrated, but do not hide the mise/supply-chain changes inside an unrelated package-list diff.

Acceptance criteria

This issue is complete when:

  1. the accepted universal baseline is available in a freshly built common image without project-specific setup;
  2. yq, shfmt and actionlint are installed through the repository's strict locked mise path with reviewed origins, checksums/provenance and legal inventory;
  3. the selected low-cost APT diagnostics/utilities are available without any new daemon, credential, mount or Linux-capability requirement;
  4. Node package-manager bridging is either safely enabled without baked arbitrary Yarn/pnpm versions or explicitly deferred with npm/npx retained as the guaranteed contract;
  5. base verification, doctor/smoke tests and documentation agree on the same baseline;
  6. exact before/after image-size evidence is recorded and the final set remains justified;
  7. existing build, isolation, notices, SBOM and vulnerability gates remain green;
  8. no deferred language/toolchain/browser/infra/App/MCP scope is pulled into the immutable base.

Research references

Revalidate these at implementation time rather than treating mutable documentation as pinned issue content:

Proposed completion

Complete through the two focused PRs above, then record the measured final image delta and accepted/rejected candidates in this issue before closing.

Activity

  1. eXPerience83 commented on Oct 4, 2026

    @eXPerience83
    OwnerAuthor

    2026-10-04 design-lock after the completed core environment contract

    Research checkpoint against current main (dd049cf41d57a18eefda26497f14dd720c5e361a) and the current hardened environment contract. No production code is changed by this checkpoint. The purpose is to update the August design before implementation now that #239/#231/#240/#250/#238/#241/#259 have defined and validated the real runtime semantics.

    Baseline boundary remains unchanged

    The current canonical public baseline is still the 29 commands in config/common-tool-baseline.txt. Project-scoped tools such as pytest/Ruff/mypy/pre-commit/ESLint/TypeScript remain out. Large language toolchains remain #180; container build/test remains #151.

    A new command added by #121 is a product API commitment: it must be directly resolvable, covered by Base Verify/Doctor/runtime smoke as appropriate, and must not require weakening read-only root, hardened /tmp, role isolation or provider state boundaries.

    Candidate decisions

    APT/system utilities — accept the current nine candidates

    Keep the issue's existing package set, subject to the measured final image delta:

    • bzip2 -> promise bzip2.
    • file -> promise file.
    • lsof -> promise lsof.
    • iproute2 -> promise both ip and ss as the intended read-only diagnostics. Do not promise privileged mutation operations merely because the package also contains them.
    • bind9-dnsutils -> promise dig; other package binaries remain incidental unless separately justified.
    • netcat-openbsd -> promise nc, bounded by the existing network/container policy.
    • openssl -> promise openssl. This is already pulled transitively by ca-certificates; making it an explicit package/baseline commitment documents the intended CLI rather than relying on a dependency accident.
    • zstd -> promise zstd. This has an additional maintenance justification: current mise-lock/legal regeneration already needs zstd and has an ad-hoc CI-only install fallback when the host lacks it.
    • gettext-base -> promise envsubst.

    Do not add ping/tcpdump/strace or any capability/security exception as part of this issue.

    The Ubuntu 26.04 package pages show these remain relatively small individually; bind9-dnsutils/iproute2 have the more meaningful dependency footprint, so the authoritative decision remains the exact before/after built-image delta rather than summing package-page sizes. Keep the existing 50 MiB uncompressed soft review threshold.

    Locked cross-project CLIs — accept all three through mise

    Revalidated stable upstreams on 2026-10-04:

    command stable line now expected mise backend notes
    yq 4.54.1 aqua:mikefarah/yq Mike Farah implementation only; MIT; upstream publishes Linux AMD64/ARM64 binaries and checksum/cosign material
    shfmt 3.14.1 aqua:mvdan/sh BSD-3-Clause; Linux AMD64/ARM64 release binaries
    actionlint 1.7.12 aqua:rhysd/actionlint MIT; Linux AMD64/ARM64 artifacts + checksums; upstream artifact attestations are available from v1.7.11 onward

    Use the short tool identities in the Remote Dev mise.toml so ordinary project-level mise selection retains the #239 semantics, but make the lock validator fail closed unless the resolved backend is exactly the reviewed Aqua backend above. Do not accept an asdf/go/backend substitution merely because the registry can resolve it.

    Preserve locked_verify_provenance=true and the existing linux-x64 / linux-arm64 lock contract. Revalidate exact current versions and upstream evidence once more at implementation time if upstream has moved since this comment.

    actionlint may use the already-global shellcheck integration. Do not add pyflakes globally merely to enable its optional Python integration; missing pyflakes must remain a non-fatal optional integration, consistent with the universal/project boundary.

    Required functional smoke beyond version output:

    • yq: parse/transform a tiny YAML fixture so the wrong Ubuntu/Python yq implementation cannot satisfy the contract.
    • shfmt: format/check a deliberately unformatted shell snippet without mutating repository sources.
    • actionlint: lint a minimal valid workflow and prove a bounded invalid workflow is rejected; do not require network.

    Also extend the #239 regression surface to at least one new mise CLI: a project selecting a missing version must fail closed, with neither implicit download nor same-name system fallback.

    Node / Corepack design

    This is the main change from the August assumptions.

    The currently pinned Node is 24.21.0; that exact Node release updated the bundled Corepack to 0.36.0. Upstream Corepack documents that it is distributed with Node up to (but not including) Node 25, and Node documents that bundled Corepack is removed starting with Node 25. Therefore this must remain a Node-24-specific bridge, not a permanent Remote Dev guarantee.

    The writable-state concern now has a narrow solution: Corepack supports COREPACK_HOME; on Linux its default would otherwise be $HOME/.cache/node/corepack, which is incompatible with our read-only-HOME design. If the bridge is enabled, give Corepack a dedicated role-private development path under /workspace/.remote-dev-tmp rather than setting generic XDG variables or making HOME writable.

    Corepack also documents COREPACK_DEFAULT_TO_LATEST=0, which disables the remote latest lookup and Last Known Good update behavior. Prefer that fail-closed/deterministic direction, but verify the exact 0.36.0 behavior before merging rather than assuming it from docs alone.

    Locked Corepack acceptance behavior

    1. Verify in the exact pinned Node artifact/runtime that corepack exists and reports 0.36.0 and that the pinned npm exposes npx.
    2. Promote npx into the canonical common command baseline.
    3. If enabled, run corepack enable only during image build to create Yarn/pnpm shims. The enable step itself must not download a Yarn/pnpm runtime.
    4. Expose a narrow role-private COREPACK_HOME; no XDG_CACHE_HOME, writable HOME, broad mount or new Compose knob.
    5. Startup, health checks, Doctor and passive command verification must cause zero package-manager downloads/network lookups.
    6. Direct user/project invocation of pnpm/yarn may fetch the exact project-declared manager version into COREPACK_HOME; that is explicit project behavior, not passive Remote Dev behavior.
    7. A fresh/empty COREPACK_HOME test must cover a project with an explicit packageManager version and prove the downloaded manager/cache stays entirely under the dedicated path.
    8. Test the no-packageManager case with COREPACK_DEFAULT_TO_LATEST=0; do not create an implicit moving global package-manager contract.
    9. Do not add yarn, pnpm or corepack to common-tool-baseline.txt merely because proxy commands exist. They are a conditional Node-line bridge whose real manager may be project-selected/downloaded, not immutable baked tools. Document this separately in the tool matrix.
    10. When Remote Dev moves off Node 24, Corepack bridging must be explicitly re-reviewed.

    If any of those conditions cannot be met cleanly in the exact candidate, defer Corepack shims without blocking #121 and keep npm + newly-guaranteed npx as the Node package-manager baseline. Do not install global Yarn/pnpm as a fallback.

    One implementation detail to test carefully: the current image puts mise shims first on PATH, and corepack enable normally writes proxies beside the Corepack executable unless --install-directory is supplied. The candidate must prove that the resulting yarn/pnpm proxies are actually reachable in normal agent PATH without introducing a second directory-sensitive wrapper layer. Prefer an explicit immutable install-directory if needed, but prove it rather than relying on incidental mise shim regeneration.

    Proposed resulting public command baseline

    If both focused PRs accept the set above, the current 29-command baseline would gain these immutable/direct commands:

    bzip2
    file
    lsof
    ip
    ss
    dig
    nc
    openssl
    zstd
    envsubst
    npx
    yq
    shfmt
    actionlint
    

    That would make 43 canonical common commands. Corepack/Yarn/pnpm proxies, if accepted, remain documented Node-24 bridge behavior rather than canonical immutable commands.

    Implementation split remains two PRs

    PR 1 — system baseline + Node bridge

    PR 2 — locked developer CLIs

    • add exact version keys to versions.env;
    • add yq, shfmt, actionlint to mise.toml;
    • regenerate mise.lock for x64/arm64;
    • extend lock/backend/provenance validation and tests;
    • extend check-upstream.yml for the three release lines;
    • extend third-party inventory/notices and legal validation;
    • add version + functional smoke checks;
    • add the new commands to the canonical baseline;
    • add the focused fix: make mise project resolution explicit and non-installing #239 missing-project-version regression for a new CLI;
    • record final size delta and SBOM/Trivy delta.

    Gates that must remain unchanged

    Before either PR is mergeable, prove against the exact built candidate:

    After both PRs, publish one exact edge candidate and do a short TrueNAS acceptance: new commands work directly in Codex + Antigravity without special prompt instructions, one new mise CLI obeys project override/fail-closed semantics, Corepack (if enabled) writes only to its dedicated path, and no hardening/provider-state regression appears.

    Decision

    The research result is therefore: accept the nine APT candidates and the three locked mise CLIs; guarantee npx; conditionally enable the Node-24 Corepack bridge only through the narrow COREPACK_HOME contract and exact-runtime no-passive-network tests, otherwise defer the bridge.

    This freezes the implementation direction without starting either implementation PR yet.

  2. eXPerience83 commented on Oct 5, 2026

    @eXPerience83
    OwnerAuthor

    2026-10-05 pre-implementation tooling re-audit

    Fresh audit against current main (dd049cf41d57a18eefda26497f14dd720c5e361a), before opening either #121 implementation PR. This checkpoint supersedes only the proposed final command set / Corepack decision in the previous design-lock; the hardened environment/tooling boundaries from #239/#240/#238/#241/#259 remain unchanged.

    1. Current public baseline is sound but incomplete relative to what the image already intentionally provides

    config/common-tool-baseline.txt currently exposes 29 commands. The image also explicitly installs procps, sqlite3 and xz-utils, and the runtime/tooling contract already treats python3 as an ordinary mise-selected project command even though those commands are not in the canonical public baseline.

    This is now considered a consistency gap rather than a reason to add more packages.

    Promote these already-present / zero-new-package commands into the public common baseline:

    • python3 — already used throughout the repository and explicitly described alongside python as an ordinary project command. Acceptance must prove from neutral CWD that python and python3 resolve to the same pinned image runtime, and that both follow the same project mise selection semantics.
    • sqlite3 — package is already explicitly installed; it is a generic local-data inspection/debugging tool with no daemon, credentials or external service requirement.
    • xz — xz-utils is already explicitly installed; this closes the archive-format inconsistency next to gzip, zip/unzip, and planned bzip2/zstd.
    • ps and pgrep — procps is already explicitly installed and pgrep is already a runtime/healthcheck dependency. Both are generic process diagnostics useful to agents and humans.

    Do not mechanically publish every executable from procps, OpenSSH, build-essential, coreutils or other packages. Public baseline remains an intentional product contract, not a dump of every binary in the filesystem.

    2. Keep the nine low-cost APT additions from the prior design-lock

    Still accept, subject to measured image delta:

    • bzip2 -> bzip2
    • file -> file
    • lsof -> lsof
    • iproute2 -> ip, ss
    • bind9-dnsutils -> dig
    • netcat-openbsd -> nc
    • openssl -> openssl
    • zstd -> zstd
    • gettext-base -> envsubst

    These cover the missing cross-project filesystem/process/network/DNS/TLS/archive/template diagnostics without a daemon, credential, socket mount or new capability.

    Still reject ping/CAP_NET_RAW exceptions, tcpdump, strace, socat, traceroute suites and other lower-level networking/debugging additions from this universal baseline unless a separate demonstrated use case justifies them.

    3. Keep the three locked universal developer CLIs

    Retain the previous decision to add through the strict mise lock/provenance path:

    • yq — Mike Farah implementation, exact reviewed Aqua backend only
    • shfmt — exact reviewed Aqua backend only
    • actionlint — exact reviewed Aqua backend only

    They are genuinely cross-project, work offline once installed, need no daemon/credentials, and complement existing jq/shellcheck rather than duplicating project-owned language linters.

    Do not expand this into yamllint, hadolint, markdown linters, pre-commit, Ruff/mypy/pytest, ESLint/Prettier/TypeScript, etc. Those remain project-scoped or need a separate demonstrated universal case.

    4. Guarantee npx; defer the Corepack/Yarn/pnpm bridge from #121

    Promote npx as a guaranteed command alongside the pinned npm.

    After re-auditing the bridge, do not enable Corepack shims in #121:

    • bundled Corepack is explicitly an experimental Node-line behavior and stops being distributed with Node starting at Node 25;
    • it adds mutable package-manager state / Last Known Good semantics that need their own lifecycle contract even with COREPACK_HOME and COREPACK_DEFAULT_TO_LATEST=0;
    • yarn/pnpm are project-selected package managers, not immutable binaries we need to promise globally today;
    • feat: add a lean universal developer tooling baseline #121 should not acquire a Node-24-specific state subsystem just to expose conditional proxies.

    The exact pinned Node may continue to contain a corepack executable incidentally, but #121 should make no public availability or shim contract for corepack, yarn, yarnpkg, pnpm or pnpx.

    If a real project later demonstrates the need, handle package-manager bridging as a focused follow-up or as part of the explicit on-demand tooling model rather than coupling it to this immutable-baseline PR.

    5. Standard shell/userland substrate: validate internally, do not inflate the public command API

    Remote Dev itself already depends on standard commands such as awk, sed, grep, sha256sum, stat, mktemp, timeout, dirname, env, readelf, setpriv, etc. Ubuntu also provides foundational file/text commands such as find/xargs.

    Do not add all of these to common-tool-baseline.txt just because they exist. Instead, keep a clear distinction:

    • public common baseline: intentional user/agent tools we promise;
    • internal runtime substrate: commands required by Remote Dev scripts/healthchecks and validated by the relevant deterministic tests.

    If implementation exposes an untested internal prerequisite, strengthen internal verification rather than turning every coreutils/binutils/util-linux command into public API.

    6. Revised target baseline

    Current public baseline: 29 commands.

    Add from #121 design:

    bzip2
    file
    lsof
    ip
    ss
    dig
    nc
    openssl
    zstd
    envsubst
    npx
    yq
    shfmt
    actionlint
    

    Also promote already-present contract gaps:

    python3
    sqlite3
    xz
    ps
    pgrep
    

    Target after #121: 48 canonical public commands.

    The five newly identified promotions should add essentially no image payload because their providing runtime/packages are already present. The authoritative size gate therefore remains focused on the nine APT candidates plus the three mise-managed binaries.

    7. Explicitly keep out of the immutable universal baseline

    No change to the larger boundary:

    • Docker/Podman CLI/runtime/socket/build backend -> Evaluate isolated container build/test tooling for coding agents #151 / separate container tooling work;
    • Rust, Go, Java, Ruby, PHP, extra Python/Node lines -> feat: support persistent on-demand project toolchains #180 on-demand toolchains;
    • pytest, Ruff, mypy, pre-commit, Poetry/PDM, ESLint, Prettier, TypeScript -> project environment;
    • global Yarn/pnpm versions or automatic package-manager bridge -> deferred;
    • Playwright/Chromium/browser automation -> separate browser tooling work;
    • kubectl/Helm/Terraform/OpenTofu/cloud CLIs -> separate demonstrated need;
    • PostgreSQL/MySQL/Redis network clients -> separate demonstrated need;
    • bat, eza, delta, tree, editor bundles and similar convenience-only additions -> not baseline;
    • heavyweight scanner/security suites -> CI or separate capability.

    8. Implementation split after this audit

    PR 1 — system baseline + consistency promotions

    • nine APT additions;
    • promote python3, sqlite3, xz, ps, pgrep;
    • guarantee npx;
    • no Corepack shim/state work;
    • baseline/Base Verify/Doctor/runtime-smoke/docs alignment;
    • exact image/dependency delta.

    For python3, add a stronger semantic smoke than simple command -v: from neutral CWD prove it selects the same pinned image Python as python, then retain the existing project-resolution regression coverage so both names obey the mise project contract.

    PR 2 — locked universal developer CLIs

    Revised decision

    Proceed with a 48-command intentional public baseline, not the previous 43-command proposal. The important new work is mostly consistency: publish python3, sqlite3, xz, ps and pgrep, which the image/runtime already intentionally depends on or provides. Keep the nine APT utilities and three locked CLIs. Guarantee npx. Remove Corepack shim enablement from #121 to keep the baseline lean, durable across future Node-line changes and free of another mutable package-manager state contract.

  3. eXPerience83 commented on Oct 6, 2026

    @eXPerience83
    OwnerAuthor

    2026-10-06 final pre-implementation design lock

    This checkpoint supersedes the proposed final tool set in the two earlier #121 comments. The existing hardening, project-tool precedence, mise fail-closed behavior, global agent guidance and scratch/cache contracts remain unchanged.

    Product goal

    Remote Dev should be compact but pre-CI capable: not a multi-gigabyte universal workstation, but a strong default toolkit that lets Codex/Antigravity inspect, edit and validate common repositories locally without repeatedly discovering/downloading routine tools.

    A bundled tool is a convenience/default. Repository-declared environments, versions and lockfiles remain authoritative for project correctness. If a project needs a missing tool or a different version, that requirement must be satisfied in project/role-private writable state, never by mutating the immutable Remote Dev toolchain. The general persistent on-demand toolchain mechanism remains #180 and is now a higher-priority follow-up after #121.

    PR 1 — system/development substrate

    Keep the nine reviewed APT additions:

    • bzip2 -> bzip2
    • file -> file
    • lsof -> lsof
    • iproute2 -> ip, ss
    • bind9-dnsutils -> dig
    • netcat-openbsd -> nc
    • openssl -> openssl
    • zstd -> zstd
    • gettext-base -> envsubst

    Add tree as a small, generic repository-inspection utility.

    Promote already-intentional image commands into the public baseline without adding package payload:

    • python3
    • sqlite3
    • xz
    • ps
    • pgrep
    • gcc
    • npx

    Do not enable Corepack/Yarn/pnpm shims in #121. Do not add privileged diagnostics or new capabilities.

    g++ remains intentionally present through the existing build-essential package, but do not broaden the deliberately strict baseline-manifest command-name grammar solely to publish a +-bearing command in PR 1; re-evaluate that inventory-format choice separately if a public C++ compiler command contract becomes necessary.

    PR 2 — universal local-validation toolkit

    Add as locked/audited universal commands:

    • ruff
    • pytest
    • pre-commit
    • yq
    • shfmt
    • actionlint

    Ruff is a cross-project syntax/lint/format preflight tool. Pytest is a small, common test runner useful immediately for simple/reproduction work. Pre-commit is a generic project-configured validation orchestrator; its cache already has a role-private writable path.

    Python CLIs such as pytest/pre-commit must be installed as isolated CLI environments through the reviewed mise/PyPI+uv locked dependency-graph path, not into the image Python site-packages. Ruff may use the reviewed locked backend that gives the cleanest immutable artifact/provenance contract.

    For all of these, a repository-owned version/environment wins for final validation. Global availability must not auto-activate .venv, rewrite PATH by CWD, install project dependencies globally, or silently substitute a different version.

    Still defer mypy/pyright/Black/isort, ESLint/Prettier/TypeScript, tox/nox, hadolint/zizmor/yamllint, CMake/Ninja/Just/Task and large language/infra/browser toolchains unless later evidence justifies them.

    PR 3 — model demand/behavior survey

    After PR 1 and PR 2 are accepted, use the exact then-current admitted Codex and Antigravity CLI/model versions to measure both:

    1. tools they say are essential/recommended/project-specific; and
    2. tools they actually attempt in controlled Python, Node/TS, shell, GitHub Actions, Dockerfile, native-build and mixed-config tasks.

    Treat model answers as demand evidence, not dependency truth. Cross-check with broad ecosystem usefulness, real repository usage, size, write/network behavior and maintenance/supply-chain cost before adding anything else.

    Non-negotiable regression gates

    Each PR must prove:

    • read-only root unchanged;
    • /tmp remains noexec;
    • inherited TMPDIR/project scratch remains the executable writable path;
    • no new broad HOME/XDG writable state;
    • no passive startup/Doctor downloads;
    • current PATH precedence and mise project selection remain intact;
    • missing project-selected versions still fail closed;
    • project-local versions/environments can override/use their own tools without mutating /opt/remote-dev;
    • launcher remains independent of developer tooling;
    • Codex and Antigravity still discover/use the baseline without special prompt instructions;
    • notices/SBOM/Trivy and exact image-size deltas remain reviewable.

    Decision

    Proceed with PR 1 now, then PR 2. Do not merge either solely because commands resolve: require functional smokes and the existing hardening/project-resolution regressions. After both, run PR 3 as a separate evidence-driven expansion review.

  4. eXPerience83 commented on Oct 7, 2026

    @eXPerience83
    OwnerAuthor

    TrueNAS post-merge validation plan — PR #263 / PR1 baseline

    Prepared before hands-on validation so the next TrueNAS session can start directly from the published edge.

    0. Safety / rollback first

    Do not change or delete persistent state. Before updating, record:

    • currently configured image reference;
    • currently running image ID;
    • current immutable RepoDigest if available;
    • current remote-dev-version output from Codex.

    Keep that digest as the rollback target.

    1. FIRST STEP: pull the latest integrated edge

    On the TrueNAS host:

    validation_image=ghcr.io/experience83/remote-dev:edge-amd64
    sudo docker pull "$validation_image"

    Then capture the exact immutable identity before recreating anything:

    edge_id="$(sudo docker image inspect "$validation_image" --format '{{.Id}}')"
    edge_digest="$(
      sudo docker image inspect "$validation_image"     --format '{{range .RepoDigests}}{{println .}}{{end}}' |
      sed -n '/ghcr.io\/experience83\/remote-dev@sha256:/p' |
      head -n1
    )"
    edge_revision="$(
      sudo docker image inspect "$validation_image"     --format '{{ index .Config.Labels "org.opencontainers.image.revision" }}'
    )"
    edge_version="$(
      sudo docker image inspect "$validation_image"     --format '{{ index .Config.Labels "org.opencontainers.image.version" }}'
    )"
    edge_channel="$(
      sudo docker image inspect "$validation_image"     --format '{{ index .Config.Labels "io.github.experience83.remote-dev.channel" }}'
    )"
    
    printf 'edge_id=%s\nedge_digest=%s\nedge_revision=%s\nedge_version=%s\nedge_channel=%s\n'   "$edge_id" "$edge_digest" "$edge_revision" "$edge_version" "$edge_channel"

    Expected:

    • channel = edge;
    • dated edge-YYYY.MM.DD-<sha> build identity;
    • full source revision present;
    • immutable GHCR digest present.

    For the first edge produced from PR #263 with no later main changes, the expected source revision is merge commit d9bda6b6f3fc7fbf051d9bec1fada116f8dd9061. If edge has advanced by validation time, use the latest successful Publish edge run as the authority and record that newer revision/digest instead.

    2. Recreate/update the TrueNAS App with the pulled edge

    Use the existing persistent tree unchanged. Do not bootstrap/migrate state merely for this PR.

    After TrueNAS recreates the services, prove that the running containers use the image that was just pulled:

    expected_id="$(sudo docker image inspect "$validation_image" --format '{{.Id}}')"
    
    for c in remote-dev-launcher codex-remote-dev antigravity-remote-dev; do
      if sudo docker inspect "$c" >/dev/null 2>&1; then
        running_id="$(sudo docker inspect "$c" --format '{{.Image}}')"
        printf '%-24s %s\n' "$c" "$running_id"
        test "$running_id" = "$expected_id" || {
          echo "ERROR: $c is not running the pulled edge image" >&2
          exit 1
        }
      fi
    done

    This host-side comparison is the strongest simple proof that TrueNAS is actually running the newly pulled bytes.

    3. Embedded identity from inside the running roles

    Codex can verify its embedded build identity itself; it intentionally cannot inspect the host Docker digest because the Docker socket is not exposed.

    Host-side:

    sudo docker exec codex-remote-dev remote-dev-version
    sudo docker exec codex-remote-dev remote-dev-version --check
    sudo docker exec codex-remote-dev remote-dev-doctor

    If Antigravity is enabled:

    sudo docker exec antigravity-remote-dev remote-dev-version
    sudo docker exec antigravity-remote-dev remote-dev-version --check
    sudo docker exec antigravity-remote-dev remote-dev-doctor

    Launcher sanity:

    sudo docker exec remote-dev-launcher remote-dev-version --check
    sudo docker exec remote-dev-launcher remote-dev-doctor

    All enabled roles should report the same image version/channel/source revision.

    4. PR1 public baseline — presence in both developer roles

    PR #263 makes the canonical public baseline 47 commands:

    bash tmux ttyd nano less fzf git git-lfs ssh gh curl wget
    ip ss dig nc openssl rg fd jq rsync file tree
    zip unzip tar gzip bzip2 xz zstd patch
    make pkg-config gcc sqlite3 envsubst ps pgrep lsof
    python python3 uv node npm npx mise shellcheck
    

    Doctor already checks this canonical inventory. Add one explicit shell proof in Codex:

    sudo docker exec codex-remote-dev bash -lc '
      set -e
      while IFS= read -r cmd; do
        command -v "$cmd" >/dev/null || { echo "MISSING: $cmd"; exit 1; }
      done < /usr/share/remote-dev/common-tool-baseline.txt
      echo "Codex canonical baseline: OK"
    '

    Repeat in Antigravity when enabled:

    sudo docker exec antigravity-remote-dev bash -lc '
      set -e
      while IFS= read -r cmd; do
        command -v "$cmd" >/dev/null || { echo "MISSING: $cmd"; exit 1; }
      done < /usr/share/remote-dev/common-tool-baseline.txt
      echo "Antigravity canonical baseline: OK"
    '

    5. PR1 semantic checks — not only command presence

    In Codex:

    sudo docker exec codex-remote-dev bash -lc '
      set -e
      cd /
      test "$(python --version 2>&1)" = "$(python3 --version 2>&1)"
      test "$(npm --version)" = "$(npx --version)"
      printf "python/python3: %s\n" "$(python --version 2>&1)"
      printf "npm/npx: %s\n" "$(npm --version)"
      file /bin/sh >/dev/null
      tree -L 1 /workspace >/dev/null
      sqlite3 :memory: "select 1;" | grep -Fx 1
      printf "abc" | gzip -c | gzip -dc | grep -Fx abc
      printf "abc" | bzip2 -c | bzip2 -dc | grep -Fx abc
      printf "abc" | xz -c | xz -dc | grep -Fx abc
      printf "abc" | zstd -q -c | zstd -q -d -c | grep -Fx abc
      printf "X=ok\n" | envsubst | grep -Fx "X=ok"
      openssl version
      gcc --version | head -n1
      echo "PR1 semantic smoke: OK"
    '

    The networking commands are validated by presence/versions; do not generate unnecessary active network traffic just to prove they exist.

    6. Preserve the hardening/scratch contract

    In Codex:

    sudo docker exec codex-remote-dev bash -lc '
      set -e
      printf "TMPDIR=%s\n" "$TMPDIR"
      test -n "$TMPDIR"
      test -d "$TMPDIR"
      probe="$TMPDIR/remote-dev-pr263-probe.sh"
      printf "#!/bin/sh\necho scratch-exec-ok\n" > "$probe"
      chmod 700 "$probe"
      test "$("$probe")" = scratch-exec-ok
      rm -f "$probe"
    
      tmp_probe=/tmp/remote-dev-pr263-noexec.sh
      printf "#!/bin/sh\nexit 0\n" > "$tmp_probe"
      chmod 700 "$tmp_probe"
      if "$tmp_probe" 2>/dev/null; then
        echo "ERROR: /tmp unexpectedly executable" >&2
        rm -f "$tmp_probe"
        exit 1
      fi
      rm -f "$tmp_probe"
      echo "scratch executable + /tmp noexec: OK"
    '

    This specifically protects the earlier class of failures where agents attempted executable work in the hardened system /tmp.

    7. Real agent-behavior sanity

    From an existing real project in Codex, ask the current model to:

    • inspect the repository without installing anything globally;
    • identify the available Remote Dev tools;
    • run the repository's normal lightweight pre-CI checks;
    • use a repository-owned environment/version when the repository declares one;
    • report any command it wanted but could not use.

    Repeat one comparable task in Antigravity.

    Pass condition: both agents naturally discover/use the baseline and do not try to repair it with apt, global pip, global npm -g, writable /opt, or executable /tmp.

    8. Regression sanity outside the new commands

    Confirm:

    • launcher page still opens and navigation works;
    • Codex terminal authentication works;
    • Antigravity terminal/authentication and existing vendor runtime still work when enabled;
    • existing projects/state/auth survive recreation;
    • Start/Resume and tmux persistence behave as before;
    • no unexpected global installs/state appeared.

    Acceptance for PR1 on TrueNAS

    PR1 is accepted on the real host when:

    • latest edge was explicitly pulled first;
    • running containers are proven to use that pulled image ID;
    • embedded channel/version/source identity is valid and consistent;
    • Doctor is green in enabled roles;
    • all 47 canonical commands resolve in Codex and Antigravity;
    • semantic PR1 smoke passes;
    • scratch remains executable while /tmp remains noexec;
    • a real Codex + Antigravity task uses the baseline normally without global-install workarounds;
    • persistent state and normal launcher/agent behavior show no regression.

    No PR2 tooling (Ruff/pytest/pre-commit/yq/shfmt/actionlint) is expected in this acceptance run; that is the next implementation phase.

  5. eXPerience83 commented on Oct 14, 2026

    @eXPerience83
    OwnerAuthor

    PR1 / #263 — TrueNAS real-world acceptance (2026-10-14)

    PASS — PR1 validated on the live TrueNAS deployment. User confirmed that launcher, Codex and experimental Antigravity browser sessions open and that both agents can resume prior conversations after deployment recreation.

    Evidence collected during this acceptance:

    • Published edge-2026.10.07-d9bda6b (source d9bda6b) identity/digest match all three running containers.
    • remote-dev-version --check passes; canonical public baseline resolves 47/47 commands in Codex and AGY.
    • Semantic checks pass in both agents: Python/python3, npm/npx, file/tree/SQLite, gzip/bzip2/xz/zstd, envsubst/OpenSSL, GCC execution from managed scratch, and /tmp noexec.
    • Real agent sessions inherit executable TMPDIR and separate package-manager caches; both detect baseline tools without special startup flags.
    • Deployment issue found and corrected: the TrueNAS Custom App YAML omitted role-private bind mounts state/{codex,antigravity}/mise -> /root/.local/state/mise; both were created mode 0700 and are now separate RW mounts. Per-project mise trust + installed Python 3.14.8 / Node 24.21.0 / uv 0.12.23 selection pass in both agents; test trust was revoked and synthetic directories cleaned.

    Notes: existing Codex workspace-root layout warning (52 unexpected entries) remains for independent read-only investigation; earlier test repository pin for uv 0.12.22 is stale against main/image uv 0.12.23, not a regression; no Docker socket inside agents by design. Trust durability across a subsequent second container recreation has not been separately tested.

    PR1 is accepted; #121 stays open for PR2 (ruff, pytest, pre-commit, yq, shfmt, actionlint) under the final 2026-10-06 design-lock comment. Preserve fail-closed mise/project precedence, security, license, lock/provenance, image-size and CI gates.

  6. eXPerience83 commented on Oct 14, 2026

    @eXPerience83
    OwnerAuthor

    2026-10-14 PR1 → PR2 sequencing checkpoint

    PR1 (#263) remains merged and has passed the real TrueNAS acceptance (47 canonical commands, project/role tools, security, sessions). PR2 has not started: six locked pre-CI validation tools ruff, pytest, pre-commit, yq (Mike Farah), shfmt, actionlint remain exactly in the 2026-10-06 design-lock scope. PR3 remains the later agent-demand survey.

    The separate dual-Python prerequisite #270 is now draft PR #273 with two exact managed runtimes (3.14 default, 3.15 project-selected), full lock/legal evidence and an expanded offline uv venv regression under Codex and AGY role fixtures. See bilingual docs/python-dual-runtime*.md on that branch. Final exact-head checks/review and the +102.38 MiB image-size trade-off remain before merge. PR #272 is a concurrent human-reviewed upstream update and must be reconciled before accepting #273 or commencing PR2.

    Next: finish/accept #273 and TrueNAS verification; then open a separate PR2 branch and enforce the original PR1 regression matrix plus functional/supply-chain/size tests for all six tools. No additional tools are promoted merely because a package is installed.

  7. eXPerience83 commented on Oct 18, 2026

    @eXPerience83
    OwnerAuthor

    2026-10-18 — PR2 pre-implementation checkpoint after dual-Python acceptance

    The accepted PR1 (#263) is complete; TrueNAS has validated the dual-Python edge image (3.14.8 default, 3.15.0 opt-in). #279 upstream refresh and #280 GHCR rescan-auth fix are now squash-merged into main. Neither contains PR2 tooling.

    Fresh upstream release metadata retrieved on 2026-10-18 for the six approved PR2 commands:

    • Ruff 0.17.1 (astral-sh/ruff; 2026-10-15)
    • pytest 9.1.1 (pytest-dev/pytest; 2026-06-19)
    • pre-commit 4.7.0 (pre-commit/pre-commit; 2026-10-13)
    • Mike Farah yq 4.54.1 (mikefarah/yq; 2026-09-29)
    • shfmt 3.14.1 (mvdan/sh; 2026-09-06)
    • actionlint 1.7.12 (rhysd/actionlint; 2026-03-30)

    These are research candidates, not accepted lock entries. PR2 must generate and inspect the exact per-architecture mise/Aqua and PyPI+uv lock artifacts, checksums, transitive Python CLI dependencies, provenance and license evidence under the current reviewed toolchain. Do not invent or hand-fill missing lock data.

    Recommended implementation approach to verify in PR2:

    1. Keep Python CLI tools in explicit isolated, immutable CLI environments pinned to the image-administrative Python 3.14 line. Do not put pytest/pre-commit in the shared Python site-packages; they must still work from repositories selecting 3.15 or containing untrusted mise config. Ruff may use the best reviewed locked backend and must remain independent of project interpreter selection.
    2. Keep yq, shfmt, actionlint on reviewed exact Aqua/mise backends and allow project-owned versions/environments to take precedence where declared.
    3. Extend version/pin checks, strict cross-architecture lock/schema tests, generated legal inventory/notices, upstream refresh ownership, canonical common-tool baseline, Base Verify/Doctor, functional CLI smokes and image-size measurements.
    4. Test both roles from neutral CWD and a project with Python 3.15 selected; test fail-closed missing project version, no passive startup/network installs, executable scratch and /tmp noexec; preserve launcher isolation and all SBOM/Trivy security gates.
    5. Avoid extra tools, Python major-line changes, Corepack shims, Docker socket/capabilities or Antigravity redistributions in PR2.

    No PR2 branch or implementation has been opened as part of this checkpoint. Continue only with a focused PR after exact artifact generation/provenance checks are available. PR3 remains the later observed-agent-demand survey.

  8. eXPerience83 commented on Oct 18, 2026

    @eXPerience83
    OwnerAuthor

    2026-10-18 — PR2 implementation candidate #281

    Exact source head: b516a87, from main 6a0fdf2 after #279/#280. Implements precisely the six tools in the final 2026-10-06 design: Ruff 0.17.1, pytest 9.1.1, pre-commit 4.7.0, Mike Farah yq 4.54.1, shfmt 3.14.1, actionlint 1.7.12. Baseline 53 commands.

    Completed: real pinned-mise/uv dependency graphs and AMD64/ARM64 artifact locks; explicit Python 3.14 isolated CLI environments; reviewed Aqua backends and actual provenance checks; immutable image integration; exact transitive/embedded-seed license closure and standalone inspection; strict validators/mutation tests/updater ownership; shared Base Verify/Doctor inventory and provider-native guidance; EN/ES docs; offline functional tests for both roles from neutral and Python-3.15 project CWDs; project fail-closed, store/role/launcher/scratch/security regression gates.

    Exact-head CI is green: https://github.com/eXPerience83/remote-dev-containers/actions/runs/39128942390 . Includes real built-image smokes, bundled notices, before/after SPDX/Trivy and no-fixable-critical gate. Both images have 0 CRITICAL before and after; SPDX package records increase 1700 → 1767, with all six tool identities represented. Local host validation passes available gates and stops at Compose because Docker is absent; it is not substituted for CI.

    Measured uncompressed sizes in that run:

    • Base: 1,276,630,336 → 1,350,456,498 bytes, +70.41 MiB.
    • Final: 1,655,638,786 → 1,727,000,846 bytes, +68.06 MiB.
    • tooling-image-delta.json and all before/after scan/SBOM reports are retained as CI artifacts. Compressed registry size was not measured; no image publication was performed.

    Remaining before PR2 acceptance: human review of the 50 MiB soft-budget excess, code/legal review under #53 (human reviewed date unchanged), and final-head CodeRabbit review after its external OSS quota resets. CodeRabbit completed the earlier review; its valid finding was fixed, and no inline threads remain. A rate-limited green check is not treated as final approval. The PR merge-ref build also passed: https://github.com/eXPerience83/remote-dev-containers/actions/runs/39128890681 .

    PR1/dual-Python acceptance remains intact. PR3 survey and #180 provisioning are not implemented here. PR2 is not merged, this issue stays open, and no stable release or TrueNAS change is made.

  9. eXPerience83 commented on Oct 18, 2026

    @eXPerience83
    OwnerAuthor

    PR2 follow-up to the independent review is complete in #281 at d242caa (still unmerged).

    Normal CI no longer builds reference images or runs their four extra SPDX/Trivy reports. Those steps and their separate artifact upload require an explicit manual compare_images=true boolean (default false); PR/push cannot enable them. Mandatory candidate controls are unchanged. Only workflow/test/Makefile changed; six-tool implementation and runtime/security/legal inputs are untouched.

    Original measured evidence is preserved: base +70.41 MiB / final +68.06 MiB uncompressed and 0 CRITICAL. No repeat extraordinary comparison was needed for this scheduling-only change. Human acceptance of the size exception and distribution/license review under #53 remain pending. No tracked phase was advanced; no merge, stable release or TrueNAS change.

  10. eXPerience83 commented on Oct 18, 2026

    @eXPerience83
    OwnerAuthor

    2026-10-18 — PR2 #281 squash-merged; pending TrueNAS acceptance

    PR2 was squash-merged into main after explicit project-owner size/distribution approval and final SHA-guarded technical review:

    • Reviewed PR HEAD d242caa7cab77b08d115c6bc6b77d7e25440622b; merged main SHA 5a56c3006135d41c913bb09a7f3be37d35c2c4df.
    • Six exact universal tools are integrated: Ruff 0.17.1, pytest 9.1.1, pre-commit 4.7.0, Mike Farah yq 4.54.1, shfmt 3.14.1, actionlint 1.7.12; canonical commands 47 → 53. Python CLI environments remain isolated, pinned and project-precedence/fail-closed contract tested.
    • Exact-final-head PR build 39159352751, default manual build 39159377402, offline inspection and CodeRabbit final incremental review all succeeded; zero unresolved review threads; candidate Trivy reports showed 0 CRITICAL. Six optional comparison operations are guarded behind manual compare_images=true and skipped on normal runs.
    • Owner-approved soft size exception: base +70.41 MiB, final +68.06 MiB, uncompressed; not compressed size. Targeted legal/distribution review and owner sign-off recorded in #53 comment; maintenance: recurring third-party license and distribution review #53 remains open for its recurring full-scope reviews. Full inventory reviewed_on was not advanced by this targeted change.
    • New main-push Build AMD64 39161916570 and Publish edge AMD64 39161916543 started on the squash SHA; their outcomes require separate observation and must not be presumed. Main publication can move experimental edge tags only after all required gates, not stable/latest.
    • Not yet accepted operationally: test the resulting exact edge digest on real TrueNAS for launcher + Codex + optional Antigravity, 53/53 common commands, functionality of the six tools, Python 3.14 default/3.15 opt-in, project version fail-closed, isolated private role state/scratch, and preservation of previous sessions. Do not close feat: add a lean universal developer tooling baseline #121 or declare a stable release until that evidence is recorded.

    PR3 remains a separate evidence-driven survey, not an add-on to this merge. No changes to TrueNAS were made as part of the merge.

  11. eXPerience83 commented on Oct 19, 2026

    @eXPerience83
    OwnerAuthor

    TrueNAS PR2 acceptance: build-time directory-mode regression (2026-10-19)

    Observed on TrueNAS SCALE, published edge-amd64: OCI source revision 5a56c3006135d41c913bb09a7f3be37d35c2c4df, image ID sha256:bc98ea91157f0c9e7b0600950a6cfd1d2883704389f4eaa39a7130abe6136baa. The launcher, Codex, and Antigravity containers are running and all use the exact same image; remote-dev-version --check, 53-command inventory, remote-dev-base-verify, all 6 pinned PR2 tool --version commands, Python 3.14.8 default and Python 3.15.0 available passed in both roles.

    Blocker: scripts/test-validation-tools.sh --local fails in its intended --cap-drop ALL --read-only --network none temporary sandbox while executing sed on /usr/share/doc/remote-dev/third_party/BUILD-VERSIONS.env: permission denied. Reproduced independently with a clean ephemeral container: /usr/share/doc is mode 0755, but /usr/share/doc/remote-dev is dr--r--r-- (0444, root:root). No search/traversal bit means even uid 0 without DAC capabilities cannot enter third_party. This is a genuine image-filesystem permission defect, not a NAS host ACL issue or a failing CLI. The initial harness's downloaded script was also mode 0600, fixed separately to 0644; this subsequent issue remained.

    Likely cause (source-verified): images/base/Dockerfile has COPY --chmod=0444 LICENSE /usr/share/doc/remote-dev/LICENSE without an explicit preceding install -d -m 0755 /usr/share/doc/remote-dev. BuildKit may create the implicit parent at 0444; the same failure mechanism is already documented/fixed for /etc/mise near lines 180–187. Followed by COPY third_party/ and manifest generation in root-capable build steps, so normal build/verification did not catch it.

    Fix requested (follow-up code PR, not docs #282):

    1. Explicitly install -d -o 0 -g 0 -m 0755 /usr/share/doc/remote-dev before COPY --chmod=0444 LICENSE ...; keep individual documentation files read-only, but traversable parent dirs.
    2. Add a build/runtime permission regression checking the exact mode of this parent and reading BUILD-VERSIONS.env with root DAC caps dropped, then exercise scripts/test-validation-tools.sh on published image in TrueNAS acceptance.
    3. Revisit other implicit parent directories created under COPY --chmod to avoid recurrence.

    Temporary verification option without mutating NAS/app/persistent volumes: run the official functional test in a disposable, read-only, networkless Docker container with --cap-drop ALL --cap-add DAC_READ_SEARCH solely to bypass the bad image-directory traversal. This is not the desired final security-test pass; acceptance of the cap-free hermetic test remains blocked until rebuilt.

    Status: #121 stays open; no stable promotion. Pending: full six-tool functional check, runtime sessions, persistence, per-role isolation, and fixed-image sandbox check.

  12. eXPerience83 commented on Oct 21, 2026

    @eXPerience83
    OwnerAuthor

    TrueNAS acceptance follow-up: functional PR2 verification passed (2026-10-21)

    Operator executed the exact-source scripts/test-validation-tools.sh --local in two disposable Docker containers, REMOTE_DEV_ROLE=codex and antigravity, against the already deployed image ID sha256:bc98ea91157f0c9e7b0600950a6cfd1d2883704389f4eaa39a7130abe6136baa (source 5a56c3006135d41c913bb09a7f3be37d35c2c4df). Both tests PASSED:

    • Two offline pytest probes per role: 1 passed each, including isolated Python 3.14 CLI use.
    • Offline pre-commit local system hook: Passed in neutral and project directories for both roles.
    • yq, shfmt, Ruff, actionlint functional positive/negative checks executed successfully; version pins matched.
    • Python 3.15.0 project selection with mise and uv venv --offline succeeded in both roles; CLI dependency isolation and immutable store snapshots passed.
    • Final outputs: Six offline functional tools, Python isolation and project selection (codex): OK and (... antigravity): OK; PR2: PRUEBAS FUNCIONALES SUPERADAS.

    Important qualification: this pass required --cap-drop ALL --cap-add DAC_READ_SEARCH, because the published image has /usr/share/doc/remote-dev directory at 0444 (independently confirmed). The temporary validation containers remained --network none --read-only, no production/persistent mounts. This is a workaround-only functional pass, NOT a pass of the stricter cap-free hermetic test. The directory-mode fix and a new-image rerun without DAC_READ_SEARCH remain open; keep #121 open and block stable promotion. Next TrueNAS checks: actual runtime health/security/mount isolation, session and persistent-state continuity.

  13. eXPerience83 commented on Oct 21, 2026

    @eXPerience83
    OwnerAuthor

    TrueNAS edge PR2 acceptance update (2026-10-21): completed real Custom App Stop/Start with recreation of all three containers; container IDs changed (launcher, Codex, Antigravity), all returned running/healthy, and version check, healthcheck and Doctor passed. Two synthetic persistence markers placed in separate Codex/Antigravity private runtime directories survived recreation and were verified and deleted. Operator independently confirmed previously existing projects and conversations can be resumed in both agents after recreation. Dataset Pool1/remote-dev passed exact-source data layout preflight and host ACL audit (posix/discard, all 14 private leaves root-owned 0700 with trivial POSIX1E ACLs). Docker hardening and disjoint write bind sources were verified. PR2's six-tool offline functional test passed for both roles with an isolated temporary DAC_READ_SEARCH allowance. Outstanding regression: built image's /usr/share/doc/remote-dev directory is mode 0444; rebuild/fix and re-run capability-free test required before stable qualification. Keep issue open.

  14. eXPerience83 commented on Oct 21, 2026

    @eXPerience83
    OwnerAuthor

    TrueNAS permission regression follow-up: isolated draft fix PR #285 #285 is opened against main at head 7f4e115. Base Dockerfile now explicitly creates /usr/share/doc/remote-dev as root:root 0755 before COPY --chmod=0444 LICENSE and has build-time mode assertion. The no-network/read-only/cap-drop ALL validation script additionally checks directory mode and runs remote-dev-notices --check. Build AMD64 CI is in progress at https://github.com/eXPerience83/remote-dev-containers/actions/runs/39549827484 . Do not modify TrueNAS dataset ACLs. Keep PR2 acceptance pending successful CI, published fixed edge, and capability-free rerun.

  15. eXPerience83 commented on Oct 21, 2026

    @eXPerience83
    OwnerAuthor

    2026-10-21 — integration status and exact TrueNAS post-fix acceptance

    PR2 tooling was merged/published previously (#281), but the real TrueNAS no-capabilities test found /usr/share/doc/remote-dev packaged as a non-traversable 0444 root:root directory. This is image-owned, not host ACL owned. The capability add-back in the earlier disposable validation container was an investigation technique, not the accepted production configuration.

    Now merged: CA/trust remediation #286 into main debb1943471c1975b5ea3e80db9443d3bb0cfe66, with patched pip 26.2.2, certifi 2026.10.19 and uv 0.13.4; exact security PR/head CI and post-merge Build/edge publish are green. Notices-dir traversal #285 also merged on main at e12c4f82c06aa3b18720edd7a1d08694a3e09820. Its exact-head CI #39553584727 is green. Current new-main Build 39554338025 and edge publication 39554337941 were still running when recorded.

    Documentation PR #282 has been truly rebased to repaired main at 4e97f0a81ed3bb5e513180ba592b0d48fca1139d. ES/EN install guides now include read-only proof of root:root 0755 for notices directory and remote-dev-notices --check, alongside 53 commands, isolated Python 3.14/3.15 and digest-pinned YAML sourced from exact image revision.

    Acceptance to execute on the live NAS only after edge publication completes:

    1. Deploy the new exact ghcr.io/experience83/remote-dev@sha256:... digest for all configured roles, preserving existing mounts, ACLs and state; confirm OCI embedded org.opencontainers.image.revision corresponds to expected main commit.
    2. No --cap-add; retain production cap_drop: [ALL], read-only rootfs and isolation. For Codex and Antigravity (if enabled), confirm stat -c '%u:%g %a' /usr/share/doc/remote-dev => 0:0 755, remote-dev-notices --check, remote-dev-version --check, remote-dev-base-verify, six PR2 CLI versions from neutral CWD.
    3. Recheck authentic login/browser sessions, tmux/history persistence, Python 3.14 default and opt-in Python 3.15 selection, workspace/state separation and Doctor. Sanitize all published diagnostic output.
    4. Do not close feat: add a lean universal developer tooling baseline #121 until that real updated image succeeds; do not release stable or rewrite host ACLs merely to work around an image-mode defect.

    No TrueNAS host changes have been performed from this GitHub workflow.

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

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions