Below is everything you need to get your development environment running and submit a good pull request.
- Python 3.12+
- Bun — bun.sh
- FFmpeg — available on your
PATH - Docker (optional, for testing the production build locally)
cd backend
pip install -r requirements.txt
uvicorn main:app --reload # Starts on http://localhost:8000cd frontend
bun install
bun run dev # Starts on http://localhost:5173The Vite dev server proxies /api and /health requests to the backend automatically — no extra configuration needed.
chronicle/
├── backend/
│ ├── routers/ # FastAPI route handlers
│ ├── schemas/ # Pydantic request/response schemas
│ └── models/ # SQLAlchemy ORM models
└── frontend/
└── src/
├── components/ # Vue components
├── views/ # Page-level components (vue-router)
└── lib/ # Shared utilities and API client
- Branch off
masterfor all changes - Use a short, descriptive branch name:
fix/rtsp-timeout,feat/camera-grid,docs/contributing
This project follows Conventional Commits:
feat: add pause button to timelapse card
fix: prevent duplicate capture jobs on restart
docs: update quick start guide
style: align storage overview table columns
refactor: extract ffmpeg helpers into separate module
chore: bump reka-ui to 2.1.0
Backend:
- Follow existing patterns — routers stay thin, business logic lives in service functions
- Keep Pydantic schemas in
schemas/and ORM models inmodels/
Frontend:
- Vue 3 Composition API (
<script setup>) throughout — no Options API - Tailwind CSS v4 for styling; use shadcn-vue components where possible
- Keep components focused — if a component is getting large, split it
We use Alembic for schema migrations. All commands should be run from the backend/ directory.
Apply all pending migrations (run this after pulling changes that include new migrations):
cd backend
alembic upgrade headCreate a new migration after changing a model in models/:
alembic revision --autogenerate -m "describe your change here"Review the generated file in migrations/versions/ before committing — autogenerate is not always perfect and may need manual adjustment.
Other useful commands:
alembic current # Show the current revision of the database
alembic history # List all revisions
alembic downgrade -1 # Roll back one revisionNote: In development, migrations run automatically when the backend starts (
uvicorn main:app --reload), so you only need to runalembic upgrade headmanually if you want to apply migrations without restarting the server.
CI publishes pre-built images to GHCR on every push to master, so the default docker-compose.yml pulls those. To test the Docker build from your local source, use the build override file:
# Build and start both containers from local source (GIT_HASH will show as "unknown")
docker compose -f docker-compose.yml -f docker-compose.build.yml up --buildTo also bake in the real commit hash (useful for testing the version footnote):
docker compose -f docker-compose.yml -f docker-compose.build.yml build --build-arg GIT_HASH=$(git rev-parse HEAD)
docker compose -f docker-compose.yml -f docker-compose.build.yml upNote: The
$(git rev-parse HEAD)substitution requires a Unix shell (bash/zsh). On Windows, run this from Git Bash or WSL.
- Fork the repo and create a branch from
master - Make your changes and verify they work end-to-end
- Test both the dev setup and, for significant changes, the Docker build
- Open a PR with a clear title and description:
- What changed and why
- Any relevant screenshots or recordings for UI changes
- Notes on anything that might need review
Pull requests should be focused — one feature or fix per PR where possible. Large, unrelated changes are harder to review and slower to merge.
When filing a bug report, please include:
- A clear description of the problem and steps to reproduce it
- What you expected to happen vs. what actually happened
- Your setup: OS, Docker version (if applicable), camera type (RTSP/USB)
- Relevant logs from
docker compose logsor the terminal output
Feature requests are welcome too — open an issue describing the use case and why it would be a good fit for Chronicle.