Profiles & configuration

The geopera CLI keeps each identity in a named profile inside a single, mode-hardened credentials file, and resolves the API base URL through a short precedence chain that environment variables and flags can override. This page documents exactly where that state lives, how every setting is resolved, and the edge cases worth knowing before you script against it.

Almost everything here is settled by two functions in the CLI’s config module — resolve_profile and resolve_api_url — plus the credential store in the auth module. The defaults are deliberately boring: install the CLI, run geopera login, and you never touch any of it. The knobs below exist for multi-environment work, CI, and debugging.

The credential store

Credentials live in one JSON file under your XDG config directory:

bash
~/.config/geopera/credentials.json

The directory is created with mode 0700 (owner-only) and the file is written with mode 0600, so the session tokens and API keys it holds are never group- or world-readable. The CLI re-chmods both the directory and the file on every write, because os.makedirs(mode=...) only applies to the leaf directory it creates and is still subject to your umask — the explicit chmod guarantees the final permissions regardless.

Writes are atomic. The store is written to a sibling credentials.json.tmp, opened 0600 from the very first byte (via os.open(..., O_CREAT | O_TRUNC, 0o600)) so the secret is never momentarily world-readable, then os.replaced over the real file. A crash mid-write can therefore never leave a truncated or partially-secret credentials.json behind — you either have the old file intact or the new one complete. The temp file is unlinked in a finally block, so a failed write does not litter .tmp files.

The file is written with sort_keys=True and two-space indentation, so profiles appear alphabetically and diffs stay stable. You normally never edit it by hand:

  • geopera login creates and updates a profile.
  • geopera logout clears the active profile’s credentials (see the gotcha about api_url).

If the file is missing, unreadable, or contains malformed JSON, the store loader returns an empty store rather than raising — so a corrupt file degrades to “not logged in” instead of crashing every command. The fix in that case is simply to log in again, which rewrites the file cleanly.

Profiles

Every identity is a top-level key in the store, keyed by profile name. Each profile entry has exactly two fields: api_url and auth.

json
{
	"default": {
		"api_url": "https://api.geopera.com",
		"auth": {
			"type": "oauth",
			"access_token": "...",
			"refresh_token": "...",
			"expires_at": 1750000000,
			"scope": "openid profile",
			"issuer": "https://api.geopera.com"
		}
	},
	"staging": {
		"api_url": "https://staging.api.geopera.com",
		"auth": { "type": "api_key", "api_key": "gpra_..." }
	}
}

The auth block is one of two shapes:

auth.typeFieldsSent as
oauthaccess_token, refresh_token, expires_at, scope, issuerAuthorization: Bearer <access_token>
api_keyapi_keyX-API-Key: <api_key>

A type: oauth profile is what a browser sign-in produces: geopera login runs the device flow and stores the resulting session token (plus its refresh material and expiry). A type: api_key profile comes from geopera login --api-key and stores a long-lived gpra_ key verbatim. The CLI picks the auth header automatically per request — Authorization: Bearer ... for a session token, X-API-Key: ... for an API key. For how these credentials are obtained and how a session token is refreshed near expiry, see Authentication and the CLI authentication page.

You can log in to as many profiles as you like; each lives independently in the store. Saving a profile rewrites only the one profile you touched and leaves the others intact, so logging into staging never disturbs default.

Selecting a profile

Profile selection follows this precedence (highest first):

PrecedenceSourceNotes
1 (highest)--profile <name> flagPer-command. Wins over everything.
2GEOPERA_PROFILE env varApplies to the whole shell session.
3 (default)the literal "default"Used when neither of the above is set.
bash
# Use the 'staging' profile for a single command
geopera whoami --profile staging

# Make 'staging' the default for this shell session
export GEOPERA_PROFILE=staging
geopera whoami

--profile is a shared option accepted by every command that needs credentials (login, logout, whoami, every geopera <resource> <action> command, and the raw op escape hatch). The resolution rule is the same everywhere: flag, then env var, then "default".

Resolving the API base URL

The base URL the CLI talks to is resolved per command with this precedence (highest first):

PrecedenceSourceNotes
1 (highest)--api-url <url> flagPer-command override.
2GEOPERA_API_URL env varWhole-session override.
3the active profile’s stored api_urlCaptured at login time.
4 (default)https://api.geopera.comThe production API.

The resolved value has any trailing slash stripped, so https://api.geopera.com/ and https://api.geopera.com behave identically and never produce a doubled //v1/... path.

bash
# One-off override against a local backend
geopera catalog search --collections sentinel-2-l2a --limit 10 \
  --api-url http://localhost:8000

# Override for the whole session
export GEOPERA_API_URL=https://staging.api.geopera.com
geopera whoami

Because each profile already stores its own api_url (captured at login time), you rarely need --api-url or GEOPERA_API_URL in day-to-day use — switching profiles already switches the endpoint. The override flags are there for pointing an existing identity at a different deployment without re-logging-in, which is most useful when testing a staging or local backend with production-shaped credentials.

The GEOPERA_API_TOKEN escape hatch

For CI and other ephemeral environments where you do not want to write a credentials file at all, set GEOPERA_API_TOKEN. When it is present it bypasses the credential store entirely — no profile auth lookup, no refresh, nothing written to disk:

bash
export GEOPERA_API_TOKEN="gpra_xxxxxxxxxxxxxxxxxxxxxxxx"
geopera orders list

How the value is interpreted depends on its prefix:

  • A value beginning with gpra_ is treated as an API key and sent as X-API-Key.
  • Any other value is treated as an opaque bearer token (a session token) and sent as Authorization: Bearer <value>.

