Markdown source of this page: /quickstart.md · generated, do not diverge
From zero to an agent doing real work through the broker. Five ideas carry the whole product: a local broker holds your credentials; sentences you write are its only authority; agents act through their native tools; every decision lands as a receipt. Everything here is the CLI you'll actually operate — no dashboard exists.
Linux amd64 (deb or tarball) and macOS arm64 (tarball) — artifacts and checksums live on the GitHub release; every path ends with the broker running.
Debian/Ubuntu:
curl -fsSLO https://github.com/suarezc/cermet/releases/download/v0.1.0/cermet_0.1.0_amd64.deb curl -fsSL https://github.com/suarezc/cermet/releases/download/v0.1.0/SHA256SUMS | sha256sum -c --ignore-missing sudo dpkg -i cermet_0.1.0_amd64.deb
Tarball (macOS arm64 shown — linux_amd64 works the same with its own file; the tarball
holds one cermet binary plus its two role aliases):
curl -fsSLO https://github.com/suarezc/cermet/releases/download/v0.1.0/cermet_0.1.0_darwin_arm64.tar.gz curl -fsSL https://github.com/suarezc/cermet/releases/download/v0.1.0/SHA256SUMS | shasum -a 256 -c --ignore-missing tar -xzf cermet_0.1.0_darwin_arm64.tar.gz ./cermet setup # asks for administrator access itself
Or build from source — Rust only, version pinned by the repo:
cargo install --path crates/cermet-bin # -> ~/.cargo/bin/cermet cermet setup # asks for administrator access itself
setup states what it needs root for — one consent — installs the broker under its own service account with a credential vault only the broker can read, and ends with the state of the box:
[cermet-setup] ✓ broker running (cermetd, starts at boot) [cermet-setup] ✓ credential vault ready (custody: systemd-host) [cermet-setup] persistent Cermet files do not contain the plaintext key; full host-image disclosure may permit recovery [cermet-setup] ✓ git integration ready (git-remote-cermet) [cermet-setup] update checks: daily against https://github.com/suarezc/cermet/releases, run as you and never by the daemon. The notice is LOCAL and nothing installs itself; applying an update stays `sudo cermet update`. Off: cermet update --daily off [cermet-setup] next: cermet connect github (or vercel, stripe)
If setup added you to the cermet-approvers group just now, log out
and back in once before connect — group membership is read at login.
If a line is ✗, cermet check is the doctor: read-only, always safe, and it names the broken thing and the remedy. You never need it when the lines are green.
cermet connect github
You paste the credential once at a masked prompt: it never enters argv or shell history, crosses to the broker exactly once, and is encrypted at rest in the broker-owned vault. No agent-facing surface can ever read it back. This step needs no ceremony beyond the paste — storing a credential grants no authority. Authority comes only from sentences (next step). For GitHub, inside a git repository, connect offers — never silently — to repoint the remote to cermet::github/<owner>/<repo>, git's own transport-helper addressing, which is what makes plain git push route its one credential-bearing hop through the broker. Same shape for the others:
cermet connect stripe cermet connect vercel
Authority is sentences. Nothing executes without one — that is the entire security model, and it is not configurable. See an example CERMET.md for the production shape and the WHERE index for every field you can bind. Two ways to author, and both end at a presence prompt only a human can answer. On Linux that is a PAM prompt — your account password, read straight from your terminal's TTY; it works over an interactive SSH session, and fails closed when there is no controlling terminal (pipes and agent harnesses cannot answer it). On macOS it is device-owner authentication — Touch ID on the physical machine; over SSH it cannot succeed. Either way, no answer within ~60 seconds fails closed.
# one-off, straight into the live corpus: cermet rules allow 'github.push where owner = "you" and name = "your-repo"'
# or the durable way — CERMET.md in your repo: cermet doc check --init # seeds CERMET.md from the live corpus, with its pin # ...edit the fenced block... cermet doc diff # your document vs what is served, as a unified diff cermet doc apply # presence-gated; makes it live, re-pins the hash
Only the managed block is authority input; prose in CERMET.md is guidance, never policy. One sentence to know: github.push is decided by git's own update hook, so you exercise it with git push, not cermet run — the refusal, if any, arrives in git's own output.
Your agent uses its native tools; the broker carries the credentialed hop.
git push # git's own plumbing, one brokered hop
cermet run stripe.get_charge --resource '{"charge":"ch_..."}'
cermet run vercel.deploy --resource '{"project":"your-site","target":"preview","team":"personal"}'
# relay verb: prints the exact `vercel deploy` line
vercel.deploy takes a third required field, team: the Vercel scope the deploy lands in — personal for a personal account, or the team id (team_…, the orgId in your repo's .vercel/project.json). Whatever the request names is frozen for the session and pins the ?teamId= the CLI stamps on every scoped call, so a deploy cannot wander into another scope mid-session. Which scopes are admissible is the sentence's business, exactly like target: a rule that spells and team = "personal" admits only that one, and a rule that never mentions team leaves the choice to the requester.
A successful run prints one JSON receipt: the provider's body under result, the broker's fields beside it — envelope.request_id chases the receipt later, artifact is the content handle for the full retained body. cermet catalog is the discovery surface: every entry carries its signature and the sentence admitting it. Running agents in an MCP harness (Claude Code, etc.)? cermet mcp install registers every admitted verb as a typed tool — the agent-facing guide covers that surface.
A denial is an answer, not an error. It costs one request, names the bound that was hit, and prints the sentence that would widen it — for you to apply, never the agent. No approve tool exists on the agent surface.
cermet run vercel.deploy --resource '{"project":"not-my-site","target":"preview","team":"personal"}' --ask-only
{
"request_id": "req_79dd89949fc53e3b",
"decision": "deny",
"reason": "vercel.deploy denied by sentence authority: rule 2 predicate 1 did not match",
"hint": "to allow: cermet rules allow 'vercel.deploy where project in {\"cermet-site\", \"not-my-site\"} and target = \"preview\"'",
"authority_kind": "sentence"
}
"rule 2" is the number cermet rules lists and cermet rules revoke 2 takes. Take the hint to whoever holds authority; apply it under presence; then the agent retries.
cermet log # recent history, as the sentences that authorized it cermet log <request_id> # one request's full record as JSON — allowed or denied cermet audit-verify # the hash-chain, checked from genesis
cermet log renders the newest 100 rows unless you pass --all; the filters (--since, --provider, --denied, --hops) narrow first, then the window applies. Every decision — allow and deny alike — is a typed row in a hash-chained log written by the enforcement point itself. When you want to know what your agent did while you were out, the answer is one command, and it is not a claim; it is a receipt.
That's the loop: install once, connect once, write sentences, agents work through native tools, everything lands in the log. Widen authority one sentence at a time, exactly as fast as your agent earns it.
Leaving should be as clean as arriving — the exact inventory, in order:
# stop and disable everything the installer scheduled sudo systemctl disable --now cermetd cermet-update-check.timer sudo dpkg -r cermet # deb boxes; tarball boxes: remove the extracted binaries
That leaves the broker gone but your data intact. The full cleanup — this destroys the credential vault and every receipt:
sudo rm -f /etc/systemd/system/cermetd.service /etc/systemd/system/cermet-credential-env.service \
/etc/systemd/system/cermet-update-check.service /etc/systemd/system/cermet-update-check.timer
sudo systemctl daemon-reload
sudo rm -f /etc/sudoers.d/cermet-agent /etc/tmpfiles.d/cermetd.conf /etc/credstore.encrypted/cermet.key
sudo rm -rf /etc/cermetd /var/lib/cermetd
sudo userdel cermet; sudo userdel cermet-agent
sudo groupdel cermet-approvers 2>/dev/null; sudo groupdel cermet-agents 2>/dev/null
rm -rf ~/.config/cermet
macOS: `sudo launchctl bootout system /Library/LaunchDaemons/dev.cermet.cermetd.plist`, remove that plist and the binaries, then the same state directories.