Repository navigation
feat: add a lean universal developer tooling baseline #121
Description
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 frommvdan/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 asss,ip addrandip route. -
bind9-dnsutils— providedig/DNS diagnostics. -
netcat-openbsd— bounded TCP/UDP connectivity/listener diagnostics throughnc. -
openssl— TLS/certificate/key inspection CLI. -
zstd— support.zst/.tar.zstand remove the current maintenance asymmetry whereregenerate-mise-lock.shmay need to install zstd ad hoc to refresh Python legal metadata. -
gettext-base— provide lightweight template/environment substitution such asenvsubst.
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
corepackCLI. - Verify
npxremains available with the pinned npm installation. - If safe and deterministic, enable Corepack shims for
yarn/yarnpkgandpnpm/pnpxwithout 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.lockfor the repository's declared lock platforms. - Extend
scripts/validate-mise-lock.pyand 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 lockregeneration model andMISE_SAFE=1boundary. - Do not enable floating mise registries for the immutable image/lock generation path.
- Extend
check-upstream.ymlso 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.jsonthrough 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-aptinventory 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.shto verify the new mandatory commands. - While touching it, compare the current documented universal tool matrix with
base-verify.shand add missing checks for already-promised essential commands where appropriate. - Extend
remote-dev-doctorwith 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 --versionandactionlint --versioncorrespond 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,/optor/usr/localwritable 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.mdso 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,treeor 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,actionlintvia 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:
- the accepted universal baseline is available in a freshly built common image without project-specific setup;
yq,shfmtandactionlintare installed through the repository's strict locked mise path with reviewed origins, checksums/provenance and legal inventory;- the selected low-cost APT diagnostics/utilities are available without any new daemon, credential, mount or Linux-capability requirement;
- 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;
- base verification, doctor/smoke tests and documentation agree on the same baseline;
- exact before/after image-size evidence is recorded and the final set remains justified;
- existing build, isolation, notices, SBOM and vulnerability gates remain green;
- 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:
- mise registry/backends: https://mise.jdx.dev/registry
- Mike Farah yq: https://github.com/mikefarah/yq
- mvdan/sh (
shfmt): https://github.com/mvdan/sh - actionlint: https://github.com/rhysd/actionlint
- Corepack: https://github.com/nodejs/corepack
- Docker Engine security/capability guidance: https://docs.docker.com/engine/security/
- Docker daemon access/security: https://docs.docker.com/engine/security/protect-access/
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
eXPerience83 commentedon Oct 4, 2026
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-> promisebzip2.file-> promisefile.lsof-> promiselsof.iproute2-> promise bothipandssas the intended read-only diagnostics. Do not promise privileged mutation operations merely because the package also contains them.bind9-dnsutils-> promisedig; other package binaries remain incidental unless separately justified.netcat-openbsd-> promisenc, bounded by the existing network/container policy.openssl-> promiseopenssl. This is already pulled transitively byca-certificates; making it an explicit package/baseline commitment documents the intended CLI rather than relying on a dependency accident.zstd-> promisezstd. 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-> promiseenvsubst.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/iproute2have 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:
yq4.54.1aqua:mikefarah/yqshfmt3.14.1aqua:mvdan/shactionlint1.7.12aqua:rhysd/actionlintUse the short tool identities in the Remote Dev
mise.tomlso 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=trueand the existinglinux-x64/linux-arm64lock contract. Revalidate exact current versions and upstream evidence once more at implementation time if upstream has moved since this comment.actionlintmay use the already-globalshellcheckintegration. 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/Pythonyqimplementation 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 to0.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-tmprather than setting generic XDG variables or making HOME writable.Corepack also documents
COREPACK_DEFAULT_TO_LATEST=0, which disables the remotelatestlookup and Last Known Good update behavior. Prefer that fail-closed/deterministic direction, but verify the exact0.36.0behavior before merging rather than assuming it from docs alone.Locked Corepack acceptance behavior
corepackexists and reports0.36.0and that the pinned npm exposesnpx.npxinto the canonical common command baseline.corepack enableonly during image build to create Yarn/pnpm shims. The enable step itself must not download a Yarn/pnpm runtime.COREPACK_HOME; noXDG_CACHE_HOME, writable HOME, broad mount or new Compose knob.pnpm/yarnmay fetch the exact project-declared manager version intoCOREPACK_HOME; that is explicit project behavior, not passive Remote Dev behavior.COREPACK_HOMEtest must cover a project with an explicitpackageManagerversion and prove the downloaded manager/cache stays entirely under the dedicated path.packageManagercase withCOREPACK_DEFAULT_TO_LATEST=0; do not create an implicit moving global package-manager contract.yarn,pnpmorcorepacktocommon-tool-baseline.txtmerely 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.If any of those conditions cannot be met cleanly in the exact candidate, defer Corepack shims without blocking #121 and keep npm + newly-guaranteed
npxas 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, andcorepack enablenormally writes proxies beside the Corepack executable unless--install-directoryis supplied. The candidate must prove that the resultingyarn/pnpmproxies 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:
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
npxtoconfig/common-tool-baseline.txt;COREPACK_HOMEscratch contract and enable immutable proxies with no build/startup download;PR 2 — locked developer CLIs
versions.env;yq,shfmt,actionlinttomise.toml;mise.lockfor x64/arm64;check-upstream.ymlfor the three release lines;Gates that must remain unchanged
Before either PR is mergeable, prove against the exact built candidate:
/tmp noexecunchanged;After both PRs, publish one exact
edgecandidate 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 narrowCOREPACK_HOMEcontract and exact-runtime no-passive-network tests, otherwise defer the bridge.This freezes the implementation direction without starting either implementation PR yet.
eXPerience83 commentedon Oct 5, 2026
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.txtcurrently exposes 29 commands. The image also explicitly installsprocps,sqlite3andxz-utils, and the runtime/tooling contract already treatspython3as 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 alongsidepythonas an ordinary project command. Acceptance must prove from neutral CWD thatpythonandpython3resolve 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-utilsis already explicitly installed; this closes the archive-format inconsistency next togzip,zip/unzip, and plannedbzip2/zstd.psandpgrep—procpsis already explicitly installed andpgrepis 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->bzip2file->filelsof->lsofiproute2->ip,ssbind9-dnsutils->dignetcat-openbsd->ncopenssl->opensslzstd->zstdgettext-base->envsubstThese 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 onlyshfmt— exact reviewed Aqua backend onlyactionlint— exact reviewed Aqua backend onlyThey are genuinely cross-project, work offline once installed, need no daemon/credentials, and complement existing
jq/shellcheckrather 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 #121Promote
npxas a guaranteed command alongside the pinnednpm.After re-auditing the bridge, do not enable Corepack shims in #121:
COREPACK_HOMEandCOREPACK_DEFAULT_TO_LATEST=0;yarn/pnpmare project-selected package managers, not immutable binaries we need to promise globally today;The exact pinned Node may continue to contain a
corepackexecutable incidentally, but #121 should make no public availability or shim contract forcorepack,yarn,yarnpkg,pnpmorpnpx.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 asfind/xargs.Do not add all of these to
common-tool-baseline.txtjust because they exist. Instead, keep a clear distinction: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:
Also promote already-present contract gaps:
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:
bat,eza,delta,tree, editor bundles and similar convenience-only additions -> not baseline;8. Implementation split after this audit
PR 1 — system baseline + consistency promotions
python3,sqlite3,xz,ps,pgrep;npx;For
python3, add a stronger semantic smoke than simplecommand -v: from neutral CWD prove it selects the same pinned image Python aspython, then retain the existing project-resolution regression coverage so both names obey the mise project contract.PR 2 — locked universal developer CLIs
yq,shfmt,actionlintvia the strict mise lock path;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,psandpgrep, which the image/runtime already intentionally depends on or provides. Keep the nine APT utilities and three locked CLIs. Guaranteenpx. 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.eXPerience83 commentedon Oct 6, 2026
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:
Add tree as a small, generic repository-inspection utility.
Promote already-intentional image commands into the public baseline without adding package payload:
Do not enable Corepack/Yarn/pnpm shims in #121. Do not add privileged diagnostics or new capabilities.
g++remains intentionally present through the existingbuild-essentialpackage, 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 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:
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:
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.
eXPerience83 commentedon Oct 7, 2026
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:
remote-dev-versionoutput 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:
Expected:
edge;edge-YYYY.MM.DD-<sha>build identity;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:
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:
If Antigravity is enabled:
Launcher sanity:
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:
Doctor already checks this canonical inventory. Add one explicit shell proof in Codex:
Repeat in Antigravity when enabled:
5. PR1 semantic checks — not only command presence
In Codex:
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:
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:
Repeat one comparable task in Antigravity.
Pass condition: both agents naturally discover/use the baseline and do not try to repair it with
apt, globalpip, globalnpm -g, writable/opt, or executable/tmp.8. Regression sanity outside the new commands
Confirm:
Acceptance for PR1 on TrueNAS
PR1 is accepted on the real host when:
/tmpremains noexec;No PR2 tooling (Ruff/pytest/pre-commit/yq/shfmt/actionlint) is expected in this acceptance run; that is the next implementation phase.
eXPerience83 commentedon Oct 14, 2026
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:
edge-2026.10.07-d9bda6b(sourced9bda6b) identity/digest match all three running containers.remote-dev-version --checkpasses; canonical public baseline resolves 47/47 commands in Codex and AGY./tmpnoexec.state/{codex,antigravity}/mise -> /root/.local/state/mise; both were created mode 0700 and are now separate RW mounts. Per-projectmise 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.eXPerience83 commentedon Oct 14, 2026
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,actionlintremain 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 venvregression under Codex and AGY role fixtures. See bilingualdocs/python-dual-runtime*.mdon 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.
eXPerience83 commentedon Oct 18, 2026
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:
astral-sh/ruff; 2026-10-15)pytest-dev/pytest; 2026-06-19)pre-commit/pre-commit; 2026-10-13)mikefarah/yq; 2026-09-29)mvdan/sh; 2026-09-06)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:
yq,shfmt,actionlinton reviewed exact Aqua/mise backends and allow project-owned versions/environments to take precedence where declared./tmp noexec; preserve launcher isolation and all SBOM/Trivy security gates.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.
eXPerience83 commentedon Oct 18, 2026
2026-10-18 — PR2 implementation candidate #281
Exact source head: b516a87, from main
6a0fdf2after #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:
tooling-image-delta.jsonand 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.
eXPerience83 commentedon Oct 18, 2026
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=trueboolean (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.
eXPerience83 commentedon Oct 18, 2026
2026-10-18 — PR2 #281 squash-merged; pending TrueNAS acceptance
PR2 was squash-merged into
mainafter explicit project-owner size/distribution approval and final SHA-guarded technical review:d242caa7cab77b08d115c6bc6b77d7e25440622b; mergedmainSHA5a56c3006135d41c913bb09a7f3be37d35c2c4df.compare_images=trueand skipped on normal runs.reviewed_onwas not advanced by this targeted change.edgedigest 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.
eXPerience83 commentedon Oct 19, 2026
TrueNAS PR2 acceptance: build-time directory-mode regression (2026-10-19)
Observed on TrueNAS SCALE, published
edge-amd64: OCI source revision5a56c3006135d41c913bb09a7f3be37d35c2c4df, image IDsha256: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--versioncommands, Python 3.14.8 default and Python 3.15.0 available passed in both roles.Blocker:
scripts/test-validation-tools.sh --localfails in its intended--cap-drop ALL --read-only --network nonetemporary sandbox while executingsedon/usr/share/doc/remote-dev/third_party/BUILD-VERSIONS.env: permission denied. Reproduced independently with a clean ephemeral container:/usr/share/docis mode 0755, but/usr/share/doc/remote-devisdr--r--r--(0444, root:root). No search/traversal bit means even uid 0 without DAC capabilities cannot enterthird_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/DockerfilehasCOPY --chmod=0444 LICENSE /usr/share/doc/remote-dev/LICENSEwithout an explicit precedinginstall -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/misenear lines 180–187. Followed byCOPY 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):
install -d -o 0 -g 0 -m 0755 /usr/share/doc/remote-devbeforeCOPY --chmod=0444 LICENSE ...; keep individual documentation files read-only, but traversable parent dirs.BUILD-VERSIONS.envwith root DAC caps dropped, then exercisescripts/test-validation-tools.shon published image in TrueNAS acceptance.COPY --chmodto 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_SEARCHsolely 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.
eXPerience83 commentedon Oct 21, 2026
TrueNAS acceptance follow-up: functional PR2 verification passed (2026-10-21)
Operator executed the exact-source
scripts/test-validation-tools.sh --localin two disposable Docker containers,REMOTE_DEV_ROLE=codexandantigravity, against the already deployed image IDsha256:bc98ea91157f0c9e7b0600950a6cfd1d2883704389f4eaa39a7130abe6136baa(source5a56c3006135d41c913bb09a7f3be37d35c2c4df). Both tests PASSED:uv venv --offlinesucceeded in both roles; CLI dependency isolation and immutable store snapshots passed.Six offline functional tools, Python isolation and project selection (codex): OKand(... 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-devdirectory 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 withoutDAC_READ_SEARCHremain open; keep #121 open and block stable promotion. Next TrueNAS checks: actual runtime health/security/mount isolation, session and persistent-state continuity.eXPerience83 commentedon Oct 21, 2026
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.
eXPerience83 commentedon Oct 21, 2026
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.
eXPerience83 commentedon Oct 21, 2026
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-devpackaged as a non-traversable0444 root:rootdirectory. 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 patchedpip 26.2.2,certifi 2026.10.19anduv 0.13.4; exact security PR/head CI and post-merge Build/edge publish are green. Notices-dir traversal #285 also merged on main ate12c4f82c06aa3b18720edd7a1d08694a3e09820. 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 ofroot:root 0755for notices directory andremote-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:
ghcr.io/experience83/remote-dev@sha256:...digest for all configured roles, preserving existing mounts, ACLs and state; confirm OCI embeddedorg.opencontainers.image.revisioncorresponds to expectedmaincommit.--cap-add; retain productioncap_drop: [ALL], read-only rootfs and isolation. For Codex and Antigravity (if enabled), confirmstat -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.No TrueNAS host changes have been performed from this GitHub workflow.