Cartos — Comprehensive Guide
Space42 Smart Solutions · sovereign geospatial vertical-AI platform. Last updated 2026-06-09. This is the single orientation doc for everything built so far — platform, modules, features, how to run it, the marketing site, and the GTM artifacts.
Contents
- What Cartos is
- Live URLs & quick links
- Architecture at a glance
- Repository layout
- The platform (Phase 1 substrate)
- The modules
- Cross-cutting features every module inherits
- Data sources, integrations & keys
- HTTP API reference
- The marketing website & GTM artifacts
- Roadmap — Phase 2 & Phase 3
- Reference docs
1. What Cartos is
Cartos is a sovereign geospatial vertical-AI platform: ingest, serve, and visualize geospatial data inside your own territory, with an AI analyst wired in from the first click. Every "vertical" (city, port, emergency services…) is a slice over one shared data fabric, one agent, and one shell — not a separate product.
It is built in three phases, each standing on its own and compounding the last:
| Phase | Name | Status |
|---|---|---|
| I | The Sovereign Platform — ingest/serve/visualize + agent, live vertical modules | Live (built) |
| II | Analyst Intelligence Layer — change detection, derived data, 3D reconstruction, auto-reporting | Planning (Gate 0, not built) |
| III | Cartos Field — offline-first mobile field-capture edge | Defined (MRD/PRD, not built) |
This guide is primarily about Phase I — what's actually built and running. Phases II/III are planning doc sets (see §13).
Two different things are called "Phase 2": the RLS / group-access work (shipped — §7) and the Analyst Intelligence Layer doc set (not built). When in doubt, ask which is meant.
2. Live URLs & quick links
| What | Where |
|---|---|
| Marketing site (live) | https://cartos.pages.dev |
| Interface tour | https://cartos.pages.dev/interface |
Commercial model (gated, password the demo password —
temporary) |
https://cartos.pages.dev/commercial-model |
| The Cartos app | runs locally — http://localhost:5173 (Vite dev) against
cartos-api on :8080 |
The app (cartos-web + cartos-api) is run locally in development; only the
3. Architecture at a glance
┌──────────────────────────────────────────────────┐
Browser │ cartos-web — React + Vite shell │
│ MapLibre (2D) · Cesium iframe (3D) · Dockable │
│ panels · AI agent chat · GeoWizard · overlays │
└───────────────┬──────────────────────────────────┘
│ HTTPS (Bearer JWT)
┌───────────────▼──────────────────────────────────┐
Rust │ cartos-api (axum) — JWT validated vs Keycloak │
workspace│ JWKS. Routes per module + agent + admin + layers │
│ ├─ cartos-agent LLM tool registry (Anthropic) │
│ ├─ cartos-data Postgres queries + RLS session │
│ └─ cartos-core TenantContext (tenant/tier/roles/groups)
└───────────────┬──────────────────────────────────┘
│
┌───────────────────┼───────────────────────┐
▼ ▼ ▼
Postgres + PostGIS Keycloak (SSO/JWT) MinIO (object store)
(RLS tenant+team) realm: cartos + Caddy reverse proxy
- Rust workspace carries the data fabric, agent, and
API; a React shell renders MapLibre 2D and Cesium 3D
(the latter via an iframe bridge to the sibling
../cesium_project_SPSapp). - Everything ships in containers you run in-territory
(
deploy/). - Tenant- and team-isolation is enforced at the database with row-level security, not just in the app layer.
4. Repository layout
| Path | What it is |
|---|---|
crates/cartos-core |
Type primitives — TenantContext
(tenant_id, tier, roles,
groups), Role, Tier. Flows
through every protected handler as an axum extractor. |
crates/cartos-data |
Postgres queries + set_tenant_session /
set_admin_bypass helpers. Every read runs in a transaction
with RLS session vars set; WHERE filters are belt-and-braces. |
crates/cartos-agent |
LLM tool registry (Tool trait,
register_*). Module-namespaced builtin tools; Anthropic
client (llm.rs). |
crates/cartos-api |
axum HTTP. JWT validation vs Keycloak JWKS. Per-module route
modules, kpi.rs, admin.rs (+
keycloak_admin.rs), mobility.rs,
layers.rs, scope.rs. |
crates/cartos-cli |
migrate, seed. Runs as the postgres
superuser → bypasses RLS by design. |
crates/cartos-codec |
Shared codec/types. |
cartos-web |
React + Vite shell. Native modules (Smart City, Maritime, Emergency Services) + Cesium-iframe modules (Situational Awareness, Maritime Domain Awareness, GIQ). |
deploy/ |
docker-compose (postgres + keycloak + minio + caddy). |
website/ |
Live marketing site at cartos.pages.dev (Cloudflare
Pages; push main = publish). See §12. |
cartos-pricing/ |
Commercial model & government business case (internal + public-safe; docx/pdf). See §12. |
cartos_screenshots/ |
Source product screenshots → optimized to
website/images/*.webp via
website/tools/optimize-screenshots.py. |
cartos-phase2/, cartos-phase3-mobile/ |
Planning + GTM doc sets (not built). ⚠
cartos-phase2/docs/strategy/ip-timing.md is
time-sensitive. |
docs/ |
Engineering & GTM docs (see §14). |
7. The platform (Phase 1 substrate)
Every vertical inherits these. They are the moat.
Sovereign data fabric + team isolation (group access)
- Keycloak
groupsclaim →TenantContext.groups→ query filterowning_team IS NULL OR owning_team = ANY($groups)→ Postgres RLS policytenant_team_isolationas a second layer. - New customer tables almost always need
owning_team TEXT+ a(tenant_id, owning_team)index. - Admin writes set data scope via
PATCH /api/admin/scope(also exposed as theset_owning_teamagent tool); both set acartos.bypasssession var inside the transaction. - RLS bites in production once the API runs under a non-super role; in
dev with the
postgresrole the WHERE filters carry the boundary. - Canonical contract:
docs/group-access-design.md— read it before adding queries, write paths, RLS policies, or detail-panel UI.
Authentication & access control
- Keycloak SSO, roles (
admin/viewer), and team-scoped data. JWT is RS256-validated against Keycloak's JWKS (fetched at startup, cached). - Native Admin module manages users and group
membership in-app via the Keycloak Admin REST API
(
/api/admin/users,/api/admin/groups, add/remove user↔︎group). 503s unlessCARTOS_KEYCLOAK_ADMIN_CLIENT_SECRETis set.
The AI agent (agent-native from click one)
- Conversational control: ask a question, focus the map, run a
workflow — the agent orchestrates the platform's own tools.
POST /api/agent/chat(503 whenANTHROPIC_API_KEYis unset). - Module-namespaced tool registry
(
crates/cartos-agent/src/builtin/):- Core:
who_am_i,current_time,viewport_focus - Smart City:
list_incidents,find_assets,nearest_sensors,list_jurisdictions - Maritime:
list_vessels,find_vessels,list_ports,list_port_incidents - Emergency Services:
list_emergency_units,list_emergency_incidents,nearest_available_unit,incidents_in_window,traffic_conditions,list_cameras - Overlays:
add_satellite_layer,list_satellite_layers - Admin:
set_owning_team
- Core:
- Tools requiring the DB are disabled gracefully if Postgres is
unreachable (server still starts;
/healthzreports honestly).
8. The modules
The canonical nav lineup (reorderable — see §9):
| Module | Type | What it surfaces |
|---|---|---|
| Smart City | Native (MapLibre + dashboard) | Assets, sensors, incidents & jurisdictions with live telemetry, KPIs, sensor-reading charts, and AI summaries. |
| Situational Awareness | Cesium iframe | A photoreal 3D common operating picture in a streaming globe (bridge
to ../cesium_project_SPS). |
| Emergency Services | Native (MapLibre + ops dashboard) | Units, incidents (+ history window), traffic & CCTV cameras with a live operations dashboard. |
| Maritime | Native (MapLibre + dashboard) | Vessels, ports & port incidents across territorial waters. |
| Maritime Domain Awareness | Cesium iframe | Wide-area maritime coverage, tracks & context rendered in 3D. |
| GIQ | Iframe | Direct GIQ integration (see
docs/giq-embedding.md). |
| Energy, Agriculture | Planned | Disabled nav placeholders (Phase 1+). |
| Admin | Native | Users & teams management (not in the module nav; opened via the user badge). |
Native module shape:
<Module>Module.tsx (MapLibre + DockablePanels) +
<Module>Dashboard.tsx. Iframe
modules use lib/iframe-bridge.ts.
9. Cross-cutting features every module inherits
- Dockable floating panels
(
DockablePanel/PanelDock): users arrange panels to taste (persisted in localStorage, ID-namespaced per module); an admin can snapshot the defaults for everyone via the user badge. This is the standard for all map-surface overlays. - GeoWizard — data-source marketplace
(
shell/GeoWizard.tsx): tabs for UAE Data, Living Atlas / ArcGIS Online search (no account needed), ArcGIS Hub, and Custom URL. Results add as styled, persistable layers. - Live raster overlays, added via a uniform
reconciler pattern (a panel emits
Active<X>Layer[]; the module adds/removes raster sources prefixedarcgis-/gibs-/stac-and re-adds onstyle.load):- ArcGIS REST services
(
ArcGisLayersPanel, AD-SDI catalog + custom URL + identify popups). - NASA GIBS satellite — a 22-layer curated WMTS
catalog in every module's Satellite Data tab
(
SatelliteDataPanel,nasa-gibs-catalog); native maps render via MapLibre, iframe modules receive layers over the bridge. - STAC catalogs (
StacCatalogPanel,lib/stac.ts).
- ArcGIS REST services
(
- Layer persistence:
/api/layers(list/save/delete) so discovered overlays survive reloads. - Layer styling on the fly
(
LayerStylePopover,lib/layer-style*). - Map tools: measure (
lib/measure.ts), export (lib/map-export.ts), camera/bookmarks (lib/map-camera.ts), geocoding (lib/geocode.ts), aLegend, and identify/floating-popup. - Global Mobility live feeds
(
lib/mobility.ts→/api/mobility/*): live aircraft (adsb.lol, no key), vessels (VesselFinder/aisstream, no key), and traffic tiles (TomTom — needs a key; serves transparent tiles without one). - KPI strip + sensor-readings charts
(
KpiStrip,SensorReadingsChart,chart-themes), with themeable dashboards (ops-floor dark / holographic). - Module nav reordering: drag to reorder (persisted in localStorage), with a reset to the default lineup.
10. Data sources, integrations & keys
| Integration | Used for | Key / config |
|---|---|---|
| Postgres + PostGIS | The sovereign data fabric (tenant + team RLS) | DATABASE_URL (:5433 on this box) |
| Keycloak | SSO, JWT/JWKS, roles & groups; admin REST | realm cartos;
CARTOS_KEYCLOAK_ADMIN_CLIENT_SECRET |
| Anthropic | The AI agent | ANTHROPIC_API_KEY (agent 503s without) |
| NASA GIBS | Satellite WMTS overlays | none (public) |
| ArcGIS REST / Online / Hub | Vector/raster service overlays + Living Atlas | none for public services |
| STAC | Imagery catalog discovery | per-catalog |
| VesselFinder / aisstream | Live vessels (Global Mobility) | reused from EarthWatch3D .env |
| adsb.lol | Live aircraft (Global Mobility) | none |
| TomTom | Traffic tiles | TOMTOM_API_KEY (optional) |
cartos-api loads a gitignored workspace-root
.env via dotenvy; the Anthropic / aisstream /
TomTom keys are reused from EarthWatch3D's .env.
11. HTTP API reference
All /api/* routes require a Bearer JWT except
/healthz.
| Method | Route | Purpose |
|---|---|---|
| GET | /healthz |
Liveness (unauthenticated) |
| GET | /api/me |
Returns the caller's TenantContext |
| POST | /api/agent/chat |
Agent conversation (503 without Anthropic key) |
| GET | /api/kpi/smart-city/summary ·
/api/kpi/maritime/summary ·
/api/kpi/emergency-services/summary |
Dashboard KPI summaries |
| GET | /api/smart-city/{incidents,assets,sensors,jurisdictions} |
Smart City data |
| GET | /api/sensors/:id/readings |
Sensor time series |
| GET | /api/maritime/{vessels,ports,port-incidents} |
Maritime data |
| GET | /api/emergency/{units,incidents,incidents/history,traffic,cameras} |
Emergency Services data |
| GET | /api/mobility/{aircraft,vessels,status} ·
/api/mobility/traffic/:z/:x/:y |
Global Mobility feeds + traffic tile proxy |
| GET/POST/DELETE | /api/layers · /api/layers/:id |
Persisted overlay layers |
| PATCH | /api/admin/scope |
Set a row's owning_team (admin) |
| GET | /api/admin/users · /api/admin/groups |
Keycloak admin (503 without secret) |
| PUT/DELETE | /api/admin/users/:user_id/groups/:group_id |
Add / remove user ↔︎ group |
12. The marketing website & GTM artifacts
website/ —
live at https://cartos.pages.dev
- Cloudflare Pages, git-connected to
main→ pushingmainpublishes. No CI/wrangler in the repo (the connection lives in the Cloudflare dashboard). - Pages:
index,vision(Map Africa / UAE Base Map),interface(product-screenshot tour),platform(Phase I),analyst-layer(Phase II),field(Phase III),why, andcommercial-model(pricing). - Plain HTML/CSS/JS, inline-SVG charts (no chart
library), strict CSP in
_headers(script-src 'self'— scripts must be external files, e.g.gate.js). images/*.webpare optimized fromcartos_screenshots/viawebsite/tools/optimize-screenshots.py(re-run after adding captures).- The footer CARTOS wordmark on every page links to
commercial-model.html.
cartos-pricing/
— commercial model & business case
build.pyis the single source of truth (pricing pillars, tier ACVs, unit economics, revenue/breakeven, customer ROI, competitive TCO, departments, personas) and emits two docs from the same data:- Internal
cartos-commercial-model.html— full financials. - Public-safe
website/commercial-model.html— our margins / cost-to-deliver / revenue / breakeven stripped; published on the site.
- Internal
make_docx.pybuilds the.docx; a headless-Chrome print builds the.pdf.- All figures are illustrative planning anchors, not quotes.
- The public page sits behind a temporary soft password
gate (
the demo password):website/gate.js, only the SHA-256 is embedded,noindex. It's demo-grade, not real security (content still ships in source) — replace with Cloudflare Access for real auth.GATE_PASSWORDlives inbuild.py.
To regenerate: python cartos-pricing/build.py, then (for
docx/pdf) the rasterize + make_docx.py steps in
cartos-pricing/README.md.
13. Roadmap — Phase 2 & Phase 3
Phase
II — Analyst Intelligence Layer (cartos-phase2/, Gate 0,
not built)
Turns the substrate into one that reasons about change, builds in 3D, derives analysis-ready data, and writes auditable reports. Five modules in waves:
| ID | Module | Wave |
|---|---|---|
| M03 | GeoAI agents & derived datasets (lineage-tracked — the moat) | 2A |
| M02 | Comprehensive reporting (HTML/DOCX/PDF from a session) | 2A |
| M01 | Temporal change detection (two epochs → change layer + confidence) | 2B |
| M04 | Gaussian-splat serving (photoreal 3D in the Cesium globe) | 2C |
| M05 | AOI → 3D LOD 2.5/3 buildings (reconstruct from imagery) | 2C |
New infra shape: a GPU-backed Python model sidecar
that cartos-api calls as async jobs. Read the three Gate-0
build contracts in cartos-phase2/docs/phase-2/specs/ before
implementing. ⚠ cartos-phase2/docs/strategy/ip-timing.md is
time-sensitive (public disclosure affects patentability).
Phase
III — Cartos Field (cartos-phase3-mobile/, MRD/PRD, not
built)
A configurable, offline-first mobile field-capture platform — the sovereign ground-truth edge that feeds the loop and multiplies seats (~10× field vs analyst).
14. Reference docs
| Doc | What |
|---|---|
docs/group-access-design.md |
Canonical group-access contract + RLS + admin write paths. |
docs/phase-1-plan.md |
Broader phase planning. |
docs/build-manual-deltas.md |
Divergences from the original build manual (Smart City first; EarthWatch3D iframe). |
docs/demo-script.md |
Demo walkthrough. |
docs/giq-embedding.md |
GIQ integration. |
docs/cesium-audit.md |
Cesium app audit. |
cartos-pricing/README.md |
How to regenerate the commercial model + docx/pdf. |
website/README.md |
Site structure + deploy. |
CLAUDE.md |
Context for Claude Code sessions (layout, conventions, startup). |
README.md |
Public-facing quick start (uses :5432 — adjust to
:5433 here). |
Questions or something out of date? Update this guide alongside the change.
Live site: https://cartos.pages.dev