{"id":47592307,"url":"https://github.com/mattmezza/monlight","last_synced_at":"2026-04-11T03:17:59.653Z","repository":{"id":340237851,"uuid":"1140902183","full_name":"mattmezza/monlight","owner":"mattmezza","description":"📈 monitoring light stack - zig based","archived":false,"fork":false,"pushed_at":"2026-02-23T22:40:06.000Z","size":532,"stargazers_count":1,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-01T19:42:00.976Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://matteo.merola.co/monlight/","language":"Zig","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mattmezza.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-23T22:50:02.000Z","updated_at":"2026-02-23T22:40:10.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mattmezza/monlight","commit_stats":null,"previous_names":["mattmezza/monlight"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/mattmezza/monlight","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattmezza%2Fmonlight","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattmezza%2Fmonlight/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattmezza%2Fmonlight/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattmezza%2Fmonlight/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mattmezza","download_url":"https://codeload.github.com/mattmezza/monlight/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattmezza%2Fmonlight/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31562697,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"ssl_error","status_checked_at":"2026-04-08T14:31:17.202Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-01T17:40:19.137Z","updated_at":"2026-04-08T16:02:39.840Z","avatar_url":"https://github.com/mattmezza.png","language":"Zig","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Monlight\n\nA self-hosted, lightweight monitoring stack built with Zig and SQLite. Four independent microservices -- error tracking, log viewing, metrics collection, and a browser relay -- each under 20MB, running on less than 50MB of RAM combined.\n\n## Architecture\n\n```\n                    Browser JS SDK                     Python Client\n                  (@monlight/browser)                    (monlight)\n                         |                                  |\n                         v                                  |\n               +---------+----------+          +------------+------------+\n               | Browser Relay      |          |            |            |\n               | :5013              |          |            |            |\n               | DSN auth, CORS     |          |            |            |\n               | Source map support  |          |            |            |\n               +---------+----------+          |            |            |\n                    |          |                |            |            |\n                    v          v                v            |            v\n           +-------+-------+  |  +-------+--------+  +-----+------+\n           | Error Tracker |  +-\u003e| Metrics        |  | Log Viewer |\n           | :5010         |     | Collector :5012|  | :5011      |\n           | POST /api/    |     | POST /api/     |  | Docker log |\n           |   errors      |     |   metrics      |  | ingestion  |\n           | Email alerts  |     | Aggregation    |  | FTS5 search|\n           | Web UI        |     | Dashboard      |  | SSE tail   |\n           +-------+-------+    | Web UI         |  | Web UI     |\n                   |             +-------+--------+  +-----+------+\n                   |                     |                  |\n             [errors.db]          [metrics.db]          [logs.db]\n                   SQLite (WAL mode, zero config)\n```\n\nEach service is a single static binary with an embedded web UI. No external database, no message queue, no runtime dependencies beyond SQLite.\n\n## Quick Start\n\n### Using pre-built images (recommended)\n\n```bash\n# 1. Clone the repo (for compose file and config templates)\ngit clone https://github.com/mattmezza/monlight.git\ncd monlight\n\n# 2. Configure secrets\ncp deploy/secrets.env.example deploy/secrets.env\n# Edit deploy/secrets.env and set your API keys\n\n# 3. Start the stack\ndocker compose up -d\n```\n\n### Building from source\n\n```bash\n# Build and run all services from source\ndocker compose -f deploy/docker-compose.monitoring.yml up -d --build\n```\n\nThe services will be available at:\n\n| Service           | URL                     | Web UI                  |\n|-------------------|-------------------------|-------------------------|\n| Error Tracker     | http://localhost:5010    | http://localhost:5010/   |\n| Log Viewer        | http://localhost:5011    | http://localhost:5011/   |\n| Metrics Collector | http://localhost:5012    | http://localhost:5012/   |\n| Browser Relay     | http://localhost:5013    | --                      |\n\nVerify everything is running:\n\n```bash\ncurl http://localhost:5010/health\ncurl http://localhost:5011/health\ncurl http://localhost:5012/health\ncurl http://localhost:5013/health\n```\n\n## Features\n\n**Error Tracker** -- Capture, deduplicate, and alert on application errors.\n- Error fingerprinting and deduplication (reopen on recurrence)\n- Stores last 5 occurrences per error with full request context\n- SMTP email alerts on new errors\n- Automatic retention cleanup for resolved errors\n- Web UI with filtering by project, environment, and resolution status\n\n**Log Viewer** -- Aggregate and search Docker container logs.\n- Docker JSON log file ingestion with cursor tracking (no duplicates on restart)\n- Multiline log reassembly (Python tracebacks become single entries)\n- FTS5 full-text search across all log messages\n- SSE live tail with container and level filtering\n- Ring buffer cleanup to cap storage at a configurable limit\n\n**Metrics Collector** -- Ingest, aggregate, and visualize application metrics.\n- Batch metric ingestion (counter, histogram, gauge types)\n- Automatic minute and hourly aggregation with percentile computation (p50/p95/p99)\n- Tiered retention (raw: 1h, minute: 24h, hourly: 30d)\n- Dashboard endpoint with request rate, latency, and error rate timeseries\n- Web UI with uPlot charts\n\n**Browser Relay** -- Browser-facing ingestion proxy for the JS SDK.\n- DSN key authentication (no server API keys exposed to the browser)\n- CORS handling with per-project origin validation\n- Source map upload and stack trace deobfuscation\n- Forwards errors and metrics to the backend services\n\n**JavaScript SDK** (`@monlight/browser`) -- Lightweight browser monitoring.\n- Automatic error capture (unhandled errors + promise rejections)\n- Web Vitals collection (LCP, FID, CLS, INP, TTFB)\n- Network request monitoring (fetch/XHR timing and errors)\n- Under 5KB gzipped\n\n**Python Client** (`monlight`) -- Instrument your Python app with a single function call.\n- Async and sync error reporting with PII filtering\n- Buffered metrics with background flush\n- FastAPI middleware and exception handler\n- `setup_monlight()` one-liner for full integration\n\n## Client SDKs\n\n### Python\n\n```bash\npip install monlight[fastapi]\n```\n\n```python\nfrom fastapi import FastAPI\nfrom monlight.integrations.fastapi import setup_monlight\n\napp = FastAPI()\n\nsetup_monlight(\n    app,\n    error_tracker_url=\"http://localhost:5010\",\n    metrics_collector_url=\"http://localhost:5012\",\n    api_key=\"your-api-key\",\n    project=\"my-app\",\n    environment=\"production\",\n)\n```\n\nOr use the clients directly:\n\n```python\nfrom monlight import ErrorClient, MetricsClient\n\n# Error reporting\nerror_client = ErrorClient(\n    base_url=\"http://localhost:5010\",\n    api_key=\"your-api-key\",\n    project=\"my-app\",\n    environment=\"production\",\n)\n\ntry:\n    risky_operation()\nexcept Exception as e:\n    error_client.report_error_sync(e)\n\n# Metrics\nmetrics = MetricsClient(base_url=\"http://localhost:5012\", api_key=\"your-api-key\")\nmetrics.start()\n\nmetrics.counter(\"user_signups\", labels={\"plan\": \"pro\"})\nmetrics.histogram(\"payment_duration_seconds\", value=0.342)\nmetrics.gauge(\"active_connections\", value=42)\n\nmetrics.shutdown()  # flush remaining on app shutdown\n```\n\n### JavaScript (Browser)\n\n```bash\nnpm install @monlight/browser\n```\n\n```html\n\u003cscript type=\"module\"\u003e\n  import { Monlight } from '@monlight/browser';\n\n  const monitor = new Monlight({\n    dsn: 'https://your-key@your-domain.com/browser-relay',\n  });\n\u003c/script\u003e\n```\n\n## API Reference\n\nAll endpoints require an `X-API-Key` header unless noted otherwise.\n\n### Error Tracker (`:5010`)\n\n| Method | Endpoint                     | Description                |\n|--------|------------------------------|----------------------------|\n| POST   | `/api/errors`                | Report an error            |\n| GET    | `/api/errors`                | List errors                |\n| GET    | `/api/errors/{id}`           | Get error details          |\n| POST   | `/api/errors/{id}/resolve`   | Mark error as resolved     |\n| GET    | `/api/projects`              | List known projects        |\n| GET    | `/health`                    | Health check (no auth)     |\n\n### Log Viewer (`:5011`)\n\n| Method | Endpoint              | Description                          |\n|--------|-----------------------|--------------------------------------|\n| GET    | `/api/logs`           | Query logs (filter, search, paginate)|\n| GET    | `/api/logs/tail`      | SSE live tail stream                 |\n| GET    | `/api/containers`     | List containers with log counts      |\n| GET    | `/api/stats`          | Log statistics                       |\n| GET    | `/health`             | Health check (no auth)               |\n\n**Query parameters for `/api/logs`:** `container`, `level`, `search` (FTS5), `since`, `until`, `limit` (default 100, max 500), `offset`\n\n### Metrics Collector (`:5012`)\n\n| Method | Endpoint              | Description                          |\n|--------|-----------------------|--------------------------------------|\n| POST   | `/api/metrics`        | Ingest a batch of metrics            |\n| GET    | `/api/metrics`        | Query metric timeseries              |\n| GET    | `/api/metrics/names`  | List known metric names and types    |\n| GET    | `/api/dashboard`      | Pre-computed dashboard data          |\n| GET    | `/health`             | Health check (no auth)               |\n\n**Query parameters for `/api/metrics`:** `name` (required), `period` (1h/24h/7d/30d), `resolution` (minute/hour/auto), `labels` (format: `key:value,key2:value2`)\n\n### Browser Relay (`:5013`)\n\n| Method | Endpoint              | Description                          |\n|--------|-----------------------|--------------------------------------|\n| POST   | `/api/errors`         | Ingest browser errors (DSN auth)     |\n| POST   | `/api/metrics`        | Ingest browser metrics (DSN auth)    |\n| POST   | `/api/sourcemaps`     | Upload source maps (admin auth)      |\n| GET    | `/api/dsn-keys`       | List DSN keys (admin auth)           |\n| POST   | `/api/dsn-keys`       | Create DSN key (admin auth)          |\n| GET    | `/health`             | Health check (no auth)               |\n\n## Environment Variables\n\n### Error Tracker\n\n| Variable             | Required | Default              | Description                          |\n|----------------------|----------|----------------------|--------------------------------------|\n| `API_KEY`            | Yes      |                      | API key for authentication           |\n| `DATABASE_PATH`      | No       | `./data/errors.db`   | SQLite database path                 |\n| `SMTP_HOST`          | No       |                      | SMTP server host for email alerts    |\n| `SMTP_PORT`          | No       | `25`                 | SMTP server port                     |\n| `SMTP_USERNAME`      | No       |                      | SMTP username for authentication     |\n| `SMTP_PASSWORD`      | No       |                      | SMTP password for authentication     |\n| `SMTP_FROM`          | No       | `errors@example.com` | Sender address for alert emails      |\n| `ALERT_EMAILS`       | No       |                      | Comma-separated recipient addresses  |\n| `RETENTION_DAYS`     | No       | `90`                 | Days to keep resolved errors         |\n| `BASE_URL`           | No       | `http://localhost:5010` | Base URL for links in alert emails|\n| `LOG_LEVEL`          | No       | `INFO`               | Logging verbosity                    |\n\n### Log Viewer\n\n| Variable         | Required | Default                         | Description                          |\n|------------------|----------|---------------------------------|--------------------------------------|\n| `API_KEY`        | Yes      |                                 | API key for authentication           |\n| `CONTAINERS`     | Yes      |                                 | Comma-separated container names      |\n| `DATABASE_PATH`  | No       | `./data/logs.db`                | SQLite database path                 |\n| `LOG_SOURCES`    | No       | `/var/lib/docker/containers`    | Docker log directory                 |\n| `MAX_ENTRIES`    | No       | `100000`                        | Max log entries to retain            |\n| `POLL_INTERVAL`  | No       | `2`                             | Seconds between log file polls       |\n| `TAIL_BUFFER`    | No       | `65536`                         | SSE tail buffer size                 |\n| `LOG_LEVEL`      | No       | `INFO`                          | Logging verbosity                    |\n\n### Metrics Collector\n\n| Variable              | Required | Default            | Description                            |\n|-----------------------|----------|--------------------|----------------------------------------|\n| `API_KEY`             | Yes      |                    | API key for authentication             |\n| `DATABASE_PATH`       | No       | `./data/metrics.db`| SQLite database path                   |\n| `RETENTION_RAW`       | No       | `3600`             | Seconds to keep raw metrics            |\n| `RETENTION_MINUTE`    | No       | `86400`            | Seconds to keep minute aggregates      |\n| `RETENTION_HOURLY`    | No       | `2592000`          | Seconds to keep hourly aggregates      |\n| `AGGREGATION_INTERVAL`| No       | `60`               | Seconds between aggregation runs       |\n| `LOG_LEVEL`           | No       | `INFO`             | Logging verbosity                      |\n\n### Browser Relay\n\n| Variable                  | Required | Default  | Description                                |\n|---------------------------|----------|----------|--------------------------------------------|\n| `ADMIN_API_KEY`           | Yes      |          | Admin API key for DSN key management       |\n| `ERROR_TRACKER_URL`       | Yes      |          | Internal URL of the error tracker service  |\n| `ERROR_TRACKER_API_KEY`   | Yes      |          | API key for the error tracker              |\n| `METRICS_COLLECTOR_URL`   | Yes      |          | Internal URL of the metrics collector      |\n| `METRICS_COLLECTOR_API_KEY`| Yes     |          | API key for the metrics collector          |\n| `CORS_ORIGINS`            | No       |          | Comma-separated allowed origins            |\n| `DATABASE_PATH`           | No       | `./data/browser-relay.db` | SQLite database path          |\n| `LOG_LEVEL`               | No       | `INFO`   | Logging verbosity                          |\n\n## Operations\n\n### Backups\n\n```bash\n# SQLite .backup for WAL-safe snapshots, 7-day retention\nbash deploy/backup.sh\n```\n\n### Upgrades\n\n```bash\n# Pull latest, rebuild, rolling restart with health checks\nbash deploy/upgrade.sh\n\n# Skip git pull (rebuild from local code)\nbash deploy/upgrade.sh --no-pull\n\n# Upgrade a single service\nbash deploy/upgrade.sh error-tracker\n```\n\nThe upgrade script tags current images as `:rollback` before rebuilding, so you can revert if something goes wrong.\n\n### Smoke Tests\n\n```bash\n# Run end-to-end tests against all services\nbash deploy/smoke-test.sh\n```\n\n## Releasing New Versions\n\nEach component is released independently. The `Makefile` automates the entire flow: bumping version files, committing, tagging, and pushing. CI then builds, publishes, and creates a GitHub Release.\n\n```bash\n# Show current versions of all components\nmake versions\n\n# Release a single service (Docker image to GHCR)\nmake release-error-tracker V=0.2.0\nmake release-log-viewer V=0.2.0\nmake release-metrics-collector V=0.2.0\nmake release-browser-relay V=0.2.0\n\n# Release all 4 Docker services at the same version\nmake release-services V=0.2.0\n\n# Release the Python client to PyPI\nmake release-python V=0.2.0\n\n# Release the JS SDK to npm\nmake release-js V=0.2.0\n\n# Release everything at once\nmake release-all V=0.2.0\n```\n\nEach target validates semver, checks for a clean working tree, updates the version in the right files, commits, tags, and pushes. CI takes over from there.\n\n| Component | Tag | Publishes to |\n|---|---|---|\n| error-tracker | `error-tracker-v*` | `ghcr.io/mattmezza/monlight/error-tracker` |\n| log-viewer | `log-viewer-v*` | `ghcr.io/mattmezza/monlight/log-viewer` |\n| metrics-collector | `metrics-collector-v*` | `ghcr.io/mattmezza/monlight/metrics-collector` |\n| browser-relay | `browser-relay-v*` | `ghcr.io/mattmezza/monlight/browser-relay` |\n| Python client | `python-v*` | [PyPI](https://pypi.org/project/monlight/) |\n| JS SDK | `js-v*` | [npm](https://www.npmjs.com/package/@monlight/browser) |\n\n## Development\n\n### Building from source\n\nEach Zig service can be built independently. Requires [Zig 0.13.0](https://ziglang.org/download/).\n\n```bash\n# Build and run a service\ncd error-tracker\nzig build\n./zig-out/bin/error-tracker\n\n# Run tests\nzig build test\n```\n\n### Python client development\n\n```bash\ncd clients/python\npip install -e \".[dev]\"\npytest\n```\n\n### JS SDK development\n\n```bash\ncd clients/js\nnpm install\nnpm test\nnpm run build\n```\n\n### Docker images\n\n```bash\n# Build a single service\ndocker build -t monlight/error-tracker -f error-tracker/Dockerfile .\n\n# Build all via compose\ndocker compose -f deploy/docker-compose.monitoring.yml build\n```\n\nImages are multi-stage Alpine builds, each under 20MB.\n\n## Project Structure\n\n```\nmonlight/\n├── error-tracker/       # Error tracking service (Zig)\n├── log-viewer/          # Log aggregation service (Zig)\n├── metrics-collector/   # Metrics collection service (Zig)\n├── browser-relay/       # Browser ingestion proxy (Zig)\n├── shared/              # Shared Zig modules (sqlite, auth, rate limiting, config)\n├── clients/\n│   ├── js/              # @monlight/browser - JS SDK (TypeScript)\n│   └── python/          # monlight - Python client library\n├── deploy/              # Docker Compose, backup, upgrade, and smoke test scripts\n├── docker-compose.yml   # Pre-built images from GHCR (for users)\n└── .github/workflows/   # CI/CD pipelines\n```\n\n## License\n\nMIT. See [LICENSE](LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmattmezza%2Fmonlight","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmattmezza%2Fmonlight","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmattmezza%2Fmonlight/lists"}