Dashboard guide â
The dashboard at https://codebeaver.dev is an authenticated React single-page app for review activity, model usage, finding outcomes, authors, and learned rules. It serves two audiences: the operator console under /dashboard (Cloudflare Access staff identity) and the hosted tenant app under /app plus /login (GitHub sign-in).
Sign in â
The operator console uses Cloudflare Access. Access controls the browser session and injects an assertion that the Worker validates with API Admin. The dashboard does not store browser authentication tokens.
Signing out clears the persisted query cache, then sends the browser through the Cloudflare Access logout route. Changing sessions must do the same so cached data from one staff member is not shown to another.
codebeaver auth login still issues CLI JWTs for command-line requests. The dashboard does not accept or store those tokens, and outside local development (ENVIRONMENT unset or non-production) dashboard endpoints reject them.
Tenant sign-in (/login, /app) â
The hosted tenant app signs in with GitHub instead of Access. /api/auth/github/start stores single-use state in KV and redirects to GitHub's OAuth consent; /api/auth/dashboard-callback exchanges the code server-side, stores the user's GitHub token in a KV session record, and sets an HttpOnly cb_session cookie carrying a signed JWT with kind: "tenant" â the GitHub token never reaches the browser. /api/auth/session reports kind: "staff" or kind: "tenant"; a staff auth outage (503) is never masked as a tenant login. Logout (POST /api/auth/logout) deletes the KV session and expires the cookie.
/loginshows the GitHub sign-in button, an error banner for OAuth failures, and redirects signed-in users to/app(tenant) or/dashboard(staff)./appis the workspace overview: workspace switcher (?ws=<installationId>), the claim call to action forpendingtenants (calls/api/tenants/claim-sessionwith the server-side session token â no second OAuth redirect), plan and monthly review usage, review activity, and an install call to action when the user has no installations./app/reposlists the repositories granted on GitHub's install screen (live from the GitHub API, 5-minute cache) with a filter (?q=) and per-repo auto-review toggles. Toggling requires installation-account admin (verified with the session's GitHub token) and writes therepo-disabled:v1:{installationId}KV flag that review dispatch and@botaction commands consult. "Add repositories" links tohttps://github.com/apps/$PUBLIC_GITHUB_APP_SLUG/installations/new(fallback: GitHub's installations settings page)./app/usageshows per-workspace reviews, tokens, and estimated model cost by period (from/api/tenant/stats), plus monthly seat count (distinct committers, metered nightly; bots excluded) and the plan's per-month review and repository allowances.
Billing behavior a tenant sees: a lapsed subscription suspends the workspace â reviews stop with one friendly comment per suspension episode (30-day flag), and the next payment (Marketplace purchased/changed) resumes it automatically. On the free plan, auto-review covers one repository per month; a second repository gets a posted skip reason pointing at the dashboard.
Tenant queries share the staff query defaults but are never persisted to local storage (guests have no user to key the cache by).
Pages â
Overview â
/dashboard reports:
- recorded review runs and distinct PRs;
- authors and reviewers;
- average model elapsed time;
- total and average provider tokens and cost;
- current OpenRouter credits and DeepSeek balance when optional billing credentials are configured;
- daily usage and review volume;
- application-cache hits, avoided calls, tokens, and cost when recorded;
- verdict distribution and repository volume;
- current in-flight reviews;
- top authors and reviewers;
- counts of feedback and learned rules.
Usage periods cover today, the current week, the current month, and the full retained review log. Averages use records that contain provider usage; older records without usage do not become zero-cost model calls.
Reviews â
/dashboard/reviews shows paginated review-log records. Search matches PR title, repository, author, reviewer, or PR number. The state filter can narrow the list to approved, changes requested, commented, or error reviews. Expanding a row fetches stored finding records with Jev evidence-check badges, scanner origin, model route, evidence-reference kinds, suggestion verification counts, provider usage, head SHA, outcome totals, and a collapsible list of removed-finding identities (up to 20, issue text truncated) tagged with the filter stage that dropped each. GET /api/stats/review/{owner}/{repo}/{pr}?format=sarif downloads the same validated records as SARIF 2.1.0.
findingsCount already includes published SAST findings. Totals use that field once; sastCount is scanner telemetry and can also include unpublished findings on incomplete runs.
Finding outcomes distinguish:
- open on the current review;
- not reported on a later review;
- verified fixed only after a successful autofix path;
- manually resolved through a GitHub thread.
A manual resolution is not code-fix proof, so resolved and outcome counts can overlap. The deprecated fixed API field aggregates notReported and verifiedFixed during the migration.
The review API uses a cursor, scans at most 1,000 index entries per request, and returns nextCursor, hasMore, partial, scanned, and scanLimit. New review rows store serialized summaries in the repo and author indexes; older pointer values remain readable during their retention period. Exact historical expansion uses /api/stats/review-run/:id.
Live Queue â
/dashboard/queue shows waiting PR reviews as they move through the normal and priority lanes. It refreshes every five seconds and reports queue length, active reviews, oldest wait, average wait, lane, rank, request time, and head SHA.
Promote moves a queued review into the priority lane. The original queue message stays safe to acknowledge when it arrives; the promoted copy carries the same invocation ID and runs once. Existing promoted reviews keep their promotion order.
Users â
/dashboard/users groups review logs by PR author. Each row includes distinct PR count, review runs, findings, approved and flagged runs, stored provider usage, and last activity. The ratio is approvals divided by approvals plus flagged reviews.
User detail pages reuse the review list with an author filter and show the same pagination and usage rules.
Learning â
/dashboard/learning shows feedback totals, false-positive rules, accepted practice rules, and repositories with accuracy data.
The rule manager supports:
- false-positive and practice tabs;
- approve or reject for pending rules;
- deletion of one rule;
- deletion of all rules of one kind for a repository.
Status updates identify a rule by pattern rather than array index. Scheduled compaction may reorder a list between the read and write, so index-based status changes would target the wrong record. Single-rule deletion identifies the rule by pattern too; the delete endpoint rejects ruleIndex with 400.
Rule listing is bounded to 100 repositories and 100 rules per repository by default. The API returns limits and a truncation flag. Read failures return partial metadata; if every listed rule value fails, the endpoint returns an unavailable error instead of an empty list.
URL state â
Shareable view state belongs in URL query parameters:
| Page | Parameter | Meaning |
|---|---|---|
| Reviews | cursor | Opaque cursor for the next bounded scan |
| Reviews | q | Search text |
| Reviews | state | all, approved, changes_requested, commented, or error |
| Reviews | review | Expanded review record ID |
| Users | page | Server-side page |
| Users | q | Author search |
| Users | sort, direction | Full bounded aggregate sort and asc/desc direction |
| User detail | page | Review page for that author |
| Learning | rules | fp or bp tab |
Invalid pages are clamped after data loads. An expanded review ID that is not on the current page is removed.
Data refresh and cache â
The Worker caches the overview statistics in KV for 60 seconds, the minimum KV expiration TTL.
TanStack Query uses these defaults:
- 30-second stale time;
- one retry;
- no automatic window-focus refetch;
- no interval refetch while the tab is in the background;
- 24-hour garbage collection and persisted-cache age.
Page intervals:
| Query | Interval |
|---|---|
| Overview stats | 30 seconds |
| Review list | 30 seconds |
| User list | 30 seconds in the current page |
| Learning rules | 60 seconds |
| Live queue | 5 seconds |
| Review detail | no interval; stale after 5 minutes |
The session bootstrap runs before query-client creation, cache hydration, or dashboard route rendering. Successful stats, reviews, users, review details, and learning-rule queries are persisted in local storage under codebeaver-query-cache:v2:<user.id>, with buster dashboard-v2:<user.id>. Queue and queue-job queries are never persisted. A session poll runs every 60 seconds. Logout and identity changes clear the in-memory and persisted cache and broadcast codebeaver-session-reset to other tabs.
The UI may show cached data while a background refresh fails, along with an error banner and retry action. A failed request must never be rendered as zero reviews, zero users, or an empty learning list.
API behavior â
The dashboard calls /api on its own protected host unless VITE_CODEBEAVER_API_BASE_URL overrides it for local development. Cloudflare Access injects its assertion into browser requests; the Worker validates it through API Admin. In production the Worker uses the API_ADMIN service binding and returns 503 when that binding is missing. Dashboard data endpoints also return 503 with a retryable error whenever the authentication backend is unavailable, rather than reporting the request as unauthenticated. Local Vite development proxies /api to CODEBEAVER_DEV_API_TARGET (default http://127.0.0.1:8787) and can inject CODEBEAVER_DEV_BEARER_TOKEN for local-only CLI testing. An expired identity (401 or 403) gets a session-ended screen; a timeout or upstream outage (503) gets a retryable unavailable screen. Background data failures do not clear the Access session.
Main endpoints:
| Endpoint | Method | Work |
|---|---|---|
/api/stats | GET | Overview, usage, learning counts, in-flight records |
/api/provider-status | GET | Cached OpenRouter credits and DeepSeek balance status |
/api/stats/reviews | GET | Paginated and filtered review log |
/api/stats/users | GET | Author aggregation |
/api/stats/review-run/:id | GET | Exact historical review run expansion |
/api/health/dashboard | GET | Read-only API Admin and review-cache dependency probe |
/api/stats/review/:owner/:repo/:pr | GET | Findings, outcomes, usage, filter counts, and private recall detail |
/api/queue | GET | Live queued reviews, lane positions, wait stats, and active review count |
/api/queue | POST | Promote one queued review with { "action": "promote", "invocationId": "..." } |
/api/stats/learning/rules | GET | Rule lists |
/api/stats/learning/rules | PATCH | Approve or reject one rule |
/api/stats/learning/rules | DELETE | Delete one rule or a repository list |
/api/tenants/claim | POST | Tenant-owner claim for one GitHub App installation via OAuth code |
/api/tenants/claim-session | POST | Tenant-owner claim using the signed-in tenant session (no OAuth code) |
/api/auth/github/start | GET | Redirect into the GitHub OAuth consent for tenant sign-in |
/api/auth/dashboard-callback | GET | OAuth callback: sets the cb_session tenant cookie |
/api/auth/logout | POST | Delete the tenant KV session and expire the cookie |
/api/tenant/workspaces | GET | Caller's installations joined with tenant state, plan, usage |
/api/tenant/repos | GET | Granted repositories for one installation, with disabled flags |
/api/tenant/repos/toggle | POST | Enable or disable auto-review for one repository (admin only) |
/api/tenant/stats | GET | Tenant-scoped stats for one installation |
/api/tenants/state | POST | Staff-only operator suspend/resume for one tenant |
Exception to the Access model: /api/tenants/claim authenticates with a fresh GitHub OAuth code (exchanged server-side) and requires no Access or staff session; it is public by design and verifies the claimant is an admin of the installation account (or the account itself). The CLI_GITHUB_ORG gate that restricts CLI sign-in does not apply to this flow. Caller-visible statuses: 400 invalid body, 401 rejected OAuth code, 403 suspended/not-admin, 404 unknown installation, 405 non-POST, 409 already claimed, 413 oversized, 422 unverifiable account login, 500 unexpected. The two 503s have different retry semantics: a verification-outage 503 has already spent the single-use OAuth code, so the client must restart the OAuth flow rather than retry the same body; an unconfigured-OAuth 503 is operator misconfiguration and only resolves when the secrets are set. Accepted by design: 404, 403-suspended, and 422 answer before the OAuth code is spent, so installation existence, suspension, and provisioning-login state are enumerable with garbage codes; 409 discloses claimed-vs-pending to anyone holding a valid code. The tenant app itself claims through /api/tenants/claim-session, which enforces the same rules against the session's server-side GitHub token (401 without a tenant session instead of the OAuth-code exchange). All /api/tenant/* endpoints require the tenant cookie session and return tenant-scoped data only; /api/tenant/repos/toggle additionally verifies installation-account admin per call.
Production dashboard requests are same-origin. The Worker keeps a CORS allowlist for explicit local overrides only.
Develop the dashboard â
pnpm install --frozen-lockfile
pnpm --dir apps/dashboard devThe Vite server listens on port 3001 and proxies /api to the local Worker.
Optional environment values:
VITE_CODEBEAVER_API_BASE_URL=http://127.0.0.1:8787
CODEBEAVER_DEV_API_TARGET=http://127.0.0.1:8787
CODEBEAVER_DEV_BEARER_TOKEN=The proxy keeps browser requests same-origin. The bearer token is optional and should only be used with a local test token.
Start the local Worker with pnpm dev; it passes --var ENVIRONMENT:local so the dev bearer path works. wrangler.toml sets ENVIRONMENT = "production", and a production-configured Worker rejects CLI JWTs and returns 503 when the API Admin binding is absent â that is the same fail-closed behavior as the deployed service.
Run its checks:
pnpm --dir apps/dashboard typecheck
pnpm --dir apps/dashboard test
pnpm --dir apps/dashboard buildThe root shortcut runs all three:
pnpm check:dashboardGenerated routeTree.gen.ts comes from TanStack Router and is ignored by the reviewer defaults. Do not hand-edit it.
Deploy â
pnpm --dir apps/dashboard deployapps/dashboard/wrangler.toml publishes dist/ as static assets with single-page fallback. The normal root deploy workflow builds and deploys the dashboard after the Worker CI gate, then checks /dashboard before publishing the CLI.
Production configuration:
| Side | Value |
|---|---|
| Dashboard build | VITE_CODEBEAVER_API_BASE_URL when overriding the Worker URL |
| Cloudflare Access team domain | VITE_CLOUDFLARE_ACCESS_TEAM_DOMAIN for logout redirects; defaults to codebeaver.cloudflareaccess.com |
| Worker | API_ADMIN_BASE_URL, CLI signing values, dashboard origin CORS policy |
| Cloudflare Access | Application, policy, and identity-provider configuration for the dashboard origin |
| API Admin | Access assertion validation and staff-directory lookup |
The deploy workflow follows redirects and checks dashboard HTML (content type and app marker) plus one emitted JavaScript asset through the Access-protected dashboard origin using Cloudflare Access service-token headers (CF_ACCESS_CLIENT_ID / CF_ACCESS_CLIENT_SECRET). Dependency health uses public /api/health/dashboard on workers.dev. When DASHBOARD_SMOKE_COOKIE is set, it also checks authenticated /api/auth/session and /api/stats on the Access origin (service tokens alone are not a staff session). Keep credentials and response bodies out of logs. After deploy, test Access sign-in, logout cache clearing, a direct review URL with ?review=..., and one rule status change against a nonproduction rule where possible.
Troubleshoot â
Login loops â
Confirm the dashboard origin belongs to the Cloudflare Access application, its policy admits the intended staff identity, and API_ADMIN_BASE_URL points to the API Admin service.
Immediate 401 â
The Access session may be expired, the staff identity may be unknown or inactive, or API Admin may reject the assertion. Run codebeaver auth status for CLI JWTs. For browser sessions, inspect the API Admin Access-validation response.
Empty charts with old rows â
Look for a refresh error banner. Persisted rows can remain visible while a stats request fails. Use the manual refresh control and inspect /api/stats before clearing storage.
Counts differ between pages â
Overview, user, and review pages aggregate bounded review logs in different shapes. Review search applies its filter before summary totals. User PR counts deduplicate by repository and PR number. Usage averages exclude records without usage. Check response limits and filters before treating a difference as data loss.
Security headers and mutation guards â
The SPA ships a _headers file (frame-ancestors/CSP/HSTS). State-changing dashboard APIs (POST /api/queue, learning-rule PATCH/DELETE) reject cross-site browser requests (Sec-Fetch-Site: cross-site, foreign Origin) and non-JSON content types; the persisted query cache buster includes the session's role and permissions.