Skip to content

[RFC][WIP] Early, early draft of displaying Galaxy notebooks in Loom & auto-syncing.#22988

Draft
jmchilton wants to merge 3 commits into
galaxyproject:devfrom
jmchilton:notebook_iframe
Draft

[RFC][WIP] Early, early draft of displaying Galaxy notebooks in Loom & auto-syncing.#22988
jmchilton wants to merge 3 commits into
galaxyproject:devfrom
jmchilton:notebook_iframe

Conversation

@jmchilton

@jmchilton jmchilton commented Jun 22, 2026

Copy link
Copy Markdown
Member

Posted by Claude (AI assistant) on behalf of @jmchilton — drafted with their direction, not authored by them personally.

⚠️ [RFC][WIP] — early, early draft. Not for merge. Sharing the Galaxy-side half of a cross-repo spike so the shape can be discussed. Expect rough edges, missing tests/docs, and churn.

What this is

The Galaxy server changes that let an external shell (Loom/Orbit) embed a private, history-attached notebook Page and have it render with full Galaxy fidelity (live dataset displays, visualizations, charts) — without publishing the page and without handing the shell a full API key.

Paired with the Loom side (the embedder + brain): galaxyproject/loom#351.

Pieces (high level)

  • Scoped embed-token auth. POST /api/pages/{id}/embed_token mints a short-TTL, page-scoped token (PageEmbedToken model + migration). A per-route embed_allowed=True decorator opts specific GET/HEAD routes into token auth (page show, dataset display/show/metadata_file, collection show); the token resolves to the owning user, with containment from the route allow-list + GET/HEAD-only marking. Load-bearing invariant: never mark a listing/enumeration route. Account endpoints (/api/users/*) are never marked, so an exfiltrated token can't reach the api-key sink.
  • Embeddable surface. Reuse /published/page?id=X&embed=true (already chrome-free via PageView); add a configurable embed_allowed_originsContent-Security-Policy: frame-ancestors (Galaxy has no frame CSP today), and an embed_url convenience field on PageDetails.
  • postMessage embed bridge in PageView.vue (ready/height/title/navigate, origin-restricted) for an iframe-based shell.
  • Embed theming — a built-in orbit theme + content theming, and hiding the duplicate title in the embedded view.

Status / asks

  • This is a design spike, not a finished PR — feedback on the auth model (per-route allow-list + full-user recovery vs. a true scoped capability) is the main thing I'm after.
  • Security notes, the route allow-list snapshot test, and the model pivot rationale live alongside the Loom-side design doc.

🤖 Generated with Claude Code

jmchilton and others added 3 commits June 21, 2026 16:41
Short-lived per-user PageEmbedToken (15m TTL, x-galaxy-embed-token header) honored only on routes opting in via embed_allowed=True (read-only GET/HEAD allow-list; never enumeration). Token recovers the full owning user; native ACLs gate which resources. Lets an external shell (Loom/Orbit) embed a private, history-attached page chrome-free without injecting the API key (Q5).

- model: PageEmbedToken (CSPRNG token) + migration f0e1d2c3b4a5
- auth: embed_token_dependency + get_api_user via request.state.embed_user; owner-gated POST /api/pages/{id}/embed_token mint
- allow-list: page show, datasets display/show/metadata_file (GET/HEAD), dataset_collections show; client dataset element repointed to FastAPI route
- embed_url on PageDetails
- frame-ancestors CSP via embed_allowed_origins config (distinct from CORS allowed_origin_hostnames)
- PageView postMessage bridge (ready/height/title/navigate), origin-restricted (never *)
- tests: embed-token API (cross-user denial, Q5, embed_url) + 3 py unit suites (token expiry, route allow-list snapshot, CSP middleware) + vitest for the postMessage bridge

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Themes only styled the masthead; embedded pages (Loom/Orbit notebook iframe)
rendered light on a dark shell. Extend the theme system into page content:

- orbit built-in theme (util/themes.py) seeded into config.themes regardless of
  themes_conf.yml (no sample fallback), admin-overridable. Its content: block
  flattens to --content-* css vars.
- App.vue honors ?theme=<id> only when embedded (resolveTheme.ts), tagging the
  root embed-themed; never overrides a logged-in user's pref.
- PageView dark prose CSS gated on #app.embed-themed (non-scoped block; a scoped
  :global() descendant is mis-compiled). Defaults preserve light, so normal and
  un-themed views are byte-unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PageView's chrome-free branch renders its own page-title Heading, but the
Markdown component also renders a sticky title header, so embedded pages showed
the title twice (the second was light-on-white and only became visible once
content theming landed). Add a hideHeader prop to Markdown and set it on
PageView's chrome-free render, keeping the intended page-title.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant