A Phoenix 1.8 web app for browsing and managing your GitHub starred repositories with pagination included. Sign in with GitHub OAuth, sync your stars into a local cache, and browse them with search, language filter, sort, etc. in a LiveView UI. More features are planned, but the current version is already useful for personal star management with a better browsing experience than GitHub's own starred-repos page.
Status: v0.1.0 — This is an early release. The app is functional and useful, but the UI and feature set are still evolving. Please report any bugs or feature requests via GitHub Issues, thank you :)
- GitHub OAuth sign-in — no password to manage, the user's GitHub identity and OAuth token are reused for the sync API call
- One-click sync — pull every starred repo into a local cache so the UI stays fast and works offline against your data
- Search across name, description, and language
- Filter by language with a dropdown populated from your actual stars
- Sort by recently-starred, oldest-starred, name, or repo star count
- URL-driven state — every filter, page, and per-page value is in the query string so views are shareable and browser-navigable
- Light / dark theme with system preference detection
- Streaming repo grid for fast first paint and incremental updates
- Elixir ~> 1.20
- Phoenix 1.8
- Phoenix LiveView 1.1
- Ecto with SQLite across dev, test, and production (single-user, sync-on-demand workload. SQLite handles it comfortably and keeps hosting costs near zero)
- daisyUI on top of Tailwind CSS 4
- Ueberauth +
ueberauth_githubfor OAuth - Req for HTTP requests to the GitHub API
The fastest way to run Centauri is with the prebuilt image from GitHub Container Registry.
Go to https://github.com/settings/developers and create a new OAuth app with this callback URL for now:
http://localhost:8080/auth/github/callback
Note the Client ID and generate a Client secret. You can optionally add a second callback URL when you have a public hostname.
mkdir centauri && cd centauri
curl -fsSL https://raw.githubusercontent.com/mdi48/centauri/main/docker-compose.yml > docker-compose.yml
curl -fsSL https://raw.githubusercontent.com/mdi48/centauri/main/.env.example > .envOpen .env and fill in GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET.
docker compose up -dOpen http://localhost:8080, sign in with GitHub, and click Sync.
The data lives in a Docker named volume called centauri_data (SQLite
database at /data/centauri_prod.db plus the auto-generated
/data/secret_key_base). To inspect it:
docker compose exec centauri ls -la /dataFor a public deployment, add a second callback URL to your GitHub OAuth app pointing at your real hostname:
https://your-host.example.com/auth/github/callback
Then set PHX_HOST=your-host.example.com in .env and restart:
docker compose up -dIt goes without saying that putting Centauri behind a reverse proxy (Caddy, Nginx, Traefik, etc.) is
recommended for production so you get TLS termination and a real
certificate. The app's GET /health endpoint is suitable for upstream
health checks. A more detailed reverse-proxy guide is on the roadmap.
docker compose pull
docker compose up -dMigrations run automatically on the next start (a one-shot migrate
service runs before the app service).
All runtime configuration is via environment variables, mostly set in
.env.
| Variable | Required | Default | Purpose |
|---|---|---|---|
GITHUB_CLIENT_ID |
yes | — | GitHub OAuth client id |
GITHUB_CLIENT_SECRET |
yes | — | GitHub OAuth client secret |
PHX_HOST |
no | localhost |
Public hostname, no scheme/port. Used for OAuth callback URLs and force_ssl. |
CENTAURI_HTTP_PORT |
no | 8080 |
Host port mapped to the container's port 4000. |
DATABASE_PATH |
no | /data/centauri_prod.db |
SQLite file path inside the container. |
SECRET_KEY_BASE |
no | auto | Phoenix secret key. If unset, a random one is generated and persisted to /data/secret_key_base on first run. |
PORT |
no | 4000 |
Internal HTTP port. Don't change unless you also update the port mapping. |
POOL_SIZE |
no | 10 |
Repo connection pool size. |
DEV_GITHUB_CLIENT_ID / DEV_GITHUB_CLIENT_SECRET are used by local development via mix phx.server and the dev Docker compose stack — see the sections below.
-
Install Elixir and Erlang. The
ELIXIR_VERSIONandOTP_VERSIONargs at the top ofDockerfileare the canonical versions. -
Clone and bootstrap:
git clone <this-repo> cd centauri mix setupmix setupis an alias that runsdeps.get,ecto.setup(creates and migrates the dev SQLite DB),assets.setup, andassets.build. -
Create a
.envfile in the project root with your GitHub OAuth dev credentials:cp .env.example .env # then edit .env and fill in DEV_GITHUB_CLIENT_ID / DEV_GITHUB_CLIENT_SECRETconfig/runtime.exsauto-loads.envonmix phx.serverstart. The same.envalso drives the local Docker dev stack — see "Local development (with Docker)" below. -
Start the server:
mix phx.serverOpen http://localhost:4000 and click "Continue with GitHub".
To exercise the same image the production stack uses, but built from your local checkout:
docker compose -f docker-compose.dev.yml up -d --buildThis builds the image from the local Dockerfile, tags it centauri:dev,
and starts a stack with a separate centauri_dev_data volume so it won't
clobber the production stack.
The dev stack reads DEV_GITHUB_CLIENT_ID / DEV_GITHUB_CLIENT_SECRET
from your .env (the same vars mix phx.server uses) and maps them onto
the container's GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET. If DEV_*
are unset they default to dummy, so migrations and the health endpoint
still boot for a smoke test — to exercise the full OAuth flow, fill them
in .env.
- SQLite across all environments. Dev and test use a file at the
project root (
centauri_dev.db,centauri_test.db); the Docker stack uses a file on thecentauri_datanamed volume at/data/centauri_prod.db. The adapter is pinned at compile time inlib/centauri/repo.extoEcto.Adapters.SQLite3— no environment-specific adapter switch.
All migrations in priv/repo/migrations/ are append-only. To drop a column
or table that the app no longer uses, write a new migration that drops it.
mix test
Or for a single file:
mix test test/centauri_web/live/stars_test.exs
To re-run only the previously-failed tests:
mix test --failed
mix precommit
This alias runs compile --warnings-as-errors, deps.unlock --unused,
format, and test in sequence. Run it before opening a PR.
lib/
centauri/ # domain contexts
accounts.ex # user upsert + get_user
accounts/user.ex
github.ex # thin client over the GitHub REST API
stars.ex # starred-repo queries and sync
stars/starred_repo.ex
repo.ex
application.ex # supervision tree (Repo, TaskSupervisor, Endpoint)
centauri_web/ # web layer
controllers/ # AuthController, HealthController
live/ # LoginLive, StarsLive
components/ # CoreComponents, Layouts
plugs/ # session-loading + require_auth
router.ex
endpoint.ex
config/ # per-env + runtime config
priv/repo/migrations/ # append-only DB migrations
assets/ # JS / CSS / vendor (daisyUI, heroicons)
test/ # ExUnit cases
docker-compose.yml # end-user self-hosted stack
docker-compose.dev.yml # local Docker-from-source stack
.github/workflows/ # CI for GHCR image publish
For the conventions a contributor should follow, see
AGENTS.md (architecture, auth flow, background-task
pattern, daisyUI usage, etc.).
- Fork & branch from
main. - Make your changes. Keep
mix precommitgreen. - Read
AGENTS.mdfor the project-specific conventions. - Open a PR with a clear description of the change.
Bug reports and feature requests are welcome via GitHub Issues :)
GNU Affero General Public License v3.0 — see LICENSE.