Metadata-Version: 2.4
Name: server-sentinel
Version: 1.0.0
Summary: Tells you when your Minecraft server crashed the SAME way as last time. Read-only log watcher with crash signatures.
Author: Jakoby Tuckta
License: LicenseRef-Proprietary-Free-Edition
Project-URL: Homepage, https://github.com/jaakoby/server-sentinel
Project-URL: Documentation, https://jaakoby.github.io/fix/
Project-URL: Source, https://github.com/jaakoby/server-sentinel
Keywords: minecraft,minecraft-server,monitoring,crash-report,forge,neoforge,modded-minecraft,log-monitoring,server-admin
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: System Administrators
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Games/Entertainment
Classifier: Topic :: System :: Monitoring
Classifier: Topic :: Utilities
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE.txt
Dynamic: license-file

# server-sentinel

**Did it crash the same way as last time?**

Your server went down for the fourth time this week. Is that one
fault repeating, or four different ones? Scrolling the log barely
helps, because every crash *looks* alike.

Sentinel fingerprints each crash from its cause and the mod in the
trace, so a repeat is obvious:

```
$ server-sentinel watch logs/latest.log

[12:05:00] CRASH
           cause:   java.lang.NullPointerException: Cannot invoke
                    "Entity.getX()" because "e" is null
           culprit: examplemod-2.1.11.jar
           sig:     0429a917c82d
[12:11:02] CRASH (repeat #2)
           cause:   java.lang.NullPointerException: Cannot invoke
                    "Entity.getX()" because "e" is null
           culprit: examplemod-2.1.11.jar
           sig:     0429a917c82d

           THIS IS THE SAME CRASH AS 1 TIME(S) ALREADY.
           Restarting will reproduce it. Fix the cause above first.
```

Line numbers are stripped out of the fingerprint, so the same bug
thrown from a slightly different place still matches. One signature
seen three times is a pattern; three signatures seen once each are
three problems. That distinction is the whole point.

### Read-only, by design

It tails the log. It never writes to your world, never sends a
command, never restarts anything. The worst it can do is be wrong
about a label.

### Honest limits

- A signature is built from the cause and the mod at fault, so two
  genuinely different bugs with the same cause in the same mod will
  collide.
- **If it cannot identify a cause or a culprit, it says so and does
  not fingerprint the crash at all.** It will not tell you two
  crashes match when it could not read either of them.
- It sees what the log says. A hard lock-up that writes nothing
  looks like silence, not a crash.
- It does not restart your server, deliberately.

## Install

```bash
pip install server-sentinel
```

One module, Python 3.8+, **no dependencies** -- it imports nothing outside the standard library.

## More

**What the paid edition adds:** `guard`, which supervises the server,
restarts it when it dies, and **refuses to restart into a crash
signature it has already seen** -- so one bad mod stops being an
all-night loop that fills the disk with identical crash reports.
Plus webhook alerts and a JSON event log.

Two bugs worth knowing about, both found by feeding real logs
through end to end rather than by unit-testing the function: the
signature used to be computed at the crash marker, *before* the
trace it describes had arrived; and a crash with no readable cause
used to be hashed as the empty string, giving every unreadable crash
the same fingerprint and a confident "this is the same crash" about
a cause the tool had never identified.

Diagnostic pages for every finding: <https://jaakoby.github.io/fix/>
Source: <https://github.com/jaakoby/server-sentinel>
Full edition: <https://kaiven.gumroad.com/l/server-sentinel>

---

*Built by Jakoby Tuckta with Claude. Every claim above is something the tool does on a real log; see the repo's tests.*
