[Foundry] Decide & document connection topology: self-hosted relay vs direct URL, and the SSRF/URL-policy exemption #143
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Motivation/Context
Live NPC/token push (see the FoundryVTT live-push investigation, 2026-07-15) needs a hard architectural decision before any of #5's connection settings work can be considered final: how does the Quest Board backend actually reach a GM's Foundry instance, given that Foundry is frequently
localhost:30000on a GM's PC, or on a home LAN/VPN, never reliably reachable by direct inbound HTTP from the backend (#24 already calls this out explicitly)? The investigation evaluated three options: (A) direct backend→Foundry REST module HTTP call, (B) an outbound WebSocket relay — the ThreeHatsfoundryvtt-rest-apimodule already named in #24 is architected exactly this way, with the Foundry-side module dialing out to a relay server and the backend talking REST to the relay — and (C) browser-mediated push (rejected: HTTPS→HTTP mixed-content blocking, no server-side retries/idempotency, tab-dependent).This also surfaces a real code-level collision: every existing admin-configured service URL is normalized through
normalize_service_url()inwebapp/backend/app/services/url_policy_service.py, called fromsettings_service.py(e.g. lines 167–171, 187–192) with private-IP targets blocked unlesssettings.allow_private_service_urlsis set. A per-campaign, GM-settable Foundry/relay URL pointing at a LAN address is by design for this feature — so #5's connection-settings shape cannot be finalized until this issue resolves whether/how the Foundry URL is exempted from that SSRF guard.Approach
Recommended architecture per the investigation: Option B (relay) as the primary mechanism, with Option A (direct URL) as a degenerate case of the same code path —
foundry/client.py(per #24) just speaks to "a base URL + API key"; whether that URL is a self-hosted relay or a directly reachable Foundry endpoint is a per-campaign configuration detail, not an architectural fork.For the SSRF collision, the investigation's recommended resolution (the safer of the two options considered) is:
app_settingslike any other admin-configured service endpoint (no SSRF exposure — it's an admin, not GM, setting).normalize_service_urlwith its own allowlist policy) should be documented as rejected, with the reasoning, if this issue confirms the admin-relay approach.Concrete deliverables for this issue:
foundryvtt-rest-apimodule's relay mode against the actual production Foundry instance and core version (per #24's explicit sequencing warning — this must happen before adapter code in #7 is written).Dependencies
Out of scope
npc_to_actor, etc.) — tracked separately in #7/#9.Acceptance criteria
foundryvtt-rest-apimodule's relay mode has been validated end-to-end against the real production Foundry instance (tested module + core version pair recorded).normalize_service_urlis specified.foundry_linksdocument-mapping table for idempotent push #146sidebar_fields, #130) intonpc_to_actor#148