Live: https://outpost-sandy-ten.vercel.app
Your personal engineering control center. Connect your GitHub repos, and Outpost shows everything that needs you in one dense, dark dashboard: PRs waiting on your review, your own PRs, failing CI, stale PRs, deployments and preview links, and releases across every repo.
Overview, rendered from the e2e mock data. See docs/SPEC.md for the product spec and milestones.
Next.js (App Router) · TypeScript · Tailwind CSS + shadcn/ui · Recharts · GitHub App (Octokit) · Vercel REST API · Neon Postgres + Drizzle · Auth.js · Vitest · Playwright
Requirements: Node.js 22+ (24 recommended), pnpm 11, and the gh CLI for repo tooling.
pnpm install
cp .env.example .env.local # then fill in the values below
pnpm exec playwright install chromium # once, for e2e tests
pnpm db:migrate # once DATABASE_URL is set
pnpm dev # http://localhost:3000Then open /dashboard, sign in with GitHub as OWNER_GITHUB_LOGIN, and connect the repos the
GitHub App is installed on.
Outpost caches GitHub data in Postgres for 5 minutes; the header's Sync button refreshes immediately. To refresh as soon as something changes, enable the GitHub App webhook:
- GitHub App settings → Webhook: check Active, set the URL to
https://<your-domain>/api/github/webhook, and set a random Webhook secret (openssl rand -hex 32). - Permissions & events → subscribe to: Pull request, Pull request review, Check suite, Check run, Status, Workflow run, Release, Deployment, Deployment status, Push.
- Set the same secret as
GITHUB_WEBHOOK_SECRETin Vercel (Production) and redeploy.
Every day at 01:00 UTC (09:00 in Manila/Singapore) Vercel Cron calls /api/cron/digest, which
posts what needs you to Slack: review requests, your PRs with failing CI or requested changes,
failing default branches, failed production deploys, stale PRs, and merged-but-unreleased work
on release-tag repos.
- Create a Slack app with an Incoming Webhook for your channel and copy its URL.
- In Vercel (Production) set
SLACK_WEBHOOK_URLto it, andCRON_SECRETto a random value (openssl rand -hex 32). Vercel sends that secret with each cron call. Redeploy. - Preview the message without posting it:
curl -H "Authorization: Bearer $CRON_SECRET" https://<your-domain>/api/cron/digest?dry=1
Set LINEAR_WORKSPACE to your workspace slug (linear.app/<slug>) and PR lists link issue
keys found in PR titles and branch names (ENG-142, eng-142-fix-login). Set
LINEAR_TEAM_KEYS (e.g. ENG,OPS) to only match your teams' prefixes; without it Outpost
skips common look-alikes such as UTF-8 and release-2.
Outpost can act, not just read: Re-run failed jobs on a repo's Workflows tab, Nudge reviewers on your PRs in My Work (a comment mentioning the pending reviewers, posted by the App's bot account), and Redeploy a failed Vercel production deploy on Deployments.
- Re-run needs the GitHub App permission Actions: Read and write; nudging needs Pull requests: Read and write. Change them under the App's Permissions & events, then accept the new permissions on the installation (GitHub emails you a link).
- Redeploy needs
VERCEL_TOKEN(a token with access to the project's team). Without it the button doesn't show.
Until a permission is granted, the button explains what's missing instead of failing silently.
E2E tests never touch GitHub or your real database. They run the app against a mock GitHub
API (tests/e2e/support/mock-github.mts) and a throwaway Postgres in Docker, and sign in by
minting an Auth.js session cookie.
pnpm e2e:db # start the e2e Postgres container (port 54329), once per boot
pnpm test:e2e # app on :3100, mock GitHub on :4010All of them are listed with comments in .env.example.
| Variable | Purpose |
|---|---|
GITHUB_APP_ID |
GitHub App ID |
GITHUB_APP_PRIVATE_KEY |
GitHub App private key (PEM) |
GITHUB_APP_SLUG |
App slug, for the install link |
GITHUB_WEBHOOK_SECRET |
Verifies GitHub webhook payloads |
GITHUB_API_URL |
Optional GitHub API base URL (GHES, or e2e mock) |
AUTH_GITHUB_ID / AUTH_GITHUB_SECRET |
GitHub OAuth client for owner sign-in |
AUTH_SECRET |
Auth.js session secret (openssl rand -base64 32) |
OWNER_GITHUB_LOGIN |
The only GitHub user allowed to sign in |
VERCEL_TOKEN |
Optional. Enables Redeploy on failed Vercel deploys |
VERCEL_TEAM_ID |
Optional Vercel team ID |
DATABASE_URL |
Neon Postgres connection string |
CRON_SECRET |
Optional. Authorizes Vercel Cron (Slack digest) |
SLACK_WEBHOOK_URL |
Optional. Slack incoming webhook for the daily digest |
LINEAR_WORKSPACE |
Optional. Linear workspace slug, enables issue links |
LINEAR_TEAM_KEYS |
Optional. Comma-separated Linear team prefixes |
| Script | What it does |
|---|---|
pnpm dev |
Start the dev server |
pnpm build |
Production build |
pnpm lint |
ESLint |
pnpm typecheck |
Generate route types, then tsc --noEmit |
pnpm test |
Unit tests (Vitest) |
pnpm test:e2e |
End-to-end tests (Playwright, video retained on failure) |
pnpm e2e:db |
Start the Docker Postgres used by e2e tests |
pnpm format |
Format with Prettier (format:check to verify only) |
pnpm db:generate |
Generate a Drizzle migration from src/db/schema.ts |
pnpm db:migrate |
Apply migrations to DATABASE_URL |
pnpm sync-repo |
Apply repo.config.json and repo settings to GitHub via gh |
Work on a branch and open a PR. CI must pass before merging. main is protected and
squash-merge only.
