The stable and dogfood OpenClaw source pins need different public-surface hardlink patch shapes while upstream is still catching up. Select the right patch per source and make package contents import the OpenAI provider policy public surface, which is the path the gateway uses before a model run.
Tests: nix fmt --accept-flake-config; git diff --check; remote mac-mini nix build .#checks.aarch64-darwin.package-contents-dogfood --no-link; remote mac-mini nix build .#checks.aarch64-darwin.package-contents --no-link
Co-authored-by: Codex <noreply@openai.com>
Dogfood now points at an OpenClaw commit with the broader plugin hardlink work merged, but the bundled public-surface loader still needs nix-openclaw's package-root hardlink compatibility patch. Keep that patch active for dogfood and add a package-content check that fails if the compiled loader rejects hardlinked package files again.
Tests: nix fmt --accept-flake-config; git diff --check; remote mac-mini nix build .#checks.aarch64-darwin.package-contents-dogfood --no-link; remote mac-mini nix build .#checks.aarch64-darwin.package-contents --no-link; remote mac-mini nix build .#checks.aarch64-darwin.default-instance --no-link
Co-authored-by: Codex <noreply@openai.com>
What:\n- expose temporary dogfood package outputs pinned to an upstream OpenClaw commit with the Nix-mode fixes merged\n- let source pins disable downstream patches that are already upstream\n- build current upstream plugin assets through upstream asset hooks, while keeping the 2026.5.7 path working\n- supply the fs-safe Git dependency as an immutable Nix source for the dogfood build\n\nWhy:\n- private deployments need to dogfood upstream fixes before the next OpenClaw release without making the published stable package depend on runtime npm work\n\nTests:\n- remote Mac mini: nix build --accept-flake-config .#openclaw-gateway-dogfood --no-link\n- remote Mac mini: nix build --accept-flake-config .#openclaw-dogfood --no-link\n- remote Mac mini: nix build --accept-flake-config .#checks.aarch64-darwin.default-instance --no-link\n- remote Mac mini: nix build --accept-flake-config .#checks.aarch64-darwin.package-contents --no-link\n\nCo-authored-by: Codex <noreply@openai.com>
Add a hash-backed npm runtime plugin path that lowers OpenClaw-style npm sources into immutable plugin roots and wires them through the existing Home Manager plugin resolver. Keep flake-backed customPlugins unchanged and document the boundary for agents and maintainers.
Tests: nix build .#checks.aarch64-darwin.default-instance --no-link; nix flake check --no-build; git diff --check
Co-authored-by: Codex <noreply@openai.com>
Move the default-instance check into the common check set so Darwin evaluates the plugin/config generation path too. Keep the Linux systemd assertion and add the Darwin launchd assertion for the Home Manager module.
Tests:
- nix build --accept-flake-config .#checks.aarch64-darwin.default-instance .#checks.aarch64-darwin.package-contents .#checks.aarch64-darwin.config-validity --no-link --option narinfo-cache-negative-ttl 0
- nix build --accept-flake-config .#checks.x86_64-linux.default-instance --no-link --option narinfo-cache-negative-ttl 0
- nix build --accept-flake-config .#checks.aarch64-darwin.ci --no-link --option narinfo-cache-negative-ttl 0
- nix build --impure --accept-flake-config .#darwinConfigurations.mac-mini.system --no-link --override-input nix-openclaw path:/Users/josh/code/nix/nix-openclaw --option narinfo-cache-negative-ttl 0
Co-authored-by: Codex <noreply@openai.com>
Align the openclawPlugin.plugins contract with OpenClaw itself: load paths control discovery, while plugins.entries.<id>.enabled controls activation.
Rename the contract field to enabled, reject the accidental enable spelling, and keep plugin roots on plugins.load.paths even when their generated activation default is false.
Extend the eval fixture to prove enabled=false defaults, user overrides from true to false, and user overrides from false to true.
Tests: nix build --accept-flake-config .#checks.x86_64-linux.default-instance --no-link --print-out-paths; nix eval --accept-flake-config --raw .#checks.aarch64-darwin.package-contents.drvPath; nix build --accept-flake-config .#checks.aarch64-darwin.package-contents --no-link --dry-run; nix build --impure --accept-flake-config .#darwinConfigurations.mac-mini.system --no-link --override-input nix-openclaw path:/Users/josh/code/nix/nix-openclaw --dry-run
Upstream review: fetched openclaw/openclaw origin/main at 36f847a60e and checked plugin discovery/config semantics before finalizing the contract.
Co-authored-by: Codex <noreply@openai.com>
Install and validate OpenClaw's dist-runtime tree so bundled runtime plugins such as ACPX are present in the Nix gateway output.
Extend the existing plugin flake contract with immutable OpenClaw plugin roots, wire those roots into generated config, and add eval fixtures proving default enablement, user overrides, and disabled entries.
Document the boundary: curated plugin artifacts are CI/Garnix-cached by nix-openclaw, while arbitrary npm or ClawHub specs need deterministic lock/hash-backed Nix artifacts cached by the user's store/cache instead of runtime npm installs.
Tests: nix build --accept-flake-config .#checks.x86_64-linux.default-instance --no-link --print-out-paths; nix eval --accept-flake-config --raw .#checks.aarch64-darwin.package-contents.drvPath; nix build --accept-flake-config .#checks.aarch64-darwin.package-contents --no-link --dry-run; nix build --impure --accept-flake-config .#darwinConfigurations.mac-mini.system --no-link --override-input nix-openclaw path:/Users/josh/code/nix/nix-openclaw --dry-run
Co-authored-by: Codex <noreply@openai.com>
What:
- update nix-openclaw to the latest stable OpenClaw source release
- refresh generated config options from that source
- keep the macOS app pin on the newest public app artifact
Why:
- keep source-built OpenClaw current without blocking on public macOS app asset lag
Tests:
- nix build .#checks.x86_64-linux.ci --accept-flake-config
- nix build .#checks.aarch64-darwin.ci --accept-flake-config
- scripts/hm-activation-macos.sh
What:
- update nix-openclaw to the latest stable OpenClaw source release
- refresh generated config options from that source
- keep the macOS app pin on the newest public app artifact
Why:
- keep source-built OpenClaw current without blocking on public macOS app asset lag
Tests:
- nix build .#checks.x86_64-linux.ci --accept-flake-config
- nix build .#checks.aarch64-darwin.ci --accept-flake-config
- scripts/hm-activation-macos.sh
What:
- let the yolo promote job dispatch the CI workflow after pushing main
- grant the promote job actions:write for workflow_dispatch
Why:
- GitHub suppresses push-triggered workflows created by GITHUB_TOKEN, so yolo-promoted commits otherwise lack a CI run on the final main SHA
Tests:
- scripts/check-flake-lock-owners.sh
- node scripts/select-openclaw-release.test.mjs
- bash -n scripts/update-pins.sh
- ruby -e 'require "yaml"; ARGV.each { |f| YAML.load_file(f) }' .github/workflows/*.yml
- GITHUB_ACTIONS=true scripts/update-pins.sh select
- nix flake show --accept-flake-config
Co-authored-by: Codex <noreply@openai.com>
What:
- update nix-openclaw to the latest stable OpenClaw source release
- refresh generated config options from that source
- keep the macOS app pin on the newest public app artifact
Why:
- keep source-built OpenClaw current without blocking on public macOS app asset lag
Tests:
- nix build .#checks.x86_64-linux.ci --accept-flake-config
- nix build .#checks.aarch64-darwin.ci --accept-flake-config
- scripts/hm-activation-macos.sh
What:
- document the QMD/mcporter packaging decision for maintainers
- capture the native OpenClaw plugin gap and proposed nix-openclaw fix
Why:
- preserve the Discord investigation so the feature work can resume later
- distinguish PR #81's bundled manifest fix from external native plugin support
Tests:
- git diff --check: pass
- nix build .#checks.aarch64-darwin.package-contents --no-link: pass
Co-authored-by: Codex <noreply@openai.com>
Configured OpenClaw documents and skills are Nix-owned targets. Replace them during activation instead of blocking on stale copied files from older generations.
Tests: manual materializer smoke replacing an existing writable target; nix build --accept-flake-config .#checks.aarch64-darwin.ci --no-link
Allow the workspace materializer to replace read-only existing targets when the state manifest is missing, while still refusing writable user-owned files. This keeps Nix-owned OpenClaw docs deployable across module upgrades.
Tests: manual materializer smoke for read-only adopt and writable refusal; nix build --accept-flake-config .#checks.aarch64-darwin.ci --no-link
Home Manager should replace the generated openclaw.json target because Nix owns that file. Without force=true, Darwin deploys can fail when the existing config symlink points at the previous generation.
Tests: nix build --accept-flake-config .#checks.aarch64-darwin.ci --no-link
Render TOOLS.md from actual package outputs instead of requested tool attrs, and include per-instance runtime packages so agent docs match the commands on PATH.
Tests: nix build --accept-flake-config .#checks.aarch64-darwin.ci --no-link
Do not require the nix-openclaw overlay when a downstream Home Manager config explicitly supplies the OpenClaw package. This keeps consumer hosts thin and lets them use flake package outputs directly.
Tests: nix eval --accept-flake-config .#checks.x86_64-linux.default-instance.drvPath; nix build --accept-flake-config .#checks.aarch64-darwin.config-validity --no-link
Point Home Manager bundled plugin sources at the current nix-openclaw-tools commit so Darwin QMD plugin installs use the fixed packaged tool.
Tests: nix eval --accept-flake-config .#checks.x86_64-linux.default-instance.drvPath; nix build --accept-flake-config .#checks.aarch64-darwin.config-validity --no-link
Add Home Manager runtimePackages/environment options that feed the gateway wrapper without polluting the user PATH. Link the same runtime package set into Codex's isolated agent home so shell calls from the Codex harness see Nix-managed plugin and helper CLIs.
Tests: ./scripts/check-flake-lock-owners.sh; nix flake show --accept-flake-config; nix build --accept-flake-config .#checks.aarch64-darwin.ci --no-link; nix build --accept-flake-config .#checks.aarch64-darwin.qmd-runtime .#checks.aarch64-darwin.bin-surface .#checks.aarch64-darwin.package-contents --no-link; nix eval --accept-flake-config .#checks.x86_64-linux.default-instance.drvPath; ./scripts/hm-activation-macos.sh
Green CI alone is not enough for downstream Nix consumers: the user-facing and gateway package outputs must be top-level Garnix artifacts so machines can substitute them without local OpenClaw builds.
Tests: git diff --check
The gateway runtime loads built plugins from dist/extensions, so the source extensions tree only needs manifest metadata for compatibility. Copy only openclaw.plugin.json files and assert the memory-core manifest exists in both compatibility and runtime trees.
Tests: sh -n nix/scripts/gateway-install.sh nix/scripts/check-package-contents.sh; git diff --check; nix build --dry-run .#checks.aarch64-darwin.package-contents .#checks.x86_64-linux.package-contents --accept-flake-config
Use a normal temporary build root and copy only final runtime outputs into the Nix output. This avoids the Garnix stall caused by recursively deleting the temporary OpenClaw source tree from inside $out during install.
Tests: sh -n nix/scripts/build-root.sh nix/scripts/gateway-install.sh; git diff --check; nix build --dry-run .#checks.aarch64-darwin.qmd-runtime .#checks.x86_64-linux.qmd-runtime --accept-flake-config
Move the temporary output build root out of the final output instead of recursively deleting it during install, and time the final wrapper/cleanup steps so Garnix logs show where gateway builds stall.
Tests: sh -n nix/scripts/build-root.sh nix/scripts/gateway-install.sh; git diff --check; nix build --dry-run .#checks.aarch64-darwin.qmd-runtime .#checks.x86_64-linux.qmd-runtime --accept-flake-config
Use NIX_BUILD_CORES for zstd extraction instead of --threads=0. Garnix macOS builders were hanging in the pnpm-store extraction step on the fresh OpenClaw gateway build.
Tests: git diff --check. Full package proof is delegated to Garnix because the fresh v2026.5.5 gateway source build is intentionally not run locally.
Remove the install-phase patchShebangs pass over node_modules/.bin. The build phase already runs patchShebangs before packaging, and the second pass can hang Garnix while not materially changing the output.
Tests: git diff --check. Full package proof is delegated to Garnix because the fresh v2026.5.5 gateway source build is intentionally not run locally.
Update nix-openclaw-tools to the Darwin QMD package that provides xcbuild for node-gyp in Garnix.
Tests: nix build .#checks.aarch64-darwin.qmd-runtime --no-link --accept-flake-config before rebasing onto the v2026.5.5 source bump; nix build .#checks.x86_64-linux.qmd-runtime --no-link --accept-flake-config before rebasing onto the v2026.5.5 source bump; git diff --check. The v2026.5.5 Darwin gateway build is left to CI/Garnix to avoid a large local source build.
What:
- update nix-openclaw to the latest stable OpenClaw source release
- refresh generated config options from that source
- keep the macOS app pin on the newest public app artifact
Why:
- keep source-built OpenClaw current without blocking on public macOS app asset lag
Tests:
- nix build .#checks.x86_64-linux.ci --accept-flake-config
- nix build .#checks.aarch64-darwin.ci --accept-flake-config
- scripts/hm-activation-macos.sh
Use QMD's real update/embed/query commands instead of the nonexistent qmd pull path, and keep the activation script in nix/scripts instead of inline Nix.
Tests: nix build .#checks.aarch64-darwin.config-validity .#checks.aarch64-darwin.qmd-runtime --no-link; nix eval .#checks.x86_64-linux.qmd-runtime.name; scripts/check-flake-lock-owners.sh; git diff --check
Bundle a Nix Python path inside the OpenClaw batteries wrapper for the safe-write helper. This avoids macOS /usr/bin/python3 triggering the Xcode command-line-tools shim on headless hosts, without exposing Python on the user PATH.
Tests: nix build .#checks.aarch64-darwin.qmd-runtime --no-link; nix eval .#checks.x86_64-linux.qmd-runtime.name; scripts/check-flake-lock-owners.sh; git diff --check
Keep the QMD package internal to OpenClaw, but source it from upstream tobi/qmd on Linux and from the repaired nix-openclaw-tools package on Darwin until upstream Darwin packaging works.
Tests: scripts/check-flake-lock-owners.sh; nix build .#checks.aarch64-darwin.qmd-runtime --no-link; nix eval .#packages.x86_64-linux.qmd.outPath; nix eval .#packages.aarch64-darwin.qmd.outPath; git diff --check
Keep upstream tobi/qmd as the Linux QMD package while retaining the nix-openclaw-tools Darwin repair package until upstream Darwin is fixed. This preserves the batteries-included runtime path without forking the Linux package surface.
Tests: nix build '.#checks.aarch64-darwin.qmd-runtime' --accept-flake-config --no-link; nix eval --option eval-cache false --raw '.#checks.x86_64-linux.qmd-runtime.name' --accept-flake-config; nix eval --option eval-cache false --raw '.#checks.aarch64-darwin.qmd-runtime.name' --accept-flake-config; git diff --check
What:
- consume QMD from nix-openclaw-tools instead of a separate upstream flake input
- expose QMD as an internal OpenClaw battery on Darwin and Linux
- add an opt-in Home Manager qmd model prewarm activation
- keep plugin packages off the user's shell PATH by default while preserving the runtime PATH
Why:
- nix-openclaw-tools owns reproducible tool packages and cacheable plugin metadata
- nixos-config should configure OpenClaw, not hand-wire runtime tools
Tests:
- nix build .#checks.aarch64-darwin.package-contents --accept-flake-config --no-link
- nix build .#checks.aarch64-darwin.qmd-runtime --accept-flake-config --no-link
- nix build .#checks.aarch64-darwin.bin-surface .#checks.aarch64-darwin.config-validity .#checks.aarch64-darwin.gateway-smoke --accept-flake-config --no-link
- nix eval .#checks.x86_64-linux.default-instance.drvPath --accept-flake-config
What:
- run upstream runtime-postbuild after tsdown in the gateway build
- assert runtime-model-auth stable alias points at a real generated target
Why:
- bundled provider auth imports need the upstream stable runtime alias materialized in the Nix package
- package-contents should catch missing runtime aliases before deployment
Tests:
- nix build .#checks.aarch64-darwin.package-contents --accept-flake-config --no-link
- nix build .#checks.aarch64-darwin.qmd-runtime --accept-flake-config --no-link
- nix build .#checks.aarch64-darwin.bin-surface .#checks.aarch64-darwin.config-validity .#checks.aarch64-darwin.gateway-smoke --accept-flake-config --no-link
Clarify that AGENTS.md is maintainer operating guidance for the public packaging repo, not consumer onboarding or private deployment policy.
Remove the private bot reference, generalize recovery wording, and keep automation focused on the public split-track publishing invariant.
Checks: git diff --check -- AGENTS.md; scripts/check-flake-lock-owners.sh
Describe the daily nix-openclaw maintainer run by desired publishing state instead of enumerated failure modes.
The automation must restore split-track freshness for latest source gateway and latest published macOS app, or report the exact blocker.
Checks: git diff --check -- AGENTS.md; scripts/check-flake-lock-owners.sh
Define daily maintainer automation health as split-track freshness: gateway tracks latest stable source, while the Darwin app tracks the newest published public macOS zip.
This prevents expected source/app version differences from being treated as failures, while requiring pipeline repair when either track is stale.
Checks: git diff --check -- AGENTS.md; scripts/check-flake-lock-owners.sh
QMD currently pulls a native better-sqlite3 rebuild that fails on Garnix Darwin because node-gyp cannot find Xcode/CLT in the builder.
Keep QMD bundled and checked on Linux, but omit it from Darwin packages until the QMD package is Darwin-cacheable.
Tests: nix eval --accept-flake-config --json .#checks.aarch64-darwin --apply 'builtins.attrNames'; nix eval --accept-flake-config --json .#checks.x86_64-linux --apply 'builtins.attrNames'; nix build --accept-flake-config .#checks.aarch64-darwin.ci --no-link --print-build-logs
What:
- make the downstream Nix-mode auto-enable patch runtime-only and remove broken degraded-state references
- allow plugin-owned channels.<id> config in generated Home Manager options
- add Telegram channel config coverage to the config validity check
- document the Nix/OpenClaw boundary in AGENTS.md
Why:
- Nix-owned openclaw.json must not be mutated at runtime
- plugin channel config should stay valid even when upstream core schema does not type every plugin-owned channel key
- future agents need the boundary documented in the packaging repo
Tests:
- patch -d /tmp/openclaw-v2026.5.4 -p1 --dry-run < nix/patches/skip-plugin-auto-enable-persist-in-nix-mode.patch: passed
- generator round-trip against OpenClaw 325df3ef produced no diff: passed
- nix eval --accept-flake-config --raw .#checks.aarch64-darwin.config-validity.drvPath: passed
- nix eval --accept-flake-config --raw .#checks.x86_64-linux.config-options.drvPath: passed
- nix build --accept-flake-config .#checks.aarch64-darwin.config-validity --no-link --print-build-logs: passed
Make QMD the Nix-supported batteries-included local memory backend by pinning the upstream QMD flake and adding qmd to the private openclaw wrapper PATH.
Keep QMD opt-in through upstream OpenClaw config with memory.backend = qmd, and document that builtin memorySearch.provider = local remains an escape hatch rather than the primary supported Nix path.
Also point nix run .#openclaw at the batteries-included bundle so app execution gets the same internal runtime PATH as the package.
Tests: sh -n nix/scripts/check-openclaw-qmd-runtime.sh; scripts/check-flake-lock-owners.sh; git diff --check; nix flake show --accept-flake-config --json; nix build .#checks.aarch64-darwin.qmd-runtime .#checks.aarch64-darwin.bin-surface .#packages.aarch64-darwin.openclaw --accept-flake-config --no-link --print-out-paths; nix build .#checks.x86_64-linux.qmd-runtime --accept-flake-config --no-link --print-out-paths; nix build .#checks.aarch64-darwin.ci --accept-flake-config --no-link --print-out-paths; nix build .#packages.x86_64-linux.openclaw .#checks.x86_64-linux.bin-surface --accept-flake-config --no-link --print-out-paths; scripts/hm-activation-macos.sh; nix build .#checks.x86_64-linux.gateway-smoke --accept-flake-config --no-link --print-out-paths; nix run .#openclaw --accept-flake-config -- --version; bash -n scripts/update-pins.sh; node --check scripts/select-openclaw-release.mjs; node --check scripts/select-openclaw-release.test.mjs; node scripts/select-openclaw-release.test.mjs
OpenClaw v2026.5.4 resolves bundled plugin public artifacts under the package-root extension surface. In the Nix store those artifacts may be hardlinked, so allow hardlinks for resolved module paths inside OPENCLAW_PACKAGE_ROOT while keeping the existing hardlink rejection elsewhere.
Tests: nix build .#checks.aarch64-darwin.gateway-smoke --accept-flake-config --no-link --print-out-paths; nix build .#checks.aarch64-darwin.ci --accept-flake-config --no-link --print-out-paths; nix build .#packages.x86_64-linux.openclaw .#checks.x86_64-linux.bin-surface .#checks.x86_64-linux.gateway-smoke --accept-flake-config --no-link --print-out-paths; scripts/check-flake-lock-owners.sh; scripts/hm-activation-macos.sh
What:
- update nix-openclaw to the latest stable OpenClaw source release
- refresh generated config options from that source
- keep the macOS app pin on the newest public app artifact
Why:
- keep source-built OpenClaw current without blocking on public macOS app asset lag
Tests:
- nix build .#checks.x86_64-linux.ci --accept-flake-config
- nix build .#checks.aarch64-darwin.ci --accept-flake-config
- scripts/hm-activation-macos.sh
Expose only the openclaw command from the default package while keeping bundled runtime tools on the wrapper PATH. Remove the public openclaw-tools package output and document runtime tools as implementation detail.
Tests: nix build .#packages.aarch64-darwin.openclaw .#checks.aarch64-darwin.bin-surface .#packages.x86_64-linux.openclaw .#checks.x86_64-linux.bin-surface --accept-flake-config --no-link --print-out-paths; nix build .#checks.aarch64-darwin.ci --accept-flake-config --no-link --print-out-paths; git diff --check --cached
Make the repo-local agent rules explicit: work on main by default, push surgical commits directly, and verify CI per pushed commit.
This prevents completed nix-openclaw maintainer work from being parked on Codex branches.
Tests: git diff --check
Patch the gateway startup path so OPENCLAW_NIX_MODE skips plugin auto-enable persistence instead of replacing a Nix-managed config symlink.
Plugins remain a declarative Nix config choice under the Home Manager module; runtime auto-enable can still report what it would have changed.
Tests: git diff --cached --check; patch -p1 --dry-run against pinned OpenClaw source; nix build .#packages.aarch64-darwin.openclaw-gateway --accept-flake-config --no-link --print-out-paths; OPENCLAW_NIX_MODE symlink-clobber smoke against the native gateway; nix build .#packages.x86_64-linux.openclaw-gateway --accept-flake-config --no-link --print-out-paths
Generate a Nix-owned source/target manifest for workspace documents and skills, then pass that manifest to the activation helper.
This keeps document and plugin source paths in the Home Manager generation closure so the NixOS VM can actually copy them during activation.
Tests: helper smoke; git diff --check; nix build .#checks.x86_64-linux.default-instance --accept-flake-config --no-link --print-out-paths; nix build .#checks.x86_64-linux.hm-activation.nodes.machine.system.build.toplevel --accept-flake-config --no-link --print-out-paths
What:
- copy Nix-managed documents and skills into the OpenClaw workspace as real files
- replace Home Manager symlink installs with an activation-time materialization helper
- extend checks to assert custom plugin skills and document files are not symlinks
Why:
- OpenClaw rejects workspace files that resolve back into the Nix store
- custom plugin skills and documents must satisfy the gateway workspace boundary
Tests:
- git diff --cached --check: passed
- nix/modules/home-manager/openclaw-materialize-workspace-files.sh smoke: copied docs and skill dirs as non-symlinks, rerun idempotent
- temporary worktree with only this staged patch: nix build #checks.x86_64-linux.default-instance --accept-flake-config --no-link --print-out-paths: /nix/store/2zihci7mhlk3mcbczmyw0s401n162vk7-openclaw-default-instance-1
- temporary worktree with only this staged patch: nix build #checks.x86_64-linux.hm-activation --accept-flake-config --no-link --print-out-paths: materialization assertions passed; later gateway open-port wait timed out under local TCG VM after 900s
What:
- copy upstream bundled skills into the gateway package output
- make package-contents require bundled SKILL.md files under lib/openclaw/skills
Why:
- upstream bundled skills are part of the runnable OpenClaw package contract
- package checks should catch missing skills before users hit runtime failures
Tests:
- git diff --cached --check: passed
- temporary worktree with only this staged patch: nix build #checks.x86_64-linux.package-contents --accept-flake-config --no-link --print-out-paths: /nix/store/zqwb4x85cwww8fx5gzj0asxy1ic5i373-openclaw-package-contents-unstable-8b2a6e57
What:
- flatten source-discriminated object unions when the variants have different fields
- regenerate secrets.providers as one submodule with a source enum and nullable variant fields
- add a default-instance regression for file-backed secret providers
Why:
- Nix cannot reliably merge attrsOf oneOf submodules for secrets.providers
- the generated module should preserve upstream config shape while staying evaluable
Tests:
- git diff --cached --check: passed
- temporary worktree with only this staged patch: nix build #checks.x86_64-linux.default-instance --accept-flake-config --no-link --print-out-paths: /nix/store/2zihci7mhlk3mcbczmyw0s401n162vk7-openclaw-default-instance-1
- temporary worktree with only this staged patch: nix build #checks.x86_64-linux.config-options --accept-flake-config --no-link --print-out-paths: /nix/store/4yrjhllg88ydyf70yqnkdmndbrv7y2c6-openclaw-source-checks-unstable-8b2a6e57
What:
- strip generated null defaults before merging user and instance OpenClaw config
- assert the default generated config keeps gateway.mode = "local"
Why:
- generated submodule defaults should not silently erase package-owned base config
- the default Home Manager instance must remain runnable without extra gateway config
Tests:
- git diff --cached --check: passed
- temporary worktree with only this staged patch: nix build #checks.x86_64-linux.default-instance --accept-flake-config --no-link --print-out-paths: /nix/store/2zihci7mhlk3mcbczmyw0s401n162vk7-openclaw-default-instance-1
What:
- export the agent-first flake template
- remove unsupported Intel macOS setup claims
- replace stale Discord channel links with the public invite path
- simplify the advanced dual-instance example and sync plugin docs
Why:
- make the documented first-run path match the flake users actually consume
- avoid sending users toward unsupported systems or dead Discord links
Tests:
- git diff --cached --check: passed
- nix flake show --json --accept-flake-config | jq -e '.templates."agent-first"': passed
- nix flake init -t /Users/josh/code/nix-openclaw#agent-first --accept-flake-config: wrote flake.nix and documents/
- rg stale onboarding strings in README.md AGENTS.md templates/agent-first/flake.nix docs: no matches
What:
- replace the misleading gateway test check with a source-checks build/config-options check
- remove the full upstream Vitest suite from the hard Nix promotion gate
- document that yolo validates the Nix-owned package contract, not upstream source test health
Why:
- the current full packageable upstream release builds as a package but has failing upstream Vitest cases when rebuilt from source
- nix-openclaw should block on packaging, smoke startup, config generation, module activation, and app artifacts rather than an upstream-owned unit test suite
Tests:
- git diff --check
- bash -n scripts/update-pins.sh scripts/hm-activation-macos.sh nix/modules/home-manager/openclaw-launchd-relink.sh nix/scripts/source-checks-build.sh nix/scripts/source-checks-check.sh nix/scripts/config-options-check.sh
- ruby -e 'require "yaml"; YAML.load_file(".github/workflows/ci.yml"); YAML.load_file(".github/workflows/yolo-update.yml")'
- nix eval --accept-flake-config --raw .#checks.x86_64-linux.source-checks.drvPath
- nix eval --accept-flake-config --raw .#checks.x86_64-linux.ci.drvPath
- nix flake show --accept-flake-config --all-systems --json
- scripts/check-flake-lock-owners.sh
- node scripts/select-openclaw-release.test.mjs
- scripts/hm-activation-macos.sh
- nix build --accept-flake-config -L .#checks.aarch64-darwin.ci
Blacksmith Linux hung twice during final verification while the same check passed once in between. Move Linux validation back to ubuntu-latest and keep explicit timeouts so the packaging gate favors reliability over runner speed.
Verification: git diff --check; ruby YAML parse
Apply the same timeout discipline to the automated OpenClaw pin updater so release validation cannot hang indefinitely before rewriting main.
Verification: git diff --check; ruby YAML parse
Add explicit CI timeouts around the Linux and macOS packaging gates so a wedged OpenClaw build fails inspectably instead of hanging indefinitely.
Verification: git diff --check; ruby YAML parse
Fix the documented minimal Home Manager plugin option to use customPlugins, remove the stale duplicate skill-file builder, and assert duplicate plugin skill paths against the paths Home Manager actually installs.
Add local plugin fixtures so the default-instance check covers the agent-facing customPlugins path and duplicate skill collisions.
What:
- move the completed OpenClaw packaging ExecPlan out of the pending slot
- document recursive self-review and the macOS Home Manager activation gate for daily maintenance
- make macOS app publishing explicitly out of scope for nix-openclaw automation
- replace an unclear README ownership label with release automation
Why:
- future maintainer runs need enough durable context to repair nix-openclaw without relearning this thread
- missing upstream macOS assets should be classified, not turned into a competing release process
Tests:
- git diff --check
- node scripts/select-openclaw-release.test.mjs
- bash -n scripts/update-pins.sh
- ruby -e 'require "yaml"; YAML.load_file(".github/workflows/yolo-update.yml"); YAML.load_file(".github/workflows/ci.yml")'
- scripts/check-flake-lock-owners.sh
- nix flake show --accept-flake-config
OpenClaw v2026.4.14 moved the gateway Vitest config under test/vitest/, but
our Nix gateway test seam still hard-coded the old root-level path. Linux CI
therefore failed before tests even started.
Prefer the old path when present, but fall back to the new upstream path so the
stable release mirror works across both layouts.
Tests:
- bash -n nix/scripts/gateway-tests-check.sh
The stable mirror had advanced main to v2026.4.14 with an app hash that only
matched prefetch output, not the unpacked tree hash that fetchzip validates.
That left macOS CI red on the pinned stable release.
Compute the app hash from the unpacked zip contents in update-pins.sh and fix
the current v2026.4.14 app pin to the actual fetchzip hash.
Tests:
- bash -n scripts/update-pins.sh
- nix build .#openclaw-app --accept-flake-config -L
What:
- split the stable-release updater into read-only selection and pin materialization modes
- rewrite yolo into select, validate-linux, validate-macos, and promote jobs
- fail yolo when the newest stable release is incomplete instead of silently sticking
- update maintainer docs to describe the new safe promotion policy
Why:
- stop direct yolo pushes from moving main without the same Linux and macOS proof as CI
- keep mirroring the newest stable release while making broken upstream releases visible
Tests:
- bash -n scripts/update-pins.sh
- ruby -e 'require "yaml"; YAML.load_file(".github/workflows/yolo-update.yml"); puts "yaml-ok"'
- GITHUB_ACTIONS=true GH_TOKEN="$(gh auth token)" scripts/update-pins.sh select
- temp copy pinned to v2026.4.11: scripts/update-pins.sh select emits v2026.4.14 tuple
What:
- restore the direct vitest entrypoint lookup in nix/scripts/gateway-tests-check.sh
- execute the discovered vitest module with node instead of relying on pnpm exec lookup
Why:
- the history rewrite dropped one real recovery fix and reintroduced the gateway-tests runner failure on Linux
- the green line already proved the direct-entrypoint approach works reliably in the Nix check environment
Tests:
- verified failing CI run 24246896697: ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL Command "vitest" not found
What:
- switch the pin updater from upstream-main commit selection to upstream stable release selection
- restore Yolo Update Pins as an hourly stable-release poller
- update maintainer and README docs to describe stable-release mirroring and the recovery lesson
Why:
- OpenClaw stable is already tag-based upstream, and nix-openclaw should mirror that release line directly
- this removes the old latest-green-main churn model and keeps source + app pins tied to one published upstream release
Tests:
- bash -n scripts/update-pins.sh
- GITHUB_ACTIONS=true GH_TOKEN="$(gh auth token)" scripts/update-pins.sh
Why:
- disabling bundled plugins globally fixed provider leakage but broke send tests that rely on bundled channels
- Linux gateway tests need bundled channels, but not real provider model augmentation or web search/fetch provider discovery
What:
- keep the gateway test env unchanged
- patch gateway test mocks to stub provider model augmentation to []
- patch gateway test mocks to stub runtime and public-artifact web search/fetch provider discovery to []
- keep the change in the Nix postpatch seam only
Tests:
- sh -n nix/scripts/gateway-postpatch.sh
- nix eval --raw --accept-flake-config .#checks.x86_64-linux.gateway-tests.src.outPath
Move nix-openclaw to the current OpenClaw daily release and refresh the
release-coupled generated artifacts.
Also fix the Nix build wrapper for A2UI bundling by running the same tsc +
rolldown steps directly instead of relying on upstream's nested pnpm runner,
which exits silently inside the Nix build environment.
Verification:
- nix build .#openclaw-gateway --accept-flake-config -L
- nix build .#checks.aarch64-darwin.ci --accept-flake-config -L
- scripts/hm-activation-macos.sh (expected local USER mismatch: josh vs runner)
What:
- harden the package build against broken prune symlinks and missing plugin metadata
- make the config-options and gateway test checks call the installed CLIs directly
- restore the bundled runtime-deps staging patch for Nix builds
- rebuild the test prebuild around the exact generated artifacts the Nix checks consume
Why:
- the release recovery failures were not random product regressions; they were Nix-owned build and test contract drift
- nix-openclaw needed the same plugin manifests, plugin-sdk artifacts, runtime deps staging, and local CLI resolution that upstream assumes
Tests:
- final green CI run on main: 24217118174
What:
- move the Linux CI job to blacksmith-16vcpu-ubuntu-2404
- switch Linux Nix installation to cachix/install-nix-action
Why:
- the recovery line exhausted disk on ubuntu-latest during the Linux aggregator build
- the larger runner kept the normal parallel build graph intact and the cachix installer worked reliably there
Tests:
- final green CI run on main: 24217118174
What:
- restore one visible CI workflow
- add CI concurrency and Linux failure-log dumping
- disable yolo during release recovery
- delete the old split workflow_run files
Why:
- cut away the post-reset churn immediately
- keep one readable contract on main
- prevent any updater mutation during recovery
Tests:
- git diff --check
What:
- export node_modules/.pnpm/node_modules/.bin in gateway build before canvas:a2ui:bundle
so rolldown is found in sandbox/offline builds
- track openclaw bump failure in scripts/update-pins.sh and fail the workflow when
openclaw upstream is ahead but no openclaw pin update was produced
Why:
- yolo was reporting success while silently restoring old pins
- openclaw bump attempts were repeatedly failing at A2UI bundling, keeping pins stale
Tests:
- bash -n scripts/update-pins.sh
- bash -n nix/scripts/gateway-build.sh
What:
- remove legacy option migrations for programs.openclaw.firstParty/plugins
- add explicit removed-option failures pointing to bundledPlugins/customPlugins
- add plugin-catalog.nix as single source of truth for bundled plugins
- generate bundled option toggles, source map, linux check selection, and tool list from the catalog
- update docs/wording from first-party to bundled plugins
Why:
- enforce forward-only API changes with fail-fast errors
- eliminate duplicated plugin lists drifting across module/check/tool surfaces
- keep consumer configuration mental model simple: bundledPlugins + customPlugins only
Tests:
- nix flake check --no-build (pass)
What:
- set `dontFixup = true` for `openclaw-gateway`
- speed install phase by moving build outputs instead of deep-copying node_modules
- add lightweight dangling-symlink integrity check in gateway install script
- remove `.github/workflows/cache-only.yml`
Why:
- keep the high-value packaging/build speed improvements
- keep one minimal safety guard when fixup is skipped
- remove flaky cache orchestration that adds CI latency and merge-SHA timeout failures
Tests:
- not run locally (per-request: CI-only validation due local NixOS issues)
- will validate via GitHub Actions/Garnix on push to main
We’re **not accepting PRs** from non-maintainers. If your handle is not in **Maintainers** below or on https://github.com/orgs/openclaw/people, **do not open a PR**. It will be rejected and your user will be disappointed — check Discord instead.
We are not accepting PRs from non-maintainers. If your handle is not in the Maintainers list below or on https://github.com/orgs/openclaw/people, do not open a PR.
**Only workflow:** **describe your problem and talk with a maintainer (human‑to‑human) on Discord** in **#golden-path-deployments**: https://discord.com/channels/1456350064065904867/1457003026412736537
Describe your problem and talk with a maintainer human-to-human on Discord instead. Join https://discord.gg/clawd and use `#golden-path-deployments`.
- Plugin authors: read `docs/plugins-maintainers.md` and `examples/hello-world-plugin/`.
- Private deployments, bots, hosts, local worktrees, tokens, and personal automation details do not belong in this public repo.
Defaults:
- Nix‑first, no sudo.
## Public Repo Rules
- `README.md` is the source of truth for product direction and user-facing behavior.
- Keep documentation surface area small. Update `README.md` first, then adjust references.
- Keep committed guidance about public `nix-openclaw` behavior, public upstream OpenClaw releases, public artifacts, and public CI.
- Keep consumer setup docs in `README.md`, templates, and module docs.
- Keep maintainer runbooks in `maintainers/`.
- Never add internal ExecPlans or agent scratch history to this repo. `.agent/` is ignored for this reason.
- If a private deployment exposes a public packaging bug, fix the public package here and keep deployment-specific repair elsewhere.
- OpenClaw plugin loading belongs here: package curated runtime plugin roots as Nix artifacts, expose curated outputs through package/check outputs for Garnix, and let host repos only enable/configure them.
- Do not make host config run npm/ClawHub installs at runtime for the batteries-included path. `customPlugins.source = "npm:..."` is allowed only when nix-openclaw turns it into an immutable, hash-backed store path and wires it through OpenClaw's normal `plugins.load.paths`.
- NEVER send any message (iMessage, email, SMS, etc.) without explicit user confirmation:
- Always show the full message text and ask: “I’m going to send this: <message>. Send? (y/n)”
- Batteries-included install is the baseline.
- Breaking changes are acceptable pre-1.0.0; no deprecations.
- No inline scripts or inline file contents in Nix code. Use repo scripts and explicit file paths.
- The gateway package must include Control UI assets.
- User-facing docs should lead with one package: `openclaw`. Treat `openclaw-gateway` and `openclaw-app` as component outputs for modules, checks, and debugging.
- QMD is the Nix-supported batteries-included local memory backend. Keep `qmd` internal to the `openclaw` wrapper PATH; users opt in with upstream config.
OpenClaw packaging:
- The gateway package must include Control UI assets (run `pnpm ui:build` in the Nix build).
- Picks latest upstream openclaw SHA with green non-Windows checks
- Rebuilds gateway to refresh `pnpmDepsHash`
- Regenerates `nix/generated/openclaw-config-options.nix` from upstream schema
- Updates app pin/hash, commits, rebases, pushes to `main`
- Manual bump (rare): `GH_TOKEN=... scripts/update-pins.sh` (same steps as above). Use only if yolo is blocked.
- To verify freshness: `git pull --ff-only` and check `nix/sources/openclaw-source.nix` vs `git ls-remote https://github.com/openclaw/openclaw.git refs/heads/main`.
- If upstream is moving fast and tighter freshness is needed, trigger yolo manually: `gh workflow run "Yolo Update Pins"`.
CI polling (hard rule):
- Never say "I'll keep polling" unless you are **already** running a blocking loop.
- If you must report status, confirm the loop is active (`tmux ls` / session name).
- Use a blocking bash loop in tmux (preferred) or a sub-agent; do not fake it.
- Example: `tmux new -s nix-openclaw-ci '/tmp/poll-nix-openclaw-ci.sh'`.
Philosophy:
The Zen of ~~Python~~ OpenClaw, ~~by~~ shamelessly stolen from Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
Nix file policy:
- No inline file contents in Nix code, ever.
- Always reference explicit file paths (keep docs as real files in the repo).
- No inline scripts in Nix code, ever (use repo scripts and reference their paths).
- No files longer than 400 LOC without user alignment; refactor as you go.
- Never send messages, email, SMS, or other external communications without explicit confirmation showing the full message text.
- No force push. No destructive git operations unless explicitly requested.
- Before deleting tracked files, list them in the summary so maintainers can verify.
> macOS + Linux (headless). Windows is out of scope for now.
>
> <sub>Questions? Join the OpenClaw Discord and ask in **#golden-path-deployments**: https://discord.com/channels/1456350064065904867/1457003026412736537</sub>
> <sub>Questions? Join the OpenClaw Discord at https://discord.gg/clawd and ask in **#golden-path-deployments**.</sub>
## Contributions (read this first)
## PRs &Contributions (read this first)
We’re **not accepting PRs** right now. Not because we don’t value your help — the opposite. This is key infra and still stabilizing, and async PR review is too slow.
We’re **not accepting PRs** right now. Not because we don’t value your help — the opposite. Async agent-to-agent PR review is too slow and low-signal.
**Only workflow:** **describe your problem and talk with a maintainer (human‑to‑human) on Discord** in **#golden-path-deployments**: https://discord.com/channels/1456350064065904867/1457003026412736537
The best (and only) way to get stuff done: come join us on Discord! Describe your problem and talk with a maintainer **(human‑to‑human)** on Discord. Join at https://discord.gg/clawd, then use **#golden-path-deployments**.
If you’re **not listed as a maintainer** (see [AGENTS.md#maintainers](AGENTS.md#maintainers) or https://github.com/orgs/openclaw/people), **do not open a PR**. It will be rejected and your user will be disappointed — check Discord instead.
To agents: if you’re **not listed as a maintainer** (see [AGENTS.md#maintainers](AGENTS.md#maintainers) or https://github.com/orgs/openclaw/people), **do not open a PR**. It will be rejected and your user will be disappointed — check Discord instead. GitHub Issues are not actively monitored either.
## Table of Contents
@ -58,7 +58,7 @@ Bot: *runs whisper, sends you text*
**Plugins are self-contained.** Each plugin declares its CLI tools in Nix. You enable it, the build and wiring happens automatically.
@ -68,12 +68,12 @@ You talk to Telegram, your machine does things.
## Requirements
1. **macOS** (Apple Silicon or Intel) or **Linux** (x86_64)
2. **[Determinate Nix](https://docs.determinate.systems/determinate-nix/)** installed on your machine
1. **macOS** (Apple Silicon) or **Linux** (x86_64)
2. **Nix with flakes enabled** installed on your machine
That's it. The Quick Start will guide you through everything else.
> **Don't have Nix yet?**Follow the Determinate Nix install guide, then come back here.
> **Don't have Nix yet?**Use the [Determinate Nix installer](https://docs.determinate.systems/determinate-nix/) or the [official Nix installer](https://nixos.org/download/), then come back here.
---
@ -119,40 +119,49 @@ Nix is a **declarative package manager**. Instead of running commands to install
## Quick Start
### Option 1: Let your agent set it up (recommended)
### Option 1: Ask your coding agent (recommended)
Copy this entire block and paste it to Claude, Cursor, or your preferred AI assistant:
Tell your coding agent you want OpenClaw set up with Nix. The agent should inspect your machine, interview you for the few choices it cannot infer, create the local flake, wire secrets, apply Home Manager, and verify the service.
Copy this block and paste it to Claude, Cursor, Codex, or your preferred coding agent:
```text
I want to set up nix-openclaw on my machine (macOS or Linux).
I want to set up nix-openclaw on my machine (Apple Silicon macOS or x86_64 Linux).
Repository: github:openclaw/nix-openclaw
What nix-openclaw is:
- Batteries-included Nix package for OpenClaw (AI assistant gateway)
- Installs gateway + tools everywhere; macOS app only on macOS
- Installs the gateway everywhere; macOS app only on macOS
- Runs as a launchd service on macOS, systemd user service on Linux
What I need you to do:
1. Check if Determinate Nix is installed (if not, install it)
2. Create a local flake at ~/code/openclaw-local using templates/agent-first/flake.nix
3. Create a docs dir next to the config (e.g., ~/code/openclaw-local/documents) with AGENTS.md, SOUL.md, TOOLS.md (optional: IDENTITY.md, USER.md, LORE.md, HEARTBEAT.md, PROMPTING-EXAMPLES.md)
1. Inspect my OS, CPU architecture, shell, Home Manager setup, and whether Nix with flakes is installed
2. Ask me only for missing choices: channel, bot/account secrets, allowed users, provider keys, and documents/identity preferences
3. Create a local flake at ~/code/openclaw-local using templates/agent-first/flake.nix
4. Create a docs dir next to the config (e.g., ~/code/openclaw-local/documents) with AGENTS.md, SOUL.md, TOOLS.md (optional: IDENTITY.md, USER.md, LORE.md, HEARTBEAT.md, PROMPTING-EXAMPLES.md)
- If ~/.openclaw/workspace already has these files, adopt them into the documents dir first (use copy/rsync that dereferences symlinks, e.g. `cp -L`)
4. Help me create a Telegram bot (@BotFather) and get my chat ID (@userinfobot)
5. Set up secrets (bot token, Anthropic key) - plain files at ~/.secrets/ is fine
6. Fill in the template placeholders and run home-manager switch
7. Verify: service running, bot responds to messages
5. Help me create or connect the channel account I choose
6. Set up secrets (bot token, provider key) - plain files at ~/.secrets/ are fine unless I already have a secret manager
7. Ask whether I want local memory through QMD; if yes, set `memory.backend = "qmd"` in OpenClaw config
8. Fill in the template placeholders and run home-manager switch
9. Verify end-to-end: package builds, service is running, gateway health works, QMD works if enabled, and the bot/channel responds if configured
Plugins are keyed by their declared `name`. If two plugins declare the same name, the **last entry wins** (use this to override a prod plugin with a local dev one).
### Tool overrides (avoid collisions)
Home Manager auto-excludes `git` when `programs.git.enable = true`.
Drop built-in tools that you already install elsewhere:
If you override `programs.openclaw.package`, use `pkgs.openclawPackages.withTools { ... }.openclaw` to apply these lists.
---
## Packaging & Updates
**Goal:** `nix-openclaw` is a great Nix package. Automation, promotion, and fleet rollout live elsewhere.
### Stable only (for now)
### Stable release mirroring
We ship a single pinned upstream commit:
- **Stable**: last known-good pin. This is the default.
We ship one default package: `.#openclaw`.
The gateway tracks the newest upstream stable OpenClaw source release that satisfies the Nix package contract:
- gateway builds on Linux and macOS
- gateway starts and answers local health checks
The macOS app is pinned separately to the newest stable public `OpenClaw-*.zip` artifact. If upstream has not promoted desktop assets for the latest source release yet, `openclaw-app` may lag; that must not block Linux users or macOS gateway users from getting the latest source-built OpenClaw.
The Nix gate is deliberately package-focused. It does not make the full upstream Vitest suite a hard promotion gate; upstream owns source test health, while `nix-openclaw` verifies the source build, generated config options, package contents, smoke startup, module activation, and newest available macOS app artifact.
Outputs:
```
.#openclaw
.#openclaw-gateway
.#openclaw-app # Darwin only
```
Pin lives in:
`.#openclaw-gateway` and `.#openclaw-app` are component outputs for modules, CI, debugging, and advanced use. Start with `.#openclaw`.
2) It selects the newest stable source release and newest stable public macOS app zip independently.
3) Newer source releases that lack public macOS app assets are reported as app lag, not skipped.
4) Yolo materializes the source pin from the newest source tag ref, updates the app asset pin from the newest public app zip, and regenerates config options from the selected source.
5) Yolo validates that source/app pin set on the same Linux + macOS contract as repository `CI`.
6) Only after both validations pass does yolo push one release-mirroring commit to `main`.
| `openclaw` (default) | Canonical package. Exposes `openclaw`; keeps runtime tools internal. macOS also links the app. |
| `openclaw-gateway` | Component output: gateway CLI/service only |
| `openclaw-app` | Component output: macOS app only |
### Local memory
`openclaw` includes QMD internally as the supported local memory backend. It is not enabled automatically. Linux uses upstream `tobi/qmd`; Darwin uses the repaired `nix-openclaw-tools` package until upstream QMD is fixed there.
Opt in through normal OpenClaw config:
```nix
programs.openclaw.config = {
memory.backend = "qmd";
};
```
QMD stays inside the `openclaw` wrapper PATH, so users do not need to install a separate `qmd` command. The builtin `memorySearch.provider = "local"` path is an escape hatch for people who want to manage `node-llama-cpp` themselves; it is not the primary Nix-supported path.
Plugin CLIs are also kept on the OpenClaw runtime PATH by default, not on the user's login shell PATH. Set `programs.openclaw.exposePluginPackages = true` only when you explicitly want plugin CLIs in `home.packages`.
- **Not:** new transports/providers; model plumbing; secrets baked in; inline scripts or ad-hoc package-manager installs; a place for random config outside its scope.
- Why not skills-only: skills without binaries can hallucinate capability. Plugins ground skills in real tools and deliver versioned, reproducible functionality.
## Two Plugin Classes
Nix capability plugins are the tool/skill/env bundles described below. They do not use OpenClaw's JavaScript plugin loader. They are the right shape for CLIs such as `goplaces`, `gog`, `qmd`, `xuezh`, `camsnap`, and `summarize`.
OpenClaw plugins are runtime plugin directories with `openclaw.plugin.json` plus built JavaScript loaded by the gateway. They include bundled upstream plugins, official external plugins from OpenClaw's catalog or ClawHub, and third-party plugins. In Nix-managed deployments, these should be immutable plugin roots, not runtime npm installs hidden in host config.
Current nix-openclaw `customPlugins` implements both sides of the contract: package binaries on the gateway PATH, materialize skills, create state dirs, validate env files, render optional tool settings, and wire declared OpenClaw plugin roots into `plugins.load.paths` with an explicit default `plugins.entries.<id>.enabled` value.
PR #81 (`fix: copy plugin manifests into dist/extensions`) was related but not the missing external-plugin feature. It fixed bundled upstream plugin manifests missing from the packaged gateway `dist/extensions/*/openclaw.plugin.json` tree. Current packaging already copies those manifests and checks them in `openclaw-package-contents`.
Package authors can bridge the existing Nix contract to OpenClaw plugins:
- Extend `openclawPlugin` with an optional plugin declaration, for example `plugins = [ { id = "openclaw-weixin"; path = "${pkg}/lib/openclaw/plugins/openclaw-weixin"; enabled = true; } ];`.
- For each selected plugin artifact, append those paths to generated `plugins.load.paths`.
- Add a default `plugins.entries.<id>.enabled` value. `enabled` defaults to true, but plugin authors can set `enabled = false` for roots that should be discoverable while disabled until the host supplies config. User config can still override either default.
- Keep OpenClaw plugin config in `programs.openclaw.config` / `instances.<name>.config` so upstream schema validation remains the source of truth.
- Add a fixture shaped like `openclaw-weixin` so `customPlugins = [{ source = ...; }]` proves both package/skill wiring and OpenClaw plugin load wiring.
env = { KEY = "/run/agenix/key"; EXTRA = "/path/to/file"; };
settings = { foo = "bar"; retries = 3; };
@ -51,23 +71,43 @@ plugins = [
- `config.settings`: JSON-rendered into `config.json` inside the first `stateDir`.
- Invariant: providing `settings` requires at least one `stateDir`.
Do not add raw npm package names to host config for the batteries-included path. Curated plugins packaged by this repo or `nix-openclaw-tools` should be exposed through package/check outputs so Garnix caches them.
OpenClaw native npm plugins use the same host list with an OpenClaw-style source:
```nix
programs.openclaw.customPlugins = [
{
source = "npm:@scope/openclaw-plugin@1.2.3";
id = "openclaw-plugin";
hash = lib.fakeHash; # replace with the sha256 Nix reports
}
];
```
- `source`: currently supports registry npm specs with an explicit `npm:` prefix.
- `id`: required because the Home Manager module must enable the plugin at eval time without importing the built JavaScript package.
- `hash`: recursive output hash for the immutable plugin root; leave as `lib.fakeHash` to have Nix report the expected hash, then commit that value.
- Runtime plugin config belongs in `programs.openclaw.config.plugins.entries.<id>.config`, not in `customPlugins.config`.
- The module adds the built root to `plugins.load.paths` and writes a default `plugins.entries.<id>.enabled` value. OpenClaw owns runtime loading after that.
Curated npm plugins can be added to this repo or `nix-openclaw-tools` so Garnix caches them. Arbitrary user npm specs are still deterministic Nix artifacts, but this repo's cache cannot cover every user's private plugin choice. The user's local store or configured binary cache reuses the artifact until the source or hash changes. OpenClaw must not reinstall it on every gateway start.
## Dev workflow (fast iteration)
- Worktree: build and test plugins outside the core repo; point OpenClaw at a local path source (e.g., `source = "path:/Users/you/code/my-plugin"`).
- Worktree: build and test plugins outside the core repo; point OpenClaw at a local path source during impure local dev (e.g., `source = "path:/Users/you/code/my-plugin"`). Committed config uses pinned refs.
- Rebuild loop: change plugin → `home-manager switch` (or host-equivalent) → gateway restarts with new PATH/skills/config; no manual copying.
- Name collisions: use the same plugin `name` to override a pinned version (last entry wins); keep unique names otherwise to avoid surprise overrides.
- Skills placement: skills land under `~/.openclaw*/workspace/skills/<plugin>/...` so you can inspect quickly; delete the workspace to fully reset cached skills.
- Skills placement: skills land under `~/.openclaw*/workspace/skills/<skill-dir-basename>/...` so you can inspect quickly; delete the workspace to fully reset cached skills.
- Env guardrails: required env vars must point to files (non-empty) or the activation fails—supply temp files during dev to exercise the checks.
- Settings JSON: inspect the rendered `config.json` in the first `stateDir` to confirm schema and defaults before committing.
- Each follows the same contract: packages + skills; env/state declared via `needs`; enabled via config toggle; sources pinned (see nix-openclaw bundledPlugins mapping).
- Source of truth: `nix/modules/home-manager/openclaw/plugin-catalog.nix`.
- Each follows the same contract: packages + skills; env/state declared via `needs`; enabled via config toggle; sources pinned via the bundled plugin catalog.
## Authoring Rules
- Keep CLIs configurable via env; honor XDG paths; no inline scripts.
@ -203,7 +203,7 @@ voicecall status --call-id abc123 # Check for responses
4. **Create state dirs** — from manifest
5. **Add `openclaw plugins` CLI** — list, enable, disable, info
That's it. No dynamic code loading, no TypeBox registration, no RPC handlers. Just: find plugins, validate their needs, put binaries on PATH, copy skills to workspace.
That's it. No dynamic code loading, no TypeBox registration, no RPC handlers. Just: find plugins, validate their needs, put binaries on the OpenClaw runtime PATH, copy skills to workspace.
Maintainer automation is an agentic repair loop for the public packaging pipeline. It is not a second release pipeline and not a private deployment monitor.
## Daily Objective
Answer first:
```text
Does nix-openclaw publish the latest upstream version for both supported tracks?
```
Answer `YES` only when:
- `openclaw-gateway` matches the newest stable upstream source release.
- `openclaw-app` matches the newest stable upstream release with a published public `OpenClaw-*.zip`.
If both tracks are current and yolo/CI are healthy, stop with a short CTO-level report:
- current gateway
- latest upstream gateway
- current app
- latest published app
- whether action was needed
## Repair Loop
If the desired state is not true, keep working until it is true or until the exact blocker is proven.
Diagnose across:
- upstream release data
- yolo selection
- pin materialization
- generated config options
- package builds
- smoke checks
- module activation
- workflow behavior
- caches
- CI runner failures
Do not ask for a repair strategy when the desired state is clear.
If the fix belongs in `nix-openclaw`, edit the repo, self-review the diff until there are no actionable findings, run the relevant targeted checks plus the full gate, commit directly to `main`, push directly to `main`, and verify GitHub Actions on the pushed commit.
If upstream has not published public macOS app assets, call that out directly, keep the app pin on the newest public zip, keep packaging the latest stable source-built gateway, and repair `nix-openclaw` only if it fails to do that.
Use targeted checks while debugging, then run the full relevant gate before handoff.
## Required Checks
- `scripts/check-flake-lock-owners.sh`
- selector tests
- updater shell syntax
- workflow YAML parse
- `nix flake show --accept-flake-config`
- Linux CI aggregator
- Darwin CI aggregator when available
- `scripts/hm-activation-macos.sh` when a macOS runner is available
## CI Verification
After pushing maintainer fixes, verify the GitHub Actions run for the pushed commit.
Never say you will keep polling unless a blocking poll is already running. If reporting a poll, name the active run or local polling session.
If CI fails, inspect the failing run, classify the failure, fix what belongs to `nix-openclaw`, and rerun until green or until the exact external blocker is proven.
This repo ships a working Nix package for OpenClaw users, not just a pin mirror.
## Product Surface
- The user-facing package is `openclaw`.
- `openclaw-gateway` is the source-built runnable gateway for Linux and macOS.
- `openclaw-app` is the Darwin-only desktop app from upstream's public app artifact.
- Component outputs exist for modules, checks, and debugging. They are not separate product tracks.
- `openclaw-dogfood` and `openclaw-gateway-dogfood` are temporary maintainer
artifacts for testing a specific upstream commit before the next stable
release. They must not become the documented consumer default.
- Do not split the repo into separate desktop and server tracks.
## Nix Ownership
- OpenClaw owns product and runtime behavior.
- `nix-openclaw` owns batteries-included Nix packaging, Home Manager/NixOS/Darwin modules, runtime PATH/env injection, launchd/systemd wiring, and package-contract checks.
- `nix-openclaw-tools` owns packaging OpenClaw-adjacent CLI tools and plugin metadata. Consume it here; do not duplicate its package definitions here.
- Downstream system repos should only choose hosts, secrets, accounts, and enabled plugins. If downstream needs bespoke scripts to make a plugin or harness work, prefer fixing this repo or `nix-openclaw-tools`.
- Nix mode means Nix owns `openclaw.json`.
- Runtime config mutation belongs upstream in OpenClaw. Downstream patches here must be small, temporary, and removed after the pinned upstream release contains the fix.
- Generated config options come from the upstream core schema.
- Plugin-owned extension surfaces, such as `channels.<plugin-id>`, must remain accepted by the Home Manager module even when core does not type every plugin key.
- Runtime tool injection belongs here. If a plugin or battery is enabled, the active OpenClaw harness must see its CLI tools and required environment without asking downstream to expose those tools globally on the user PATH.
- OpenClaw plugin roots belong here too. The Home Manager module consumes `openclawPlugin.plugins` declarations from plugin flakes and writes `plugins.load.paths` plus default `plugins.entries.<id>.enabled` values into the generated config.
- Raw npm/ClawHub plugin names are not batteries-included deployment config. Curated plugins packaged here must be exposed through packages/checks so CI/Garnix caches them. Arbitrary user specs need a deterministic lock/hash-backed Nix builder so Nix reuses the user's store/cache and only rebuilds when the spec, lock, or hash changes.
## Build Contract
- The gateway package must include Control UI assets.
- No inline scripts or inline file contents in Nix code. Use repo scripts and explicit file paths.
- Keep runtime tools internal to the `openclaw` wrapper unless they are intentionally part of the public package surface.
- QMD is the Nix-supported batteries-included local memory backend. Keep `qmd` internal to the `openclaw` wrapper PATH; users opt in with upstream config.
- ACPX is the first bundled OpenClaw plugin proof. It is consumed from OpenClaw's built `dist-runtime/extensions/acpx` tree, not installed or repaired by npm at runtime.
- Keep files under 400 lines unless a maintainer explicitly accepts the larger file.
## Investigations
### mcporter and QMD
- `mcporter` is an OpenClaw-owned optional MCP/CLI bridge, not a QMD requirement.
- OpenClaw defaults to direct `qmd` CLI execution. Keep that as the Nix-supported baseline until measured startup or per-query overhead proves otherwise.
- Package `mcporter` in `nix-openclaw-tools` as an optional tool when needed, but do not add it to the default `openclaw` runtime PATH just because QMD is bundled.
- If `memory.qmd.mcporter.enabled = true`, nix-openclaw should make `mcporter` visible to that instance and require the matching mcporter server config for `qmd mcp`.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.