Skip to content

feat(embed): add a versioned postMessage API for host pages - #1468

Merged
giswqs merged 2 commits into
mainfrom
feat/issue-1462-embed-postmessage-api
Jul 27, 2026
Merged

feat(embed): add a versioned postMessage API for host pages#1468
giswqs merged 2 commits into
mainfrom
feat/issue-1462-embed-postmessage-api

Conversation

@giswqs

@giswqs giswqs commented Jul 27, 2026

Copy link
Copy Markdown
Member

Fixes #1462

What this adds

URL parameters (?url=, ?maponly, ?tool=) configure an embed once, at load. A host page that frames GeoLibre had no way to keep talking to a live map: every interaction meant reloading the iframe, throwing away the analyst's session, and nothing the user did inside the map was visible to the host.

This adds an opt-in, versioned postMessage protocol layered on the existing embed plumbing.

Host to GeoLibre

Type Payload Effect
loadProject { url } Loads a .geolibre.json without reloading the iframe
setView { bbox } or { center, zoom, bearing, pitch, duration } Fits a box, or flies the camera
highlightFeature { layerId, featureId | featureIds | filter, fit } Selects and highlights; filter matches properties
openTool { id, params } Opens Processing on a tool with params pre-filled (runtime twin of ?tool=)

GeoLibre to host: ready, ack (for any message sent with a requestId), projectLoaded, selectionChanged, viewChanged (throttled to ~4/s), toolCompleted, serverFileWritten.

Every message is { v: 1, type, payload }; app messages also carry source: "geolibre" so a host can filter its own traffic.

Security

The API is off by default. It activates only when the deployment names the origins it trusts:

docker run -e GEOLIBRE_EMBED_ORIGINS="https://portal.example.com" ghcr.io/opengeos/geolibre:latest

(or VITE_GEOLIBRE_EMBED_ORIGINS at build time for a static deployment). A public deployment can therefore never be driven by whoever frames it. The allowlist is enforced in both directions: unlisted senders are ignored, and outbound messages are addressed to a listed origin, never *. The entrypoint validates each entry and fails the boot on a malformed one.

