Open-source AI log monitoring that finishes the job
It finds the bug. Then it writes the fix.
Your monitoring already tells you something broke. guiltyspark is a self-hosted AI agent that does the next part: it reads your logs, works out what's actually wrong, writes the patch and the regression tests in an isolated clone, runs your test suite, and opens a pull request explaining what it found.
You wake up to a code review instead of an alert.
Apache-2.0 · Self-hosted · Reads Grafana Loki · Runs on a Raspberry Pi
The work product
nobody was awake for this
This is what's waiting in the morning.
Not a dashboard to go read. Not an alert to go triage. A pull request, with the evidence that motivated it, the reasoning behind the change, the diff, and tests that fail without the fix and pass with it. It writes to you as the Monitor — that's the personality — but the contents are ordinary, checkable engineering.
● Open
guiltyspark wants to merge 1 commit into main from guiltyspark/null-safe-inventory
fix: handle missing inventory items without throwing
guiltyspark opened this at 02:19 · 4 minutes after the first error
Containment Record
Greetings, Reclaimer. inventory-api logged 612 failed product requests between 02:14 and 02:18. Each attempted to read an item that no longer existed, producing the same nil-reference malfunction. The anomaly is recurrent and, I am pleased to report, entirely correctable.
Evidence Archive
02:14:07 ERROR NoMethodError: undefined method `name' for nil
02:14:07 ERROR app/services/inventory_service.rb:47:in `get_product'
… 610 further occurrences, clustered to one issue
Causal Assessment
The repository returns nil when an item is absent, but the service dereferences it before checking. Every trace terminates at the same line after requests for deleted product IDs. A missing item should produce a controlled 404, not an internal server error.
Corrective Protocol
2 files changed · within allowed paths · under change limit
app/services/inventory_service.rb
product = repository.find(id)
+ raise NotFound, id unless product
ProductResponse.from(product)
Verification Sequence
✓ returns 404 for a missing product — fails without patch
✓ bundle exec rspec — 128 passed in 6.4s
✓ bundle exec rubocop — clean
Final authorization remains yours, Reclaimer.
It shows its work
Every claim in the PR traces back to log lines it actually saw. If the diagnosis is wrong, the evidence is right there to catch it — you're reviewing an argument, not trusting a black box.
The tests come with it
A fix ships with a regression test that fails without the patch. That's the difference between a plausible-looking change and a demonstrated one.
You never see the bad ones
Patches that break the suite, wander outside allowed paths, or exceed the change limit die in the clone. Only work that passed gets as far as your review queue.
How it got there
five minutes, unattended
Detect. Diagnose. Fix. Verify.
The loop is deliberately boring, because boring is auditable. Here's the whole thing, timestamped against the pull request above.
-
02:14 · DETECT
Polls Loki on an interval and collapses 612 near-identical lines into a single coherent incident. Noise it has ruled on before never surfaces again.
-
02:15 · DIAGNOSE
Hands the evidence to Codex along with the source repo and your notes on which warnings are expected, so benign chatter isn't mistaken for a fault.
-
02:17 · FIX
Clones the repository somewhere disposable and writes the smallest change that addresses the cause, plus a test that proves it.
-
02:18 · VERIFY
Checks the patch against your policy — allowed paths, file count — then runs your real test suite. A failure here ends the attempt quietly.
-
02:19 · HAND OFF
Opens the pull request and stops. It will not reopen this issue while your PR is still sitting there, and it stays quiet for a cooldown after you merge.
Progressive autonomy
An agent that writes code should have to earn it.
So it starts with nothing. Every repository begins in observe, where it can look and reason but not touch. Watch its diagnoses for a while; promote it only once you believe them. It cannot push code unless you have explicitly moved that target to draft-pr or pr, one repo at a time.
Default
observe
Detect and diagnose only. Nothing is cloned, nothing is written. Read the findings and judge them.
Then
fix
It writes the patch and validates it in an isolated clone — but nothing leaves that clone. Fixes without consequences.
Then
draft-pr
Same checks, then it pushes a branch and opens a draft. Visible to your team, mergeable by nobody until you say so.
Once you trust it
pr
A review-ready pull request, like the one above. It still ends at your approval — that line never moves.
Built for trust
The fences that make this safe.
Letting an AI agent write to your repositories is only reasonable if it's boxed in properly. Here's the box.
The AI never holds the keys
Install a GitHub App scoped to only your target repos. guiltyspark mints short-lived tokens for its own Git operations — Codex never receives credentials, so a bad model day can't become a bad security day.
Patches stay in their lane
Per-target allowed paths, a cap on files changed, and required test commands. All of it enforced in a short-lived clone that's thrown away either way.
Rehearse before you commit
guiltyspark replay re-runs a captured incident against a local checkout, so you can watch it work a real bug before granting it push access to anything.
One issue, one PR
It dedups on the underlying problem, not the exact log line. An issue with an open PR is never re-filed, and a configurable cooldown keeps it quiet after merge. It won't bury you.
No API key required
Runs on a ChatGPT-authenticated Codex session, so your existing plan covers it rather than a separately billed OpenAI key. Known problems short-circuit from the local store — a model call is spent only on something genuinely new.
Yours to run and audit
Apache-2.0, self-hosted, no vendor telemetry. A native arm64 image means the whole loop can live on a Raspberry Pi in your house, on infrastructure you control.
The console
for when you do want to look
Teach it what to ignore.
Most of what a log monitor sees isn't worth a pull request, and you're the one who decides which. The dashboard is where you draw that line: silence a noisy pattern, point a new repository at the agent, or check what it's seeing right now. Changes reach the running daemon within one poll cycle — no restart.
343 Guilty Spark
Monitor · Installation Log Surveillance
Uplink nominal
I have grouped the remaining anomalies for your review. None of them warrant a corrective measure yet.
grouped
TLS handshake retries from the health checker
4 variants · ×612 events
Silence group
Proposed silence pattern
would suppress 612 current events · your call
edge-proxy: ^TLS handshake retry \(attempt [0-9]+\)
unassigned
legacy-cron
×38 · no repository configured for this one
Silence
Add target
Silence that sticks
Mute one fingerprint, a whole group, or commit a service-scoped regex that catches future variants too. Every entry keeps a note explaining why, and anything can be restored.
It suggests, you decide
Codex clusters loose anomalies and proposes silence patterns, showing exactly how many live events each would swallow before you commit. Nothing is ever auto-applied.
Nothing falls off the map
Errors that match no configured repository still surface, flagged as unassigned — so a service you forgot to wire up doesn't quietly rot.
The dashboard is unauthenticated and can modify configuration. Keep it on a trusted LAN — never expose it to the public internet.
Reasonable questions
The ones you should be asking.
Does it change production code on its own?
No. Every target starts in observe, where nothing is cloned or written. It pushes only from repos you deliberately promoted to draft-pr or pr — and even then it opens a pull request. Merging is a human action, always.
What if the fix is wrong?
Then you close the PR — that's the whole point of ending at review. Wrong patches also have to survive your test suite and patch policy first, so the common failure isn't a bad merge, it's an attempt that dies in the clone and you never hear about.
How is this different from an AI log summarizer?
A summarizer explains text you paste in and stops. This runs on its own, remembers what it has already ruled on, knows which repo each service comes from, writes and validates real code, and finishes with a pull request. The summary was never the hard part.
What do I need to run it?
Docker, a Loki instance to read from, and a Codex session signed in with your ChatGPT account. It's happy on any always-on box you already own — the arm64 image exists because it was built to live on a Raspberry Pi.
Will it flood me with pull requests?
It dedups on the underlying issue rather than the log line, remediates one representative per run, won't re-file while a PR is open, and waits out a cooldown after merge. Noise you silence stays silenced.
Why does it talk like that?
343 Guilty Spark is the endlessly polite AI caretaker from Halo, left alone for millennia to monitor an installation and quite certain he's being helpful. It seemed like the right voice for something that watches your logs while you sleep. You can ignore the reference entirely — the pull requests still make sense.
Quick start
Four commands, then leave it alone.
It comes up in observe mode, so this is a safe thing to point at a real repository on a Tuesday afternoon.
# configure targets, then:
cp .env.example .env
docker compose pull
docker compose run --rm \
guiltyspark codex login --device-auth
docker compose up
guiltyspark once- Poll Loki once and analyze what's there right now.
guiltyspark daemon- Run forever, polling on an interval. The normal mode.
guiltyspark doctor- Check your configuration and connectivity before trusting any of it.
guiltyspark dashboard- Serve the console on port 8343.
guiltyspark replay- Re-run a captured incident against a local checkout.
Greetings, Reclaimer
Point it at your logs. See what it fixes.
It starts in observe mode and can't touch anything. The worst case is you learn something about your own logs.