Skip to content

[hosting] Dual-deploy: HF Space (weights from HF CDN) alongside GitHub Pages (weights from Cyfronet) #64

Description

@grzanka

Context

Related apps like Xenova/whisper-web (repo) demonstrate the same shape of app — a static, client-side transformers.js app — hosted as a Hugging Face Static Space. Since aidedx already builds a fully static bundle (@sveltejs/adapter-static, pnpm buildbuild/), the same target is reachable with no framework changes.

Proposal: maintain two live deployments rather than choosing one:

  • GitHub Pages (existing, deploy.yml) — weights from the Cyfronet S3 mirror (MODEL_MIRROR_HOST, docs/model-hosting-cyfronet.md).
  • Hugging Face Space (new) — weights from Hugging Face's own CDN (transformers.js default env.remoteHost).

This is the exact escape hatch the code already anticipated: src/lib/models/remote.ts's MODEL_MIRROR_HOST comment says it's hardcoded "rather than build-time config... until a second host is needed." This issue is that second host.

Findings from a design conversation (2026-07-15)

  • Build output is already portable. svelte.config.js normalizes BASE_PATH for GitHub Pages' /aidedx subpath; a Space serves from its own root, so the Space build just needs BASE_PATH="". No other adapter changes needed.
  • The original case for self-hosting weights on Cyfronet (docs/model-hosting-cyfronet.md "Why") was three things: (1) control over CORS/CORP response headers ahead of COEP isolation, (2) not depending on HF's CDN availability/rate limits, (3) EU/Poland latency for the target user base. Revisiting each for the HF-Space case:
    1. Weaker than originally stated. docs/threading-coop-coep.md (the later COOP/COEP threading spike) found that COEP credentialless loads cross-origin subresources fine with no CORP header at all — that's why the Cyfronet bucket's plain ACAO: * CORS policy was sufficient without adding a CORP header. HF's CDN would very likely work the same way under credentialless, but this is unverified — see open question below.
    2. Inverted once the app itself is on HF. The original reasoning assumed the app shell (GitHub Pages) was independent of HF's infrastructure. If a second deployment's app shell is an HF Space, an HF outage already takes that deployment down regardless of where its weights live — so fetching weights from HF too is a much smaller incremental risk for that specific deployment. It's still a good reason to keep Cyfronet as the weight source for the GitHub Pages deployment.
    3. Unchanged / untested. No latency comparison between HF's CDN and Cyfronet has been run.
  • Net read: the two-deployment split lets each surface use its natural weight source instead of forcing one global choice — GitHub Pages stays on Cyfronet (EU latency, header control), the HF Space uses HF's own CDN (simplest, no mirror maintenance for that surface, and the app is already coupled to HF's uptime either way).

Plan

  1. Model-host build switch. MODEL_MIRROR_HOST is currently a hardcoded constant read unconditionally in download.ts/transcribe.ts. Needs to become build-time-selectable, mirroring how BASE_PATH already works — but note remote.ts runs in the client bundle, not svelte.config.js's Node build step, so it needs a Vite-exposed env var (e.g. $env/static/public or import.meta.env.VITE_MODEL_HOST), not raw process.env. Default (no env var) should stay Cyfronet, matching current behavior.
  2. Second deploy workflow. New .github/workflows/deploy-hf-space.yml (or similar) paralleling deploy.yml: build with BASE_PATH="" and the HF-CDN model host, then push build/ to an HF Space git repo. Needs an HF_TOKEN secret with write access to the Space.
  3. HF Space repo setup. Create the Space, add a README.md with sdk: static YAML frontmatter (HF's required manifest for a static Space).
  4. COOP/COEP verification on HF. Confirm whether HF's "static" Space type allows setting custom response headers at all, or is static-file-only like GitHub Pages. If the latter, coi-serviceworker.js (already vendored in static/) stays needed on both deployments — no simplification there, just a difference in weight source.
  5. Live verification. Once both deployments exist, confirm on the real HF Space (not just local simulation) that whisper-small loads from huggingface.co under COEP credentialless with no CORP headers, matching the local finding in docs/threading-coop-coep.md.
  6. Docs. Record the outcome in a new or updated docs/*.md (per this repo's convention that spike/investigation conclusions land in a committed doc, not just issue comments) — likely an update to docs/model-hosting-cyfronet.md's "Why" section once the CORP-header question is verified live, plus a new short doc for the Space deployment itself.

Open questions

  • Does an HF static Space allow custom response headers, or is it as constrained as GitHub Pages? Determines whether step 4 is a no-op or needs its own investigation.
  • Is there a measurable latency difference between HF's CDN and the Cyfronet mirror for the actual target audience? Not blocking, but would sharpen the "why keep two deployments" story for future readers.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions