Concepts

This page explains the moving parts of constellation-utils — useful both for people writing code against it and for anyone trying to understand how Constellation manages secrets.

What is 1Password and why is it here?

1Password is the password manager Constellation uses for the whole company. Beyond storing your personal logins, it has a feature called shared vaults — folders of credentials that a team can share. Constellation has a vault called Engineering that holds every API key, database password, cloud credential, and service-account token the engineering team uses.

Putting credentials in 1Password (instead of a .env file or a config file in a git repo) gives us:

  • One source of truth. When someone rolls a key, you update it in one place and every consumer picks it up.

  • Audit logs. 1Password records who accessed what and when.

  • Granular access. New teammates can see only the items they need.

  • No leaks to git. Secrets are never in code, never in commit history, never in screenshots of .env files.

What is the op CLI?

The op CLI is the command-line tool 1Password ships. It’s how scripts and processes talk to 1Password without a web browser.

op read 'op://Engineering/R2 Testing/access_key_id'

That command prints the value of the access_key_id field on the R2 Testing item in the Engineering vault. The op://... notation is 1Password’s standard “secret reference” format.

constellation-utils runs op read under the hood every time it fetches a credential.

Authentication modes

Reading from 1Password requires authentication — the op CLI needs to prove it’s allowed to access the vault. Constellation uses two different auth modes depending on where the code runs:

Where

Mode

How it works

Your laptop

Biometric, via the desktop app

After installing 1Password and toggling “Integrate with 1Password CLI” in Settings → Developer, the op CLI piggybacks on the desktop app’s session. Touch ID unlocks vaults; op read just works.

Lab rigs (Mac minis), CI, anything unattended

Service-account token

A long-lived token lives in the OP_SERVICE_ACCOUNT_TOKEN env var. The op CLI consumes it transparently — same op read calls, no desktop app involved.

You don’t pick the mode explicitly — op detects which auth context exists and uses it. constellation-utils doesn’t know or care which one is active.

For laptops, the desktop-app-integrated mode is strongly preferred over op signin (which creates a non-app session that requires re-authentication every few hours). After the toggle is on, you should never need op signin.

Profiles: testing vs production

Some credentials have two flavors — for example, the Constellation R2 setup has both a Testing bucket (for development and CI) and a Production bucket (for real recordings from rigs). The same code needs to talk to one or the other depending on context.

constellation-utils handles this with profiles, picked by an environment variable:

CONSTELLATION_PROFILE=testing      # default; safe for laptops, tests, smoke
CONSTELLATION_PROFILE=production   # rigs running real uploads

Each profile is just a YAML file shipped with the package that maps logical names to op://... URIs:

# secrets.testing.yaml (excerpt)
r2:
  endpoint:           op://Engineering/R2 Testing/endpoint
  access_key_id:      op://Engineering/R2 Testing/access_key_id
  ...
# secrets.production.yaml (excerpt)
r2:
  endpoint:           op://Engineering/R2 Production/endpoint
  access_key_id:      op://Engineering/R2 Production/access_key_id
  ...

Switching profiles changes which 1Password item gets read — same code, different credentials. The YAML files contain no secret values, only references; they live in the repo because they’re just configuration.

The two backends

Inside the package there are two ways to actually fetch a credential value:

  • OpCLIBackend — the real one. Calls op read. Used on every laptop and rig.

  • EnvBackend — the test one. Reads values directly from R2_* environment variables. Used by the test suite and any CI step that doesn’t have 1Password access.

You don’t choose; the package picks for you. If R2_ACCESS_KEY_ID is set in your environment, you get EnvBackend. Otherwise it looks for op on $PATH and uses OpCLIBackend. If neither is available you get a clear error explaining what to install.

You can override the choice for debugging:

CONSTELLATION_SECRETS_BACKEND=op_cli   # force the op CLI
CONSTELLATION_SECRETS_BACKEND=env      # force env vars

What you get back

Each secrets.<name>() function returns a frozen Pydantic model — a small immutable object with named fields:

r2 = secrets.r2()
r2.endpoint           # str
r2.bucket             # str
r2.access_key_id      # str
r2.secret_access_key  # str
r2.region             # str (defaults to "auto")

“Frozen” means you can’t accidentally modify a credential after fetching it. “Pydantic” means the values are validated — if a field is missing in 1Password, you get a clean error pointing at the missing field instead of a confusing KeyError later.

Caching

Each secrets.<name>() call is cached for the lifetime of the Python process. The first call shells out to op (which takes a few hundred ms); every subsequent call in the same process is free.

If you need to bust the cache (rare, mostly in tests), the function exposes .cache_clear():

from constellation_utils import secrets
secrets.r2.cache_clear()
secrets.r2()  # fresh read

What’s NOT here

constellation-utils is intentionally narrow. It does not:

  • Store or rotate secrets — 1Password does that.

  • Talk to AWS / GCP / R2 directly — that’s the consumer’s job (e.g. the data-engine uploader’s R2 client).

  • Provide a generic config system — only credentials.

Adding a new secret is a deliberate, reviewed change to the package. See Tutorials → Adding a new secret.