Deploying (Railway)
Navidiff ships as two Railway services from one repo — the app (the bun + Elysia server that also serves the SPA) and the docs (this VitePress site) — plus a managed Postgres. Both build from a Dockerfile already in the repo.
1. Postgres
Add the PostgreSQL plugin to your Railway project. It exposes a DATABASE_URL; the app reads it (or NAVIDIFF_DATABASE_URL if you set one). The schema — app tables and Better Auth's — is created automatically on first boot, so there's no migration step to run.
2. App service
Point a service at the repo's root Dockerfile (single process and /data volume). Navidiff reads Railway's injected PORT; NAVIDIFF_PORT is only a local/container fallback.
- Volume: mount one at
/data— it holdsevents.jsonl(the verdict log, which has no upstream to refetch from) and the on-demand repo clones. - Reference
DATABASE_URLfrom the Postgres plugin intoNAVIDIFF_DATABASE_URL. - Port: Navidiff binds to Railway's injected
PORT. Add the domain after the first successful deployment so Railway detects the bound port, or set the domain's target port to the value Railway assigns (currently8080for this service). - Custom domain: add yours (e.g.
app.navidiff.com).
Environment
| var | what |
|---|---|
NAVIDIFF_DATABASE_URL | reference the Postgres plugin's DATABASE_URL |
ANTHROPIC_API_KEY | the Claude Agent SDK's key (no host ~/.claude on Railway) |
GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY, GITHUB_APP_SLUG | the GitHub App that reads PRs and powers the in-app installation flow |
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET | the GitHub App's Client ID/secret for “Continue with GitHub” (below) |
BETTER_AUTH_SECRET | a random 32+ char secret — openssl rand -base64 32 |
BETTER_AUTH_URL | the app's public origin, e.g. https://app.navidiff.com |
NAVIDIFF_OWNER_LOGINS, NAVIDIFF_OWNER_EMAILS | optional explicit bootstrap owners on a fresh deployment |
3. Docs service
Add a second service pointing at the same repository and configure it as an isolated monorepo service:
- Root Directory:
/site - Railway Config File:
/site/railway.json(the config path is absolute; it does not follow the root directory)
That builds this VitePress site with its own Dockerfile; Caddy serves it on Railway's $PORT. Attach docs.navidiff.com as its documentation domain. If this service also hosts the marketing homepage, attach navidiff.com as a second custom domain. Then point the app's "Open the app" link at the app domain.
For a direct CLI deploy of just the docs directory, run this from site/:
railway up . --path-as-root --service navidiff-docs --environment productionGitHub account + App connection
On the GitHub App's settings page, set the Callback URL to:
https://app.navidiff.com/api/auth/callback/githubCopy that App's Client ID and client secret into GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET, and set GITHUB_APP_SLUG. Also set the GitHub App's Setup URL to:
https://app.navidiff.com/github/installedNow the login wall offers Continue with GitHub. Once signed in, Workspace lets the user link a password account to GitHub and install the App on exactly the account and repositories they choose.
Access model
Password accounts are always available; GitHub is an optional second sign-in method. Access is organization-based (Better Auth's org plugin):
- The first account created on a fresh deployment becomes the owner of the default organization.
NAVIDIFF_OWNER_LOGINSandNAVIDIFF_OWNER_EMAILSprovide additional explicit bootstrap owners. Everyone after the first account needs an invitation. - Owners and admins invite members by email and assign owner / admin / member roles and teams from the in-app Organization panel (bottom-left when signed in).
- A signed-in user who isn't in any org sees an invite-pending screen, not the app.
Locally, password accounts work without GitHub OAuth. Set NAVIDIFF_AUTH_DISABLED=1 only for a disposable demo that deliberately bypasses the login wall.