Observability that outlives the process it watches.
A 1.5 kB core, zero runtime dependencies, and a sidecar that runs outside your application — so a SIGKILL, a kernel OOM or a blocked event loop still gets reported. Self-host the whole stack on a free tier.
The process that wrote this was not the process that died.
Three things an in-process SDK can never tell you.
SIGKILL and kernel OOM
The kernel killed the process without warning. To report that, your SDK would have to still be running. It isn't.
A blocked event loop
To report that the loop is blocked, you would need the very loop that is blocked. The app is alive and answering nothing.
A process that never started
It failed before your instrumentation ever ran. There was nobody there to catch it.
MoeLog reports all three, because the reporter does not live inside your app.
Your app writes to a socket and forgets about it.
No network in your process.
The SDK validates, pushes to a pre-allocated ring buffer and returns. Serialising, compressing and sending happen somewhere else.
It survives the app.
Launched detached, it outlives the process it watches. It reads the crash markers off disk and classifies the death: fatal, crashed, exited_error, or a clean goodbye.
It fits in a free tier.
No Kafka. No ClickHouse. No four-container compose file. A Node process and a Postgres database.
Six ways a process dies, each with its own signal.
| TYPE | WHAT HAPPENED | SEVERITY | SOURCE |
|---|---|---|---|
| app.fatal | Uncaught exception. The app died of an error, and the full stack came with it | severe | in-process |
| app.crashed | Abrupt death with no goodbye: SIGKILL, kernel OOM or a runtime panic | severe | sidecar only |
| app.blocked | The heartbeat stopped. The app is still alive and is answering nothing | degraded | sidecar only |
| app.recovered | The heartbeat came back after a block | recovered | sidecar only |
| app.exited_error | Terminated with a non-zero exit code | error | sidecar |
| app.failed_to_start | It never came up at all | severe | sidecar only |
blocked and recovered usually arrive as a pair. The dashboard shows them joined. Click any row to test-inject signal
Every package has a byte budget, and CI enforces it.
| PACKAGE | WHAT IT DOES | GZIP | STATUS |
|---|---|---|---|
| @moecorp/moelog-core | Ring buffer, scope, sampling, hooks, MLWP | 4.0 kB | shipped |
| @moecorp/moelog-sidecar | Protocol, transport, daemon and CLI | 3.9+4.5 | shipped |
| @moecorp/moelog-node | What Node does differently | 0.95 kB | shipped |
| @moecorp/moelog-bun | What Bun does differently | 1.3 kB | shipped |
| @moecorp/moelog-browser | onerror, unhandledrejection, sendBeacon | ≤ 3 kB | planned |
| @moecorp/moelog-edge | fetch transport with waitUntil | ≤ 2 kB | planned |
| @moecorp/moelog-react | Error boundary and useMoeLog | ≤ 1 kB | planned |
| @moecorp/moelog-next | App Router wrappers, route instrumentation | ≤ 2 kB | planned |
| moelog.min.js | Async loader with a pre-hydration queue | ≤ 600 B | planned |
A feature that does not fit does not ship. It becomes an optional package.
Six rules the code is held to.
Budget before features.
Every package has a byte limit checked in CI.
Zero runtime dependencies.
Not one, in any published package, ever.
The main thread is sacred.
Capture is validate plus push. Nothing else.
Fail silently.
No throw ever escapes the SDK. If MoeLog breaks, your app never finds out.
No PII by default.
Nothing identifiable is collected unless you declare it explicitly.
The protocol is the product.
The spec is the reference, not the JavaScript implementation.
Clone it, point it at Postgres, deploy it.
git clone https://github.com/Moe-Corp/MoeLog.server DATABASE_URL=postgres://… bun run migrate vercel deploy # or fly, railway, a VPS, your laptop Umami did this for analytics. MoeLog does it for logs and errors. If you can run a Node process and a Postgres database, you can run your own observability stack, and nobody bills you per event.
Where it stands.
| CAPABILITY | MoeLog | Sentry | SigNoz |
|---|---|---|---|
| Browser SDK, gzip | 1.5–3 kB | 25–90 kB | n/a |
| Reports SIGKILL and OOM | yes | no | no |
| Self-host requirements | Node + Postgres | many services | ClickHouse + 4 containers |
| License | AGPL-3.0 | BSL / FSL | Apache-2.0 |
It is alpha, and here is exactly where it is.
MoeLog is one person competing with Sentry. What makes it different is not the design budget — it is that it sees things the others cannot.
Node and Bun SDKs · the sidecar and all seven failure modes · ingest API · live dashboard · alerts · issue grouping by fingerprint · SQLite
browser and edge packages · source-map upload CLI · read authentication · Postgres · the frozen MLWP v1 spec · ports to Python, Go and Rust