Skip to content
getgeolens.com

Settings Reference

The Admin -> Settings page in the GeoLens web UI groups every system setting into seven tabs. This page orients you to what each tab controls and where to find the deeper documentation. The settings UI is the recommended interface for day-to-day configuration. Many of these settings also have an environment variable that supplies the initial value at deploy time; settings marked as admin settings below do not.

Replace https://geolens.example.com with your GeoLens instance’s URL in every example below.

The General tab covers the instance’s public URLs, the announcement banner, publishing and editing policy, and logging. Most of it is operational defaults, but two settings are not cosmetic: the public URLs are what the OAuth callback builds its post-login redirect from, and Enable Dataset Editing decides whether data can be edited in place.

SettingPurpose
Public App URL / Public API URLpublic_app_url, public_api_url: the externally reachable origins GeoLens builds absolute links, OGC self-links, and OAuth redirects from. Per-map link previews (a public map shared in Slack, Teams, Discord or social posts shows its own title, description and image) also need public_app_url for their absolute URLs. The OAuth callback resolves them from explicit configuration rather than a forwarded host header, so it fails closed (500) when they are unset. public_base_url is a deprecated alias for public_api_url — it still resolves, but logs a startup warning
Privacy Policy URLprivacy_url: shown as a link on the login and register pages; leave empty to hide the link
Require Metadata for Publishingrequire_metadata_for_publish (default: false). Datasets must carry complete metadata before they can be published
Enable Dataset Editingenable_dataset_editing (default: false). Allows geometry drawing and attribute editing on dataset detail pages
Restrict Public Visibility to Adminsrestrict_public_visibility (admin setting, not read from .env; default: false). When on, only administrators can set datasets and maps to public visibility. Other users keep every narrower visibility level
Audit Log Retentionaudit_retention_days (admin setting, not read from .env; unset by default, which keeps every row). Deletes audit rows older than this many days. The minimum is 90, and the field warns below 365. The worker prunes at start and then every six hours, 5,000 rows per batch, and each batch writes an audit.prune event with its count and cutoff. Pruning does not archive, so export first if you need the rows. See Infrastructure & Monitoring
Log Level / JSON Logginglog_level, log_json: runtime log verbosity and structured-log output
Site bannerbanner_enabled, banner_text, banner_color: a site-wide announcement banner shown to every visitor (logged-in and anonymous) when enabled with non-empty text; pick a color (info, success, warning, or destructive — warning is the default and the fallback for an unrecognized value). Any https:// URL in the text renders as a link that opens in a new tab; wrap it in angle brackets (<https://...>) to have the URL taken verbatim, including trailing punctuation. Banner text is escaped, so markup in it is never rendered, and text over 2,000 characters renders as plain text with no linking. Visitors can dismiss it for the rest of their browser session

These map to environment variables documented under Configuration Reference -> General. Persistent settings stored in the database take precedence over .env defaults.

The Map tab controls which basemaps the instance offers, the default view for new maps, and which map-builder plugins are available. Existing maps keep their per-map settings; this tab only seeds the defaults shown when a user creates a fresh map.

SettingPurpose
Basemapsbasemaps: the list offered to map authors. Four presets ship enabled — OpenFreeMap Positron, OpenFreeMap Dark, OpenStreetMap, and OpenFreeMap Bright — and each can be switched off individually. Custom entries take a name, a URL — an XYZ/TMS tile template, a MapLibre GL style JSON URL, or (since 1.16) a PMTiles archive, either as a bare https:// URL ending in .pmtiles or the same URL pmtiles://-prefixed — attribution, and an optional API key interpolated server-side into an {api_key} placeholder. A raster PMTiles archive renders directly as a basemap. A bare vector archive is rejected when you add it — it carries no styling information — so reference a vector archive from a style JSON URL instead; the form reads the archive’s header before saving to tell the two apart
Default map centermap_defaults.center_lat / map_defaults.center_lng (default: 20, 0)
Default zoom levelmap_defaults.zoom (default: 2). Zoom 0 to 22; typical values 2 to 4 for global views, 10 to 14 for city scale
Enabled pluginsenabled_plugins: which map-builder plugins are available

The basemap list is configurable: you can add MapTiler, Mapbox, or self-hosted basemap servers. Configuration details are in Configuration Reference.

The Auth tab covers token lifetimes, registration, sign-in policy, and OAuth providers. Password policy (PASSWORD_MIN_LENGTH, PASSWORD_REQUIRE_CLASSES) is set in .env only; see Configuration Reference -> Authentication. The OAuth Providers section is the entry point for the OAuth/OIDC setup walkthrough.

