Skip to content

Replace local microserver asset retrieval (/retrieve/) with hosted HTTPS asset delivery #93

Description

@petercorke

Migrated from FUTURE-NO-MICROSERVER.md (repo root), written 2026-07-26,
before the frontend rebuild. File references in the original doc were
already stale by the time it was written down (swift/public/js/lib.js
-> shapes.js/main.js; next-swift/server.ts no longer exists,
next-swift/+swift/out/ were removed entirely) -- but the architectural
direction below still applies and hasn't been implemented.
SwiftRoute.py still has the /retrieve/ passthrough this proposes
removing. See also #86 (JupyterLite/Pyodide transport) -- the
WebSocketTransport seam built during the rebuild is a narrower,
comms-only piece of this same underlying problem (assets, not just
messages).

Goal

Replace local /retrieve/... file access with standard HTTPS asset
delivery, while preserving: normal desktop Python usage; notebook
embedding (browser="notebook"); practical support for Jupyter and
JupyterLite display workflows.

Current state

start_servers() spins up a WebSocket service and a local HTTP server
serving the UI + a /retrieve/... route that reads an arbitrary local
filesystem path and serves it back. Notebook mode embeds a local URL in
an IFrame. Scene object loading (ColladaLoader/STLLoader/
OBJLoader/etc.) consumes ob.filename values as URLs -- currently
always local.

Architecture direction

Adopt an explicit Asset URL strategy:

  1. viewer UI served from a web origin (or local dev during development
    only);
  2. mesh/texture assets fetched by normal HTTPS URLs;
  3. no direct filesystem reads through /retrieve routes;
  4. Python emits resolvable URLs (or URL templates), not local file paths.

Suggested API shape: asset_mode: "local" | "remote" | "auto",
asset_base_url (e.g. https://assets.example.com/swift-assets/),
optional asset_url_resolver(path) -> str callback override. Pass
through URLs unchanged; convert local/relative paths under
asset_base_url when asset_mode=remote; keep legacy behavior for
asset_mode=local.

Phased plan (originally sketched)

  1. Discovery: inventory every place ob.filename is produced,
    classify (absolute local / relative package / already-remote), build
    a compatibility matrix (desktop+browser, Jupyter local, JupyterHub
    remote, JupyterLite/Pyodide).
  2. URL resolution layer (Python): central resolver utility per the
    API shape above, with unit tests (local path, remote passthrough,
    Windows path normalization, URL escaping).
  3. Frontend hardening: ensure all loaders accept remote URLs as-is,
    remove local-path-specific mutations, surface loader errors with the
    attempted URL + HTTP status.
  4. Remove /retrieve: delete the passthrough branch from
    SwiftRoute.py; keep static UI serving for local-only mode if still
    wanted.
  5. Server-side asset hosting: stable URL prefix (e.g.
    /swift-assets/), deterministic paths, immutable cache policy,
    correct content-types per format, version/hash in path to avoid
    stale-cache collisions.
  6. Jupyter/JupyterLite display strategy: explicit ui_url/ws_url/
    rtc_url/asset_base_url options for hosted notebook mode.
    JupyterLite specifically has no local CPython server process
    available at all -- initial target should be a display-only
    playback mode (precomputed trajectory/events JSON, animate from
    static data, no live control loop), with full interactive mode via a
    remote WS backend as a later option.

CORS implications by hosting target

  • GitHub-hosted (Pages / raw.githubusercontent.com / release
    assets): limited per-path header control, especially on Pages --
    prefer same-origin viewer+assets where possible; front with a
    CDN/proxy if cross-origin header tuning is unavoidable.
  • Other managed hosting (e.g. Apache/Nginx): explicit
    Access-Control-Allow-Origin/-Methods/-Headers/-Expose-Headers
    config available via server config or .htaccess; use an explicit
    allow-list rather than wildcard for stricter security postures. If
    the WebSocket endpoint is also hosted there: TLS (wss://), reverse
    proxy upgrade headers, origin checks at the WS server layer.

Security requirements

  1. Remove filesystem passthrough serving entirely.
  2. Enforce HTTPS URLs for remote mode.
  3. Optional allow-list for asset host domains.
  4. Clear logs for denied/invalid asset URLs.

Testing plan (sketch)

  • Unit: URL resolver (local->remote, HTTPS passthrough, Windows path
    normalization, escaping).
  • Integration: viewer with remote asset URLs loads meshes correctly; no
    /retrieve path required; Jupyter embed smoke test (iframe loads, a
    robot + a shape appear, stepping updates pose); negative test for a
    disallowed origin / missing CORS header producing a clear surfaced
    error.
  • Manual matrix: local desktop + local browser; JupyterLab local;
    JupyterHub remote; JupyterLite static demo playback.

Rollout strategy

Add the resolver behind opt-in flags -> ship dual mode (local +
remote) for one release cycle -> gather feedback, fix notebook/cloud
edge cases -> deprecate /retrieve with a warning -> remove /retrieve
and local filesystem access in a later minor/major release.

Open questions (unresolved)

  1. Where should canonical asset hosting live long-term (GitHub+CDN vs.
    another managed host)?
  2. Do we require fully offline local mode forever, or can it become
    optional?
  3. Should remote asset URLs be generated by Swift only, or pre-baked by
    upstream model packages?
  4. Is live interactive JupyterLite a requirement, or is display-only
    acceptable for a first release?

Definition of done

  1. No runtime reliance on /retrieve in the production path.
  2. Remote HTTPS assets load in browser, notebook, and hosted notebook
    contexts.
  3. CORS behavior documented and tested for chosen host target(s).
  4. Jupyter display works with hosted UI/assets.
  5. JupyterLite has at least one supported display mode with documented
    limitations.

Likely files to touch

  • src/swift/Swift.py -- public launch/config API for asset hosting
    mode and URL options.
  • src/swift/SwiftRoute.py -- remove /retrieve passthrough; support
    notebook mode with externally hosted ui_url and explicit WS/RTC
    URLs.
  • src/swift/public/js/shapes.js -- ensure loaders use the provided URL
    as-is; improve error callbacks/logging for failed fetches.
  • tests/ -- resolver unit tests, no-/retrieve integration tests,
    notebook-oriented smoke tests where feasible.
  • README.md / docs -- hosted-asset deployment docs, CORS expectations,
    notebook/JupyterLite supported modes.
  • examples/ -- a hosted-assets example, a JupyterLite-compatible
    display-only playback example.

Metadata

Metadata

Assignees

No one assigned

    Labels

    tech-debtKnown technical debt / deferred cleanup, not a live bug

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions