Everything you need to deploy, configure, and use GitDash.
The fastest way to get running. No OAuth App needed.
git clone https://github.com/dinhdobathi1992/gitdash.git cd gitdash cp .env.local.example .env.local # Edit .env.local: # MODE=standalone # SESSION_SECRET=$(openssl rand -hex 32) npm install npm run dev
Open http://localhost:3000 — you will be redirected to /setup to enter your PAT.
For teams sharing a single deployment. Requires a GitHub OAuth App.
# 1. Create a GitHub OAuth App: # Settings → Developer settings → OAuth Apps → New OAuth App # Callback URL: http://localhost:3000/api/auth/callback # 2. Configure .env.local MODE=organization GITHUB_CLIENT_ID=your_client_id GITHUB_CLIENT_SECRET=your_client_secret SESSION_SECRET=$(openssl rand -hex 32) # 3. Run npm install && npm run dev
SESSION_SECRET with: openssl rand -hex 32| Scope | Purpose |
|---|---|
repo | Repository list + workflow data (private repos) |
workflow | Workflow runs and job details |
read:org | Organization membership + org repo list |
read:user | User identity (avatar, name, login) |
Actions: read and Contents: read scoped to specific repos.docker run -d \ --name gitdash \ -p 3000:3000 \ -e MODE=standalone \ -e SESSION_SECRET=your_32_char_secret_here \ --restart unless-stopped \ dinhdobathi1992/gitdash:latest
docker run -d \ --name gitdash \ -p 3000:3000 \ -e MODE=organization \ -e GITHUB_CLIENT_ID=your_client_id \ -e GITHUB_CLIENT_SECRET=your_client_secret \ -e SESSION_SECRET=your_32_char_secret_here \ -e NEXT_PUBLIC_APP_URL=https://gitdash.example.com \ --restart unless-stopped \ dinhdobathi1992/gitdash:latest
Create a docker-compose.yml alongside your .env.local:
services:
gitdash:
image: dinhdobathi1992/gitdash:latest
ports:
- "3000:3000"
env_file:
- .env.local
restart: unless-stoppeddocker compose up -d # start docker compose down # stop docker compose logs -f # view logs
GitDash deploys to Vercel automatically on push to main. To deploy your own fork:
Authorization callback URL in your GitHub OAuth App to https://your-vercel-url/api/auth/callbackDATABASE_URL must be added manually in Vercel's project settings → Environment Variables → Production. It is not included in the repository.NEXT_PUBLIC_APP_URL to your public URL (e.g. https://gitdash.example.com)x-forwarded-proto header — the app uses this to enforce HTTPS redirects| Variable | Required | Description | Example |
|---|---|---|---|
SESSION_SECRET | Required | Random string ≥ 32 characters. Encrypts session cookies with AES-256-GCM. App refuses to start in production if missing or too short. | openssl rand -hex 32 |
MODE | Optional | Default: standalone. Set to organization, org, or team to enable org mode. | organization |
GITHUB_CLIENT_ID | Org mode only | GitHub OAuth App Client ID. | Ov23liXXXXXXXX |
GITHUB_CLIENT_SECRET | Org mode only | GitHub OAuth App Client Secret. Never commit this value. | a1b2c3d4e5... |
DATABASE_URL | Org mode only | PostgreSQL connection string (Neon or any Postgres). Required for alert rules, reports, and sync features. | postgresql://user:pass@host/db |
NEXT_PUBLIC_APP_URL | Optional | Public URL of the deployment. Used to build OAuth callback URLs and enforce HTTPS redirects. | https://gitdash.example.com |
NEXT_PUBLIC_APP_VERSION | Optional | App version shown in the sidebar version badge. Set automatically by the release workflow. | 2.3.0 |
.env.local — StandaloneMODE=standalone SESSION_SECRET=replace_with_openssl_rand_hex_32_output NEXT_PUBLIC_APP_URL=http://localhost:3000
.env.local — OrganizationMODE=organization GITHUB_CLIENT_ID=your_oauth_app_client_id GITHUB_CLIENT_SECRET=your_oauth_app_client_secret SESSION_SECRET=replace_with_openssl_rand_hex_32_output DATABASE_URL=postgresql://user:pass@host/db?sslmode=require NEXT_PUBLIC_APP_URL=https://gitdash.example.com
| Field | Value |
|---|---|
| Application name | GitDash |
| Homepage URL | https://your-domain.com |
| Authorization callback URL | https://your-domain.com/api/auth/callback |
| Standalone | Organization | |
|---|---|---|
| Auth method | Personal Access Token (PAT) | GitHub OAuth App |
| Login page | /setup | /login |
| Best for | Personal use, local dashboards | Teams sharing one deployment |
| OAuth App required | No | Yes |
| Database features | Not available | Available |
| Alert rules | Not available | Available |
| Reports / sync | Not available | Available |
| Cost Analytics | Own repos only | All org repos (Enhanced Billing Plan required) |
| Multi-user | No — single session | Yes — isolated sessions per user |
Change the MODE environment variable and restart the server. All session data is invalidated automatically — users will be redirected to the appropriate login page.
# Switch to org mode MODE=organization # Switch back to standalone MODE=standalone # or unset MODE entirely
/Browse all your personal and organization repositories. Fuzzy search with keyboard navigation. Switch between personal repos and any org you belong to via the sidebar.
/repos/[owner]/[repo]/workflows/[id]5-tab deep-dive into any workflow. Auto-refreshes every 30 seconds while runs are in progress.
/cost-analyticsGitHub Actions minutes and cost breakdown. Requires org mode with the Enhanced Billing Platform (Team/Enterprise). Shows per-repo, per-workflow spend.
/reportsScheduled and historical reports backed by the database. Available in organization mode only.
/alertsDefine alert rules triggered by workflow outcomes, duration thresholds, or failure streaks. Available in organization mode only.
/settingsManage your PAT (standalone) or view your OAuth session (org mode). Includes a GitHub Actions billing widget showing remaining free minutes.
| Tab | What you see |
|---|---|
| Overview | Rolling success rate, duration trend, outcome pie chart, run frequency heatmap |
| Performance | Per-job avg/p95 bar chart, stacked job waterfall per run, slowest steps table |
| Reliability | MTTR, failure streaks, flaky branch detection, re-run rate, pass/fail timeline |
| Triggers | Event breakdown, top branches, hour-of-day heatmap, day-of-week chart, actor leaderboard |
| Runs | Sortable table with commit message, PR link, run attempt count, CSV export |
Browser ──── request ────► Middleware (decrypt session cookie)
│
├─ No token? ──► Redirect to /setup or /login
│
▼ Valid session
/api/github/* routes
│
Retrieve encrypted token
from session (server-side)
│
▼
GitHub REST API
(Bearer token auth)
│
▼
JSON response
(token never sent to browser)| Layer | Mechanism |
|---|---|
| Encryption | AES-256-GCM via iron-session v8 — industry-standard authenticated encryption |
| Cookie flags | HttpOnly (no JS access), Secure (HTTPS only in production), SameSite=Lax (CSRF protection) |
| Session secret | 32+ character minimum enforced at startup in production. Generates with openssl rand -hex 32. |
| Rate limiting | /api/auth/setup: 5 attempts/min/IP (standalone). /api/auth/login: 10 attempts/min/IP (org mode) |
| Input validation | All owner, repo, org params validated against [a-zA-Z0-9_.-]{1,100} |
| HTTP headers | CSP, HSTS, X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy |
| Docker | Runs as non-root user nextjs (uid 1001). Built from node:20-alpine. |
| Logout | session.destroy() deletes the cookie immediately. POST method prevents CSRF. |
Run these in the project root to verify security claims yourself:
# PAT never in browser storage
grep -r "localStorage|sessionStorage" src/ --include="*.tsx" --include="*.ts"
# Expected: 0 matches ✓
# PAT never returned in API responses
grep -rn "return.*pat|json.*pat" src/app/api --include="*.ts"
# Expected: 0 matches ✓
# PAT never logged
grep -rn "console.*${.*pat|console.*pat)" src/ --include="*.ts" --include="*.tsx"
# Expected: 0 matches ✓
# XSS: dangerouslySetInnerHTML not used
grep -r "dangerouslySetInnerHTML" src/
# Expected: 0 matches ✓
# Check dependency vulnerabilities
npm audit --production
# Expected: 0 high/critical vulnerabilities ✓I see a blank screen or 500 error after deploying.
Check that SESSION_SECRET is set and is at least 32 characters. The app throws at startup in production if it is missing or too short. Check server logs for [startup] errors.
Cost Analytics shows a 404 error about billing.
In org mode, Cost Analytics requires the GitHub Enhanced Billing Platform (Team or Enterprise). Your org may not be on this plan. In standalone mode, only your personal billing data is available — org billing is not accessible via a PAT.
OAuth callback fails with 'state mismatch' or 'expired state'.
OAuth state tokens expire after 5 minutes. If the user takes longer to authorize, they will need to click "Sign in with GitHub" again. Also verify that NEXT_PUBLIC_APP_URL exactly matches the callback URL registered in your GitHub OAuth App.
DATABASE_URL is set but reports/alerts still don't work.
Confirm you are in MODE=organization. These features are disabled in standalone mode regardless of whether DATABASE_URL is set. Also check that the database is reachable from your deployment (Neon requires the correct SSL mode: ?sslmode=require).
SESSION_SECRET must be at least 32 characters — but I'm running locally.
This check only applies when NODE_ENV=production. In development (npm run dev) any value is accepted. For local testing you can use any string; for production, generate one with openssl rand -hex 32.
The sidebar shows 'standalone' but I set MODE=organization.
Restart the server after changing environment variables — Next.js reads env vars at startup. Also confirm the variable is set in the correct file (.env.local for local; Vercel/Docker env for production) and that there is no trailing whitespace.
How do I update to a new version?
Pull the latest Docker image: docker pull dinhdobathi1992/gitdash:latest then restart your container. For self-built deployments, pull the latest commit and rebuild.
Can I use a fine-grained PAT instead of a classic PAT?
Yes. Use a fine-grained PAT with Actions: read and Contents: read scoped to specific repositories. This gives least-privilege access. Note that fine-grained PATs cannot access organization data (read:org scope), so org-level features will not work.