| Filename | Latest commit message | Latest commit date |
|---|---|---|
Per #4128 (mara: allow-everywhere false positives go in a central list, otherwise fix in source). Testing surfaced better fixes than the plan posted on the issue: - 5x Microsoft.Contractions 'that is' idiom false positives: adding the missing comma ("that is, ...") both reads better and satisfies the rule's own negative-lookahead, so no suppression is needed at all. Fixed in docs/integrations/forge.md, docs/tools/forge.md, docs/tools/hivectl.md, docs/web-ui/dashboard.md, and swarmctl-cli.md's generated source (swarmctl/src/main.rs, doc comment regenerated via markdown-docs). - persistence.md's 'is not' matching inside 'is nothing': reworded to 'there'\''s nothing' rather than add any exception -- dodges the trap and is a genuine contraction besides. - ca.md's 'it is' matching inside the already-correct 'it isn'\''t': tried a central .vale.ini TokenIgnores entry first per the allow-everywhere framing, but testing against the real file (not just a synthetic snippet) found it silently fails to suppress whenever markdown emphasis syntax appears earlier in the same file -- an offset-drift bug in how Vale applies TokenIgnores, not a config mistake. Reworded to "it'\''s not" instead, same fix shape as persistence.md. - config.md's 3 genuine Microsoft.Avoid 'backend' exceptions (already flagged and accepted on #4139 -- an actually-pluggable LLM API provider, matching the nix option's own name, not one internal system to name): scoped inline vale suppression around just that section, since this one really is context-specific rather than a rule bug. Verified: fresh 'vale docs/ --minAlertLevel=error' is 0 errors AND 0 warnings (was 10 errors). nix fmt 0 changed beyond the edits themselves. pre-push lints (tracker-tag/comment-block/doc-pointer) clean. cargo clippy -p swarmctl -- -D warnings clean. Diffed the regenerated swarmctl-cli.md against the old copy to confirm only the intended line moved. |
||
| .. | ||
| src | ||
| Cargo.toml | ||
| README.md | ||
swarmctl
Swarm-level operator CLI. Runs as root on the host running
swarm-controller, and acts on that host directly.
Distinct from hivectl, which drives one hive's hive-c0re over its
admin socket. This crate does not link swarm-controller, for the same
reason hivectl does not link hive-c0re.
Why root, and why no socket
The first verb writes authelia's users database. Making that write
rootless was examined and rejected — relocating the file only turns a
write problem into a read problem. Move users.yml into a directory
the controller owns and the controller can write it, but authelia then
has to read it across the same boundary in the other direction. Making
that work needs either a hand-pinned gid (the container's uids are
allocated inside it, at activation — see the uid-assignment issue) or
world-readable password hashes. Both are worse than root.
So there is no socket, no HTTP route and no privileged helper here. When
a verb has to run as a non-root user or from another host, the answer is
a group-gated admin socket, separate from the controller's 0666
gateway-facing one — not a widening of what root does here.
One file, two writers
users.yml — authelia's own users database — is read and written
directly. There is no second store.
There used to be: a private users.json here, canonical, with users.yml
rendered from it, while swarm-authelia-bridge kept its own pair against
the same physical file. Two canonical stores for one file is a seam, and
it bit — a writer whose own JSON was missing could not tell "nothing here
yet" from "someone else's users", and refused to write.
The argument for the split was that it let this crate work without a YAML parser. It didn't: the JSON was read back on every run, so the round-trip was already being paid — the two files differed only in format.
⚠️ The file is round-tripped, so comments and hand-formatting do not
survive a write. Values do, and so do keys this binary does not model.
See swarm-authelia-bridge/README.md for what both writers must uphold.
Configuration
Every path comes from the nix module that installs the binary, because every one is derived from an option that module owns. They are required rather than defaulted — a default would be an address we hope points at something, and one that resolves cleanly to the wrong place is worse than an error.
| variable | what |
|---|---|
SWARMCTL_AUTHELIA_BIN |
the configured authelia; argon2 params must match the verifier's |
SWARMCTL_AUTHELIA_USERS_FILE |
host-side path of the users database |
SWARMCTL_AUTHELIA_MACHINE |
container name, for systemctl -M |
SWARMCTL_AUTHELIA_UNIT |
authelia's unit inside that container |
Usage
# swarmctl user add mara --display-name "Mara" --group admins
The password is generated by authelia (crypto hash generate argon2 --random) and printed once. It is never passed on a command line:
/proc/<pid>/cmdline is world-readable, so a password in argv is readable
by any local process for the lifetime of the call.