A focused tool for managing inactive forked repositories on GitHub. Identify, archive, and delete stale forks in bulk with a clean, no-framework interface.
- 🍴 Stale Fork Detection - Automatically identifies forks inactive beyond a configurable threshold
- ⚙️ Configurable Threshold - Set staleness period from 30 to 730 days (default: 180)
- 🔍 Advanced Filtering - Filter by visibility (public/private/internal) and archived status
- ☑️ Bulk Operations - Select and archive/delete multiple repositories at once
- 🔐 Secure OAuth - GitHub OAuth with minimal required scopes (
repo,delete_repo) - 🍪 Session-based Auth - HttpOnly cookies with 24-hour expiry
- 📊 Visual Dashboard - See repository details, parent links, and staleness metrics
- 🚦 Rate Limit Handling - Graceful handling of GitHub API rate limits
- 📝 Comprehensive Logging - All API calls logged for auditing
- 🎨 No Framework UI - Pure HTML/CSS/JavaScript for minimal complexity
- 🔄 Real-time Data - Always fetches fresh data from GitHub (no caching)
Built with simplicity and security as core principles:
- Backend: Node.js + Express with minimal dependencies
- Frontend: Vanilla JavaScript (no frameworks)
- Authentication: GitHub OAuth with express-session
- API Client: @octokit/rest for typed GitHub API interactions
- Session Storage: In-memory sessions for authentication tokens
All GitHub API interactions are isolated in github-api.js for clean separation of concerns.
- Node.js 22+ or Docker
- GitHub account with repositories to manage
- Either: GitHub Personal Access Token (recommended for local use)
- Or: GitHub OAuth App credentials (for shared/production deployments)
You can authenticate using either a Personal Access Token (PAT) or OAuth:
Why PAT?
- No OAuth app setup required
- Fine-grained permissions with short expiration
- Perfect for local development
- No client secrets needed
Create a Fine-Grained Token:
- Go to GitHub Token Settings
- Click "Generate new token" → "Fine-grained token"
- Configure the token:
- Token name: Stale Fork Manager
- Expiration: 7 days (or custom short period)
- Repository access: All repositories (or select specific ones)
- Permissions:
- Repository permissions → Administration: Read and write
- Click "Generate token" and copy it
- Paste the token into the application UI when prompted
🔒 Security Best Practice: Use short expiration periods (7-30 days) and regenerate tokens regularly.
- Navigate to GitHub Developer Settings
- Click "New OAuth App"
- Configure the application:
- Application name: Stale Fork Manager (or your choice)
- Homepage URL:
http://localhost:3000 - Authorization callback URL:
http://localhost:3000/auth/github/callback
- Click "Register application"
- Note your Client ID
- Generate and note your Client Secret
⚠️ Important: The callback URL must match exactly, including the/auth/github/callbackpath.
Create a .env file in the project root:
Minimum configuration (PAT only):
# Required: Session secret (generate a random string)
SESSION_SECRET=your_random_session_secret_hereFull configuration (with OAuth):
# Required: Session secret
SESSION_SECRET=your_random_session_secret_here
# Optional: GitHub OAuth credentials (leave blank to use PAT only)
GITHUB_CLIENT_ID=your_client_id_here
GITHUB_CLIENT_SECRET=your_client_secret_here
# Optional: Base URL for production
# BASE_URL=https://your-domain.com
# Optional: Node environment
# NODE_ENV=productionGenerate a secure session secret:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"npm installDevelopment mode (with hot reload):
npm run devProduction mode:
npm startThe application will be available at http://localhost:3000
With Personal Access Token:
- Authenticate: Paste your fine-grained token in the input field and click "Authenticate"
- Configure: Adjust the staleness threshold (default: 180 days)
- Filter: Apply visibility and archived filters as needed
- Select: Check the forks you want to manage
- Archive: Click "Archive Selected" to archive (reversible)
- Delete: Click "Delete Selected" and type "DELETE" to confirm (permanent)
With OAuth:
- Login: Click "Login with GitHub" on the landing page
- Authorize: Grant the requested permissions (
repo,delete_repo) - Follow steps 2-6 above
GET /auth/github- Initiate OAuth flowGET /auth/github/callback- OAuth callback handlerGET /api/user- Get authenticated user infoPOST /api/logout- Destroy session
GET /api/forks/stale- List stale forks with filters- Query params:
daysThreshold,visibility,includeArchived
- Query params:
POST /api/forks/archive- Archive repositories (bulk)- Body:
{ "repoIds": [123, 456] }
- Body:
POST /api/forks/delete- Delete repositories (bulk)- Body:
{ "repoIds": [123, 456] }
- Body:
GitHub API rate limits:
- Authenticated: 5,000 requests/hour
- Unauthenticated: 60 requests/hour
The application:
- Shows remaining rate limit when exceeded
- Displays wait time from GitHub response headers
- Does not retry automatically - user decides when to retry
MIT