Repository navigation
Description
Summary
Fleet repositories that read config, secret, and env files each harden that read their own way, or not at all. HomeAutomation-Config's scripts/paths.sh is the hardened implementation today. require_secret, secret_verdict, read_env_value, and the path checks distinguish a missing file from a dangling symlink or an unreadable ancestor. They refuse symlinks, FIFOs, devices, group or other access, a wrong owner, and CRLF. read_env_value reads one value through a child bash, so nothing in the file is exported. ptr727/Blog's deploy/make-release.sh does the same job with an existence check and set -a; . "$ENV_FILE", with no mode, owner, symlink, or CRLF check. The hub has no equivalent library, and spec/files.json carries no code except the .github/skills tree.
Decision
The maintainer chose a synced copy refreshed at resync over a runtime pull from the hub and over a git submodule.
- Not a runtime pull. This code runs on maintainer hosts under systemd timers (backup, healthcheck, crash-loop breaker) from an installed copy, sometimes as root. A hub fetch at run time makes a GitHub outage or a bad
maincommit stop a backup, and runs code the consuming repository never reviewed against its secrets. The fetch-latest principle stays right for gates and hooks, where "the tool did not run" is an acceptable answer and CI is the backstop. - Not a submodule. Every worktree would need a submodule init, the whole hub would come along, and
carry.pycannot audit inside one.
Design
- The hub hosts the generic library, for example
lib/sh/secrets.sh, with its tests run in hub CI. It takes the generic parts of HomeAutomation-Config'sscripts/paths.sh. A repository-specific part stays in its repository, such aspush_interlock, and the root-owneddocker.envowner exemption becomes a parameter. Shell comes first, since no fleet repository has a Python env reader and the hub's host setup avoids needing Python. - It is carried byte-for-byte through
spec/files.jsonto a fixed consumer path, for examplelib/fleet/, with its tests, so the consumer's own CI runs them against its copy. A repository extends it in its own files that source it, never by editing it. - A stale copy is visible.
carry.pyalready tells a stale copy from a modified one. A consumer's CI reports a stale library copy as a warning rather than a failure, and the audit and resync apply the refresh. A failure would break every downstream pull request on each hub fix. GOVERNANCE.md"Hub-Hosted Tooling" gains the rule. Code a repository's own runtime executes is carried, while machinery that measures a repository is still reached. The section today calls carrying machinery a copy whose only future is to go stale, and a runtime library is the case where offline operation and review in the consuming repository outweigh that. The same rule gives the existing byte-identicalhub_fetch_run.pycopy a declared home.- Rollout. HomeAutomation-Config, today's source, moves onto the carried copy first. Its installer already ships
scripts/*to the host, so the carried path joins that list. ptr727/Blog'smake-release.shthen usesrequire_secretandread_env_value, which pairs with Rename the Host Env Files to the Fleet's Underscore-Joined Form Blog#550's env file rename. Each repository's adoption is its own issue in that repository.
Done When
The hub hosts the library with tests, spec/files.json carries it, a stale copy warns in a consumer's CI, "Hub-Hosted Tooling" states the runtime-library rule, and HomeAutomation-Config runs on the carried copy.
🤖 Generated with Claude Code
Activity
ptr727 commentedon Oct 26, 2026
Design settled with the maintainer before the first edit. Two premises in the design above did not hold against the source.
carry.pydoes not tell a stale copy from a modified one. Its tree comparison reports any byte difference asmodified, so design step 3 needs a mechanism of its own.scripts/paths.shhas no owner check, so there is nodocker.envowner exemption to turn into a parameter. The one parameter that exists is theprivate|sharedaccess mode.The maintainer's answers:
host-secrets, which a repository declares inregistry/repos.jsonto carry the library.validate-task.ymldoes not change.repo_gate.pycheck compares each carried tree with the hub tree the pinned action already holds. It reports a difference as a warning note that never changes the exit code, so the result can be reproduced at the pin, and the audit and resync apply the refresh.unresolved_link_reason,secret_statethroughrequire_secret, andread_env_value. The installer path helpers, the push interlock, andwrite_state_filestay in HomeAutomation-Config. The owner check is out of scope.Host the Hardened Secret and Env Readers in the Hub and Carry Them as…
ptr727 commentedon Oct 26, 2026
The hub side landed in #3509 and is in promotion #3512, which closes this issue when it merges. The library, its tests, the
spec/files.jsontree, the stale-copy warning (thecarried-treesrepository gate check), and the "Hub-Hosted Tooling" rule are all in. The last Done When item, HomeAutomation-Config running on the carried copy, moves to ptr727/HomeAutomation-Config#1435 per design step 5. Blog's adoption is ptr727/Blog#553. Both wait on the release that carries #3512.Promote the Hub-Hosted Secret and Env Readers to Main (#3512)