Share a module
Publish a module other people can require, pull one in with niwa.use, and read the lock entry that pins it by hash.
When to use this
Share a module when the same declarations belong on machines that do not share a config repo: yours and a colleague's, or one repo per team. Inside a single repo you need none of this. A module is a file, and your own sugar is a function.
What must already be true
- The module lives in a GitHub repository.
github:is the one sourceniwa.usereads. - You pin a ref. A source without one is refused, and the refusal names the pinned form to write instead.
niwa updatehas resolved it. A plan never fetches.
Publish a module
A shared module is one file: init.luau at the repository root. niwa
loads that file and nothing else, so a second file of your own does not
resolve, and @self/ names the config repo that used the module rather
than the module. A shared module ships declarations, not files.
-- Rust, the way this team sets it up. One file, one job.
local niwa = require("@niwa")
niwa.brew.formula { "cargo-nextest", "cargo-watch" }
local rustup = niwa.brew.formula("rustup-init")
if rustup.changed then
niwa.run("rustup-init -y --no-modify-path", { creates = "~/.cargo/bin/rustc" })
endThe module calls the same API a config calls, through the same
require("@niwa"). There is no module format to learn.
Tag the commit people should pin, and push the tag.
git tag v1
git push origin v1Pull one in
One line names the source and the ref.
niwa.use("github:you/niwa-rust@v1")Then resolve it. niwa update clones the ref, records the commit, hashes
the tree, and caches it under that hash. On a machine that has the lock
but not the cache, a run stops and says to run niwa update there too.
niwa update niwa-rustA bare name filters to pins whose name contains it, so one module
re-resolves without moving the rest. niwa update
covers the verb in full.
The lock entry
niwa update writes one table per module, and the file is committed.
[use."github:you/niwa-rust"]
ref = "v1"
commit = "e5b19d7"
sha256 = "41ac03bd8f2e6c5a9d7b1e0f3c2a5d8e7f6b9c0a1d2e3f4a5b6c7d8e9f0a1b2c"The key drops the ref, because the ref is a field and a ref can move. The
sha256 is a digest over every file in the tree, sorted, with .git left
out, and it is also the cache directory's name under
~/.local/share/niwa/modules/. Two machines reading one lock read the
same bytes, whatever the tag points at today.
What the sandbox promises
A module declares. Only niwa acts, and only through the same API your own
config uses, so everything a module would do has a plan line. Its
declarations carry the module's own source location rather than yours, so
niwa plan and niwa explain say where a line came from.
Read the plan before you trust it. That is the check, and it is available before the first apply.
The plan is a reporting boundary, not a permission boundary. Your config
is code and the repo is the trust boundary: whoever can move the tag you
pinned can change your machines at your next niwa update. The lock is
what keeps that a decision with a diff.
Next
- Set up a second machine — a shared module reaches a new Mac the same way the rest of your config does
