CLI & Manifests
The GeoLens CLI (geolens) is the fastest way to get data into an instance
from a terminal or CI pipeline. It wraps the Python SDK to publish single files,
scan directories, export STAC metadata, and apply declarative manifests
(geolens.yaml) that describe a whole catalog.
Install
Section titled “Install”pip install geolens-cli # installs the `geolens` command# or, for an isolated tool install:pipx install geolens-cliVerify the install:
geolens --versiongeolens --helpTo run a command without installing anything, uvx fetches the package on
demand:
uvx --from geolens-cli geolens --versionPin the version in automation (uvx --from geolens-cli==<version> geolens ...).
The examples repo
keeps the pinned form of the manifest commands on this page, next to a
working manifest and GitHub Actions workflow.
Authenticate
Section titled “Authenticate”Log in to an instance and store credentials. The instance URL is the API
base, so include the /api suffix:
geolens login http://localhost:8080/api# prompts for your admin username and password (auto-generated at install; see your .env)By default the bearer token is stored in your operating system keyring. Pass
--no-keyring to fall back to a credentials.toml file instead (useful on
headless hosts without a keyring service):
geolens login https://geolens.example.com/api --no-keyringYou can also store a token or API key non-interactively (handy for scripts):
geolens login https://geolens.example.com/api --token "$JWT"geolens login https://geolens.example.com/api --api-key "$GEOLENS_API_KEY"# pass `-` to read the secret from stdin (keeps it out of argv / shell history):echo "$JWT" | geolens login https://geolens.example.com/api --token ---token and --api-key are mutually exclusive; pass only one.
Check who you are, or clear credentials:
geolens whoami # prints the active user and instancegeolens logout # removes stored credentials for the active instancePublish a single file
Section titled “Publish a single file”geolens publish uploads one local vector or raster file and runs the full
ingest flow (upload -> preview -> commit), then prints the new dataset’s URL:
geolens publish ./city-parks.geojson --name "City Parks"geolens publish ./elevation.tif --name "Elevation" --description "10m DEM"By default the command waits for ingestion to resolve the dataset id. Use
--no-wait to return immediately with a job-search URL instead:
geolens publish ./big-raster.tif --no-waitScan a directory
Section titled “Scan a directory”geolens scan walks a directory and reports what would be ingested, a dry
run with no upload. Use it to preview a bulk import:
geolens scan ./gis-exportsgeolens scan ./gis-exports --include-ext .gpkg,.tif --max-depth 2geolens scan ./gis-exports --json # machine-readable outputManifests: repeatable catalogs
Section titled “Manifests: repeatable catalogs”For multi-dataset catalogs that you want to version and re-apply, describe your
sources in a geolens.yaml manifest and apply it declaratively.
geolens init # scaffold ./geolens.yaml (errors if it already exists)geolens init --force # overwrite an existing manifestgeolens init catalog.yaml # scaffold at a custom pathgeolens validate geolens.yaml # local schema check, no API callgeolens apply geolens.yaml # validate + apply via the GeoLens APIgeolens apply geolens.yaml --dry-run # preview apply outcomes without writesEach dataset carries a stable key, so re-applying an edited manifest updates
the matching datasets instead of creating duplicates. The server matches each
entry to an existing dataset by key and fingerprints the rest, then answers
create (new key), update (known key, changed entry), or skip (unchanged
entry) per dataset. An unchanged manifest is therefore safe to apply on every
push. --dry-run returns the same verdicts without writing, but the server
evaluates it, so it needs a credential like a real apply.
Apply reconciles the declaration rather than the bytes behind it: a source URL
that serves new content under an unchanged entry still skips. geolens refresh <dataset_id> is the manual refresh from the dataset page, for datasets with
an upstream to re-pull (Service, registered PostGIS table, STAC). A source the
server downloaded for a manifest is an ordinary upload once ingested, so refresh
refuses it. See Where Your Data Lives.
Manifest schema (v1)
Section titled “Manifest schema (v1)”The top level requires manifest_version: "1", a catalog block, and a
non-empty datasets array.
Print the packaged JSON Schema for geolens.yaml — for editor validation or
CI schema checks — without contacting an API:
geolens schema # print to stdoutgeolens schema -o geolens.schema.json # write to a filecatalog: required title; optional description, organization, and a
contact object (name, email, url).
datasets[]
| Field | Required | Notes |
|---|---|---|
| key | Yes | Stable identity for idempotent apply. Must start with a lowercase letter or digit, then a-z 0-9 . _ -; up to 128 chars. |
| title | Yes | Human-readable dataset title. |
| description | No | Longer description. |
| sources | Yes | Exactly one source object (below); the schema caps the array at one entry. |
| metadata | No | Tags, CRS, license, bbox (below). |
| publication | Yes | Publication intent (below). |
sources[]
| Field | Required | Notes |
|---|---|---|
| type | Yes | One of vector, raster_cog. |
| uri | Yes | Relative path, https://, or s3:// / gs:// / az:// / abfs:// URI. Must end in an extension the ingest path recognizes: zip, gpkg, geojson, json, csv, xlsx, or xls for vector; tif or tiff for raster_cog. A query string or fragment may follow. |
| format | No | Driver hint, e.g. geojson, gpkg. |
| layer | No | Layer name for multi-layer sources. |
| title / description | No | Per-source overrides. |
metadata (all optional): tags (string array), organization, crs
(EPSG:NNNN), license, attribution, bbox ([minx, miny, maxx, maxy] in
WGS84).
publication: required intent. The schema does not fix the values; they
come from the workflow statuses your deployment defines, and the server
validates them at apply time. The community default set is draft, ready,
internal, published, which is why geolens validate can accept an intent
your instance then rejects.
Example manifest
Section titled “Example manifest”manifest_version: "1"catalog: title: Regional Open Data description: Public datasets published by the Regional Data Office. organization: Regional Open Data Officedatasets: - key: regional-trails title: Regional trails description: Recreational trail network. sources: - type: vector uri: https://data.example.com/trails.geojson format: geojson metadata: tags: [trails, recreation] crs: EPSG:4326 license: CC-BY-4.0 attribution: Regional Open Data Office bbox: [-78.0, 38.0, -76.5, 39.5] publication: intent: readyExport STAC metadata
Section titled “Export STAC metadata”Export STAC API 1.0 item metadata for a raster dataset (vector datasets are rejected with a clear message):
geolens export stac <dataset_id> # pretty JSON to stdoutgeolens export stac <dataset_id> -o item.json # write to a filegeolens export stac <dataset_id> --compact # single line, for piping to jqCI integration
Section titled “CI integration”The CLI is built for pipelines: authenticate through the environment, validate
offline, preview with --dry-run on a pull request, apply on a push to main.
--json is a global option and goes before the subcommand (geolens --json apply geolens.yaml; geolens apply --json is an error), and every command
returns a non-zero exit code on failure.
The examples repo has a ready-made GitHub Actions workflow that splits validate, preview, and apply into three jobs so the write token never reaches a pull request, and a walkthrough of the secrets and environment it expects.
Global options
Section titled “Global options”These apply to every command and go before the subcommand:
| Flag | Effect |
|---|---|
| --instance <url> | Override the active instance for this command. |
| --json | Machine-readable JSON output. |
| -v, --verbose | Debug logging to stderr. |
| -q, --quiet | Suppress non-error output. |
| --version | Print the CLI version and exit. |