The AI that works your on-call night shift — the safe kind.
nightshift watches your services, and when something breaks it diagnoses the
root cause with an LLM, files one deduplicated ticket, and (optionally) drafts a
fix. It never touches production on its own — every change needs a human's
approval, and its entire cloud footprint is read-only.
Most "AI SRE" tools are Kubernetes-first and race toward full auto-remediation.
nightshiftis built for the messy reality most teams actually have — serverless & multi-account AWS, dozens of hand-deployed functions, no service mesh — and it's built to be trusted: read-only everywhere, human-in-the-loop by architecture.
an error in your logs a person asks a question
│ │
▼ push (automatic) ▼ pull (interactive)
┌──────────────────┐ ┌──────────────────────┐
│ Triage pipeline │ │ Investigation agent │
│ fingerprint → │ │ read logs + code → │
│ dedup → LLM → │ │ typed resolution → │
│ ticket + alert │ │ you approve │
└──────────────────┘ └──────────────────────┘
└──────────────┬─────────────────────────────┘
▼
one deduplicated, root-caused ticket
(+ an optional PR you review and merge)
- Safe by design. Read-only in the cloud (
logs:*and read metrics — nothing else). The only write in the whole system is opening a pull request, and that only happens on an approver's explicit click. A prompt-injected message can at most steer a diagnosis; worst case is a bad PR nobody merges. The allowlist and the identity-bound approval are enforced in code (core/safety.py), not prompt wording — and there's a test where a hijacked model asks to delete a table and can't. - One bug → exactly one ticket. A two-layer dedup engine (a fingerprint that strips volatile noise + an atomic claim) means 500 copies of one crash collapse into a single ticket with a counter — not 500 pages at 3am.
- Fixes aren't always code. Resolutions are typed:
code_pr,aws_infra(a copy-paste runbook),db_change(a reversible runbook),manual, or a plainanswer. It proposes the right kind of fix, not always a diff. - Provider-agnostic. Log source, LLM, ticket sink, and chat platform are all plugins. Ships with a zero-cloud demo path so you can try it before wiring anything.
- Cost-aware. The LLM is invoked only on new errors; repeats just bump a counter. Circuit breakers and per-service caps keep the bill proportional to real incidents, not traffic.
git clone https://github.com/ranjan98/nightshift
cd nightshift
pip install -e .
nightshift demo # the triage pipeline (what the GIF shows)
nightshift demo-agent # the read-only investigation agentdemo feeds a sample crash log through the full pipeline — the level gate skips
an INFO line, the ignore-list drops rate-limit noise, two real bugs are filed,
and a replay of the same errors files nothing (dedup). demo-agent runs the
tool-use agent over the same log. Both run fully offline; set
ANTHROPIC_API_KEY to get real AI root-cause analysis and typed fixes.
# triage a log file → console tickets (add creds to file real ones)
nightshift triage --logs app.log --component checkout-api
# file real GitHub issues + post Slack alerts
export GITHUB_TOKEN=ghp_... SLACK_BOT_TOKEN=xoxb-...
nightshift triage --logs app.log --github-repo you/ops --slack-channel C0XXXXXX
# ask the read-only agent to investigate (searches logs, reads source, reads a screenshot)
nightshift investigate "why are checkouts failing?" --logs app.log --src . --screenshot err.png
# render a self-contained HTML dashboard of a triage run
nightshift report --logs app.log --out report.htmlSee .env.example and nightshift.example.yaml
for configuration, and docs/architecture.md for how the
pieces fit.
nightshift/slack/ is a transport-agnostic Slack Events app: a person reports a
problem in a channel → the agent investigates read-only → it replies in-thread
with a diagnosis and, for a code_pr, an Approve button → only the configured
approver's click opens a real pull request. Signature-verified, retry-deduped, and
it acks in <3s then does the slow work async. Drop SlackApp.handle(body, headers, now) into Flask / FastAPI / a Lambda — no Slack SDK needed. See
docs/slack.md.
| Layer | Plugin interface | Ships working | Also included |
|---|---|---|---|
| Where errors come from | LogSource |
file (offline) |
CloudWatch, GCP Logging, Datadog |
| The brain | LLMProvider |
echo (offline) |
Anthropic, OpenAI |
| Where tickets go | TicketSink |
console |
GitHub Issues, Jira, Linear |
| Where alerts land | ChatPlatform |
console |
Slack |
| Where fixes land | CodeRepo |
— | GitHub PR (diff → blobs → tree → commit → PR) |
| Agent tools | read-only callables | LocalToolkit (logs + source) |
cross-account CloudWatch |
| Dedup state | DedupStore |
in-memory | DynamoDB atomic conditional put |
The core (nightshift/core/) is provider-agnostic and dependency-free:
fingerprint.py, dedup.py, safety.py, triage.py, agent.py, patch.py
(a strict unified-diff engine that refuses rather than mis-applies). Every
integration is stdlib-only where it can be (GitHub, Slack, Jira, Linear, Datadog
all use urllib — no SDK); the heavier ones are optional extras
(nightshift[anthropic,openai,aws,gcp]).
- Triage pipeline — fingerprint → two-layer dedup → LLM root-cause → ticket + alert, with level gate, ignore-list, and circuit breakers.
- Investigation agent — read-only tool loop, typed resolutions,
identity-bound
approve(), screenshot vision, local toolkit + CLI. - Slack Events surface — report → diagnosis in-thread → Approve → real PR (signature-verified, retry-deduped, async).
- Self-healing PRs — a strict unified-diff engine + GitHub git-data-API PR opener; refuses to guess, falls back to posting the diff for manual apply.
- Providers — logs: CloudWatch · GCP · Datadog · file; brains: Anthropic · OpenAI · echo; tickets: GitHub · Jira · Linear · console; dedup: DynamoDB · memory.
- Reporting — a self-contained HTML dashboard (
nightshift report).
Ideas and PRs for new providers welcome — each is one small class behind an interface.
Read docs/security.md before deploying. The short version:
the design's whole point is that the AI cannot take a destructive action — see the
safety module, which enforces the read-only tool allowlist and the
identity-bound approval in code, not just convention.
pip install -e ".[dev]"
pytest # 19 tests, <1s
python scripts/make_demo_gif.py # regenerate assets/demo.gif (needs pillow)MIT — see LICENSE.
Built by @ranjan98. Not affiliated with any employer; a clean-room implementation of ideas I've found useful for keeping serverless fleets healthy.
