Operations
Dot Module Registry
The dot cli command discovers reusable dotfile modules from a JSON index published over HTTPS. The default registry is hosted by this repo at:
https://doc.dotfiles.io/registry.json
This page documents the JSON contract and the contribution flow. It is the §3 / Months 12-18 deliverable from HARD_AUDIT_2026.md — the registry is the network-effect feature that turns the framework into a category, not just one person's setup.
Quick start (consumer side)
The published index has no modules yet: docs/registry.json carries an empty modules array. rust-dev-setup below, and in the JSON contract, is an illustrative name, not a module you can install today.
The registry index is cached locally under ${XDG_CACHE_HOME:-~/.cache}/dotfiles/registry/ with a 6 hour TTL, in a file named for the URL it was fetched from (index-v2-<digest>.json) so changing the registry URL never serves the previous registry's index. When a fetch fails, a cached index is used only while it is younger than 7 days. Override the URL one-off via DOTFILES_REGISTRY_URL=<url> dot cli list --available.
Every fetched index must carry a minisign signature at <url>.minisig that verifies against security/registry.pub in this repository; an index without one is refused. DOTFILES_REGISTRY_PUBKEY=<file> verifies against another key (a private registry). For local development only, DOTFILES_REGISTRY_UNSIGNED=1 lets a file:// index skip verification, with a warning; it never applies to https://.
JSON contract
A registry index is a single JSON document:
Required keys: name (kebab-case, no more than 32 characters), description (no more than 200 characters), version (semver), archive_url (immutable HTTPS archive), and sha256 (64 lowercase hexadecimal characters).
Optional keys: repo (HTTPS project URL), tags (lower-case array), maintainer, and license (SPDX identifier). The machine-readable contract is docs/schema/dot-registry-v1.json.
Contributing a module
- Build a gzip-compressed tar archive containing a chezmoi-source-compatible directory of plain files. Templates (
*.tmpl), scripts (run_*),.chezmoi*files and theexact_,remove_,create_,modify_,symlink_,encrypted_andexternal_attributes are refused anywhere in the tree, as are links and absolute or..paths. Publish it at an immutable HTTPS URL, such as a versioned GitHub release asset. - Open a PR against
sebastienrousseau/dotfilesadding one entry todocs/registry.json(alphabetical byname). - The PR runs CI checks for:
- Runtime contract validity and unique, sorted module names.
- Valid JSON for both the index and its published JSON Schema.
- A pinned SHA-256 digest for every archive.
- The maintainer re-signs the index offline (
minisign -Sm docs/registry.json -s <offline key>) and commitsdocs/registry.json.minisig. CI (tools/ci/check-registry.sh) fails when the committed signature does not match the index. - Once merged, the GitHub Pages workflow re-deploys the registry;
dot cli list --availablepicks it up within 6 hours (or immediately if the consumer purges the cache).
Install pipeline
dot cli install <name> is preview-first and does not mutate the workstation. Pass --yes only after reviewing the preview. The installer:
- Verify the index signature, schema and freshness, then resolve the module entry.
- Download the versioned archive using HTTPS and TLS 1.2 or newer (an
https://index may only namehttps://archives). - Verify the archive against the registry's SHA-256 digest.
- Reject absolute paths, parent traversal, symbolic links, hard links and every chezmoi-active name before extraction.
- Preview without chezmoi: the archive listing, then
diff -uagainst each file that already exists in$HOME. - With
--yes, persist it at${XDG_DATA_HOME:-~/.local/share}/dotfiles/modules/<name>/<version>and apply that exact verified source withchezmoi apply --config /dev/null --config-format tomland a throwaway persistent state and cache, so your chezmoi config, hooks, data and script history are never involved.installed.jsonrecords every target path the module wrote.
Security model
- Modules never execute: only plain files are accepted, and they are written with your user privileges. Review the preview and publisher before passing
--yes. - The SHA-256 pin binds installation to the reviewed archive bytes, even if the hosting release later changes.
- The index is signed with an offline minisign key; its public half is
security/registry.pub. HTTPS adds transport confidentiality, not trust. - Rollback: the newest
updatedtimestamp ever accepted per registry URL is kept under${XDG_STATE_HOME:-~/.local/state}/dot/cli/registry/, and an older (or undated) index is refused. - Registry text (descriptions, maintainers, tags) is stripped of control characters before it is printed.
Why this lives in this repo (for now)
A vendor-neutral registry would be ideal but adds operations cost. Hosting registry.json under this repo's docs/ directory and serving it via GitHub Pages keeps the maintenance burden near zero while the registry is small. If/when the registry outgrows GitHub Pages, the JSON contract is stable and the index can move to a dedicated subdomain.