Skip to content

Integrate app hardening and shared runtime updates - #119

Merged
jaywedgeworth22 merged 3 commits into
mainfrom
codex/integrate-app-hardening-shared-20260630
Jun 30, 2026
Merged

Integrate app hardening and shared runtime updates#119
jaywedgeworth22 merged 3 commits into
mainfrom
codex/integrate-app-hardening-shared-20260630

Conversation

@jaywedgeworth22

@jaywedgeworth22 jaywedgeworth22 commented Jun 30, 2026

Copy link
Copy Markdown
Owner

Summary

  • integrates the current app-hardening work on top of the latest shared-package main
  • keeps the shared runtime consumption from PR Use shared package for peer share payloads #118 and preferred ticker repair from PR Resolve preferred-share ticker variants #117
  • adds CodeQL workflow, disclosure latency watch, webhook target handling, usage telemetry, subscription/billing hardening, and client/mobile docs/UI updates
  • excludes local Xcode user-state files while keeping workspace metadata needed by the iOS project

Verification

  • npm ci
  • npm run typecheck
  • npm test (73 files, 647 tests)

Notes


Note

High Risk
Touches production deploy gates, billing webhook idempotency, webhook outbound security, and new D1 migrations—areas where mistakes affect live schema, payments, or delivery.

Overview
This PR integrates app hardening and shared-runtime updates: deploy/ops paths, billing, delivery security, admin tooling, and the mobile client API surface.

Deploy & preview: ship.sh now requires ADMIN_TOKEN by default, checks /api/health (not /health), and runs idempotent POST /api/admin/migrate; --deploy-only skips admin steps. Preview deploy auto-provisions when config is missing and validates preview health. Docs (STATUS.md, PR template) steer production schema through admin migrate, not remote Wrangler D1 migration apply.

Security & reliability: New CodeQL workflow on main. Stripe webhooks use a durable stripe_webhook_events ledger (claim → process → mark/release on failure) with duplicate-event tests. Webhook delivery blocks unsafe production targets (private/metadata IPs), uses redirect: 'manual', and aborts slow POSTs (~10s). Subscription secrets are redacted in admin/client command persistence ( hasSecret only).

Ingestion & admin: Migrations 0021 (FMP disclosure latency watch) and 0022 (Stripe events), mirrored in admin migrate. Admin adds GET /disclosure-latency and POST /disclosure-latency/probe; cron runs the FMP probe. Reprocess uses hasHardFailureFlags (includes bad_asset_name). House backfill respects maxFilings before marking filings seen so capped runs can enqueue later.

Client API: Public GET /api/client/v1/trade/:id, ticker/:ticker, and member/:memberIdOrName with feed-style envelopes, summaries, and bootstrap endpoint pointers.

Other: Usage-monitor and FMP disclosure watch env examples; iOS workspace + .gitignore for Xcode user state; widespread docs/UI copy from “member” to “politician” (API field names largely unchanged).

Reviewed by Cursor Bugbot for commit 4ddd3f3. Configure here.

@cursor

cursor Bot commented Jun 30, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_e6748377-ce1c-4200-9f81-9b79974302ae)

@jaywedgeworth22
jaywedgeworth22 merged commit 9a30bb5 into main Jun 30, 2026
2 checks passed
@jaywedgeworth22
jaywedgeworth22 deleted the codex/integrate-app-hardening-shared-20260630 branch June 30, 2026 17:09

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4ddd3f31fb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread app/src/billing/routes.ts
let claimed = false;
try {
claimed = await claimStripeWebhookEvent(c.env, event.id, event.type);
if (!claimed) return c.json({ received: true, duplicate: true });

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 Badge Avoid skipping unprocessed Stripe webhook events

If the Worker crashes, times out, or otherwise exits after inserting the stripe_webhook_events row but before markStripeWebhookEventProcessed runs, the row remains with processed_at = NULL. On Stripe's retry for that same event id, claimStripeWebhookEvent returns false and this branch acknowledges it as a duplicate, so the subscription/customer side effects can be skipped permanently even though the event was never processed. Check processed_at (or use a stale/in-progress claim) before returning duplicate.

Useful? React with 👍 / 👎.

row.sourceUrl,
row.filedDate,
row.filerName,
JSON.stringify(row.payload).slice(0, PAYLOAD_LIMIT),

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 Badge Store valid JSON for truncated FMP payloads

When an FMP disclosure row serializes beyond PAYLOAD_LIMIT, this blind .slice() can store an invalid JSON fragment. The probe reads these observations back and calls JSON.parse(providerRow.payload), so one large provider payload can make every matching probe abort instead of updating candidates. Truncate a field inside a valid wrapper or skip storing the oversized payload rather than cutting the JSON string mid-token.

Useful? React with 👍 / 👎.

@@ -304,10 +301,5 @@ export async function runWatcher(env: Env, now: Date = new Date()): Promise<void
}
} catch (err) {
await recordSourceError(env, 'senate', now.toISOString(), err);

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 Badge Preserve poll backoff after source failures

When the Senate source hits a transient 403/429 or similar upstream failure, this catch now only logs the failure and leaves last_poll:senate at the previous successful poll. Once the configured interval has elapsed, every minute-long cron tick still satisfies shouldPollNow, so the Worker retries and writes ingest_log rows every minute until the source recovers instead of respecting the configured cadence. Stamp a failure/backoff timestamp here, as before, to avoid hammering the source and flooding logs.

Useful? React with 👍 / 👎.

Comment on lines +373 to +376
await Promise.all([
fetchFmpLatest(apiKey, 'house', max, fetchImpl),
fetchFmpLatest(apiKey, 'senate', max, fetchImpl),
])

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 Badge Count disclosure probes against FMP usage

When FMP_DISCLOSURE_WATCH_ENABLED=true, the scheduled handler runs this probe every minute, making two FMP calls per tick, but these calls never increment the shared FMP daily counter used by /enrich-securities/status and the new usage telemetry. That means enabling the latency monitor can consume thousands of FMP requests per day while the app still reports only enrichment/price-refresh usage and the internal FMP_DAILY_CALL_CAP cannot protect the key. Record these calls with the same budget/telemetry path or a separate explicit counter.

Useful? React with 👍 / 👎.

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