Setup

First-time setup for new Ursa users on a fresh laptop. Walks through prerequisites, profile selection, and a sanity check that proves your credentials work end-to-end.

If something doesn’t work, jump to Troubleshooting — the four most common first-contact failures and their fixes are listed there with the actual error strings.

Prerequisites

uv

Ursa uses uv for Python package management. Install via your platform’s instructions; curl -LsSf https://astral.sh/uv/install.sh | sh works on macOS and Linux.

op (1Password CLI)

Ursa reads R2 credentials through the op CLI via constellation-utils. No op, no R2 access.

  • macOS: brew install --cask 1password-cli. Then open 1Password desktop, go to Settings → Developer, and enable “Integrate with 1Password CLI” so the biometric session signs you in transparently.

  • Linux (Debian/Ubuntu): follow 1Password’s CLI install guide — keyring import + apt install 1password-cli. The exact apt-key block rotates, so use the canonical page rather than copying a snippet from here.

  • Headless / CI: set OP_SERVICE_ACCOUNT_TOKEN from the Engineering 1Password vault and skip the desktop biometric path.

Verify the install:

op --version
op whoami

op whoami should print your email and a user_* UUID. If it errors, see Troubleshooting.

1Password vault access

You must be a member of the Engineering vault in 1Password. R2 access keys, bucket names, and the rest of Constellation’s credentials live there.

If you’re not yet a member, post in #engineering-support with your 1Password email and your Constellation role — channel-routing reaches whoever is on rotation.

Install Ursa

Once ENG-1064 ships v0.1.0:

uv add ursa

Until then, work from a checkout:

git clone git@github.com:constellationlab/ursa.git
cd ursa
uv sync --all-extras

M2 is single-machine, single-user — there is no separate “researcher install” flavour yet.

Configure CONSTELLATION_PROFILE

CONSTELLATION_PROFILE selects which R2 buckets and credentials Ursa resolves. Valid values: testing, production. The default is testing — if the variable is unset, constellation-utils falls through to the testing buckets.

For first-contact users, leave it unset (or export CONSTELLATION_PROFILE=testing). The testing buckets are the on-ramp by design: they hold ingest fixtures and demo data, and running against them costs nothing operationally. Switch to production only once you’ve verified the testing path works and you have a concrete read-only researcher use case.

# Recommended on-ramp — defaults to testing, no env var needed.
unset CONSTELLATION_PROFILE

# Or explicit:
export CONSTELLATION_PROFILE=testing

Sanity check

uv run python -c "import ursa; print(ursa.load_config())"

A working print is an UrsaConfig object whose stores dict maps role names (assets_rw, assets_ro, raw_ro) to R2StoreConfig(...) entries — no secrets are printed, only role/cred names and bucket prefixes. The exact shape comes from ursa.store.config.UrsaConfig (frozen Pydantic, extras forbidden). raw_rw is intentionally omitted from the packaged config: data-engine is the sole writer of constellation-data, and Ursa never holds the RW key.

If this raises, jump to Troubleshooting.

First query

Three lines that prove the catalog is reachable end-to-end:

import ursa
from ursa.catalog import Catalog
from ursa.store import get_store

cat = Catalog.from_store(get_store("assets_ro"))
print(len(ursa.query(catalog=cat, limit=5)))

Expected: a small integer (≤ 5) — the row count of recordings in the catalog under your current profile. If Catalog.from_store raises a 403, or if query returns 0, see the Troubleshooting recipes below.

For a deeper walk-through (inspect a QueryResult, materialise it with get(), look at the modality payloads), use examples/getting_started.py.

Troubleshooting

FileNotFoundError: op (or OSError: ... 'op')

The op CLI isn’t installed or isn’t on PATH. Install it (see Prerequisites) and restart your shell so the new binary is picked up.

[ERROR] you are not currently signed in from op

Your 1Password CLI session has expired (or never started). On macOS with the desktop app and “Integrate with 1Password CLI” enabled, just run op whoami again — the biometric prompt should re-auth you. Headless boxes: re-export OP_SERVICE_ACCOUNT_TOKEN from the Engineering vault.

ValueError mentioning CONSTELLATION_PROFILE

You set CONSTELLATION_PROFILE to something other than testing or production. The exception wording varies by where it surfaces — constellation-utils raises one form when resolving secrets, ursa.layout raises another when resolving bucket names — but both fire on the same root cause. Fix:

unset CONSTELLATION_PROFILE     # falls back to the testing default
# or
export CONSTELLATION_PROFILE=production

Note: an unset CONSTELLATION_PROFILE is valid and uses the testing default. Only invalid string values raise.

403 / permission denied on Catalog.from_store or first query call

Your Engineering vault permissions aren’t yet propagated to the R2 access keys, or op is signed into a different account than you expect.

  1. op whoami — confirm you’re signed into the Constellation account.

  2. If the account is right, post in #engineering-support with: the redacted error message, the output of op whoami, and your CONSTELLATION_PROFILE value. Whoever is on rotation will check vault membership.

ursa.query(...) returns 0 rows

The catalog is reachable but no recordings match. This is most commonly an empty bucket under your current profile, not a bug:

  1. Re-run with the production profile:

    CONSTELLATION_PROFILE=production uv run python -c "import ursa; from ursa.catalog import Catalog; from ursa.store import get_store; print(len(ursa.query(catalog=Catalog.from_store(get_store('assets_ro')), limit=5)))"
    
  2. If production also returns 0, try the pinned demo recording from ENG-1063:

    ursa.query(catalog=cat, recording_hash="rec_20260325_195950_c924")
    

    That recording is known to be registered in the production catalog. If it doesn’t match either, something is wrong with Catalog.from_store; post in #engineering-support.