AviationStack API calls cost ~$100/day because each cache miss triggered
114 individual API calls from Vercel Edge (where isolates don't share
in-flight dedup). This moves all AviationStack fetching to the Railway
relay (like market data, OREF, UCDP) and reduces to 40 top international
hubs (down from 114).
- Add AVIATIONSTACK_AIRPORTS constant (40 curated IATA codes)
- Add startAviationSeedLoop() to ais-relay.cjs (2h interval, 4h TTL)
- Make Vercel handler cache-read-only (getCachedJson + simulation fallback)
- Delete Vercel cron (warm-aviation-cache.ts) and remove from vercel.json
- Add wildcard CORS headers in vercel.json for /api/* routes so Vercel
infra 500s (which bypass edge function code) still include CORS headers
- Bump rate limit from 300 to 600 req/60s in both rate-limit files to
accommodate dashboard init burst (~30-40 parallel requests)
- Add smartraveller.gov.au (bare + www) to Railway relay RSS allowlist
- Add redirect hostname validation in fetchWithRedirects to prevent SSRF
via open redirects on allowed domains
Vercel Firewall injects an obfuscated bot-protection challenge script
into served pages. Our hash-based script-src in vercel.json blocked
this injected script, causing CSP violations and SyntaxError.
The fix uses a dual-policy approach:
- vercel.json header: 'unsafe-inline' (allows Vercel challenge scripts)
- index.html <meta> tag: strict hash-based (enforced on our pages)
When both CSP policies exist, browsers enforce both — our <meta> tag's
strict hashes still protect our actual pages.
Vercel routes dynamic [domain] segments BEFORE specific directory names,
so api/[domain]/v1/[rpc].ts was catching every request meant for
api/intelligence/v1/[rpc].ts, api/economic/v1/[rpc].ts, etc. and
returning 404. Delete the catch-all so requests reach the real handlers.
Also: add missing CSP script hash, add list-iran-events cache tier,
and update route-cache-tier test to read from server/gateway.ts.
Remove 'unsafe-inline' from script-src in both index.html and vercel.json,
replacing it with SHA-256 hashes of the two static inline theme-detection
scripts. Add security.txt, sitemap.xml, and Sitemap directive in robots.txt
to improve scanner reputation. Fix stale variant-metadata test that was
reading vite.config.ts instead of the extracted variant-meta.ts module.
Expand Permissions-Policy from 3 to 19 directives (16 fully disabled,
3 delegated to YouTube origins for embed compatibility). Remove
unencrypted ws: and dev-only http://localhost:5173 from production CSP
connect-src. Add 5 guardrail tests to prevent regressions.
- Remove PostHog analytics runtime and configuration
- Add API rate limiting (api/_rate-limit.js)
- Harden traffic controls across edge functions
- Add runtime fallback controls and data-loader improvements
- Add military base data scripts (fetch-mirta-bases, fetch-osm-bases)
- Gitignore large raw data files
- Settings playground prototypes
Vercel's :path* wildcard doesn't match trailing slashes that
PostHog SDK appends (e.g. /ingest/s/?compression=...), causing 404s.
Switch to :path(.*) which matches all path segments including
trailing slashes. Ref: PostHog/posthog#17596
Add X-Content-Type-Options, X-Frame-Options, Strict-Transport-Security,
Content-Security-Policy, Referrer-Policy, and Permissions-Policy headers
to all routes via vercel.json catch-all pattern.
Invert the path logic from an allowlist of watched directories to an
exclusion list (*.md, .planning, docs, e2e, scripts, .github). This
brings the command from 318 to 242 characters while keeping the same
build-trigger behavior for all source code changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add /ingest/* rewrites in vercel.json → us.i.posthog.com
- Web uses /ingest proxy, desktop uses direct PostHog host
- Enable capture_pageview so every visitor registers
Guard ignoreCommand against unset VERCEL_GIT_PREVIOUS_SHA (first deploy,
force deploy) which caused git diff to fail → build canceled. Also add
data/ to watched paths.
- fred-data: batch mode (comma-separated series_id) reduces 7 edge
function invocations to 1; cap at 15 series; propagate upstream
502s instead of masking as empty 200; add X-Data-Status header
- ucdp-events: parallelize page fetches; track failed pages and use
short cache TTL for partial results instead of caching at full 6h
- ucdp: add OPTIONS/method guard matching ucdp-events pattern
- middleware: exact-match social bot paths instead of startsWith
- vercel.json: use VERCEL_GIT_PREVIOUS_SHA for multi-commit diffs;
add middleware.ts, settings.html, vercel.json to watch list
- Panel.ts: use safeHtml() allowlist sanitizer for tooltip content
- dom-utils: add safeHtml() with tag/attribute allowlist and
javascript: URI blocking
- vercel.json: add no-cache headers for / and /index.html so CDN never serves stale HTML
- main.ts: add vite:preloadError listener to auto-reload once on chunk 404s
- vite.config.ts: remove HTML from SW precache, add NetworkFirst for navigation requests