SettingMaps to
JWT signing secretJWT_SECRET_KEY (env-only, not editable in UI)
Access token lifetimeACCESS_TOKEN_EXPIRE_MINUTES (default: 15)
Refresh token lifetimeREFRESH_TOKEN_EXPIRE_DAYS (default: 7)
Self-registrationREGISTRATION_ENABLED (default: false); see User management
Require Email Verificationemail_verification_required (default: true; EMAIL_VERIFICATION_REQUIRED sets the default from 1.24, earlier releases do not read it from .env). Applies to password sign-ups while self-registration is on. With SMTP configured (see Configuration Reference -> Outbound notifications), a sign-up that confirms its email is active right away with no admin approval. Without SMTP, or with this off, sign-ups wait as Pending for an admin to approve. SSO sign-ups are not affected
Default role for new sign-upsregistration_default_role (admin setting, not read from .env; default: viewer). Role granted to a password sign-up when it activates by verifying its email. Admin approval picks its own role, and SSO accounts get the provider’s default role
Login-as-Landing PageLANDING_FIRST (default: false). Redirects unauthenticated visits to / to the login page instead of the catalog
Password Login Enabledpassword_login_enabled (default: true; PASSWORD_LOGIN_ENABLED sets the default from 1.24). When off, POST /auth/login returns 403 for every user without manage_settings; admins keep password login as a break-glass path
Login rate limitlogin_rate_limit (default: 5 attempts/min; LOGIN_RATE_LIMIT sets the default from 1.24, earlier releases do not read it from .env). Applies to POST /auth/login per client IP
Allowed Email Domainsallowed_email_domains (default: empty = any domain; ALLOWED_EMAIL_DOMAINS sets the default from 1.24). Enforced on sign-up, login, SSO, and admin-create. When the list is non-empty, a first-time SSO identity must present a verified email in an allowed domain or the login is refused; a returning user is checked against their stored address when the IdP claim is unverified, and a returning user with manage_settings keeps the same break-glass exemption
OAuth providersDatabase-stored; see OAuth/OIDC setup

JWT_SECRET_KEY is set in .env and not editable through the UI. Rotating it invalidates all active JWT tokens, so every user has to log in again. Stored OAuth client secrets are encrypted with SECRET_ENCRYPTION_KEY when that is set, and otherwise with a key derived from JWT_SECRET_KEY. Without SECRET_ENCRYPTION_KEY, a JWT rotation leaves every stored client secret undecryptable, and each one has to be re-entered in the admin UI before anyone can sign in with SSO. Set SECRET_ENCRYPTION_KEY and re-encrypt the stored secrets with backend/scripts/rotate_secrets.py before you rotate the JWT secret; RUNBOOK.md section 11 has the steps.

The Storage tab controls upload limits, per-user quotas, and how long rendered tiles stay cached. Where files physically live is environment-only: STORAGE_PROVIDER and the S3_* / AZURE_* credentials are read from the environment and are not editable here — see Configuration Reference -> Storage provider.

SettingMaps to
Upload max sizeUPLOAD_MAX_SIZE_MB (default: 500)
Allowed extensionsUPLOAD_ALLOWED_EXTENSIONS
Tile Cache TTLTILE_CACHE_TTL (seconds; default: 300)
Max Storage per Usermax_storage_bytes_per_user (admin setting, not read from .env; default: 0 = unlimited)
Max Datasets per Usermax_datasets_per_user (admin setting, not read from .env; default: 0 = unlimited)

Raising Upload max size here is not enough on its own: the frontend’s nginx enforces its own ceiling, CLIENT_MAX_BODY_SIZE, and uploads above it fail with a 413 before they reach GeoLens. Under Compose it follows UPLOAD_MAX_SIZE_MB in .env, so raise that too. See the Configuration reference.

Backup off-site replication reuses the storage S3_* values, and there are no backup-specific bucket or credential overrides, so off-site dumps land under backups/ in S3_BUCKET. See Backups & restore for BACKUP_S3_ENABLED.

The Network tab covers CORS origins, the API rate limits, and the OGC API Features page-size ceiling.

SettingMaps to
CORS originsCORS_ALLOWED_ORIGINS: comma-separated list of frontend origins allowed to make credentialed or native-route calls to the API
Global rate limitglobal_rate_limit (admin setting; default 60 requests/second)
Semantic search rate limitsemantic_search_rate_limit (admin setting; default 30 requests/min)
Basemap proxy rate limitbasemap_proxy_rate_limit (admin setting; default 120 requests/min)
OGC Features max page sizeogc_items_max_page_size (admin setting, not read from .env; default 1000). Ceiling for limit on /api/collections/{id}/items; a larger limit is clamped silently

The login rate limit lives on the Auth tab (login_rate_limit, default 5 attempts/min), not here.

