Skip to content

feat: add standalone Cap status app - #2475

Merged
richiemcilroy merged 5 commits into
mainfrom
feat/service-status
Oct 13, 2026
Merged

richiemcilroy merged 5 commits into
mainfrom
feat/service-status

Conversation

@richiemcilroy

@richiemcilroy richiemcilroy commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

Adds a standalone status app for status.cap.so, matching the current Cap landing page’s typography, clouds, colours, and spacing. It can be deployed to its own Vercel project without importing the main web app or using its database.

Behaviour

  • Monitors website, authentication API, video sharing, storage/CDN, media processing, and cloud rendering using lightweight checks every five minutes (72 normal requests per hour across all six services). Page views only read cached results; they never probe Cap’s services.
  • Keeps 90 days of observed uptime and automatic incident history in dedicated Redis storage. Retries failed checks, confirms incidents and recovery across consecutive samples, and prevents overlapping or duplicate collections with an atomic expiring lease.
  • Rejects query parameters before touching Redis and keeps status development on port 3003, excluded from the main development commands.
  • Shows missing configuration, stale observations, and monitoring failures as unknown. Includes mobile layouts, keyboard-accessible daily history, service details, and an incident timeline.

Deployment

Create a separate Vercel project with root apps/status on a plan supporting five-minute cron jobs. Attach a dedicated Upstash database and configure CRON_SECRET plus the service URLs listed in the app README. Website and authentication defaults are supplied; sharing/storage canaries and media/render health URLs require configuration. Add an external heartbeat monitor to detect scheduler or Vercel-wide failures, and configure the documented Vercel WAF rate limit before public launch.

This PR does not deploy production infrastructure or change DNS. These are availability probes, not complete recording/upload/playback transactions.

Validation

  • 22 passing tests, including the collector → Redis REST → public API flow using local fixtures, authentication, concurrent runs, lost leases, retries, incident/recovery thresholds, retention, and stale data.
  • Production build, TypeScript, scoped Biome checks, and frozen-lockfile validation pass.
  • Browser checks pass for healthy, outage, unconfigured, and stale states, service expansion, keyboard navigation, and mobile overflow. Automated WCAG 2.1 A/AA checks pass for healthy and outage views.
  • Root script tests and Turbo dry runs confirm status is excluded from the standard, manual, mobile, extension, and Windows development entry points.
  • Adds a scoped status-app CI workflow.

Screenshots

Production-build previews with clearly labelled sample data; these do not represent live uptime.

Desktop · Mobile

Cap status desktop preview with sample uptime data

Cap status mobile preview with sample uptime data

RetriggerConfidence Score: 5/5

The PR appears safe to merge; the previous finding is fixed and no new blocking issues were found.

Summary

Adds a standalone Cap status app with scheduled checks, Redis history, and a public status page.

  • Cap checks six services on a five-minute schedule.
  • The status API serves cached monitoring snapshots.
  • Visitors can check Cap service health and explore its history.
  • The status app has its own deploy setup and checks.

Reviews (2) · Last reviewed commit: "fix: isolate status development and prot..." · Reviewed by Greptile

@socket-security

socket-security Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​upstash/​redis@​1.39.098100100100100

View full report

Comment thread apps/status/components/status-page.tsx

@superagent-security superagent-security Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superagent found 1 security concern(s).

return Response.json(publicSnapshot(await readState(getRedis())), {
headers: {
"Cache-Control":
"public, max-age=0, s-maxage=30, stale-while-revalidate=30",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Unique query strings bypass the status API cache and repeatedly consume Redis and function resources.

Unique query strings bypass the 30-second per-URL cache; each miss reads the dedicated Redis store.

Reject query strings and apply an edge rate limit before the Redis-backed handler.

AI prompt
Check if this security scanner issue is valid. If so, understand the root cause and fix it. If appropriate, update or add tests. Keep the change focused and preserve intended behavior.

<file name="apps/status/app/api/status/route.ts">
<violation number="1" location="apps/status/app/api/status/route.ts:11">
<priority>P2</priority>
<title>Unique query strings bypass the status API cache and repeatedly consume Redis and function resources.</title>
<evidence>The new public GET handler reads the dedicated Redis state at line 8, then sets `Cache-Control: public, max-age=0, s-maxage=30, stale-while-revalidate=30` at line 11. The handler does not inspect or reject query parameters, so an unauthenticated caller can request `/api/status?x=&lt;unique-value&gt;` for each request. Vercel's official CDN documentation says function responses are cached when they set `s-maxage` (https://vercel.com/docs/caching/cdn-cache), and its cache-key documentation says the request URL is part of the cache key, with query strings ignored only for static files (https://vercel.com/docs/caching/cdn-cache/purge). These distinct function URLs therefore create distinct cache entries rather than reusing the intended 30-second cache. Each cache miss runs `readState(getRedis())` and performs a Redis GET; `publicSnapshot` then projects six services across 90 history days (`apps/status/lib/state.ts:152-175`). The app provides no query normalization/rejection or per-caller rate limit before the Redis read. The package pins Next.js 16.3.0 (`apps/status/package.json:15`); Vercel documents the function-response caching behavior used here. Repeated unique requests can consume the status deployment's function invocations and dedicated Redis request quota, affecting availability and/or exhausting usage limits.</evidence>
<recommendation>Reject requests with any query string before calling `getRedis()` and enforce an edge-level rate limit or request quota for `/api/status`. Keep the public cache on a single canonical URL so caller-controlled URL variants cannot force repeated Redis reads.</recommendation>
</violation>
</file>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed the cache-busting path in 6807174: requests with query parameters return 400 before getRedis() or readState(). The regression test covers multiple unique and encoded query strings and asserts zero Redis commands. The production HTTP check confirms the same behavior. The separate status project’s WAF rate-limit setup is documented as a requirement before public launch; no production firewall configuration has been changed by this PR.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The developer’s fix directly remediates the reported cache bypass by rejecting requests with query parameters before any Redis interaction, and the regression tests verify that no Redis commands are issued for cache-busting URLs. Treating edge rate limiting as an infrastructure/WAF prerequisite documented for public launch is a reasonable separation of concerns, so this reply satisfactorily addresses the finding.

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found 1 potential issue (1 high).

View 2 other findings in Capy.

Open in Capy

Comment thread apps/status/package.json Outdated

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found no issues.

View 1 other finding in Capy.

Open in Capy

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found no issues.

View 1 other finding in Capy.

Open in Capy

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found no issues.

View 1 other finding in Capy.

Open in Capy

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found no issues.

View 2 other findings in Capy.

Open in Capy

Copy link
Copy Markdown
Member Author

@greptileai please rereview

@richiemcilroy
richiemcilroy merged commit eefa6ef into main Oct 13, 2026
31 checks passed

This branch was successfully deployed

1 active deployment
Preview — 6807174f Deployed Oct 13, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant