api-relay-audit
toby-bridges
Local security audit for AI API relays and LLM proxies: detects prompt injection, model substitution, tool-call rewriting, SSE anomalies, error leakage, and Web3 wallet risks.
liyixuan201211/nightshift
Run an agent unattended for hours and get a morning brief that is an audit, not a summary. Signs a manifest before the work, prices every step at the execution boundary, rolls back a failed verification, and reports what the gate refused. DSH plugin + skill, zero dependencies.
PROJECT TOPICS
INSTALL REFERENCE
dsh plugin --profile web add github:liyixuan201211/nightshift
该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。
PROJECT README
Run an agent unattended for hours — and get a morning brief that is an audit, not a summary.
Sign a manifest before the work: what it is for, which commands prove it worked, what must never run, and what it is allowed to spend. Then wrap each mutating command in a checkpoint that rolls itself back when verification fails. In the morning, get a brief generated from the run's own journal — what changed, what was verified, what was not, and the exact command to undo the night.
dsh plugin --profile web add github:liyixuan201211/nightshift
No install needed to try it:
npx --yes github:liyixuan201211/nightshift --help
The tools this belongs to each answer one question about one moment —
ctx-budget what it will cost, blastradius what it would destroy, deadend
whether this failed before, mcp-cap what a server can do, skillnotary what a
skill may do, rewind how to get it back. All of them are point-in-time and
stateless.
An unattended run is neither. Once the person leaves, nobody is standing at those checkpoints — and the failures that matter over hours are the quiet ones. Drift: the objective changing, the budget passing, a step failing silently, a loop spinning. No single-point tool is watching for those.
nightshift is the layer that watches. It is also the generalisation of
something the family already does by hand — ctx-budget's own bundle notes that
it reads a tools/list dump that mcp-cap already produced.
The manifest is a contract you sign while you are still awake. The brief is the audit of that contract.
nightshift init --root ~/work \
--objective "finish the auth refactor and get the suite green" \
--deliverable "npm test passes" \
--accept "tests=npm test" \
--accept "types=npx tsc --noEmit" \
--forbid "git push" \
--command "npm run build"
nightshift preflight --root ~/work # may I start?
nightshift step --root ~/work --verify "npm test" -- npm run build
nightshift acceptance --root ~/work
nightshift brief --root ~/work
Only two of the declared things are enforceable, and that is the point:
acceptance[] is a list of shell commands, so "done" is an exit code rather
than a claim; forbidden[] is matched before execution, so a rule is a gate
rather than a post-mortem. An objective with no acceptance checks produces a
brief that says unclear and means it.
The brief is a rendering of the journal, never a summary written from memory. That direction is what makes it worth reading:
5/6 family tools)rewind undo line to reverse the nightIts bottom line is one word: verified, failed, incomplete, recovered,
nothing-done, refused, or unclear.
preflight judges the commands a run declares. But a step can run anything,
and an unattended run has hours in which to change its mind. So the same
judgement runs again at every execution:
blastradius, read from the exit
code (lost → terminal, costly → recoverable, safe → two-way,
anything else → unknown)--curfew-minutes, no new one-way action may start
(two-way actions stay allowed: refusing to save a file at 6am would be a worse
failure than the one a curfew prevents)A refusal means the command did not run and nothing was changed.
blastradius calls this lost:
{ "recoverability": "lost",
"why": "not inside a git repository, so nothing can restore it",
"samples": ["data.txt"], "verdict": "lost" }
It means git cannot restore it. But rewind exists precisely to restore files
git never tracked, with or without git — so for a path inside the protected
root, the checkpoint taken moments later does restore it.
Trusting either tool alone is wrong: believing blastradius refuses nearly every
real refactor; believing the snapshot treats rm -rf ~/something-else as
recoverable when the snapshot never covered it. So nightshift uses
blastradius's own samples to decide:
| at-risk paths | result |
|---|---|
| all inside the protected root | downgraded to one_way_recoverable — warned, allowed |
| any outside the root | stays one_way_terminal — refused |
| findings name no paths | stays one_way_terminal — refused |
The last row is deliberate: being unable to tell is never an upgrade.
preflight exits 4 and starts nothing when a run would be unrecoverable. The
usual cause is that rewind is unavailable — an unattended run with no way
back is the one thing this refuses outright. Fix the cause; there is no flag
that gets past it.
| Exit | Meaning |
|---|---|
0 |
ok |
1 |
error |
2 |
usage error |
3 |
attention — a preflight warning, or a step that failed and was rolled back |
4 |
refused — nothing was done, on purpose |
5 |
degraded — it ran, but some checks could not be performed |
6 |
nothing — there was nothing to do |
-q suppresses output and never the exit code.
--exclude .nightshift is not optional. nightshift keeps its journal
inside the directory rewind protects, so every rewind call passes the
exclude. Without it the journal would dirty its own snapshots and rewind status would never be clean. (Tested.)3 means OVER_BUDGET in
ctx-budget, LOST in blastradius, DRIFT in deadend, CHANGED in mcp-cap, and
"the tree differs" in rewind. Each tool's dialect is data in src/tools.js,
overridable per run, and nightshift tools prints the table.unverifiable is not ok. blastradius exits 5 for any command outside
its vocabulary, and its JSON says "verdict": "none" when it means "I have no
analysis for you". Reading that string as "no risk" waves through
curl … | sh, so nightshift maps on the exit code and treats the verdict
as corroboration only.info, never as a pass. blastradius only
understands destructive commands, so pricing touch legitimately returns "I
cannot tell you". Escalating that to a warning every time would train the
operator to ignore warnings — so it is visible in the brief without raising the
verdict. (onUnpriced, default ignore.)blastradius and recovers with
rewind; it does not isolate.npx fallback unless --allow-npx is
passed. Resolution is local: --tools-dir, $NIGHTSHIFT_TOOLS_DIR, the
configured search path, or PATH.See DESIGN.md for the architecture, the exit-code normalisation table, and the reasoning behind the ordering rule that a refused run must not leave a snapshot behind.
MIT
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: agent-safety、audit、coding-agents。