{
  "markdown": "# Baltimore Bird\n\nWeb-based platform for exploring and reporting on automotive time series data (MF4 / CAN bus).\n\n[![Codacy Badge](https://app.codacy.com/project/badge/Grade/9761719c527d46cea247a9c0852a1f35)](https://app.codacy.com/gh/Vulriss/baltimorebird/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)\n[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-7a60f4.svg)](https://www.gnu.org/licenses/gpl-3.0)\n[![Python 3.10+](https://img.shields.io/badge/python-3.10+-7a60f4.svg)](https://www.python.org/downloads/)\n[![Vite](https://img.shields.io/badge/Vite-6.0-646CFF.svg)](https://vitejs.dev/)\n\n![Baltimore Bird Interface](docs/screenshots/gui-overview.png)\n\n**Live version:** [baltimorebird.cloud](https://baltimorebird.cloud)\n\n## Why this exists\n\nExploring CAN logs usually means expensive proprietary tooling, or a pile of one-off\nPython scripts that nobody maintains. Baltimore Bird sits in between: drop an MF4 file\n(with an optional DBC for decoding) in the browser, get every channel listed in\nmilliseconds, and plot millions of points interactively. Signals are loaded lazily and\ndownsampled server-side with LTTB (Numba-accelerated), so even multi-gigabyte\nrecordings with thousands of channels stay responsive.\n\n## Features\n\n- **Interactive EDA** - drag-and-drop signal exploration, synchronized cursors,\n  boolean zone highlighting, categorical signal rendering\n- **Dashboard** - block-based editor to build reusable report templates\n- **Reports** - browse and export completed analyses\n- **Scripts** - Python editor with sandboxed execution\n- **Conversion** - MF4 to CSV, MF4 concatenation\n- **Multi-user** - accounts, roles, per-user storage with quotas\n\n## Getting started\n\nYou need Python 3.10+ and a JavaScript runtime. I use [bun](https://bun.com/) for the\nfrontend, but node/npm works the same way.\n\n### Backend\n\n```bash\ncd src/backend\n\npython -m venv venv\nsource venv/bin/activate        # Windows: venv\\Scripts\\activate\npip install -r requirements.txt\n\ncp .env.example .env            # then fill in the values\npython server.py                # http://localhost:5000\n```\n\nThe server boots with two demo sources (a real OBD2 MF4 recording and a synthetic\ndataset), so you can poke around before uploading anything.\n\n### Frontend\n\n```bash\ncd src/frontend\n\nbun install\nbun run dev                     # http://localhost:5173, proxies /api to :5000\n```\n\nFor a production bundle, `bun run build` generates `dist/` with hashed assets and the\n`views/` and `components/` folders copied in.\n\n### Smoke test\n\nWith the backend running, this exercises the whole critical path (auth, uploads,\nlazy sessions, access control) in about a minute:\n\n```bash\npip install -r tests/requirements-dev.txt\npython tests/smoke_test.py\n```\n\nRun it before any deployment. It also accepts `--base-url` to target a remote server.\n\n## Configuration\n\nEverything lives in `src/backend/.env` (see `.env.example`). The two values that\nmatter in production:\n\n```env\nAUTH_SECRET_KEY=   # python -c \"import secrets; print(secrets.token_hex(32))\"\nCORS_ORIGINS=https://your-domain.com\n```\n\nWithout `AUTH_SECRET_KEY`, the server generates a temporary key at startup and tells\nyou so - fine for development, not for production.\n\n## Deployment\n\nThe production stack is deliberately boring: nginx serves the Vite build and proxies\n`/api/` to gunicorn, managed by systemd.\n\n```ini\n# /etc/systemd/system/baltimorebird.service\n[Unit]\nDescription=Baltimore Bird API\nAfter=network.target\n\n[Service]\nWorkingDirectory=/var/www/baltimorebird/src/backend\nExecStart=/var/www/baltimorebird/src/backend/venv/bin/gunicorn \\\n    -w 1 --threads 8 \\\n    --access-logfile - --error-logfile - \\\n    -b 127.0.0.1:5000 server:app\nRestart=always\n\n[Install]\nWantedBy=multi-user.target\n```\n\nKeep a single worker: conversion tasks and EDA sessions live in process memory, so\nmultiple workers would not see each other's state. Threads handle the concurrency.\nThe access/error log flags route everything to journald (`journalctl -u baltimorebird -f`).\n\n```nginx\nserver {\n    listen 443 ssl http2;\n    server_name your-domain.com;\n\n    root /var/www/baltimorebird/src/frontend/dist;\n    index index.html;\n\n    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;\n    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;\n\n    client_max_body_size 1500M;    # MF4 uploads\n\n    location /assets/ {\n        expires 1y;\n        add_header Cache-Control \"public, immutable\";\n    }\n\n    location / {\n        try_files $uri $uri/ /index.html;\n    }\n\n    location /api/ {\n        proxy_pass http://127.0.0.1:5000;\n        proxy_http_version 1.1;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n        proxy_read_timeout 300s;\n        proxy_request_buffering off;\n    }\n}\n```\n\nDeploying an update comes down to:\n\n```bash\ncd src/frontend && bun run build\nsudo systemctl restart baltimorebird\nsudo systemctl reload nginx\npython tests/smoke_test.py --base-url https://your-domain.com\n```\n\n## Project notes\n\nNA (for now)\n\n## Contributing\n\nContributions are welcome - see [CONTRIBUTING.md](CONTRIBUTING.md), or open an issue\nfor bugs and feature requests.\n\n## License\n\nGNU General Public License v3.0 - see [LICENSE](LICENSE).\n\n## Author\n\nGeoffrey DOMERGUE",
  "bytes": 5431,
  "sha": "bd29f1969eab4d0e59b5def6fd63763d1b44993e871d66fcf92ca59770020769",
  "repo_slug": "vulriss/baltimorebird",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/okf_vulriss_baltimorebird_okf_index_md_428107ca/readme"
}