{
  "markdown": "# GeoLens\n\n[English](README.md) | [Español](README.es.md) | [Français](README.fr.md) | [Deutsch](README.de.md)\n\n**Your team's self-hosted spatial data hub: searchable, mappable, and shareable in one place.**\n\nGeoLens is an open-source spatial data hub for GIS and data teams: one place to find and work with data on infrastructure you control, with no telemetry. GeoLens itself phones home to nothing, except the default basemap tiles, which load from tiles.openfreemap.org until an admin configures a different one. (Other features you opt into can make outbound calls: AI assist to your chosen OpenAI-compatible endpoint or Anthropic key, OAuth/OIDC sign-in, SMTP, remote/S3 data sources, and off-site backups.) Upload files, create datasets in the browser, register tables already in GeoLens's own PostGIS database without copying them, import one-shot copies from WFS, ArcGIS FeatureServer, or OGC API Features, or reference remote STAC assets live. GeoLens records each dataset's origin, indexes catalog metadata with pg_trgm for fuzzy search out of the box (pgvector adds semantic ranking once you configure an embedding provider and enable semantic search), and serves OGC/STAC APIs that QGIS, ArcGIS, and MapLibre clients connect to natively. Compose, style, and share multi-layer maps right in the browser. Built on FastAPI and React. Deployed with one command.\n\n<p align=\"center\">\n  <a href=\"https://demo.getgeolens.com\"><img src=\"https://img.shields.io/badge/%E2%96%B6%20Try%20the%20live%20demo-demo.getgeolens.com-2563eb?style=for-the-badge\" alt=\"Try the live demo\" /></a>\n  <br />\n  <sub>No install required. Browse the sample catalog and maps without an account, or sign in with Google, GitHub, or Microsoft to try the map builder. Demo data may be wiped at any time.</sub>\n</p>\n\n[![CI](https://github.com/geolens-io/geolens/actions/workflows/ci.yml/badge.svg)](https://github.com/geolens-io/geolens/actions/workflows/ci.yml)\n[![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)\n[![Python: backend 3.13+ / SDK 3.10+](https://img.shields.io/badge/python-3.13%2B_backend_%7C_3.10%2B_SDK-blue.svg)](https://www.python.org/)\n[![PostgreSQL 18 + PostGIS 3.6](https://img.shields.io/badge/PostGIS_3.6-PostgreSQL_18-336791.svg)](https://postgis.net/)\n[![OGC API](https://img.shields.io/badge/OGC_API-Features_%7C_Records-green.svg)](https://ogcapi.ogc.org/)\n\n```bash\ngit clone https://github.com/geolens-io/geolens.git && cd geolens\nbash scripts/install.sh   # read it first: it writes .env, generates secrets, runs docker compose up -d\n# Open http://localhost:8080, then log in with the credentials you chose\n```\n\nOr the one-line form, which runs the same script and pulls the prebuilt images:\n\n```bash\ncurl -fsSL https://getgeolens.com/install.sh | sh\n```\n\nImages are published for linux/amd64 and linux/arm64. A fresh install runs six containers at about 1.3 GB resident.\n\n<p align=\"center\">\n  <img src=\".github/assets/geolens-manhattan-3d-hero.jpg\" alt=\"GeoLens map builder with Manhattan building footprints extruded into a 3D skyline, colored by construction era, with the subway and the drag-orderable layer stack beside the map\" width=\"900\" />\n  <br />\n  <em>The map builder: every Manhattan building extruded to its true roof height and colored by the era it was built, the subway threading beneath, built from open data with <code>scripts/seed-showcase.py</code></em>\n</p>\n\n> [!NOTE]\n> **API stability.** The standards surfaces (OGC API Features/Records, STAC,\n> and the tile endpoints) track their specifications and are safe to build\n> against. GeoLens's own REST API can still change between minor releases:\n> contract changes are listed in the [CHANGELOG](CHANGELOG.md), and breaking\n> ones keep the old form working for at least one more minor release. Hit a\n> rough edge? [Open an issue](https://github.com/geolens-io/geolens/issues).\n\n## Documentation\n\nFull user, admin, and API documentation lives at **[docs.getgeolens.com](https://docs.getgeolens.com)**. The [Reference](#reference) table below links each guide.\n\n## Published artifacts\n\nGeoLens is published through the standard package registries:\n\n```bash\npip install geolens          # Python SDK\npip install geolens-cli      # CLI; installs the `geolens` command\npip install geolens-mcp      # MCP server for coding agents (read-only)\nnpm install @geolens/sdk     # TypeScript/JavaScript SDK\n```\n\nPrebuilt public API and frontend images are published to GitHub Container Registry:\n\n```bash\ndocker pull ghcr.io/geolens-io/geolens-api:latest\ndocker pull ghcr.io/geolens-io/geolens-frontend:latest\n```\n\nThe `latest` tag tracks the newest published stable release.\n\n## Why GeoLens?\n\nSpatial data ends up scattered: shapefiles on shared drives, tables in database schemas, rasters in cloud buckets, metadata in spreadsheets. Finding the right dataset means asking Slack or grepping file servers. Sharing it means exporting, emailing, and hoping the CRS matches.\n\nGeoLens replaces that workflow:\n\n- **One data hub:** upload files, create datasets, register tables already in GeoLens's database, import feature-service snapshots, or reference remote STAC assets — then search and preview them together\n- **Source state, not guesswork:** see how each dataset entered the catalog, when it was last refreshed or checked, how its last refresh compares with its declared cadence (fresh, due, overdue, or unknown), and whether a remote Service or STAC origin is still reachable\n- **Works with your tools:** OGC API Features/Records with server-side CQL2 filtering, STAC API 1.0, direct tile URLs for QGIS, ArcGIS, and MapLibre\n- **No lock-in:** your catalog and the copies GeoLens manages stay on infrastructure you control and leave through open formats. Vector datasets export to GeoPackage, GeoJSON, Shapefile, CSV, GeoParquet, FlatGeobuf, or PMTiles; rasters download as Cloud-Optimized GeoTIFF; and any OGC API client reads the catalog directly\n- **Semantic and spatial search:** pg_trgm fuzzy matching out of the box; add an embedding provider and enable semantic search to rank datasets by meaning (pgvector)\n- **Built-in map builder:** compose multi-layer maps, style them, and share via public link or embeddable iframe\n- **AI-assisted (optional):** chat with your maps, auto-generate descriptions, search by natural language. Bring an OpenAI-compatible endpoint or Anthropic key, or skip it entirely\n\n## See it in action\n\nThe examples below use a JWT bearer token. Mint one against the local stack (the login endpoint accepts an OAuth2 password form, so use `-d` with form fields, not JSON). Substitute your admin username and the password from `.env` (`grep '^GEOLENS_ADMIN_PASSWORD=' .env`):\n\n```bash\nTOKEN=$(curl -s -X POST http://localhost:8080/api/auth/login/ \\\n  -d 'username=admin&password=<your-admin-password>' | jq -r '.access_token')\n```\n\nSemantic search takes a one-time admin setup: an embedding provider and the AI + Semantic Search toggles in the admin AI settings, plus an embedding backfill for data ingested before setup (the [search guide](https://docs.getgeolens.com/guides/user/search/) walks through it). Once that's on, search datasets by meaning instead of exact keyword matches:\n\n```bash\n# Semantic search ranks by meaning: \"hydrology\" surfaces the lake and river\n# network datasets whose titles never mention the word\ncurl \"http://localhost:8080/api/search/datasets/?q=hydrology&limit=3\" \\\n  -H \"Authorization: Bearer $TOKEN\" | jq '.features[].properties.title'\n```\n\nOne search-endpoint behavior to know when consuming it programmatically: the\nfirst page augments the dataset results with up to five matching collections,\nso `numberReturned` can exceed `limit` on page 0 only. That is deliberate, not\na bug — `limit` still bounds the number of *datasets* per page.\n\nEvery dataset is also a standard OGC API Features endpoint:\n\n```bash\n# Grab a public collection id from the catalog. Search anonymously (no token) so\n# the id is one anyone can read, matching the unauthenticated items request below.\nCID=$(curl -s \"http://localhost:8080/api/search/datasets/?q=countries&limit=1\" \\\n  | jq -r '.features[0].id')\n\n# GeoJSON features with a bbox filter, works in QGIS, ArcGIS, any OGC client\ncurl \"http://localhost:8080/api/collections/$CID/items?bbox=-10,35,30,60&limit=5\"\n```\n\nPostGIS and pgvector share one database, so with semantic search enabled you can rank datasets by meaning *inside* a spatial window in a single query. See the [search guide](https://docs.getgeolens.com/guides/user/search/) for how semantic and spatial search work together.\n\nConnect directly from QGIS: **Layer > Add WFS / OGC API Features** and point at `http://localhost:8080/api/`.\n\nThe same endpoints from the tools you already use: [geolens-examples](https://github.com/geolens-io/geolens-examples) holds single-file MapLibre, Leaflet, OpenLayers and ArcGIS JS pages, QGIS and DuckDB walkthroughs, both GeoLens SDKs, a semantic catalog search, a STAC browser, a saved-map embed, a Python/GeoPandas analysis, a catalog-as-code manifest for the CLI, and an MCP setup. The read-only ones run against the live demo, and CI replays them there on every push and once a week, so what you copy is code that worked this week. [Browse the gallery](https://geolens-io.github.io/geolens-examples/).\n\n## Features\n\nEach example above has a full guide in the [docs](https://docs.getgeolens.com/guides/). What GeoLens reads, writes, and exposes:\n\n### Data ingestion and export\n\n- **Five source modes:** Uploaded and Created data are managed locally; Register Table serves an existing table in GeoLens's own PostGIS database in place; Service imports are one-shot local copies; STAC datasets keep a live reference to the remote asset\n- **Vector:** Shapefile, GeoPackage, GeoJSON, GeoParquet, FlatGeobuf, KML/KMZ, zipped File Geodatabase, CSV, XLSX\n- **Raster:** GeoTIFF and Cloud-Optimized GeoTIFF (COG) with automatic conversion\n- **Mosaics:** VRT-based raster mosaics from multiple source files\n- **Export:** GeoJSON, Shapefile, GeoPackage, CSV, and FlatGeobuf with CRS reprojection; GeoParquet (always EPSG:4326); PMTiles as a self-contained tile archive for static hosts that support range requests\n- **Source state:** origin and last-refreshed/last-checked timestamps, cadence-based source freshness, and on-demand health checks for Service and STAC origins\n- Provenance tracking and metadata editing\n\n### Analysis\n\n- **Buffer** (metres, kilometres, feet, or miles), **centroid**, **clip** by a drawn area or by another polygon layer, and **dissolve** with an optional group-by column; **spatial join** and **select by location** match features on intersection, **measure** adds `area_sqm` and `length_m` columns, and **intersect** writes the pairwise overlay with attributes from both sides\n- All operations preview on the map except dissolve, which is materialize-only; previews are capped at 500 features. **Create dataset** then runs any of the eight over every feature as a background job, within per-operation source limits (250k features for dissolve, 500k for buffer)\n- The output is an ordinary vector dataset — styleable, exportable, and served through the OGC API endpoints like any other\n- The chat assistant can run buffer, centroid, and layer-based clip previews on request\n\n### Standards and interop\n\n- OGC API - Features (with server-side CQL2 filtering and per-collection `/queryables`) and OGC API - Records; STAC API 1.0 catalog endpoint; JSON-LD catalogs for DCAT 3, DCAT-US 3.0, and GeoDCAT-AP\n- Direct tile URLs and per-user API keys for QGIS, ArcGIS, MapLibre, and any OGC client\n- Vector tiles omit attribute columns below zoom 10 to keep low-zoom tiles small; add the `cols=<column>,<column>` query parameter to a tile URL to opt specific columns in at every zoom (names are validated against the dataset's columns, unknown names are dropped)\n- JWT + OAuth 2.0/OIDC, RBAC with per-dataset permissions\n- Interface in English, Spanish, French, and German\n\n<details>\n<summary>Security</summary>\n\n- JWT authentication with refresh tokens\n- API key management per user\n- OAuth 2.0 / OIDC support (Google, Microsoft, generic providers)\n- Role-based access control (RBAC) with per-dataset permissions\n- Self-serve registration is off by default; when enabled with SMTP verification,\n  registration email delivery is uniform for new and colliding submissions\n- Audit logging for all administrative actions\n\n</details>\n\n## Screenshots\n\n<p align=\"center\">\n  <img src=\".github/assets/geolens-search.png\" alt=\"GeoLens catalog search for 'tallest peaks in Europe' semantically returning the swissALTI3D Matterhorn terrain dataset, with type, location, and temporal filters\" width=\"900\" />\n  <br />\n  <em><strong>Find:</strong> search by meaning. \"Tallest peaks in Europe\" finds the Matterhorn terrain model even though no result contains any of those words, alongside type, location, and temporal filters</em>\n</p>\n\n<p align=\"center\">\n  <img src=\".github/assets/geolens-dataset.png\" alt=\"GeoLens dataset detail for Significant Volcanic Eruptions: a global map preview of 900 eruption sites along plate boundaries above schema stats and typed metadata\" width=\"900\" />\n  <br />\n  <em><strong>Inspect:</strong> every dataset gets a map preview, schema stats, and typed metadata. Here, 6,000 years of significant volcanic eruptions from NOAA NCEI</em>\n</p>\n\n<p align=\"center\">\n  <picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\".github/assets/geolens-dataset-chat-dark.png\" />\n    <img src=\".github/assets/geolens-dataset-chat.png\" alt=\"GeoLens Ask AI panel on the Meteorite Landings dataset answering 'How many meteorites were seen falling versus found later?' with a prose summary, a Fell/Found count table, and a button to open the result in the map builder\" width=\"900\" />\n  </picture>\n  <br />\n  <em><strong>Ask your data:</strong> question a dataset in natural language. \"How many meteorites were seen falling versus found later?\" comes back with the answer, the counts (1,096 vs 31,090), and a one-click jump into the builder</em>\n</p>\n\n<p align=\"center\">\n  <img src=\".github/assets/geolens-matterhorn-terrain.jpg\" alt=\"GeoLens map builder rendering the Matterhorn as a 3D terrain mesh from swissALTI3D lidar, with labeled peaks, climbing routes, the drag-orderable layer stack, and a legend\" width=\"900\" />\n  <br />\n  <em><strong>Build:</strong> compose multi-layer maps in the browser with a drag-orderable layer stack and per-layer editors (here: the Matterhorn as a 3D terrain mesh from swissALTI3D lidar)</em>\n</p>\n\n<p align=\"center\">\n  <img src=\".github/assets/geolens-ai-labels.png\" alt=\"GeoLens Ask AI panel adding volcano-name labels to the Restless Earth map from the natural-language request 'Label the volcanoes with their names'\" width=\"900\" />\n  <br />\n  <em><strong>Ask AI:</strong> edit maps in natural language. \"Label the volcanoes with their names\" adds readable labels to the Restless Earth map (optional: bring an OpenAI-compatible endpoint or Anthropic key)</em>\n</p>\n\n<p align=\"center\">\n  <picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\".github/assets/geolens-admin-overview-dark.png\" />\n    <img src=\".github/assets/geolens-admin-overview.png\" alt=\"GeoLens admin overview with an all-systems-operational health panel showing database, storage, cache, and tile-cache latencies, dataset and storage totals, and AI provider status\" width=\"900\" />\n  </picture>\n  <br />\n  <em><strong>Operate:</strong> the built-in admin plane covers live health, usage, users, jobs, audit log, and AI status — nothing extra to stand up</em>\n</p>\n\n## Quick start\n\n**Prerequisites:** Docker Engine 24+ and Docker Compose v2. The bundled stack\nships PostgreSQL 18. If you point GeoLens at an externally managed database, it\nmust be **PostgreSQL 13+** (for `gen_random_uuid()`) with **pgvector 0.5+** (for\nHNSW semantic-search indexes), plus PostGIS, pg_trgm, and unaccent. The API and\nworker run in containers (Python 3.14 bundled, no host Python needed). The\noptional CLI runs on your host and requires Python 3.11+; the Python SDK and\nseed scripts require Python 3.10+.\n\nClone the repo and run the installer from the checkout. You can read the script before running it; from a clone it builds the images locally:\n\n```bash\ngit clone https://github.com/geolens-io/geolens.git\ncd geolens\nbash scripts/install.sh\n```\n\nThe one-line form runs the same script and pulls the prebuilt, version-pinned images instead of building them:\n\n```bash\ncurl -fsSL https://getgeolens.com/install.sh | sh\n```\n\nEither way, `scripts/install.sh` copies `.env.example` to `.env`, generates a JWT signing\nsecret, sets up admin credentials, and runs `docker compose up -d`. The admin **username**\ndefaults to `admin`; the admin **password** is auto-generated as a strong random value\n(written to `.env`, never printed to your terminal) unless you supply your own.\nFor unattended installs, set `GEOLENS_ADMIN_USERNAME` and `GEOLENS_ADMIN_PASSWORD` in the\nenvironment before running and the prompts are skipped. Re-running the script is idempotent:\nexisting values in `.env` are preserved.\n\nWait about 60 seconds for services to start, then open [http://localhost:8080](http://localhost:8080).\nLog in with your admin username and the generated password (retrieve it with\n`grep '^GEOLENS_ADMIN_PASSWORD=' geolens/.env` — the one-line installer clones\ninto `geolens/` under the directory you ran it from; inside a source checkout\nit's just `.env`).\n\nVerify all services are healthy:\n\n```bash\ndocker compose ps\n```\n\nFirst-run notes: the one-line install **pulls** prebuilt images and is up in about\na minute (only the small PostGIS + pgvector database layer builds locally). Cloning\nand running `bash scripts/install.sh` instead **builds** every image from source:\n5-10 minutes on the first run (GDAL + Postgres extensions + the frontend bundle);\nsubsequent starts settle in ~60 seconds either way. If ports 5434/8001/8080 are\nalready taken, change `DB_PORT`, `API_PORT`,\nor `FRONTEND_PORT` in `.env`. For port conflicts, stuck startups, out-of-memory,\nand migration warnings, see the [Troubleshooting guide](https://docs.getgeolens.com/guides/quickstart/install/#troubleshooting).\n\nFor production deployment, see the [Install Guide](https://docs.getgeolens.com/guides/quickstart/install/). A Kubernetes Helm chart lives in the separate [`geolens-deployments`](https://github.com/geolens-io/geolens-deployments) repo.\n\n### Verify the installer\n\nEach [GitHub Release](https://github.com/geolens-io/geolens/releases) attaches a `SHA256SUMS`\nfile generated by CI alongside `install.sh`. To confirm a downloaded installer was not tampered\nwith before running it, download both assets from the same release and place them in the same\ndirectory, then run:\n\n```bash\n# Linux / Windows WSL\nsha256sum -c SHA256SUMS\n\n# macOS\nshasum -a 256 -c SHA256SUMS\n```\n\nA passing check prints `install.sh: OK`.\n\n### Upgrading\n\nTo upgrade a prebuilt install, run `./scripts/upgrade.sh` from your install\ndirectory. It backs up the database, pulls the new images, runs migrations\nbehind a health gate, and prints a rollback recipe if anything fails. See\n[`UPGRADING.md`](UPGRADING.md) for the prebuilt and source-build flows plus\nrollback, or the online [Upgrade Guide](https://docs.getgeolens.com/guides/quickstart/upgrade/).\n\n### Add your first dataset\n\nThe repo ships a small `city-parks.geojson`. Upload and publish it in one command with the **GeoLens CLI**:\n\n```bash\npip install geolens-cli                              # installs the `geolens` command\ngeolens login http://localhost:8080/api              # use your admin username + password\ngeolens publish examples/manifests/first-catalog/city-parks.geojson --name \"City Parks\"\n```\n\n`geolens publish` runs the upload → preview → commit ingest flow and prints the new dataset's URL. One command takes a local file to a published, mappable dataset.\n\nFor repeatable, multi-dataset catalogs, describe your sources in a **manifest** (`geolens.yaml`) and apply it with `geolens apply`. Manifest sources are referenced by HTTP(S) URL, S3 URI, or a path already staged on the server; the examples in [`examples/manifests/`](examples/manifests/) are templates to adapt. Scaffold a fresh one with `geolens init` and edit it for your sources:\n\n```bash\ngeolens init                       # writes geolens.yaml in the current directory\ngeolens validate geolens.yaml      # local schema check, no API call\ngeolens apply geolens.yaml         # validates + applies via /ingest/manifest/apply\n```\n\nSee the [CLI guide](https://docs.getgeolens.com/guides/cli/) for the full manifest schema, source kinds, and CI integration patterns.\n\n### Seed data\n\n`scripts/seed-showcase.py` builds seven showcase maps from public open data: a global\ntectonics story over real ocean-floor relief, the Manhattan 3D skyline colored by\nconstruction era (the hero above), Atlantic hurricane tracks since 1950, clustered\nmeteorite falls, the Matterhorn in 2 m lidar 3D terrain, by-reference Sentinel-2\nimagery of New York, and a hurricane-exposure map computed in place from the storm\ntracks with buffer, intersect and dissolve:\n\n```bash\npip install httpx\npython scripts/seed-showcase.py --username admin --password \"$(grep '^GEOLENS_ADMIN_PASSWORD=' .env | cut -d= -f2-)\"\n```\n\nRequires internet access to the upstream open-data sources. See\n[`scripts/README.md`](scripts/README.md) for flags (`--no-terrain`, `--prune`, …).\n\n## Architecture\n\nGeoLens is a small set of services around a single PostgreSQL/PostGIS database: the\nAPI serves the catalog, search, and OGC/STAC endpoints; a worker handles ingestion;\nand Titiler serves raster tiles from object storage.\n\n```mermaid\nflowchart TB\n    B[\"Browser: React + MapLibre app\"]\n    OGC[\"QGIS · ArcGIS · OGC/STAC clients\"]\n\n    NG[\"Nginx reverse proxy<br/>serves the React build, routes /api and tiles\"]\n\n    subgraph Application\n      API[\"FastAPI<br/>catalog · semantic search · OGC/STAC · vector tiles\"]\n      W[\"Worker<br/>GDAL/ogr2ogr ingestion\"]\n      TT[\"Titiler<br/>COG raster tiles\"]\n    end\n\n    subgraph store [Data and storage]\n      PG[(\"PostgreSQL 18<br/>PostGIS · pgvector · pg_trgm<br/>+ Procrastinate queue\")]\n      OBJ[(\"Object storage<br/>local files or S3/MinIO\")]\n      CACHE[(\"Valkey cache\")]\n    end\n\n    B --> NG\n    OGC --> NG\n    NG --> API\n    NG --> TT\n    API <--> PG\n    API --> OBJ\n    API -. tile/query cache .-> CACHE\n    PG == job ==> W\n    W --> PG\n    W --> OBJ\n    TT --> OBJ\n```\n\n| Component | Technology |\n|-----------|-----------|\n| Frontend | React 19, Vite, MapLibre GL v6, TanStack Query, Tailwind CSS |\n| Backend API | FastAPI (Python), GDAL/ogr2ogr, Procrastinate (task queue) |\n| Raster Tiles | Titiler (COG tile server) |\n| Object Storage | MinIO (S3-compatible, local dev) or any S3 provider |\n| Cache | Valkey (tile and query cache) |\n| Database | PostgreSQL 18 + PostGIS 3.6 + pgvector + pg_trgm (minimum: PostgreSQL 13, pgvector 0.5) |\n| Reverse Proxy | Nginx (production) / Vite dev proxy (development) |\n\n## Configuration\n\nAll configuration is managed through environment variables in `.env`. See the [Configuration Reference](https://docs.getgeolens.com/guides/quickstart/configuration/) for the full list of options with defaults and descriptions.\n\n### Connection pool budget\n\nGeoLens ships tuned for a **single PostgreSQL** instance: the API, worker, and admin\npools fit within **70 of 80 max_connections** out of the box (Postgres\n`max_connections` is set to 80), sized by `DB_POOL_SIZE` (`pool_size`) and\n`DB_MAX_OVERFLOW` (`max_overflow`, default 3). See\n[Connection Pool Tuning](https://docs.getgeolens.com/guides/quickstart/configuration/#connection-pool-tuning)\nfor the per-process budget and how to raise the ceiling.\n\n### Backups\n\nAutomated, scheduled backups run **by default**. You do not need a `--profile backup` flag.\nThe backup service starts alongside `api`, `worker`, and `db` on every\n`docker compose up` and runs `pg_dump` on a daily/weekly schedule alongside an\narchive of the object-storage staging volume, so a restore reproduces a working\ninstance (DB + uploaded files).\n\n**Off-site (S3) upload** is additionally gated on `BACKUP_S3_ENABLED=true`. The\nbuilt-in uploader signs requests with **AWS Signature V4** (awscli), compatible\nwith Cloudflare R2, modern AWS S3, and MinIO. A failed upload surfaces a visible\n`ERROR` in container logs (not a swallowed warning), so silent offsite backup\nloss is detectable immediately.\n\nFor day-2 operations, restore procedures, and incident response, see\n[RUNBOOK.md](RUNBOOK.md). For provider-specific configuration options, see\n[Backups & Restore](https://docs.getgeolens.com/guides/admin/backups/#backup-destinations).\n\n### Monitoring\n\nThe API and worker export Prometheus metrics out of the box (HTTP rate/latency/\nerrors, job-queue depth, DB pool, tile-cache). Reference scrape config, alert\nrules, and a Grafana dashboard ship in [`infra/monitoring/`](infra/monitoring/);\nsee [RUNBOOK.md §4](RUNBOOK.md#4-monitoring) for the setup steps.\n\n## Reference\n\n| Guide | Description |\n|-------|-------------|\n| [Install Guide](https://docs.getgeolens.com/guides/quickstart/install/) | Step-by-step deployment with Docker Compose |\n| [Upgrade Guide](https://docs.getgeolens.com/guides/quickstart/upgrade/) | Upgrading between versions with rollback procedures |\n| [Configuration Reference](https://docs.getgeolens.com/guides/quickstart/configuration/) | All environment variables and their defaults |\n| [Admin Guide](https://docs.getgeolens.com/guides/admin/) | User management, datasets, system health |\n| [Self-host on AWS, GCP, or DigitalOcean](https://docs.getgeolens.com/guides/quickstart/cloud-deployment/) | Managed database, object storage, and cache deployment guides |\n| [CLI & Manifests](https://docs.getgeolens.com/guides/cli/) | Publish files and manage catalogs with the `geolens` CLI |\n| [API Reference](https://docs.getgeolens.com/guides/api/) | Auto-generated reference at docs.getgeolens.com; development-mode stacks also serve Swagger UI at `/api/docs` (disabled in production) |\n| [Manifest examples](examples/manifests/) | Template `geolens.yaml` manifests to adapt: public-cog (remote COG), url-source, s3-source, publication-states |\n| [Client examples](https://github.com/geolens-io/geolens-examples) | Runnable browser, QGIS, DuckDB, SDK, CLI, embed, Python, and MCP examples; the read-only ones are verified against the live demo in CI ([gallery](https://geolens-io.github.io/geolens-examples/)) |\n\n## Community\n\n- [GitHub Discussions](https://github.com/geolens-io/geolens/discussions): questions, ideas, show and tell\n- [Support](SUPPORT.md): where to ask for help and how problems get routed\n- [Contributing Guide](.github/CONTRIBUTING.md): development setup, code style, and PR guidelines\n\n## Known limitations\n\n- Single PostgreSQL instance, with no built-in high availability or clustering.\n- GeoLens is designed for one organization per self-hosted deployment.\n- Terrain rendering assumes DEM units are in meters; datasets in other vertical units may render exaggerated.\n- GeoLens's own REST API may still change between minor releases (see the API stability note above).\n\n## License\n\nGeoLens is licensed under the [Apache License 2.0](LICENSE). The GeoLens name, logo, and brand assets are not covered by this license. See [TRADEMARKS.md](TRADEMARKS.md). Third-party sample-data attribution is in [THIRD_PARTY_DATA.md](THIRD_PARTY_DATA.md).\n\nProject policies: [governance](GOVERNANCE.md) · [maintainers](MAINTAINERS.md) · [contributing](.github/CONTRIBUTING.md) · [security](.github/SECURITY.md) · [release process](RELEASE.md) · [egress &amp; air-gap](EGRESS.md).\n",
  "bytes": 27688,
  "sha": "601d1b5a240fe050f900dbee4aa3cb3630e797a8e1907dd48450ec14418fce45",
  "repo_slug": "geolens-io/geolens",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_geolens_io_geolens_9b79cd58/readme"
}