Thanks for your interest in improving ytzero! This is a personal, self-hosted project, so contributions are welcome but kept lightweight. Please read this guide before opening an issue or pull request.
- Report bugs — open an issue with steps to reproduce, what you expected, and what happened.
- Suggest features — open an issue describing the use case and why it would be useful.
- Submit code — fix a bug or implement an agreed-upon feature via a pull request (see below).
For anything non-trivial, please open an issue first so we can agree on the approach before you spend time on a PR.
app/ # Backend — Hono on Bun, TypeScript (runtime, no build step)
ui/ # Frontend — React + Vite + TypeScript
scripts/ # setup / dev / build / start helpers
data/ # Local runtime data (SQLite + image cache), gitignored
The backend is TypeScript executed directly by Bun. The frontend is built with
Vite, and in production the backend serves the static files from ui/dist.
- Bun (the only required toolchain — no Node.js needed)
- Optionally Docker, if you want to test the container build
# Install dependencies for both app/ and ui/
bun run setup
# Run backend (:3001) and frontend (:5173) together with hot reload
bun run dev- UI dev server: http://localhost:5173
- API: http://localhost:3001
You can also run the halves separately with bun run dev:app and
bun run dev:ui.
To test a production-like build locally:
bun run build # builds ui/dist
bun run start # backend serves ui/dist on :3001Run the same checks CI runs, so nothing breaks after merge:
# Type-check both packages
cd app && bunx tsc --noEmit
cd ../ui && bunx tsc --noEmit
# Build the frontend
bun run buildBoth packages use strict TypeScript — please keep the build type-clean.
The files in CANONICAL_SCHEMA_FILES in app/src/databaseMigrations.ts
describe the current schema for new installations. Any change to one must add
the next versioned entry to app/src/databaseMigrations.ts, with explicit
sqlite and postgres implementations and updated schema fingerprints. Never
add a new one-off startup migration to db.ts.
Run bun run check:database-migrations after changing the schema. The same
check is part of check:validate and CI, and rejects schema changes that do not
include migration paths for both supported database engines.
main is protected. All changes go through a pull request:
- Branch off
main(e.g.fix/live-detection,feat/playlist-import). - Make your change and keep commits focused.
- Make sure type-checks and the build pass locally.
- Open a PR against
mainwith a clear description of what and why.
Notes:
- PRs are squash-merged, so the final commit on
mainis your PR title and description. A clean per-commit history on your branch is appreciated but not required — it gets squashed anyway. - History on
mainis linear; force-pushes and direct pushes tomainare blocked.
YT Zero product releases use calendar versions in the form YYYY.MM.N, and
the Git tag is the version itself without a v prefix. The month is always
zero-padded (01 through 12), while N is a positive, non-padded release
counter that starts at 1 each month. For example, the first two releases in
August 2026 are 2026.08.1 and 2026.08.2; the first September release is
2026.09.1.
Pushing a valid release tag builds the GitHub release assets and publishes
container tags for the exact release, its month and year channels, and
latest (for example 2026.08.1, 2026.08, 2026, and latest). The
private app/package.json and ui/package.json versions are package-manager
metadata and are intentionally independent from the product release tag.
- Match the surrounding code — naming, structure, and comment density.
- Keep changes scoped; avoid unrelated refactors in the same PR.
- User-facing UI text is translated via
ui/src/i18n.tsx. When you add new copy, provide a string for every language defined there (theLanguagetype lists the currently supported locales) so no language falls back to a missing key. - Use icons from
lucide-reactrather than emoji in the UI.
By contributing, you agree that your contributions are licensed under the
project's GNU Affero General Public License v3.0 only
(AGPL-3.0-only), the same
license as the rest of the project. See the full text in LICENSE.