The GitBrag web interface provides a browser-based way to view GitHub contribution reports. Users can authenticate with GitHub OAuth, generate reports, and share them via URL.
- GitHub OAuth Authentication: Secure login using GitHub OAuth with minimal permissions
- Public Data Only: Only displays publicly accessible GitHub data
- Report Generation: View pull request contributions by time period
- Period Filtering: Filter reports by 1 year, 2 years, or all time
- Repository Breakdown: See contributions organized by repository
- Cached Reports: Reports are cached for performance, with automatic staleness detection
- Responsive Design: Mobile-first design with dark mode support
docker compose upThis starts both the web server and Redis cache.
- Install dependencies:
uv sync- Start Redis:
docker run -p 6379:6379 redis:alpine- Configure environment variables (
.env):
# GitHub OAuth
GITHUB_APP_CLIENT_ID=your_client_id
GITHUB_APP_CLIENT_SECRET=your_client_secret
# Session Security
SESSION_SECRET_KEY=your-secret-key-at-least-32-chars
# Web Settings
OAUTH_CALLBACK_URL=http://localhost/auth/callback
REQUIRE_HTTPS=false
OAUTH_SCOPES=read:user- Start the web server:
uvicorn gitbrag.www:app --host 0.0.0.0 --port 80GITHUB_APP_CLIENT_ID: GitHub OAuth app client IDGITHUB_APP_CLIENT_SECRET: GitHub OAuth app client secretSESSION_SECRET_KEY: Secret key for session encryption (32+ characters)
OAUTH_CALLBACK_URL: OAuth callback URL (default:http://localhost/auth/callback)REQUIRE_HTTPS: Enforce HTTPS for secure cookies (default:false)OAUTH_SCOPES: OAuth permission scopes (default:read:user)SESSION_MAX_AGE: Session lifetime in seconds (default:86400/ 24 hours)REPORT_CACHE_STALE_AGE: Age when cached reports are considered stale (default:3600/ 1 hour)
- Go to GitHub Settings → Developer settings → OAuth Apps
- Click "New OAuth App"
- Fill in:
- Application name: "GitBrag"
- Homepage URL: Your domain (e.g.,
http://localhost) - Authorization callback URL:
http://localhost/auth/callback
- Save and copy the Client ID and Client Secret
- Sessions are stored server-side in Redis
- Session cookies are HttpOnly and SameSite=Lax
- OAuth tokens are encrypted using Fernet symmetric encryption
- Sessions expire after 24 hours (configurable)
GitBrag proactively validates GitHub tokens to ensure accurate authentication state and prevent cascading failures:
Token Validation:
- Every authenticated web request validates the token with GitHub's
/userendpoint - Invalid or expired tokens trigger automatic session invalidation
- Users receive clear error messages prompting re-authentication
- Background jobs validate tokens before scheduling to fail fast
Benefits:
- Accurate State: No false "logged in" state with expired tokens
- Better UX: Clear error messages instead of confusing failures
- Resource Efficiency: Prevents wasted background jobs with invalid tokens
- System Reliability: Reduces cascading failures from expired tokens
Implementation Details:
# In get_authenticated_github_client() dependency:
async with client:
is_valid = await client.validate_token()
if not is_valid:
invalidate_session(request, reason="token validation failed")
raise HTTPException(401, detail="Your session has expired. Please log in again.")See GitHub API Integration for technical details.
- Reports are cached in Redis with period-based keys
- Cache keys include username, period, and feature flags (like star increase)
- Cached reports include metadata (creation time, creator)
- Stale reports (older than
REPORT_CACHE_STALE_AGE, default 24h) trigger background regeneration for authenticated users - Unauthenticated users see cached reports but cannot trigger regeneration
The web interface uses FastAPI BackgroundTasks for asynchronous report generation, providing instant page loads while reports generate in the background.
Key Features:
- Instant Response: Serve cached reports immediately when available
- Background Refresh: Stale reports trigger background regeneration automatically
- Task Deduplication: Prevents multiple simultaneous generation of the same report
- Per-User Rate Limiting: Only one report generates at a time per GitHub username
- Visual Feedback: Spinner and auto-refresh during generation
Request Flow:
- Authenticated + Fresh Cache (<24h): Serve immediately
- Authenticated + Stale Cache (≥24h): Serve with "updating" notice + background refresh
- Authenticated + No Cache: Show "generating" message + background task
- Unauthenticated: Serve cache if available, show "Login to Refresh" if stale
Task Tracking (Redis-based):
- Task keys:
task:report:{username}:{period}:{params_hash} - User keys:
task:user:{username}:active(per-user rate limiting) - TTL: 300s (auto-cleanup for hung tasks)
- Max tasks per user: 1 (configurable via
MAX_REPORTED_USER_CONCURRENT_TASKS)
See Cache Documentation for detailed caching patterns.
- User clicks "Login with GitHub"
- App redirects to GitHub OAuth with CSRF state token
- GitHub redirects back to callback with authorization code
- App exchanges code for access token
- Token is encrypted and stored in session
- User is redirected to original page
GitHub usernames are case-insensitive, but GitBrag normalizes them to lowercase for consistent caching and URL handling:
- Cache Keys: All cache keys use lowercase usernames to prevent duplicate entries
- URL Redirects: URLs with uppercase usernames automatically redirect (301 Permanent) to lowercase
- Query Preservation: Redirects preserve all query parameters (e.g.,
?period=2_years) - SEO Benefit: Canonical lowercase URLs prevent duplicate content issues
Example: /user/github/TEDIVM → 301 Redirect → /user/github/tedivm
- User visits
/user/github/{username}?period={period} - If username contains uppercase, 301 redirect to lowercase URL
- System checks for cached report (using lowercase username)
- If cache is stale and user is authenticated, regenerate
- If no cache and user is not authenticated, show 404
- Generate report by collecting PRs from GitHub API
- Store report in cache with metadata
- Render report template with data
GET /: Home page with login optionGET /auth/login: Initiate GitHub OAuth flowGET /auth/callback: Handle OAuth callbackGET /auth/logout: Log out and clear sessionGET /user/github/{username}: View user's contribution report (cached or generate)
/user/github/{username}?period={period}: Filter by time period (1_year,2_years,all_time)/user/github/{username}?force=true: Force regenerate report (requires authentication)/auth/login?return_to={url}: Redirect to URL after login
- Only requests
read:userscope - No write permissions
- No access to private repositories
- No organization access
- OAuth flow uses state parameter for CSRF protection
- State token stored in session and validated on callback
- OAuth tokens are encrypted at rest
- Encryption uses Fernet symmetric encryption with PBKDF2 key derivation
- 100,000 PBKDF2 iterations for key strengthening
- Session cookies are HttpOnly to prevent XSS
- Enable
REQUIRE_HTTPS=truein production - Forces secure cookies and HTTPS-only OAuth callback
- Shared header and navigation
- Conditional login/logout based on authentication status
- Footer with links
- Landing page with login button
- Feature list
- Authenticated user message
- Report header with username and date range
- Period selector (1 year, 2 years, all time)
- Summary card with statistics
- Repository sections with PR tables
- Cache status indicator
- Generic error page for 404, 401, 429, 500
- Contextual error messages
- Optional login button
- Mobile-first responsive design
- Dark mode support via
prefers-color-scheme - CSS custom properties for theming
- Print-friendly styles
- Status badges for PR states (merged, open, closed)
404 Not Found: Page doesn't exist401 Unauthorized: Authentication required429 Rate Limited: Too many requests500 Internal Server Error: Server error400 Bad Request: Invalid parameters
Run web interface tests:
make test tests/test_www.pyStart in development mode with auto-reload:
uvicorn gitbrag.www:app --reload --host 0.0.0.0 --port 80Enable debug logging:
export LOG_LEVEL=DEBUGView container logs:
docker compose logs -f www- Reduces GitHub API calls
- Faster page loads for cached reports
- Automatic cache invalidation based on age
- Persistent storage recommended for production
- Configure memory limits appropriately
- Monitor cache hit rates
- Public data only (no private repositories)
- Rate limited by GitHub API (5000 requests/hour for authenticated users)
- Stale cache for unauthenticated users (cannot regenerate)
- No database (all data cached in Redis)
- Session-based authentication only (no API tokens)
- User dashboard showing own reports
- Custom date ranges beyond predefined periods
- Export reports to PDF or JSON
- Share reports via unique shareable links
- Repository filtering options
- Enhanced statistics and charts
- Organization-level reports