Skip to content

Option A's proxy doesn't exist on the standalone (non-WordPress) deployments

Area: Spike 6 / URL strategy / mandala-om deployment topology / WAF Raised during: Spike 6 (2026-08-20) — generalizing the /proxy/json gate in mandala-om Jira: (add when available) Priority: Medium — no known break today, but it means the decided URL strategy covers only part of the deployment matrix, which matters at D11 cutover Priority: Medium → raised 2026-08-20: browser evidence now indicates the WAF fires for browser cross-origin requests generally, not just from the thlib.org origin — so the standalone deployments (including production mandala.kmaps.virginia.edu) are very likely genuinely exposed, not theoretically so. Matters at D11 cutover and arguably before.

What we found

ADR-less decision, 2026-08-12: Option A — route every asset JSON fetch through mandala-wp-proxy's same-origin /proxy/json, so the browser never makes the cross-origin call the AWS WAF blocks. The spike doc describes this as generalizing the proxy "to all apps" and calls the proxy "the permanent architecture."

But the proxy is a WordPress plugin, and most mandala-om deployments have no WordPress. Counted directly in mandala-om (release/v1.1.0-rc): REACT_APP_WP_PROXY is defined in 2 of 11 env files.

Env file REACT_APP_WP_PROXY Deployment
.env.tibet.prod https://thlib.org/proxy WordPress-embedded (thlib.org)
.env.tibet.staging https://staging.thlib.org/proxy WordPress-embedded (staging)
.env.production Standalone — mandala.kmaps.virginia.edu
.env.development Local dev
.env.uf / .env.uf.prod Standalone (UF)
.env.cj.dev / .env.cj.prod Standalone (CJ)
.env.contport.prod Standalone (has REACT_APP_JSON_PROXY but no WP_PROXY)
.env.tibet (base tibet, no proxy set)
.env.test Test

So Option A, as implemented, protects the thlib.org WordPress embeds. The standalone builds — including the production mandala.kmaps.virginia.edu deployment — have no /proxy/json endpoint available and must keep making direct cross-origin JSONP calls, which is exactly the fetch pattern that produced the 2026-07-29 Sources 503.

Why this wasn't visible before

The 2026-07-29 fix and the 2026-08-12 decision were both driven by the Sources incident, which surfaced on thlib.org — a WordPress-embedded deployment where the proxy does exist. The question "what about the deployments without WordPress?" never came up because the incident didn't occur there. The spike doc's Option A analysis discusses WordPress as an implementation detail of the proxy ("adds a proxy hop + couples to WordPress; owner/host TBD") but doesn't note that the coupling makes the strategy inapplicable to a subset of deployments.

Current state (2026-08-20)

The client-side generalization (mandala-om branch feat/generalize-json-proxy-all-sites, commit e6e712ae) deliberately preserves the fall-through: when REACT_APP_WP_PROXY is unset, the code proceeds to direct JSONP rather than erroring. That is correct behaviour given the above — the absence of a proxy is a supported configuration, not a misconfiguration. An earlier framing of this fall-through as a possible bug ("should it fail loudly?") was withdrawn once the env-file counts were checked.

Net effect: the standalone deployments are no worse off than before, but they are not covered by the decided strategy either.

What's undecided

  1. Are the standalone deployments actually WAF-exposed? The 503 was observed against sources.mandala.library.virginia.edu from a browser on thlib.org. Whether the same rule fires for a browser on mandala.kmaps.virginia.edu was never tested — different origin, possibly different WAF evaluation. This is the cheapest thing to check and should come first; if they aren't exposed, the gap is theoretical.
  2. ~~Are the standalone deployments actually WAF-exposed?~~ Substantially answered 2026-08-20 — and the answer points to "yes, exposed." A real Chrome browser on http://localhost:3000 (a non-Mandala, non-WordPress origin) requesting sources.mandala.library.virginia.edu/sources-api/json/25581?callback=… received a 503, while every Solr request on the same page load returned 200. So the WAF rule is not scoped to the thlib.org origin — it fires for browser cross-origin requests generally, which is precisely what a standalone deployment does. This is strong evidence rather than proof for mandala.kmaps.virginia.edu specifically, since that exact origin still hasn't been tested; but the "maybe standalone origins are evaluated differently" hope is no longer the likely case, and the gap should be treated as real, not theoretical.

Narrowed 2026-08-24: the exposure is Sources-only. The four other Drupal hosts were tested cross-origin for the first time, from a real browser on https://thlib.org using <script>-tag injection: texts, images, visuals and audio-video all served cross-origin JSONP normally, while sources returned the 503. Sources is also specifically blocked rather than merely unhealthy — the same URL returns 200 to curl even with a browser User-Agent, Origin and Referer, and the site root is 200.

So the standalone deployments are exposed for Sources detail views only, not for asset JSON generally. That shrinks the gap considerably, and it retires the assumption — recorded in this note's own framing, and inherited by the mandala-om branch — that the WAF rule is a general property of the Drupal hosts. It never was; the original July fix's own code comment said so, and the broader claim was inference that nobody had tested. See session log 2026-08-24.

Still untested: the mandala.kmaps.virginia.edu origin specifically.

Methodological warning for whoever re-tests this: a curl replay carrying full browser headers (Origin, Referer, Sec-Fetch-*, Chrome UA) returned 200 for the very same URL that the real browser got 503 on. The rule keys on something header-spoofing cannot reproduce (TLS/JA3 fingerprint, header ordering, or similar). Only a real browser is valid evidence here — a curl 200 means nothing. An intermediate diagnosis in the 2026-08-20 session was built on exactly that false negative and had to be retracted. 2. If they are exposed, what covers them? Options that don't depend on WordPress: - Native CORS on D11 (the spike's Option B, currently marked superseded) — the standalone deployments are exactly the case Option B was suited to. - A non-WordPress proxy tier (the spike's own "dedicated proxy service" alternative, raised in the pre-findings and never resolved). - Accept that standalone deployments run without WAF protection, documented as a known limitation. 3. Does "Option A is the permanent architecture" need restating? As written, the decision reads as covering everything. It covers the WordPress embeds. Whether that warrants a superseding note or just a scope clarification in the spike doc is a judgement call for whoever picks this up — the decision itself isn't wrong, its stated scope is too broad.

Cross-references