* feat(embed): let a layout=viewer embed opt into drawing `layout=viewer` deactivates the geometry editor, so a host page that embeds the read-only viewer cannot let the user draw, even though getDrawnFeatures and featuresChanged (#3021) can read a drawing back. Add a `draw` URL flag. With `?layout=viewer&draw` the geometry editor is let through (`isViewerBlockedPlugin`), so `?plugin=geo-editor` or a project that lists it can open its toolbar. Annotations and GeoAgent stay blocked. The flag only takes effect when the deployment grants `project:edit`, and without it nothing changes. `draw` is reserved so it never becomes a tool parameter in a `?tool=` link. The viewer also stops offering ways to edit or move data around the deployment's capability gates: Identify no longer shows Edit geometry or Edit attributes (this was already reachable in `layout=viewer` before this change), and the editor toolbar drops its Open/Save file buttons. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * fix(embed): set viewer file-tools flag during render, document drawing readback needs The geometry editor reads its file-tools flag once at construction, and the plugin restore and ?plugin= deep link construct it from effects. Setting the flag from an effect in the guard hook could lose that race against a child's effect, so set it while rendering, which precedes every effect. Add a render test for it and record why an empty fileModes list is safe upstream. Docs: reading drawings back needs the embed allowlist and the export:data capability, not only project:edit; use a self-hosted placeholder URL in the drawing example. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * Address review feedback - Move the viewer gate on Identify's edit actions into useIdentifyEditActions and pass usePluginDeepLink the layout options instead of two booleans, so DesktopShell.tsx stays within the untested-module growth allowance (it had reached 1341 lines against a 1334 cap once merged with main), which failed Build and test. - Note in useViewerPluginGuard that layoutOptions.viewer comes from the launch URL and is fixed for the page's lifetime, so the render-time file-tools flag can never go stale and needs no reset on unmount. --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> Co-authored-by: giswqs <giswqs@gmail.com>
7.3 KiB
Deployment Capabilities
A deployment can pin what the app it serves is allowed to do — a read-only kiosk, a classroom instance, and everything between that and the full app.
Capabilities are coarse on purpose. Each one names a whole class of action ("may add data at all"), not an individual menu item, so a locked-down deployment cannot be defeated by one item somebody forgot to list.
!!! warning "Client gates are not a general authorization boundary" Withholding a capability removes the affordance: the menu is not rendered, the command palette does not list or run the action, the keyboard shortcut does nothing, and the embed API refuses the command. These client gates do not stop someone with browser devtools.
The Docker container also enforces its final deployment policy at nginx:
selected `/sidecar/` route families require `processing:run` or `data:add`,
and `/ai` is disabled unless the final policy enables AI and the approved
proxy environment is configured. See the exact route table and startup
rules in [Self-Hosting](self-hosting.md#container-policy-enforcement).
This enforcement is **container-only**: it does not restrict browser WASM
engines, desktop processing, or a separately hosted sidecar.
Keep Basic Auth or a real auth proxy and `GEOLIBRE_CONVERSION_ROOTS` in
place. Container policy enforcement is not user authentication, and
capabilities remain the interface half of the story outside those routes.
Not the same as UI Profiles
UI Profiles also hide menus and items, and the two run independently. The difference is who decides:
| UI Profiles | Deployment capabilities | |
|---|---|---|
| Purpose | Reduce clutter for the audience | Pin what the deployment permits |
| Set by | The user, primary policy interface, or legacy admin profile |
Primary runtime policy; legacy runtime/build environment fallbacks |
| Reversible in the app | Yes, from Settings → Interface (unless lock is set) |
No — never surfaced in the UI |
| Granularity | Individual items, data sources, plugins | Whole capabilities |
Where both apply, the capability is checked first. An action the deployment withheld is never on offer, whatever the profile says.
The capabilities
| Capability | Grants |
|---|---|
project:edit |
Authoring the project: New, Open, Open Recent, Import, Project History, Save, Save As, Duplicate, Save as Template, Collaborate, StoryMap; Undo/Redo (the menu items and the Ctrl/Cmd+Z, Ctrl/Cmd+Shift+Z, and Ctrl+Y shortcuts); Export Selection; adding a review comment; the embed API's loadProject; and the draw option of a layout=viewer embed, which is ignored without it. |
data:add |
Bringing data in: the whole Add Data menu, dragging a file onto the map (browser and desktop), and the embed API's addLayer and addData. |
processing:run |
The whole Processing menu — Whitebox, SQL, Python, the AI assistant, geocoding, Model Builder, conversion/vector/raster tools — and the embed API's openTool. |
export:data |
Getting data or a rendering back out: Share, Export HTML, Print, Print Layout, Offline Basemap, and the embed API's exportImage, getLayerFeatures, and getDrawnFeatures. |
plugins:install |
The Plugins menu, plugin-registered toolbar menus, plugin items in the built-in menus, activating or deactivating a plugin, and the plugin marketplace ("Manage plugins"). |
settings:manage |
The Settings dialog and the Style Manager. |
Anything not listed is unprivileged and stays available in every configuration: panning and zooming, the View and Controls menus, layer visibility and ordering, identify, the selection tools, and Help.
Configuring it
Runtime (recommended)
Set capabilities in deployment.json, or use the
Docker runtime override:
docker run --rm -p 8080:80 \
-e GEOLIBRE_CAPABILITIES=data:add,export:data \
ghcr.io/opengeos/geolibre:latest
Runtime policy support ships in the published image from v3.3.0 on; see
Deployment Policy. none grants nothing with
the environment variable; JSON uses "capabilities":[] for the same empty
grant. Omitted sources grant the default full set.
Legacy input — VITE_GEOLIBRE_CAPABILITIES still honoured
VITE_GEOLIBRE_CAPABILITIES is a legacy build-time fallback:
VITE_GEOLIBRE_CAPABILITIES="data:add,processing:run,export:data" npm run build
On a prebuilt client, runtime deployment policy takes precedence, followed by
window.__GEOLIBRE_DEPLOYMENT_ENV__, then this build-time value. The legacy
parser drops unknown tokens; an unrecognized-only nonblank value grants none,
and blank means unset. These rules do not describe JSON policy parsing or
Docker's strict source validation.
VITE_GEOLIBRE_CAPABILITIES=none npm run build
VITE_GEOLIBRE_CAPABILITIES="export:data" npm run build
VITE_GEOLIBRE_CAPABILITIES="project:edit,data:add,processing:run,export:data" npm run build
The examples above are legacy build-time configuration, still honoured; they do not configure Docker nginx route enforcement.
!!! warning "Client policy loading can fall back" Web loading waits at most 3 seconds. A missing or late policy uses the runtime environment fallback, then the legacy build environment, then the default full grant. Client gates and browser WASM are not access control; use Docker nginx policy enforcement or protect APIs independently.
Runtime examples
A kiosk or exhibit terminal, with no optional capabilities:
docker run --rm -p 8080:80 -e GEOLIBRE_CAPABILITIES=none ghcr.io/opengeos/geolibre:latest
Allow visitors to export an image:
docker run --rm -p 8080:80 -e GEOLIBRE_CAPABILITIES=export:data ghcr.io/opengeos/geolibre:latest
A classroom instance with project authoring, data and processing tools, and export, but no plugin installs or settings:
docker run --rm -p 8080:80 \
-e GEOLIBRE_CAPABILITIES=project:edit,data:add,processing:run,export:data \
ghcr.io/opengeos/geolibre:latest
An embedded map can use an empty grant to refuse commands such as
loadProject, addLayer, addData, openTool, exportImage,
getLayerFeatures, and getDrawnFeatures (an empty grant also suppresses
featuresChanged events), while setView, highlightFeature, and
layer-visibility commands remain available.
Embed API behavior
A denied command rejects rather than silently doing nothing, so the host page can tell the difference between "refused" and "no effect":
await map.addData("https://example.com/data.geojson");
// Error: Missing data:add capability
See Embedding & Sharing for the full command list.
The embed origin allowlist (GEOLIBRE_EMBED_ORIGINS) and capabilities are
independent: the allowlist decides who may send commands, capabilities decide
which commands exist.
Related pages
- Deployment Policy — primary policy and Docker enforcement
- Self-Hosting — exact Docker server-side route guards
- UI Profiles — non-destructive interface filtering
- Embedding & Sharing — embed API and origin allowlist
- Getting Started — container configuration