Skip to content

Maps & external images

A mini app runs walled-in, and by default its sandbox can’t reach the internet at all — the browser blocks every external image, fetch, and request (the app’s content policy starts at default-src 'none'). That’s deliberate: an app you fork or run from the store can’t phone home or quietly ship your data somewhere.

But some apps genuinely need one outside resource — most often map tiles or a remote image. For those, you grant the app a short, explicit list of hosts, and Catalyst compiles exactly those hosts into the app’s sandbox rules. Nothing else gets through.

There are two separate lists, because loading an image and making a data request carry very different risk:

Image hosts

For <img> loads — raster map tiles, remote pictures, and logos. The request is one-way and GET-only, so it’s low risk. This is exactly what a map drawn from OpenStreetMap tiles needs.

Connect hosts

For fetch / XHRvector map tiles and JSON / REST APIs. This is a full two-way channel, so it’s reviewed more strictly. It’s also finicky: these requests are CORS-checked and the sandbox’s origin is null, so many hosts reject them — a JSON API or a vector-tile map often won’t work unless that host explicitly allows Origin: null.

Each list is a set of bare hostnamestile.openstreetmap.org, or a single leading wildcard like *.tile.openstreetmap.org. https is always forced, so you don’t write a scheme; ports, paths, and a bare * are rejected. You can grant up to 10 hosts per list.

The canonical case is a slippy map drawn from OpenStreetMap’s raster tiles. It needs one image host and no connect host at all:

  1. Ask the assistant to build the map. For example: “Build me a map centered on New York using Leaflet.” Leaflet isn’t one of the built-in libraries, so the assistant adds it as an on-demand package and writes the component.

  2. Grant the tile host as an image host. The assistant usually does this for you — it wrote the tile URL, so it knows the host. If a tile load is blocked, add *.tile.openstreetmap.org under Image hosts (see How to grant a host below).

  3. The tiles render. Leaflet builds each tile URL like https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png, and with the host granted the browser now allows those image loads.

import React, { useEffect, useRef } from 'react';
import L from 'leaflet'; // added on demand — ask the assistant for it
export default function MapApp() {
const ref = useRef(null);
useEffect(() => {
const map = L.map(ref.current).setView([40.71, -74.0], 12);
// Tile loads are <img> requests — covered by the image host grant.
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap',
}).addTo(map);
return () => map.remove();
}, []);
return <div ref={ref} className="h-screen w-full" />;
}

You don’t have to use Leaflet — a hand-rolled map that places tiles as plain <img> elements works the same way, since both just load images from the granted host.

There are three ways a host ends up on an app’s list — you’ll mostly rely on the first:

  • The assistant declares it. When you ask for a map or an app with a remote image, the assistant wrote the URL, so it knows which host to grant and adds it as it builds. A well-built app shows no blocked loads.
  • The editor’s Properties → Network. Open the app in the editor; the Properties pane has Image hosts and Connect hosts fields right below the Collections and Workflows grants. Add or remove hosts as chips.
  • The Preview’s “Allow this host” chip. If a running app trips a blocked load, the live preview surfaces a chip naming the host with a one-click button — for example Allow *.tile.openstreetmap.org. Click it and the host is added, the app reloads with the new rules, and the resource appears.

A grant-less app reaching a tile server is about as risky as opening a maps website — it has none of your Catalyst data to leak. So an image-only grant on an app with no data grants is the low-friction path.

The combination to watch is an app that pairs a network host with a data grant (a collection or a workflow). That’s the shape that could send your data somewhere, so it’s the case Catalyst reviews closely before an app is published to the store. Keep grants minimal: only the hosts an app actually needs, and don’t pair network reach with data access unless the app genuinely requires both.

The example above uses OpenStreetMap’s public tile server, which is run by volunteers for light, community use under its Tile Usage Policy. That policy requires requests to identify themselves with a valid User-Agent / Referer, rate-limits traffic, and forbids heavy or bulk use. It’s fine for a personal map or a quick prototype; for anything in production or with real traffic, use a dedicated tile provider — which you grant exactly the same way (add its tile host as an image host).