Skip to content

Repository files navigation

Watch Org

Local GitHub organization activity workspace built with Next.js, PostgreSQL, and a GitHub App.

The app keeps PostgreSQL as the canonical local source of truth, syncs GitHub activity on demand, and exposes organization, repository, and user views without querying GitHub for every screen render.

Try the live demo on Vercel — pre-loaded with synthetic data, no setup required.

What it does

  • organization overview focused on visible work
  • people workspace with user detail pages
  • repositories workspace with repository activity logs
  • local historical storage in PostgreSQL
  • gap-based backfill for missing time windows
  • 90-day bootstrap flow for first-time setup
  • rebuildable daily aggregates for faster dashboard queries
  • export endpoints for user activity datasets

Supported GitHub data model

Current sync coverage includes:

  • commits
  • pull requests
  • pull request reviews
  • pull request review comments
  • pull request timeline events
  • issues
  • issue comments
  • workflow runs
  • check runs
  • deployments and deployment statuses

Canonical entities are stored in normalized tables with unique external IDs. Daily aggregates are stored separately and can always be rebuilt from canonical event data.

Architecture

  • Next.js for UI and API routes
  • PostgreSQL as the canonical local database
  • GitHub App for authenticated API access
  • coverage_windows to track local temporal coverage
  • sync_jobs for audit, operational logs, and warnings
  • user_activity_daily for rebuildable daily aggregates

Quick start

If you want the shortest path from clone to a working local dashboard:

  1. Create a GitHub App and install it on your target organization.
  2. Copy .env.example to .env.local and fill in the required values.
  3. Start PostgreSQL.
  4. Install dependencies and apply migrations.
  5. Start the Next.js app.
  6. Run the 90-day bootstrap.
  7. Open the dashboard and switch the range filter to 90d.

Commands:

cp .env.example .env.local
npm install
npm run db:up
npm run db:migrate
npm run dev
APP_BASE_URL=http://localhost:3000 npm run sync:bootstrap -- --repository all --lookbackDays 90

Then open http://localhost:3000.

Demo mode

If you want a public demo without connecting a real GitHub App, the repo now includes mock-data scripts that populate PostgreSQL directly.

Seed a fresh demo dataset:

npm run demo:seed

Clear the demo dataset:

npm run demo:clear

Optional flags:

  • --org demo-org to choose the organization login stored in the database
  • --days 90 to control the generated time window

The demo seed:

  • creates a synthetic organization, repositories, members, branches, and recent activity
  • populates canonical event tables and daily aggregates
  • inserts coverage windows and completed sync jobs so the dashboard feels alive
  • loads .env.local automatically to find DATABASE_URL

For a demo deployment, use a dedicated database and set GITHUB_ORG to the same demo org login you seeded, for example demo-org.

Create the GitHub App

The repo is usable from scratch, but you do need your own GitHub App installation.

Start here:

Short version:

  1. Go to GitHub developer settings and create a new GitHub App.
  2. Install it on the organization you want to analyze.
  3. Generate a private key and copy the App ID and Installation ID.
  4. Grant the repository permissions listed in the setup guide.
  5. Add the resulting values to .env.local.

Recommended repository permissions for the current implementation:

  • Metadata: Read-only
  • Contents: Read-only
  • Pull requests: Read-only
  • Issues: Read-only
  • Actions: Read-only
  • Checks: Read-only
  • Deployments: Read-only

Recommended organization permissions:

  • Members: Read-only

Notes:

  • Members: Read-only is helpful, but member discovery can also be improved with GITHUB_ORG_MEMBERS_TOKEN.
  • GITHUB_APP_WEBHOOK_SECRET is optional for the current local workflow. It is reserved for webhook ingestion and can stay empty if you are only using discovery, sync, bootstrap, and dashboard views.
  • If a permission is missing for timeline events, checks, or deployments, bootstrap still completes and reports those families as warnings.
  • For a mock-only demo deployment, you can skip GitHub App setup entirely and use npm run demo:seed instead.

Prerequisites

  • Node.js 20+
  • PostgreSQL 14+
  • a GitHub App installed on the target organization

Environment

Copy the example file and fill in your values:

cp .env.example .env.local

Environment variables:

Variable Required Purpose
DATABASE_URL yes PostgreSQL connection string used by the app and all CLI wrappers
GITHUB_ORG yes organization login to analyze
GITHUB_APP_ID yes numeric GitHub App ID
GITHUB_APP_PRIVATE_KEY yes private key generated from the GitHub App settings page
GITHUB_APP_INSTALLATION_ID yes installation ID for the app on the target organization
DASHBOARD_REPOSITORY_BLACKLIST no comma-separated repositories hidden from workspace rankings and filters
GITHUB_APP_WEBHOOK_SECRET no webhook secret reserved for webhook ingestion
GITHUB_ORG_MEMBERS_TOKEN no personal token used to improve org member discovery when membership is private

