Self-hosted error tracking and monitoring for developers who want control.
Beacon ingests errors from any service, groups them by root cause, and surfaces everything in a live terminal dashboard β no third-party service, no data leaving your infrastructure, no monthly bill.
Think lightweight Sentry, built for the terminal, owned by you.
Beacon_updated_WebUI_compressed.mp4 #
git clone https://github.com/Tboworst/beacon.git
cd beacon
cp .env.example .env # add your API key and Slack webhook
docker-compose up
Server is running at http://localhost:7000
.
Open the dashboard in a separate terminal:
pip install textual
python3 start_dashboard.py
git clone https://github.com/Tboworst/beacon.git
cd beacon
pip install -r requirements.txt
cp .env.example .env
python3 start_server.py # terminal 1
python3 start_dashboard.py # terminal 2
Beacon also ships a browser dashboard β same data as the TUI, served straight from the ingest server. Issues, LLM calls, alerts and deploys, with search, environment filters, resolve / reopen, and GitHub issue creation.
Build it once, then the server does the rest (requires Node.js 18+; Docker users skip this β the image builds it automatically):
cd web
npm install
npm run build
Start the server as usual and open http://localhost:7000
:
python3 start_server.py
For frontend development, run the Vite dev server alongside β it proxies /api
to the ingest server and hot-reloads:
python3 start_server.py # terminal 1
cd web && npm run dev # terminal 2 β http://localhost:5173
The TUI keeps working unchanged β both dashboards read the same beacon.db
.
macOS note: AirPlay Receiver also listens on port 7000. If
http://localhost:7000
misbehaves, usehttp://127.0.0.1:7000
.
Copy sdk/node/
into your project (or npm install beacon-monitor
once published):
const beacon = require('./beacon');
beacon.init({
endpoint: 'http://your-beacon-server:7000/ingest',
service: 'my-app',
environment: 'production',
apiKey: 'your-secret-key-here',
});
Unhandled exceptions and promise rejections are captured automatically. For caught errors:
try {
riskyOperation();
} catch (err) {
beacon.capture(err);
}
LLM call tracking:
const t0 = Date.now();
try {
const res = await openai.chat.completions.create({ model: 'gpt-4o', messages });
beacon.captureLlm({
model: 'gpt-4o',
inputTokens: res.usage.prompt_tokens,
outputTokens: res.usage.completion_tokens,
latencyMs: Date.now() - t0,
costUsd: res.usage.prompt_tokens * 0.000005 + res.usage.completion_tokens * 0.000015,
feature: 'document-summarizer',
});
} catch (err) {
beacon.captureLlm({ model: 'gpt-4o', inputTokens: 0, outputTokens: 0,
latencyMs: Date.now() - t0, costUsd: 0,
feature: 'document-summarizer', error: err });
}
Zero dependencies β uses Node's built-in http
/https
modules only.
Install in your app:
pip install requests
Copy sdk/python/beacon/
into your project, then add two lines to your entry point:
import beacon
beacon.init(
endpoint="http://your-beacon-server:7000/ingest",
service="my-app",
environment="production",
api_key="your-secret-key-here" # matches BEACON_API_KEY in .env
)
Every unhandled exception is now automatically captured. For handled exceptions:
try:
risky_operation()
except Exception as e:
beacon.capture(e)
| Variable | Description |
|---|---|
BEACON_API_KEY |
|
| Secret key required on all ingest requests. If not set, server accepts all requests (local dev mode). | |
SLACK_WEBHOOK_URL |
|
| Slack incoming webhook URL. Alerts fire when an error group crosses the threshold. |
raw errors: "NoneType has no attribute 'email'"
"NoneType has no attribute 'username'"
fingerprint: AttributeError
NoneType has no attribute <attr> β normalized
handle_request β get_current_user β find_user_by_token
result: same group. one bug. one row.
Fingerprint = hash of exception type + normalized message + function call chain. Line numbers are ignored β they change on every reformat. Function names are stable.
beacon/
βββ core/ β ingest server, storage, fingerprinting (Python β Go)
βββ dashboard/ β live TUI dashboard (Textual)
βββ web/ β web dashboard (Vite + React, served by the ingest server)
βββ sdk/
β βββ python/ β Python SDK
βββ start_server.py β python3 start_server.py
βββ start_dashboard.py β python3 start_dashboard.py
βββ docker-compose.yml
βββ requirements.txt
Any service can send errors directly over HTTP β no SDK required:
curl -X POST http://localhost:7000/ingest \
-H "Content-Type: application/json" \
-H "X-Api-Key: your-secret-key-here" \
-d '{
"timestamp": "2024-01-15T10:23:45Z",
"exception_type": "AttributeError",
"message": "NoneType object has no attribute email",
"stack_trace": [
{"function": "handle_request", "file": "/app/server.py", "line": 42},
{"function": "get_current_user", "file": "/app/auth.py", "line": 87}
]
}'
- Deploy markers β correlate errors with deploys
- Environment tagging β separate prod vs staging
- Spike detection β alert on rate of increase, not just total count
- Regression alerts β error quiet for 7 days that suddenly fires again
- Resolve / ignore groups from the TUI
- GitHub issue creation from any error group
- Node.js SDK
- Go rewrite of the core server with Redis hot path
| Layer | Tech |
|---|---|
| Ingest server | Python + Flask |
| Storage | SQLite |
| Dashboard | Python + Textual |
| Alerts | Slack webhooks |
| Containerisation | Docker |
Sentry is excellent. Beacon is for when you want:
- No data leaving your network
- No per-event pricing at scale
- A terminal-native workflow
- Something you can read, modify, and own completely
Built in public. Stars appreciated.