Setting the allowlist also narrows the existing Jupyter / ?embed=1 project and scripting bridges to those origins, closing the "any framing parent is trusted" gap for deployments that can name their hosts. With no allowlist configured, nothing about those bridges changes (the Jupyter widget's host origin is arbitrary and cannot be listed in advance).

Verification

Verified against the real app: a harness page served from a second origin framing the dev server, with a project of three field polygons.

  • loadProject swapped the project in place: projectLoaded + ack ok, no reload
  • setView with a bbox and with center/zoom/pitch, both acked and animated
  • highlightFeature by filter: {crop: "corn"} highlighted 2 of 3 features, fitted to them, and reported selectionChanged {featureIds: ["f1","f3"]}; by featureId; and {layerId} alone cleared it
  • openTool aspect with {z_factor: 2} opened the Whitebox dialog preselected and pre-filled
  • A malformed loadProject (javascript: URL) was refused with ack {ok: false, error} rather than acted on
  • Running Centroids in the UI emitted toolCompleted {id: "centroids", status: "success", outputLayerNames: ["Centroids"]}
  • A second harness on a non-allowlisted origin received no ready and its loadProject was ignored (map stayed empty)
  • Both light and dark themes

serverFileWritten is derived from the same processing-history entry's outputPath and was not exercised live (it needs a sidecar file-based tool).

39 new unit tests cover origin parsing/allowlisting, envelope validation, every verb payload, and highlight resolution. Full frontend suite (4000 tests), production build, and pre-commit all pass.

Docs

  • docs/user-guide/embedding.md: new "Talking to the map at runtime" section with the message reference and a complete host-page example
  • docs/getting-started.md: the Docker env var
  • docs/features.md: one line under Deployment

Summary by CodeRabbit

  • New Features
    • Added a versioned framed postMessage embed API to control embedded GeoLibre maps (load projects, change view, highlight features, open tools).
    • Embedding is now allowlist-based using GEOLIBRE_EMBED_ORIGINS (Docker/runtime) and VITE_GEOLIBRE_EMBED_ORIGINS (build), with stricter origin validation.
  • Security/Improvements
    • Pre-handshake “ready” dispatch is now scoped to allowed origins, and unapproved origins are rejected during handshake completion.
  • Documentation
    • Added end-to-end embedding/host-page setup docs and updated feature references.
  • Tests
    • Added comprehensive tests for origin handling, request/ack parsing, command validation, and highlight resolution.

URL parameters configure an embed once, at load. A host page that frames
GeoLibre had no way to keep talking to the map: every interaction meant
reloading the iframe with a new `?url=`, throwing away the analyst's
session, and nothing the user did inside the map was visible outside it.

Add an opt-in runtime protocol on top of the existing embed plumbing.
Host to app: `loadProject`, `setView`, `highlightFeature`, `openTool`
(the runtime twin of `?tool=`). App to host: `ready`, `ack`,
`projectLoaded`, `selectionChanged`, `viewChanged` (throttled),
`toolCompleted`, `serverFileWritten`. Every message is versioned
`{v, type, payload}`, and app messages carry `source: "geolibre"` so a
host can filter its own postMessage traffic.

The API is off by default. It activates only when the deployment names
the origins it trusts (`GEOLIBRE_EMBED_ORIGINS` on the Docker image,
`VITE_GEOLIBRE_EMBED_ORIGINS` at build time), so a public deployment can
never be driven by whoever frames it. The allowlist is enforced in both
directions, and setting it also narrows the existing Jupyter/`?embed=1`
project and scripting bridges to those origins.

Fixes #1462
Copilot AI review requested due to automatic review settings July 27, 2026 14:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 42f03a21-2d1c-40b6-85f1-842b037d59fd

📥 Commits

Reviewing files that changed from the base of the PR and between ab850e4 and b3058a2.

📒 Files selected for processing (4)
  • apps/geolibre-desktop/src/hooks/useEmbedApi.ts
  • apps/geolibre-desktop/src/lib/embed-api.ts
  • docs/user-guide/embedding.md
  • tests/embed-api.test.ts

📝 Walkthrough

Walkthrough

GeoLibre adds an opt-in, versioned postMessage API for embedded maps with origin allowlists, host commands, acknowledgements, map and project events, Docker/runtime configuration, tests, and documentation.

Changes

Embedded postMessage API

Layer / File(s) Summary
Protocol contract and validation
apps/geolibre-desktop/src/lib/embed-api.ts, tests/embed-api.test.ts, docs/features.md, docs/getting-started.md, docs/user-guide/embedding.md
Defines versioned commands, events, origin allowlists, payload validation, highlight resolution, tests, and usage documentation.
Embedded runtime bridge
apps/geolibre-desktop/src/hooks/useEmbedApi.ts, apps/geolibre-desktop/src/hooks/embedHost.ts, apps/geolibre-desktop/src/hooks/useEmbedBridge.ts, apps/geolibre-desktop/src/components/layout/DesktopShell.tsx
Initializes the bridge, scopes communication to allowed origins, processes commands, emits application and map events, and cleans up listeners and pending work.
Deployment configuration and runtime delivery
Dockerfile, docker/entrypoint.sh
Passes embed origins through build and runtime configuration, validates runtime HTTP(S) origins, and logs activation.

Estimated code review effort: 4 (Complex) | ~60 minutes

Poem

A rabbit hops where map lights glow,
Sending tiny messages to and fro.
Origins guard each framed-up view,
Commands bloom and events too.
“Ready!” cries Bun beneath the moon.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The entrypoint change includes an unrelated AI proxy configuration refactor that is not required by the embedding API issue. Split the AI proxy refactor into a separate PR or revert it, keeping only the embed-origin and runtime-config changes.
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main addition of a versioned postMessage API for embedded host pages.
Linked Issues check ✅ Passed The code, docs, and tests implement the requested runtime postMessage bridge, versioned envelope, commands, events, and origin allowlist.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-1462-embed-postmessage-api

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/geolibre-desktop/src/hooks/useEmbedApi.ts`:
- Around line 122-134: The applyHighlight function currently acknowledges
success when resolveHighlightIds finds no features, potentially clearing the
selection for source-backed layers. Update applyHighlight to locate or rehydrate
matching features from the layer’s MapLibre source when layer.geojson is
unavailable, and reject with an unsuccessful acknowledgement when no target
features can be resolved; only call selectFeatures, highlightFeature, and return
success after matches are found.

In `@docs/user-guide/embedding.md`:
- Around line 102-105: Update the embedding documentation’s message-delivery
statement to qualify that outbound messages target listed origins, but may use
"*" when the allowlist contains the wildcard or no allowlist is configured.
Preserve the existing explanation that unlisted inbound origins are ignored.

In `@tests/embed-api.test.ts`:
- Around line 269-271: Update the “requires an id” test to match the surrounding
error-case assertions by storing or reusing the parsed request and checking it
with a truthiness guard plus the `"error" in parsed` pattern, rather than
calling hasOwnProperty directly.
- Around line 275-279: Update the features array annotation in the test to use
Feature<null>[] so its null geometries match the declared type. Align any nearby
related feature-array annotations that also contain null geometries, while
leaving feature data unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e99453dd-0240-47bc-98c4-f43931b683b2

📥 Commits

Reviewing files that changed from the base of the PR and between 33488ca and ab850e4.

📒 Files selected for processing (11)
  • Dockerfile
  • apps/geolibre-desktop/src/components/layout/DesktopShell.tsx
  • apps/geolibre-desktop/src/hooks/embedHost.ts
  • apps/geolibre-desktop/src/hooks/useEmbedApi.ts
  • apps/geolibre-desktop/src/hooks/useEmbedBridge.ts
  • apps/geolibre-desktop/src/lib/embed-api.ts
  • docker/entrypoint.sh
  • docs/features.md
  • docs/getting-started.md
  • docs/user-guide/embedding.md
  • tests/embed-api.test.ts

Comment thread apps/geolibre-desktop/src/hooks/useEmbedApi.ts
Comment thread docs/user-guide/embedding.md Outdated
Comment thread tests/embed-api.test.ts
Comment thread tests/embed-api.test.ts Outdated
…ng them

Explicit `featureId`/`featureIds` were passed through unchecked, so a
`highlightFeature` naming an id no feature carries — a typo, or any layer
whose features live in its MapLibre source rather than `layer.geojson` —
selected a phantom id, drew no highlight, and still answered `ack {ok:
true}`. Resolve ids against the layer's features under the same
`String(feature.id ?? index)` convention the map controller uses, and
reject a request that names features but resolves to none, leaving the
user's existing selection untouched. A request naming nothing is still
the documented "clear the highlight" form.

Also fix the docs claim that outbound messages are never addressed to
`*` (they are, before the handshake, when the `*` wildcard is
configured), type the null-geometry test fixtures as `Feature<null>[]`
(`Feature` defaults its geometry parameter to `Geometry`), and match the
surrounding assertion style in one test.
@giswqs
giswqs merged commit a76bbbf into main Jul 27, 2026
50 checks passed
@giswqs
giswqs deleted the feat/issue-1462-embed-postmessage-api branch July 27, 2026 14:49
@GJohanntoBuerenNEXAT

Copy link
Copy Markdown

This is great. Looking at the code closer now, i had the impression that the embedding for jupyter books already does something similar so parts of this might be a little redundant to it.

I will test and prepare a PR with the findings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: postMessage bridge for embedded GeoLibre

3 participants