The opaque-bearer mode lets you inject, for example, a session token minted by your CI provider without the CLI trying to refresh it. In this mode expires_at is set to null, so no proactive refresh is ever attempted — the token is used exactly as given, and a 401 surfaces as an error rather than triggering a refresh-and-retry. (Refresh-on-401 only ever runs for a stored oauth profile that actually carries a refresh_token.)

--api-url / GEOPERA_API_URL still apply on top of the env-token path, and the active profile’s stored api_url is still consulted as the default. That means even when the token comes from the environment, the endpoint is resolved with the full precedence chain above, so you can point an env-token command at any deployment:

bash
GEOPERA_API_TOKEN="$CI_GEOPERA_TOKEN" \
  geopera orders list --api-url https://staging.api.geopera.com

Note the asymmetry: GEOPERA_API_TOKEN short-circuits the auth lookup but does not short-circuit URL resolution. The profile is still resolved (so the profile name shown by whoami reflects --profile/GEOPERA_PROFILE), and its stored api_url is still used as the level-3 default — only the profile’s auth block is ignored in favour of the env token.

Environment variables

VariablePurposePrecedence note
GEOPERA_PROFILEActive profile nameBelow --profile, above the "default" fallback
GEOPERA_API_URLAPI base URLBelow --api-url, above the profile’s stored api_url
GEOPERA_API_TOKENOpaque bearer / API key; bypasses the credential store’s authOverrides the entire stored auth; URL resolution is unaffected

There is no env var for the credential file location — the path is fixed at ~/.config/geopera/credentials.json. If you need an isolated store (for example to keep CI credentials out of your interactive profile), prefer the GEOPERA_API_TOKEN escape hatch over relocating the file.

Worked example: a dedicated CI profile

Suppose you maintain a long-lived default profile for interactive work and want CI to run against staging with a scoped API key, without ever writing to the runner’s home directory.

Locally, mint and store the staging key under its own profile so you can test the exact path CI will use:

bash
geopera login --api-key gpra_xxxxxxxxxxxxxxxxxxxxxxxx \
  --api-url https://staging.api.geopera.com \
  --profile staging

geopera whoami --profile staging
bash
sub:             [email protected]
principal_type:  service_account
org_id:          org_01H...
scope:           orders:read orders:write
api_url:         https://staging.api.geopera.com
profile:         staging

In CI, do not log in at all — hand the key in through the environment so nothing is persisted on the runner:

yaml
# .github/workflows/smoke.yml (excerpt)
env:
  GEOPERA_API_TOKEN: ${{ secrets.GEOPERA_STAGING_KEY }}
  GEOPERA_API_URL: https://staging.api.geopera.com
steps:
  - run: geopera orders list

Because GEOPERA_API_TOKEN starts with gpra_, the CLI sends it as an API key and the credential store is never read or written. If you would rather pin the endpoint per command than via GEOPERA_API_URL, drop the env var and add --api-url https://staging.api.geopera.com to each invocation — the precedence is identical, the flag simply wins one level higher.

Inspecting the resolved configuration

geopera whoami is the quickest way to confirm which identity, endpoint, and profile a command will actually use — it prints the resolved api_url and profile alongside the principal:

bash
geopera whoami --profile staging --api-url https://staging.api.geopera.com
bash
sub:             [email protected]
principal_type:  service_account
org_id:          org_01H...
scope:           orders:read orders:write
api_url:         https://staging.api.geopera.com
profile:         staging

For the machine-readable form, add --json to get the raw userinfo payload (see the gotcha about what --json is bound to internally):

bash
geopera whoami --json

Gotchas

  • There is no geopera --version. The CLI does not register a --version flag. To check what you have installed, query the package directly — for example pip show geopera — since the CLI is a thin shell over the published geopera SDK.
  • logout keeps api_url. geopera logout removes only the auth block from the active profile; the profile’s stored api_url is preserved. A subsequent geopera login against that profile therefore defaults back to the same endpoint. If logout finds no stored auth, it reports No stored credentials for profile '<name>'. and changes nothing.
  • --json is internally the raw parameter. Commands such as whoami accept a --json flag that switches output from the human-readable summary to raw JSON. In the CLI source this option is bound to a parameter named raw, so any error message or shell-completion entry referring to raw is talking about --json. (This is unrelated to the structured commands’ --json body flag, which carries an operation’s request payload.)
  • GEOPERA_API_TOKEN overrides auth but not the URL. Setting it ignores the stored auth for the resolved profile, but the profile is still resolved and its api_url is still used as the level-3 default. Set GEOPERA_API_URL or --api-url if you also need to change the endpoint.
  • A non-gpra_ API key warns but still works. geopera login --api-key prints a note to stderr if the key does not start with gpra_, then validates and stores it anyway. New Geopera keys are expected to use the gpra_ prefix.
  • A corrupt store degrades gracefully. If credentials.json is unreadable or contains invalid JSON, the loader treats it as empty rather than erroring — every command then behaves as “not logged in” until you geopera login again, which rewrites the file.
  • Trailing slashes are stripped. Whatever endpoint you supply via --api-url, GEOPERA_API_URL, or a stored api_url, the resolved value has its trailing / removed, so https://api.geopera.com/ and https://api.geopera.com are equivalent.

See also

  • CLI authenticationlogin, logout, whoami, the device flow, and API keys
  • Authentication — session tokens, gpra_ API keys, and how the API validates them
  • Scopes — what a credential is allowed to do
  • Operations — the POST /v1/op/{operation_id} model the CLI dispatches to
  • Errors — the problem+json shape the CLI surfaces on failure