Notes:

  • GITHUB_ORG has no hardcoded fallback. Set it explicitly.
  • GITHUB_APP_PRIVATE_KEY accepts either real newlines or escaped \n.
  • GITHUB_ORG_MEMBERS_TOKEN is optional, but it is often useful when people are active in the org and their GitHub organization membership is still private.

Example:

DATABASE_URL=postgres://postgres:postgres@localhost:5432/watch_org

GITHUB_ORG=your-org
GITHUB_APP_ID=1234567
GITHUB_APP_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
GITHUB_APP_INSTALLATION_ID=98765432

DASHBOARD_REPOSITORY_BLACKLIST=legacy-repo-a,legacy-repo-b
GITHUB_APP_WEBHOOK_SECRET=
GITHUB_ORG_MEMBERS_TOKEN=

Install

npm install

Database

If you want to use the included Docker setup:

npm run db:up

For a first local run, applying migrations is enough:

npm run db:migrate

If you are changing the schema locally, generate new migration files with:

npm run db:generate

Run the app

npm run dev

The API-side CLI wrappers automatically try local Next servers on common ports. If your server runs on a custom port, or you want deterministic behavior, set:

APP_BASE_URL=http://localhost:3000

90-day bootstrap

The recommended first run is the bootstrap flow. It performs:

  1. organization discovery
  2. 90-day extended sync
  3. aggregate rebuild

CLI:

APP_BASE_URL=http://localhost:3000 npm run sync:bootstrap -- --repository all --lookbackDays 90

The same flow is also available from the System drawer in the UI.

Nightly sync job

For an unattended nightly run, use the dedicated command below. It syncs the previous UTC day by default, refreshes discovery first, and rebuilds aggregates for the same date window.

APP_BASE_URL=http://localhost:3000 npm run sync:nightly

Useful flags:

  • --date 2026-04-10 to sync a specific UTC day
  • --daysAgo 2 to sync two days ago instead of yesterday
  • --repository your-repo to limit the run to one repository
  • --user octocat to scope to one user
  • --branch main to scope to one branch

Example cron entry at 01:30 every night with a persistent log file:

h30 1 * * * cd <project-path> && APP_BASE_URL=http://localhost:3000 npm run sync:nightly >> /var/log/watch-org-nightly.log 2>&1

Each repository sync already writes an audit row to sync_jobs with status completed or failed, so you have both cron logs and database traceability for the execution outcome.

Discovery

Fetch repositories and members from the configured organization:

APP_BASE_URL=http://localhost:3000 npm run sync:discovery

HTTP:

curl -X POST http://localhost:3000/api/discovery

Sync commands

Sync all supported activity families for a recent rolling window:

APP_BASE_URL=http://localhost:3000 npm run sync:all -- --repository all --lookbackDays 30

Sync a precise day-based window:

APP_BASE_URL=http://localhost:3000 npm run sync:all -- --repository all --from 2026-03-01 --to 2026-03-31

Single-family commands:

APP_BASE_URL=http://localhost:3000 npm run sync:commits -- --repository all --lookbackDays 30
APP_BASE_URL=http://localhost:3000 npm run sync:prs -- --repository all --lookbackDays 30
APP_BASE_URL=http://localhost:3000 npm run sync:reviews -- --repository all --lookbackDays 30
APP_BASE_URL=http://localhost:3000 npm run sync:review-comments -- --repository all --lookbackDays 30
APP_BASE_URL=http://localhost:3000 npm run sync:pr-timeline -- --repository all --lookbackDays 30
APP_BASE_URL=http://localhost:3000 npm run sync:issues -- --repository all --lookbackDays 30
APP_BASE_URL=http://localhost:3000 npm run sync:issue-comments -- --repository all --lookbackDays 30
APP_BASE_URL=http://localhost:3000 npm run sync:workflows -- --repository all --lookbackDays 30
APP_BASE_URL=http://localhost:3000 npm run sync:checks -- --repository all --lookbackDays 30
APP_BASE_URL=http://localhost:3000 npm run sync:deployments -- --repository all --lookbackDays 30

Supported sync flags:

  • --repository <name> or all
  • --user <login> or all
  • --branch <name> or all
  • --lookbackDays <n>
  • --from <YYYY-MM-DD>
  • --to <YYYY-MM-DD>

Date windows are interpreted as:

  • from => YYYY-MM-DDT00:00:00.000Z
  • to => YYYY-MM-DDT23:59:59.999Z

Backfill planning

You can inspect missing windows before launching a sync:

curl -X POST http://localhost:3000/api/backfill \
  -H 'content-type: application/json' \
  -d '{
    "org": "your-org",
    "repository": "all",
    "from": "2026-03-01",
    "to": "2026-03-31",
    "activityType": "all",
    "user": "all",
    "branch": "all"
  }'

The planner returns only uncovered windows thanks to gap-based coverage evaluation. Empty windows are persisted as coverage too, so they are not re-fetched unnecessarily.

