Página aún sin traducir — contenido en inglés

Manual/Referencia

Published viewer

The published viewer is a small anonymous TanStack Start host. / serves the existing authoring application. A named path such as /bogota asks the admin API for the public descriptor, renders the existing web application HTML, and places the descriptor in <script type="application/json" id="meratyn-publication">. The browser then boots the same map runtime with that descriptor. The host does not create a second React root, require a login, or expose a admin session.

The descriptor is fetched before the HTML response is returned, so the document and its policy are ready when the client starts. Layer data and map rendering still load after first paint in the normal application lifecycle. A missing or invalid descriptor returns an error response and never falls back to the authoring app for a named path.

Admin owns publication snapshots, immutable documents, and prepared preview data. The viewer host owns the anonymous HTML boundary and metadata; the existing web client consumes the descriptor marker and boots the same map runtime. The JavaScript bundle is shared across publications, with capability controls loaded lazily from the publication policy rather than compiled into a per-project bundle. The host does not fetch a screenshot while building the HTML response. Preview generation may prepare and cache an exact-view image later on the admin/renderer path, using the immutable revision and view parameters in the preview URL.

Publishing and viewer URLs

Admin keeps the latest project upload separate from its public publication. Publishing or replacing a snapshot through the protected project publication operation assigns a stable slug and captures its feature policy; uploading a new draft alone does not change the public page. The anonymous descriptor route is GET /api/publications/:slug, while publication admin remains under the authenticated project API. A successful publish is therefore visible at /bogota without rebuilding this frontend. Replacing a snapshot keeps the old immutable descriptor for the backend’s five-minute revision grace period so links containing ?publication=<id>&version=<versionId> remain usable while the host cache expires.

Bundled Explore projects are seeded under the reserved showcase account and receive their named publication during admin startup. An unchanged catalog version and slug is idempotent; a new repository-managed version refreshes that bundled snapshot while retaining its policy. The Explore page links directly to /<slug> on the configured viewer host, while the canonical raw source document remains available at /showcase/<slug>.meratyn.json.

The viewer accepts up to 16 repeated filter parameters and one fit layer. Each filter is JSON with a layerId, field, and one of eq, neq, lt, lte, gt, gte, contains, or in; filters are canonicalized and applied together (AND semantics) after the publication policy allowlist is checked. For example, a stable URL is /bogota?filter=%7B%22field%22%3A%22name%22%2C%22layerId%22%3A%22roads%22%2C%22op%22%3A%22contains%22%2C%22value%22%3A%22north%22%7D&fit=roads. Vector rendering filters can be applied to tile layers when the provider supports the corresponding expression. Full-source queryable vector data, GeoJSON, and supported ArcGIS providers also expose feature rows for data operations. A tile-only layer without a full query source cannot produce a full result set, so fit returns an explicit unsupported capability instead of fitting only the currently visible tiles. fit is available only for a policy-allowlisted layer with bounds that the provider can expose.

Local host

Deploy the updated admin service and admin app with the viewer host. The service’s existing startup migration runner applies 0020_project_publications.sql and 0021_publication_revisions.sql; those files must be included in the service image. Publishing remains unavailable until that service update is running. Configure the gateway and persistent asset volume below before directing public viewer traffic to the new host.

Build the web application into an isolated viewer-host directory and then build the Start server:

bun run build:viewer-host
PUBLICATION_API_ORIGIN=http://127.0.0.1:8787 \
PUBLICATION_VIEWER_ORIGIN=http://127.0.0.1:5175 \
HOST=127.0.0.1 PORT=5175 bun run start:viewer-host

PUBLICATION_API_ORIGIN is a fixed trusted server origin. PUBLICATION_VIEWER_ORIGIN is used for canonical links and is never taken from Host or forwarded headers. The API must expose anonymous GET /api/publications/:slug; the response is a validated PublishedProject descriptor containing absolute document and preview URLs. The local defaults are the values shown above.

The Docker image uses the same commands:

docker build -f docker/Dockerfile.viewer-host -t meratyn-viewer .
docker run --rm -p 5175:5175 \
  -e PUBLICATION_API_ORIGIN=https://api.example.com \
  -e PUBLICATION_VIEWER_ORIGIN=https://maps.example.com \
  -v meratyn-viewer-assets:/var/lib/meratyn-viewer \
  meratyn-viewer

For the existing browser image, keep nginx as the gateway and run this host as the viewer-host upstream. The checked-in docker/nginx.viewer-host.conf fragment routes /, named pages, the copied /assets/ tree, /_start-assets/, the service worker, and the web manifest to Start. Include it in the existing server block after the /sidecar/, /mapillary/, and optional /ai/ locations, and replace that block’s generic static and SPA locations as the fragment instructs. Those existing proxy locations remain in control of their services. Keep the exact /meratyn-runtime-config.js location from docker/nginx.conf: entrypoint.sh writes that file at container startup, so runtime AI/embed configuration is still generated by the gateway and is served before any upstream fallback. The Start host itself has no auth or secret injection layer.

For a Compose deployment, publish the host on the private network and use viewer-host:5175 as the upstream name. The gateway and host must come from the same release so HTML and hashed assets cannot be mixed:

services:
  viewer-host:
    build:
      context: .
      dockerfile: docker/Dockerfile.viewer-host
    environment:
      PUBLICATION_API_ORIGIN: http://admin:8787
      PUBLICATION_VIEWER_ORIGIN: https://maps.example.com
    volumes:
      - viewer-assets:/var/lib/meratyn-viewer
    expose: ["5175"]

volumes:
  viewer-assets:

Do not point the fragment at the old nginx static directory while serving Start HTML. Keep the previous immutable asset set available during the worker update window described below.

Caching and revisions

Successful descriptors are held in a bounded native Effect cache with at most 1,000 entries for 30 seconds. Concurrent requests for the same slug and revision share one lookup. Failures retry quickly and are not retained as a stale publication. HTML responses use public, max-age=0, s-maxage=30, and the ETag includes the publication id, version id, normalized semantic view, and the viewer template revision. Tracking parameters do not affect the generated variant; filter and fit are parsed, policy-checked, and canonically sorted.

When a publication is replaced, the backend keeps the previous immutable descriptor for its documented grace period. A URL containing ?publication=<id> requests that exact revision so an already-cached page does not silently switch to the newest document. Canonical links omit the revision selector, while preview URLs retain the immutable revision and the semantic view. Publishing a new project therefore updates the descriptor and cache naturally; it does not require rebuilding the frontend assets.

Asset rollout

The host copies the production web build into its own output and keeps its hashed viewer assets separate from Start’s _start-assets. Missing assets return 404 and are never served by a SPA fallback. The current web service worker only falls back to index.html for / and its query string, so named publication routes reach the host. A tab controlled by an older worker can remain in the previous worker’s transition until it updates and reloads.

At startup the host copies only immutable /assets/ and /_start-assets/ files into MERATYN_VIEWER_RETENTION_DIR. It keeps every release that was superseded within the last seven days, never fewer than the three most recent release sets and never more than twenty, because a tab stays on its release until the user applies the in-app update and fetches lazy sections on first use. It never archives index.html, sw.js, the web manifest, or runtime configuration, and it never uses an archived file for a mutable route. Mount /var/lib/meratyn-viewer in the Docker image as shown above. If a deployment cannot retain that volume, publish each host image under a release directory or keep the previous image behind the same gateway for at least one worker update interval, switch the gateway atomically, and garbage collect only old hashed assets after that interval.