@cooplux/praxis
v0.2.0
Published
Attended, resumable runbook engine (init/validate/run/resume/status/attest/verify) with an append-only, attested, hash-chained audit trail. Attestation is honor-system (recorded name + timestamp); rollback argv is printed, never auto-run.
Maintainers
Readme
@cooplux/praxis
Attended, resumable runbooks with an append-only, attested, hash-chained audit trail.
praxis runs a procedure one step at a time. It executes command/check steps, pauses at
manual gates for a human to attest, and records every state transition to an append-only
receipts.jsonl ledger whose lines are hash-chained (each line embeds the prior line's SHA-256,
Rekor-style, offline). If the process crashes mid-run, praxis resume picks up where it left off —
and if a step was interrupted with an unknown outcome, it stops and asks rather than blindly
re-running.
Honest scope — read this. Attestation is honor-system: a recorded operator name + timestamp. It is NOT a verified login, it does NOT enforce segregation-of-duties, and it is NOT a compliance certification. The ledger is append-only and hash-chained (tamper-evident) — nothing is cryptographically signed. Rollback argv is recorded and printed for the operator; praxis never auto-runs it. The trail is evidence a team may use, not a guarantee.
Install / use
npx @cooplux/praxis init # scaffold praxis.json
npm i -g @cooplux/praxis && praxis --helpNode ≥ 20.
Commands
praxis init [file]— scaffold a starterpraxis.json(refuses to clobber an existing one).praxis validate [file]— validate a runbook against the contract (errors + advisory warnings).praxis run [file]— start a new run.--dry-run— classify each step safely-previewable vs side-effecting and loudly flag the latter (it does not execute anything).--yes— auto-attest non-required-humanmanual gates asci:auto(never an operator name).--force-lock— override a run-lock held on another host.
praxis resume [file]— resume the latest unfinished run. Stops on any in-doubt step.--resolve <id>=done|redo(repeatable) — record the operator's decision for an in-doubt step.--force-redo— permitredoof a non-idempotent step (may double-apply a side effect).
praxis status [file]— read-only: per-step status, chain integrity, the run-lock, in-doubt steps, and the rollback argv to run by hand after a crash.praxis attest <stepId> [file]— record an attestation for a manual gate.--as "<name>"— the operator name to record (required unless--yes).--note "<text>"— an optional note.--yes— auto-attest asci:auto(forbidden together with--as; refused on arequired-humangate).
praxis verify [file]— verify the append-only hash-chain of the ledger (offline; not a signature check).
Runbook format (praxis.json)
{
"schema": "cooplux.praxis.manifest.v1",
"name": "deploy-runbook",
"steps": [
{ "id": "preflight", "type": "check", "run": ["node", "--version"], "sideEffecting": false },
{ "id": "gate", "type": "manual", "prompt": "Confirm the change window is open.", "requiredHuman": true },
{ "id": "apply", "type": "command", "run": ["./deploy.sh"], "idempotent": false,
"sideEffecting": true, "rollback": ["./rollback.sh"] }
]
}command— an argv executed withexecashell:false(no shell parsing, no injection). Side-effecting by default.check— an argv treated as a read-only probe (previewable in--dry-run).manual— a human gate: praxis pauses; an operator attests viapraxis attest.idempotent(defaultfalse) — only idempotent steps may setattempts> 1 and be auto-retried. Non-idempotent steps are never auto-retried, and an in-doubt non-idempotent step cannot beredone without--force-redo.rollback— argv recorded/printed for the operator, never auto-run.requiredHuman— a manual gate that refuses--yes/ auto-attest.
Configuration (env-only)
Everything is credential-free by default and driven by environment variables — nothing is bundled. Pass secrets via the environment, never on a step's command line; praxis additionally redacts secret-shaped tokens from argv before persisting them, and does not capture step stdout/stderr into the receipt.
| Variable | Purpose | Default |
| --- | --- | --- |
| PRAXIS_MANIFEST | default manifest path | praxis.json |
| PRAXIS_LEDGER | ledger path | receipts.jsonl beside the manifest |
| PRAXIS_RECEIPT_URL | optional POST target for the summary receipt (argv stripped → hashed) | — (no publish) |
| PRAXIS_RECEIPT_TOKEN | optional bearer for the publish target | — |
Crash-safety (how resume stays honest)
Before spawning any step, praxis appends a step-start marker to the ledger and fsyncs it. If the
process is killed between "the step changed the world" and "state → ok", that marker survives. On
resume, a step that started but has no ok/failed outcome is in-doubt: praxis stops, shows
the exact argv, and asks you to inspect the system and resolve it (--resolve <id>=done|redo). It
never guesses.
Attestation honesty (--yes)
--yes auto-attest always stamps attestedBy: "ci:auto", method: "ci-auto",
machine_attested: true, four_eyes: false — it can never carry an operator name, --as is
forbidden alongside it, and a required-human gate refuses it outright. This keeps machine sign-offs
distinguishable from human ones in the ledger.
Part of the CoopLux constellation
praxis executes attended procedures; enfilade sequences tools; datum holds reference data at
rest. Schema tags: cooplux.praxis.manifest.v1 / cooplux.praxis.receipt.v1. Content hashes use the
family SRI dialect sha256-<base64> over canonicalized content. @qbts = task-pipeline engine;
@qbtz = quantum-env orchestrator — not the same tool.
License
MIT © Dane Anthony Cooper
