Skip to content

feat(obs): apiHelpers on 5 more routes + drop fake demo stats from /api/dashboard/stats#167

Merged
operatoruplift merged 1 commit intomasterfrom
apihelpers-batch
Apr 27, 2026
Merged

feat(obs): apiHelpers on 5 more routes + drop fake demo stats from /api/dashboard/stats#167
operatoruplift merged 1 commit intomasterfrom
apihelpers-batch

Conversation

@operatoruplift
Copy link
Copy Markdown
Owner

Two wins, one PR

1. Trust-gate coverage 11/44 → 16/44

Five small routes adopted the withRequestMeta/X-Request-Id pattern from @/lib/apiHelpers that the /api/tools/* family adopted in #123 and the observability sweep extended to receipts/memory/agents/notifications/audit in #130. Now these routes propagate request IDs the same way:

Route Lines
app/api/providers/route.ts 6
app/api/capabilities/route.ts 25
app/api/receipts/public-key/route.ts 26
app/api/health/llm/route.ts 27
app/api/dashboard/stats/route.ts 31

Each one is a tight, mechanical add of withRequestMeta(...) + spreading meta.headers into the response. No response shape changes, no behavior changes.

2. Fix residual fabrication in /api/dashboard/stats

While adding apiHelpers I caught one more leftover: when the route's auth check fails, it was returning this hardcoded fallback:

return NextResponse.json({
  activeAgents: 14,
  chatSessions: 8,
  memoryNodes: 12400,
  securityBlocks: 47,
});

These are the exact same fake values PR #164 removed from the rendered dashboard. The dashboard now reads localStorage directly so it doesn't call this route, but anything else that hits /api/dashboard/stats unauthenticated still got the lie. Now returns zeros.

Trust impact

Verified

pnpm exec tsc --noEmit
# exit 0

pnpm check
copy-check: clean. Scanned 160 files across src, app.
capability-check: clean. Scanned 10 route files under app/api/tools.
trust-gate: clean. 16/44 route files import @/lib/apiHelpers and all call withRequestMeta.   <- was 11/44
check: 3 passed, 0 failed, 0 skipped

Net diff

5 files changed, 28 insertions(+), 15 deletions(-)

Rollback

Single git revert.

Two improvements in one PR.

## (1) Trust-gate coverage 11/44 -> 16/44

Five small routes adopted the @/lib/apiHelpers withRequestMeta pattern
that the larger /api/tools/* routes already use (PR #123) and the
observability sweep extended to the receipts/memory/agents/
notifications/audit families (PR #130). Now every response from
these routes carries `X-Request-Id`, supporting the request-id
propagation contract trust-gate enforces.

Routes:
- app/api/providers/route.ts        (6 lines)
- app/api/capabilities/route.ts    (25 lines)
- app/api/receipts/public-key/route.ts (26 lines)
- app/api/health/llm/route.ts      (27 lines)
- app/api/dashboard/stats/route.ts (31 lines)

## (2) Fix the residual fabrication in /api/dashboard/stats

While adding apiHelpers I found one more fabrication leftover: when
the route's auth check fails, it was returning a hardcoded fallback
payload:

  {
    activeAgents: 14,
    chatSessions: 8,
    memoryNodes: 12400,
    securityBlocks: 47,
  }

These are the **exact same fake values** PR #164 removed from the
rendered dashboard. The dashboard now reads from localStorage
directly so it doesn't call this route, but anything else that
hits /api/dashboard/stats unauthenticated still got the lie.

Now: returns zeros on the unauthenticated path. No fake demo data.

## Trust impact

Closes the dashboard fabrication arc started in #164/#165/#166. Adds
the request-id contract to 5 more routes.

## Verified

- pnpm exec tsc --noEmit: clean
- pnpm check: 3 passed, 0 failed
- trust-gate: 16/44 route files (was 11/44)

## Rollback

Single git revert. 5 files.
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 27, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
website Ready Ready Preview, Comment Apr 27, 2026 7:48am

@operatoruplift operatoruplift merged commit 7e24178 into master Apr 27, 2026
4 checks passed
operatoruplift added a commit that referenced this pull request Apr 27, 2026
)

Continuing the broadening from #167 / #168. Five more routes adopt the
withRequestMeta / errorResponse / validationError pattern.

| Route | Lines | Audience |
|---|---|---|
| app/api/waitlist/route.ts | 40 | Public signup |
| app/api/access/check/route.ts | 52 | Beta access gate |
| app/api/access/verify-payment/route.ts | 62 | On-chain payment confirm |
| app/api/integrations/google/connect/route.ts | 65 | OAuth start |
| app/api/integrations/google/callback/route.ts | 59 | OAuth complete |

The two integrations routes are notable because they redirect to
`/integrations?error=...` instead of returning JSON. Both now append
`&ref=<requestId>` to the redirect URL so support can correlate the
oauth-failure URL the user sees with the server log line, and
`withRequestMeta` still attaches `X-Request-Id` to the redirect.

The Google callback also gets a structured one-line JSON log on the
error path (matching the shape `errorResponse()` would produce, but
in-line because the response is a redirect, not a JSON envelope).

## Verified

- pnpm exec tsc --noEmit: clean
- pnpm check: 3 passed, 0 failed
- trust-gate: 26/44 route files (was 21/44)

## Rollback

Single git revert. 5 files.
operatoruplift added a commit that referenced this pull request Apr 27, 2026
)

Continuing the broadening from #167 / #168 / #169. Four more routes
adopt the withRequestMeta / errorResponse / validationError pattern.

| Route | What it does |
|---|---|
| app/api/profile/briefing/route.ts (GET + PUT) | Daily-briefing toggle on Profile |
| app/api/tools/notes/route.ts | Tier-1 notes tool (Supabase) |
| app/api/tools/tasks/route.ts | Tier-1 tasks tool (Supabase) |
| app/api/tools/imessage/route.ts | Photon iMessage send |

The four /api/tools/* routes already had a clean honest-status flow
(503 + action_required when an adapter is inactive, no fake success
on demo callers); this PR adds the X-Request-Id + structured error
envelope on top so the diagnostic line in CI logs and the response
header agree on the request identifier.

The imessage route's adapter-not-configured response now also carries
the canonical fields (errorClass: 'provider_unavailable', requestId,
timestamp, nextAction) so the chat UI can render it the same way it
renders calendar/gmail provider errors.

## Verified

- pnpm exec tsc --noEmit: clean
- pnpm check: 3 passed, 0 failed
- trust-gate: 30/44 route files (was 26/44)

## Rollback

Single git revert. 4 files.
operatoruplift added a commit that referenced this pull request Apr 27, 2026
)

Continuing the broadening from #167-#170. Three more routes adopt the
withRequestMeta / errorResponse / validationError pattern.

| Route | What it does |
|---|---|
| app/api/tools/web/route.ts | Web search + page fetch (Tier 1 tool) |
| app/api/tools/reminders/route.ts | Morning nudges (Tier 1 tool) |
| app/api/whoami/route.ts | Admin auth diagnostic (paywall bypass debug) |

The whoami route is particularly useful for support, ops can pass
?ref=<requestId> when troubleshooting a paywall-bypass issue and
the response carries the matching requestId for log correlation.

Web + reminders both already had the honest "demo_mode/simulated:true"
guard from the earlier session work, this PR just standardizes the
error envelopes (errorClass, requestId, timestamp, nextAction) and
attaches X-Request-Id to every response.

## Verified

- pnpm exec tsc --noEmit: clean
- pnpm check: 3 passed, 0 failed
- trust-gate: 33/44 route files (was 30/44)

## Rollback

Single git revert. 3 files.
operatoruplift added a commit that referenced this pull request Apr 27, 2026
…> 37/44) (#172)

Continuing the broadening from #167-#171. Four more routes adopt the
withRequestMeta / errorResponse / validationError pattern.

| Route | What it does |
|---|---|
| app/api/chat/route.ts | Main chat endpoint (highest-traffic) |
| app/api/cron/daily-briefing/route.ts | Cron job: daily agenda pinned message |
| app/api/cron/morning-briefing/route.ts | Cron job: 8am self-to-self Gmail summary |
| app/api/dev/reliability/timeout/route.ts | Admin-gated 504 simulator for /dev/reliability |

The chat route already had a private newRequestId() and a manual
{requestId, startedAt} hand-roll. This PR replaces both with the
canonical helper. Behavior is identical; the structured logs now
share a single `route` taxonomy ("chat" instead of inline string).

Both cron routes now log with a real requestId so a Vercel cron
failure can be traced from the cron log -> server log without
guessing. The unauthenticated-cron 401 also carries the requestId
so an attacker who probes the cron endpoint shows up in logs with a
correlatable identifier.

The reliability timeout harness already had a hand-rolled requestId
identical in shape to withRequestMeta's. Replaced.

## Verified

- pnpm exec tsc --noEmit: clean
- pnpm check: 3 passed, 0 failed
- trust-gate: 37/44 route files (was 33/44)

## Rollback

Single git revert. 4 files.
operatoruplift added a commit that referenced this pull request Apr 27, 2026
…on (#173)

Trust-gate 37/44 -> 41/44 (~93%). Includes one residual honesty fix.

## Routes adopted

| Route | What it does |
|---|---|
| app/api/debug/subscription/route.ts | Admin paywall diagnostic |
| app/api/debug/solana-wallet/route.ts | Admin x402 wallet diagnostic |
| app/api/tools/x402/route.ts | x402 fetch proxy + retry-with-proof |
| app/api/audit/publish-root/route.ts | Merkle-root publish to Anchor program |

## Honesty fix: x402 "charge" action retired

While adopting apiHelpers on app/api/tools/x402/route.ts I caught a
residual fabrication in the legacy "charge" action. It was building a
chargeRecord with `tx_signature: \`x402-devnet-${Date.now()}\``, a
fake transaction signature that mimicked a real on-chain identifier,
then logging it as `status: 'approved'`. The same fabrication class as
the council UI (#147), the dashboard widget (#164), and the
dashboard/stats fallback (#167).

Real settlement lives in /api/tools/x402/pay where a genuine ed25519
receipt is produced. Nothing in the codebase calls the legacy charge
action (grep'd to confirm). Replaced the action with a 410 Gone +
nextAction pointing callers at /api/tools/x402/pay.

The "fetch" and "retry_with_proof" actions are unchanged; both are
pure proxies that don't fabricate anything.

## Verified

- pnpm exec tsc --noEmit: clean
- pnpm check: 3 passed, 0 failed
- trust-gate: 41/44 (was 37/44)
- grep for `x402-devnet-`: zero hits

## Rollback

Single git revert. 4 files.
operatoruplift added a commit that referenced this pull request Apr 27, 2026
…IDs (#174)

The final 3 routes adopt the apiHelpers pattern. Trust-gate now reports
44/44 route files import @/lib/apiHelpers and all call withRequestMeta.

| Route | What it does |
|---|---|
| app/api/tools/tokens/route.ts | Tokens API (search, resolve, asset, price, risk, markets) |
| app/api/subscription/route.ts | Subscription GET + POST (create_invoice, dev_simulate, confirm) |
| app/api/webhooks/photon/route.ts | iMessage webhook receiver (signature-verified, idempotent) |

## Notable

### subscription
Already had a hand-rolled requestId + startedAt + X-Request-Id pattern
identical in shape to withRequestMeta. Replaced the local newRequestId()
function and the manual header construction with the helper. Behavior
identical; structured logs share a single `route` taxonomy.

### webhooks/photon
The webhook receiver had no request-id propagation, every response
just returned `{ ok: true, ... }`. Adding meta.headers to all 7
response paths (signature-fail, parse-fail, no-supabase, duplicate,
insert-error, success, GET-health-probe) and including requestId in
each body. Spectrum's webhook retry can now correlate to the server
log line.

### tools/tokens
Tier-1 tokens lookup tool. Already had a clean honest demo-mode 403
guard from earlier session work; this PR standardizes the error
envelope (errorClass, requestId, timestamp, nextAction) and adds
X-Request-Id to every response.

## Trust-gate journey

11/44 (25%) -> 16/44 -> 21/44 -> 26/44 -> 30/44 -> 33/44 -> 37/44 ->
41/44 -> 44/44 (100%) across PRs #167-#173 plus this one.

## Verified

- pnpm exec tsc --noEmit: clean
- pnpm check: 3 passed, 0 failed
- trust-gate: 44/44 route files (100%)

## Rollback

Single git revert. 3 files.
operatoruplift added a commit that referenced this pull request Apr 27, 2026
The legacy "confirm" fall-through path on POST /api/subscription was
a real security gap. Any authenticated user posting:

    POST /api/subscription
    Authorization: Bearer <valid-privy-jwt>
    Content-Type: application/json

    { "tx_signature": "anything-here" }

would be marked tier=pro, status=active without any on-chain
verification. The TODO at line 159 admitted it: "Verify the Solana
tx on-chain before activating. For devnet/demo, we trust the
client-provided signature." Same fabrication class as the council
PRs cleaned up (#147, #155, #156, #164, #167, #173).

No code path calls this fall-through (`grep -rn "tx_signature.*\
subscription"` returns zero in app/, src/). The paywall page uses
action="create_invoice" exclusively, and real settlement runs
through /api/access/verify-payment which DOES call verifyPayment()
against the Solana RPC.

Closed the path with HTTP 410 Gone + a nextAction pointing callers
at /api/access/verify-payment, the route that actually verifies the
on-chain tx.

The two known-good actions ("create_invoice" and "dev_simulate")
are unchanged.

## Verified

- pnpm exec tsc --noEmit: clean
- pnpm check: 3 passed, 0 failed
- grep -rn "tx_signature.*subscription" returns 0 callers

## Rollback

Single git revert. One file.
operatoruplift added a commit that referenced this pull request Apr 27, 2026
#183)

The middleware was a hidden gap in the trust-gate contract. Source
trust-gate (#167-#174) confirmed every route file imports
withRequestMeta. But the middleware in middleware.ts intercepts every
/api/* request first, and its 401 "Authentication required" response
went out without an X-Request-Id header.

Two changes:

## middleware.ts
- Mints `req_<uuid>` if the incoming request doesn't already carry an
  X-Request-Id, otherwise propagates.
- Attaches the ID to:
  - the 401 unauthorized response (status, body envelope, header)
  - every pass-through (forwarded as a request header so the route
    handler's `withRequestMeta` reads the same value)
- The 401 body now also carries `requestId` and `timestamp` to match
  the canonical envelope shape.

## tests/e2e/request-id-runtime.spec.ts (new)
- 16 probes against representative endpoints, all four response
  shapes (200 success, 401 middleware-block, 400/410/403 handler
  envelopes).
- Each asserts `X-Request-Id` is present and matches `^req_[0-9a-f-]+`.
- Picked endpoints to cover: public (capabilities, providers,
  sns.resolve, health.adapters, health.llm, waitlist, auth.login),
  auth-gated (whoami, dashboard.stats, receipts.public-key,
  subscription, x402, tools.web, tools.notes, access.check, risk).

## .github/workflows/ci.yml
- request-id-runtime.spec.ts joins the hermetic spec list, so a
  middleware regression that drops the header fails CI before merge.

Trust-gate is now enforced at BOTH layers: source (every route file
imports the helper) and runtime (every response carries the header).

## Verified

- pnpm exec tsc --noEmit: clean
- pnpm check: 3 passed, 0 failed
- pnpm exec playwright test request-id-runtime.spec.ts: 16/16 pass

## Rollback

Single git revert. Three files.
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