Every rate limit on this tab is counted in a store shared by the whole deployment when REDIS_URL is set, and once per API worker when it is not. The OGC Features page-size ceiling is not a rate limit and is unaffected. Without the store, a saved value of 60 requests/second is enforced as 60 per worker, and the bundled production compose file runs two API workers by default. Each worker logs rate_limit_storage_not_configured at startup when the variable is unset. See Configuration.

CORS_ALLOWED_ORIGINS defaults to empty. That does not mean no cross-origin browser access: anonymous requests to the standards paths (/api/, /api/conformance, /api/collections*, /api/stac*, the DCAT routes) and to the vector and raster tile routes always get a credential-free Access-Control-Allow-Origin: *, so a public page on any origin can read the public catalog and draw public tiles with no setup. Since 1.14.1 the anonymous catalog search routes (/api/search/datasets/, /api/search/facets/) get the same credential-free wildcard, with a preflight that advertises only the GET they answer, so a cross-origin page can query native search directly. Saved searches (/api/search/saved/) stay authenticated and get no wildcard. Both public policies also expose Retry-After, so a cross-origin caller can read the retry window on a 429.

The setting governs everything else: any request that carries a credential (X-Api-Key, Authorization, cookies) and every native application route. Those get credentialed CORS responses (Access-Control-Allow-Credentials: true), and the browser spec forbids pairing that with a wildcard, so the middleware rejects * and you must list exact origins (comma-separated). Set them explicitly for any browser client served from a different origin than the API that signs in or sends a key.

Outbound notification channels (SMTP email and webhooks for signup, ingest, and health events) are environment-only — their credentials are secrets and never stored in the settings database, so they do not appear in this tab. See Configuration Reference -> Outbound notifications.

The AI tab toggles AI chat across GeoLens — the map builder panel, the dataset-detail Ask AI assistant, and the read-only map viewer — and configures the upstream LLM provider. The AI feature toggle ships enabled, but AI is inert until a provider is configured: an API key, or for Claude a cloud platform with its settings (see Configuration Reference -> Claude on a cloud platform). With none, AI endpoints return unavailable.

SettingMaps to
LLM Providerllm_provider (admin setting, not read from .env). Selects which provider serves inference: Anthropic or OpenAI-compatible
Anthropic API keyANTHROPIC_API_KEY (environment-only; the tab shows a configured / not-set indicator, not an input)
Claude platformANTHROPIC_PLATFORM (environment-only; the admin UI cannot change where Claude credentials go): anthropic (default), foundry, bedrock or vertex. A cloud platform with its settings counts as configured without ANTHROPIC_API_KEY, and LLM_MODEL must then be that platform’s own model ID. See Configuration Reference -> Claude on a cloud platform
LLM Modelllm_model (admin setting). The tab has a single model input, whichever provider is selected; its initial value follows the provider: LLM_MODEL for Claude (default claude-sonnet-5-5; Bedrock and Vertex have no default, so set it to the platform’s model ID) and OPENAI_MODEL (default gpt-4o) for the OpenAI-compatible provider. Until an admin picks a provider, it is Claude when Claude is configured, by API key or by a cloud platform with its settings, and OpenAI-compatible otherwise
Light LLM Modelllm_model_light: the cheaper model used for the AI’s SQL generation and metadata suggestions, not for chat. The tab has no input for it — set it with PUT /api/settings/, or seed the default from OPENAI_MODEL_LIGHT. Falls back to LLM_MODEL_LIGHT when set. Otherwise it is claude-haiku-5-5 on Anthropic’s own API (a default that takes effect when the setting is unset, so it can differ from the old claude-haiku-4-5-20251001 on an upgraded install), the LLM_MODEL value on Foundry, Bedrock and Vertex, and OPENAI_MODEL_LIGHT or the configured OpenAI-compatible model on the OpenAI-compatible provider
Max AI Tokens per User per Daymax_ai_tokens_per_user_per_day (admin setting, not read from .env; default: 0 = unlimited)
OpenAI-compatible API keyOPENAI_API_KEY (environment-only; the tab shows a configured / not-set indicator, not an input)
OpenAI-compatible base URLOPENAI_BASE_URL: for non-OpenAI endpoints (Azure, local servers)
OpenAI-compatible API styleOPENAI_API_STYLE (environment-only): chat_completions (default) or responses. Choose responses for OpenAI models that call tools only through the Responses API, such as gpt-6.1-sol on Azure OpenAI. A model that needs it but is set to Chat Completions fails with a message that names this setting
AI feature toggleai_enabled (admin setting, not read from .env; default: true). When off, the AI chat panel is hidden for all users
Send Sample Valuesai_send_sample_values (admin setting, default: true). Includes sample column values from the dataset in the prompt sent to the provider; turn it off to send column names and types only
Semantic Searchsemantic_search_enabled (admin setting, not read from .env; default: false)
Embedding ModelEMBEDDING_MODEL (default: text-embedding-3-small); the OpenAI-compatible model that generates search vectors
Embedding Base URLEMBEDDING_BASE_URL: a separate endpoint for embedding generation; falls back to OPENAI_BASE_URL when empty
Embedding DimensionsEMBEDDING_DIMS (default: 1536); Detect probes the configured model rather than making you look the number up