In the dashboard, commit coverage treats both complete and empty windows as covered. An empty window means the selected range was checked successfully and no commits were found for that repository.

Aggregates

user_activity_daily stores rebuildable daily aggregates per organization, repository, and user.

Rebuild recent aggregates:

APP_BASE_URL=http://localhost:3000 npm run aggregates:rebuild -- --repository all --lookbackDays 90

Rebuild a precise range:

APP_BASE_URL=http://localhost:3000 npm run aggregates:rebuild -- --repository all --from 2026-03-01 --to 2026-03-31

Verify your first import

After bootstrap completes:

  1. open http://localhost:3000
  2. switch the top range control to 90d
  3. open People and Repositories to confirm that members and repositories were discovered
  4. open the System drawer and inspect Status, Coverage, Discovery, and Service Log

Useful verification routes:

System drawer

The off-page System drawer groups operational and technical information away from the main work surfaces.

Tabs currently include:

  • Status
  • Coverage
  • Discovery
  • Service Log
  • Sync

The Sync tab exposes the 90-day bootstrap action and reports processed repositories, attempted activity families, and warnings.

Missing permissions for timeline events, checks, or deployments do not fail the whole bootstrap. They are reported as warnings and the affected coverage windows are marked as partial.

Internationalization

The UI uses next-intl with an English-first setup.

  • active locale: en
  • no locale prefix in routes
  • messages centralized under src/i18n/messages
  • structure is ready for additional locales later

Repository visibility

Workspace rankings and repository-focused views intentionally hide archived or blacklisted repositories by default.

  • archived repositories are excluded from workspace ranking surfaces
  • repositories listed in DASHBOARD_REPOSITORY_BLACKLIST are hidden from standard workspace filters and leaderboards
  • canonical data is still kept in PostgreSQL

Troubleshooting

Bootstrap or sync hangs for a long time

  • long-running sync jobs can take several minutes on large organizations
  • the CLI now prints heartbeats while it waits
  • prefer setting APP_BASE_URL=http://localhost:3000 explicitly so the wrapper does not guess the wrong port

I only see recent data

  • the default range preset is 30d
  • after importing 90 days, switch the UI range to 90d
  • if raw tables are fresh but charts still look stale, run npm run aggregates:rebuild

Member discovery looks incomplete

  • check whether the user has public organization visibility enabled on GitHub
  • if not, add GITHUB_ORG_MEMBERS_TOKEN to improve org member discovery
  • then run npm run sync:discovery again

Some activity families are empty

  • issues or deployments may legitimately be empty for the selected repositories
  • timeline events, checks, and deployments may also be skipped when the GitHub App lacks the required permission
  • bootstrap reports those cases as warnings instead of failing completely

The CLI returns HTML instead of JSON

  • this usually means the Next.js server crashed while rendering the API route
  • restart npm run dev and retry the same command

Vercel demo deployment

For a public demo on Vercel, keep it simple:

  1. create a dedicated Postgres database only for demo data
  2. run npm run demo:seed against that database once
  3. deploy the app to Vercel with DATABASE_URL pointing to the demo database
  4. set GITHUB_ORG to the demo org login you seeded, for example demo-org

Recommended database options:

  • Vercel Postgres if you want the easiest Vercel-native setup
  • Neon if you want a simple serverless Postgres with good free-tier ergonomics

The application uses a standard PostgreSQL connection string, so either option works without code changes.

Main paths

  • / overview
  • /people people workspace
  • /people/[login] user detail
  • /repositories repositories workspace
  • /repositories/[name] repository detail
  • /api/dashboard overview payload API
  • /api/bootstrap 90-day bootstrap API
  • /api/sync sync API
  • /api/backfill coverage planning API
  • /api/discovery discovery API
  • /api/aggregates/rebuild aggregate rebuild API
  • /api/export/user user export API

Operational sequence

Typical first-time setup:

  1. configure the GitHub App and environment variables
  2. start PostgreSQL
  3. install dependencies
  4. run migrations
  5. start the app
  6. run the 90-day bootstrap
  7. switch the range to 90d and inspect the workspace views

Typical ongoing usage:

  1. open a dashboard view
  2. inspect current local data
  3. run targeted sync or bootstrap when coverage is missing or stale
  4. rebuild aggregates when needed

Verification

Recommended checks during development:

npm run typecheck
npm run lint

Project structure

src/app                    Next.js routes and API handlers
src/components/dashboard   workspace UI surfaces
src/features/dashboard     dashboard data shaping and workspace models
src/lib/github             GitHub App auth and remote fetchers
src/lib/db                 schema, persistence and aggregate rebuilds
src/lib/backfill           coverage planning and gap detection
src/i18n                   next-intl setup and message catalogs
scripts                    CLI wrappers for sync/bootstrap/rebuild flows
drizzle                    SQL migrations and schema snapshots
docs                       project and setup documentation

About

Local GitHub organization activity workspace built with Next.js, PostgreSQL, and a GitHub App.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages