Documentation
Owner rules: deny sudo, containers, installs
Configure rules.json: default-denied sudo, containers, global packages, daemons, and logins, plus optional guards, question parking, and the resolved view.
Updated
How do I stop a coding agent from running sudo or installing packages?
One file, .nightshift/rules.json, carries every rule. Five elevation categories are denied out of the box, every optional guard stays empty until you set it, hooks read the file on every tool call, and Status and Doctor print one resolved view naming the source of each effective setting.
Five categories denied by default
Elevation is the one guard that ships switched on. Each category carries the pattern the hardhat and the permission preflight both match against, so the guard and the preflight can never disagree about what a command needs. Elevation gates creating system state, never using what already exists: connecting to a running database, calling a local API, or running migrations against your own dev stack is ordinary work.
- sudo — sudo and doas, including a path prefix and one layer of quoting.
- containers — the Docker socket, DOCKER_HOST, and the create-state verbs run, create, start, build, and compose up. docker ps and docker logs are reads.
- global-packages — brew, apt, dnf, winget, npm -g, pip install, cargo install, and go install. brew list is a read.
- daemons — systemctl, launchctl, service, brew services, pg_ctl, and the database servers.
- external-services — interactive logins such as gh auth login, npm login, docker login, az login, gcloud auth, and aws configure.
{
"elevation": {
"containers": { "policy": "allow" }
}
}Optional guards you switch on
Four guards stay empty until the owner writes them, and Setup proposes them rather than imposing them. forbiddenCommands is a free-form pattern denied against shell commands; protectedDirs names directories never to add, commit, tag, or push; expectedEmail denies a commit under any other Git identity; neverCommitPatterns denies a commit whose diff matches. POSIX matches with grep -E, native Windows with .NET regular expressions, and an invalid pattern fails closed and names the setting to fix.
- A one-shift elevation allowance authorizes its own category and nothing else; protected paths, commit patterns, and identity are never lifted with it.
- toolDeny denies any exact host tool name with a message the agent reads; Codex reports file edits as apply_patch.
- During a shift the rules file itself is guarded, so only the owner sets or lifts a rule.
Questions park by default
The shipped file carries three native question-tool entries: AskUserQuestion for Claude Code, request_user_input for Codex, and AskQuestion for Cursor. A non-empty value denies that tool with the owner's message, which tells the agent to choose a production default, record it in the parking lot, and continue. An empty string allows that host to ask and wait. Keep all three keys present; a missing key is a configuration error, not a hidden default.
One resolved view
Three layers decide a setting: the built-in default, your permanent answer in rules.json, and tonight's snapshot in shift-policy.json, which records the deadline, verification level, tooling policy, and any one-shift allowance with its provenance. The host's own permission boundary sits above all three as a ceiling no Nightshift key can lift.
- Each row of the resolved table ends in its origin: built-in, rules, or one-shift.
- An edit to rules.json applies from the next tool call; no sync, no restart, no second copy.
- The hooks read the file with a bundled reader that needs neither jq nor python3; a malformed file fails closed and does not arm.
"$NIGHTSHIFT_PLUGIN_ROOT/runtime/ns" shift-policy resolve --tableStart with a bounded prompt
This prompt names the outcome and preserves Nightshift’s review boundary. Paste it into the supported coding host from the project you want to change.
Show me this project's resolved Nightshift policy with the source of every setting, and list which punch-list items need an elevation category that is not allowed. Change nothing.What this workflow does not claim
- Hardhat matches command text; it is hardening against accidental drift by a cooperative agent, not a sandbox or unbypassable isolation.
- No Nightshift rule widens the host's own permission boundary, and an organization policy above the host stays above it.
Evidence and sources
These links support the released behavior, public outcomes, or problem language described on this page.
Frequently asked questions
Does Nightshift stop a coding agent from running sudo or docker run?
Yes, by default. Both fall under elevation categories the shipped rules file denies. Read-only forms such as docker ps stay ordinary work, and the owner can allow a category for one shift or permanently.
Can I allow Docker for one night only?
Yes. A composition step records a one-shift allowance on the shift policy with its provenance. Saying always allow docker here writes a permanent allowance to rules.json instead, while the shift is unarmed.
Is the hardhat a sandbox?
No. It matches command text and stops accidental drift by a cooperative agent. Host permissions and isolation remain separate controls that Nightshift never replaces.
Do I need jq or Python to use the rules file?
No. The plugin ships shell and PowerShell only. POSIX hooks read rules.json with a bundled reader, and native Windows uses PowerShell's built-in JSON parser.