The provider is an explicit setting, not an inference from which key happens to be present. llm_provider defaults to Anthropic when Claude is configured (an ANTHROPIC_API_KEY, or a cloud platform chosen with ANTHROPIC_PLATFORM and its settings) and to the OpenAI-compatible path (OPENAI_API_KEY + OPENAI_BASE_URL) otherwise, but an admin can change it on this tab at any time. Whichever provider is selected must itself be configured. Readiness is checked against the selected provider only, so credentials for the other one do not make AI available.

Some AI settings live only in the environment: provider credentials, the Claude platform and its variables, ANTHROPIC_BASE_URL, OPENAI_API_STYLE and OPENAI_REASONING_EFFORT. The UI cannot change them; set them in .env and restart. Everything else on this tab is admin-editable at runtime.

The AI chat is gated by both the use_ai_chat permission (see User management) and the global feature toggle. Disabling the toggle takes effect immediately for all sessions; users see the AI panel disappear on next page load.

Semantic search lives in the Semantic Search & Embeddings section of the AI tab. Turning the toggle on is only one of three steps, and it is the one that does the least on its own: with the toggle on and no embeddings generated, search silently falls back to keyword-only results.

  1. Configure an OpenAI-compatible API key. Embeddings always go through the OpenAI-compatible path (OPENAI_API_KEY, optionally with OPENAI_BASE_URL for Azure, Ollama, or a local server) — an Anthropic key drives chat but cannot generate embeddings. Set Embedding Model to the model you want vectors from, then click Detect next to Embedding Dimensions to probe the model for its output size instead of entering it by hand. The button stays disabled until an OpenAI-compatible key is configured.

  2. Turn on the Semantic Search toggle. This is semantic_search_enabled, an admin setting stored in the database and not read from .env. Putting SEMANTIC_SEARCH_ENABLED in your .env file has no effect.

  3. Generate the embeddings. The Embedding Coverage panel shows how many catalog records currently have a vector (embedded_records/total_records). Click Generate Missing Embeddings to embed the records that lack one. Coverage has to be non-zero before semantic ranking changes any result.

Embeddings for new and edited records are generated as they are saved, so the backfill is a one-time catch-up for the catalog you already have. Use Regenerate All after changing the embedding model or dimensions: existing vectors were produced by the old model and are not comparable to new ones.

Since 1.19.0 the coverage panel also reports the runs themselves, so you can watch a backfill instead of inferring it from the coverage figure:

  • A run in flight shows its progress. The panel reads out records embedded against the run’s total, refreshed as it goes. A run that has not finished selecting its records has no total yet, and the readout says it is starting rather than showing a count.
  • The last five finished runs are listed, newest first, each with how it ended (complete, failed or cancelled), the records it embedded, the records it could not embed where the run recorded a count, and when it finished. A finished run with a non-zero failure count left coverage gaps.
  • An estimate appears before you start one. Each button that applies is costed in whole minutes, taken from the throughput of the most recent completed run, so the figure describes this deployment’s own provider rather than a generic rate. There is no estimate until one run has completed, and it gives way to the progress readout while a run is in flight.
  • Both buttons are disabled while any run is in flight, including one another operator started. The run slot is shared, not per admin session, and reopening the page mid-run shows you the run in progress.

The same three steps are available over the API: POST /api/settings/detect-embedding-dims/ probes the model, PUT /api/settings/ writes semantic_search_enabled, and GET /api/admin/embedding-stats/ plus POST /api/admin/backfill-embeddings/ report coverage and run the backfill (add ?force=true for the regenerate-all behavior). The stats response carries current_run, recent_runs and estimate alongside the coverage counts, which is what the panel above renders. See Search & Discovery for what changes on the user-facing side once it is on.

The Permissions tab is where the role-to-capability matrix is overridden. The default matrix is documented at User management -> Roles & permissions; this tab lets you grant additional capabilities to non-admin roles or restrict capabilities further.

Common customizations:

  • Restricted viewer: remove export from the viewer role for read-only audit-trail use cases
  • Metadata-only editors: remove upload and create_layers from editor so the role can curate metadata without adding data
  • Locked-down public: disable use_ai_chat for all non-admin roles to control LLM API spend

The matrix covers the three built-in roles (viewer, editor, admin). Creating additional roles is not supported.

Custom permission matrices persist in the database as a single role_permissions setting and survive restarts. To reset to the shipped defaults, use Reset to Defaults on the Permissions tab itself — it restores the built-in role/capability matrix.