Command-line client for the IB1 Directory member API. Built to make the API easy to exercise as it grows, and to be drivable by scripts and code agents (machine-readable output, real exit codes, no prompts).
Authenticate either by logging in (browser, authorization-code + PKCE, token cached in
the OS keyring) or by pasting a token (--token / DIRECTORY_TOKEN) for short runs and
CI.
pip install ib1-directory-cliFor local development from a clone:
pip install -e ".[dev]" # or: uv sync --extra devThis installs a directory command.
| Option | Env var | Default |
|---|---|---|
--api-url |
DIRECTORY_API_URL |
https://directory.core.sandbox.trust.ib1.org |
--token |
DIRECTORY_TOKEN |
(none) |
--organization |
DIRECTORY_ORGANIZATION |
(none) |
--json |
pretty-printed |
The default API is the sandbox. Point --api-url or DIRECTORY_API_URL at another
environment, or at http://localhost:8000 for a locally running API.
Login needs no configuration beyond the API URL. directory login asks the API for its
Cognito hosted UI domain, client id and scopes (GET /.well-known/directory-cli). These optional
variables override what the API publishes, e.g. against an API that doesn't publish it:
| Env var | Meaning |
|---|---|
DIRECTORY_COGNITO_DOMAIN |
Hosted UI base URL, e.g. https://<prefix>.auth.<region>.amazoncognito.com |
DIRECTORY_COGNITO_CLIENT_ID |
The public (no-secret) CLI app client id |
DIRECTORY_OAUTH_SCOPES |
Space-separated scopes. Default: whatever the API publishes, else openid email |
DIRECTORY_REDIRECT_PORT |
Default 8400 (must match the client's registered callback) |
The API is only asked when DIRECTORY_COGNITO_DOMAIN and DIRECTORY_COGNITO_CLIENT_ID aren't
both set.
directory login # opens a browser, caches the token in your OS keyring
directory logout # clears the cached token for this API
directory token # prints a current id token (refreshing if needed)After directory login, me get / me update use the cached token automatically. Token
precedence is --token then DIRECTORY_TOKEN then the keyring cache.
Tokens are cached per API URL, so you can be logged in to several environments at once and
--api-url picks which token is used. The Cognito client that issued a token is cached with
it, so refreshing the token doesn't call the API.
directory token is the bridge for agents/CI that can't open a browser: a human runs it and
passes the value as DIRECTORY_TOKEN. The token is short-lived, so this suits short runs.
# Read your organisation
directory --token "$ACCESS_TOKEN" me get
# Same, token from the environment, compact JSON for piping
export DIRECTORY_TOKEN=...
directory --json me get | jq .identifier
# Partial update (only the flags you pass are sent — a merge-patch)
directory me update --street-address "1 New Road" --email ops@acme.example
directory me update --country GB --postal-code "AB1 2CD"Editable fields: --email, --street-address, --locality, --region, --state,
--postal-code, --country, --privacy-policy, --data-protection-url.
Most users own a single organisation and need nothing extra. Some own several — for example an
energy data provider that also owns an example CAP organisation, which it acts as to generate test
certificates. List the ones you own, then pass --organization <identifier> (or set
DIRECTORY_ORGANIZATION) on any command to say which one you are acting as.
# List the organisations you own
directory me orgs
# Act as a specific organisation
directory --organization cap12345 me get
directory --organization cap12345 cert sign abc12345 client
# Or set it once for the session
export DIRECTORY_ORGANIZATION=cap12345
directory me getIf you own more than one organisation and don't pass a selector, the API returns an error asking you
to pick one. There is no -o short flag for --organization because -o is the output-path flag on
ca download and cert download.
# List your applications (optionally filter by scheme short name)
directory apps list
directory apps list --scheme perseus
# Read one application by identifier
directory apps get abc12345
# Create an application under a scheme (roles are repeatable role identifier URLs)
directory apps create --scheme perseus --title "My App" \
--role https://registry.trust.ib1.org/scheme/perseus/role/data-provider \
--home-page-url https://app.example.com
# Create with a data service (title, conforms-to and endpoint-url go together)
directory apps create --scheme perseus --title "My App" \
--data-service-title "My Feed" \
--data-service-conforms-to https://standard.example.com \
--data-service-endpoint-url https://api.example.com
# Partial update (only the flags you pass are sent; --role replaces the whole set)
directory apps update abc12345 --title "Renamed" --support-url https://support.example.com
# Delete (destructive, so --yes is required; the API refuses with 409 if certificates exist)
directory apps delete abc12345 --yesThe publisher on a data service is set by the server to your organisation, so there is no
flag for it. Application create/update accept --description, --home-page-url,
--support-url, --message-delivery, --role, and the four --data-service-* flags.
Admin commands require your Cognito account to be in the directory admin group; other users get a 403.
# Onboard a new organisation (creates the org, its scheme membership + role, and the
# officer contacts). The scheme is fixed per environment; the role is a slug within it.
directory admin create-org \
--legal-name "Acme Ltd" --email contact@acme.example \
--street-address "1 Main St" --locality London --postal-code "AB1 2CD" \
--company-number 12345678 --role energy-data-provider \
--data-officer-name "Dana" --data-officer-email dana@acme.example \
--licence-officer-name "Lee" --licence-officer-phone "+441234567890"
# Add a user to an organisation and send them a Cognito invite (they become an owner)
directory admin add-member <organization-identifier> --email new.owner@acme.exampleEach officer needs an email or a phone. --region and --effective-date (ISO YYYY-MM-DD,
default today) are optional. Onboarding = create-org, then add-member for each owner.
# Sign a certificate for an application. With no --csr, a private key and CSR are
# generated locally; the key is written to disk (mode 0600) and the signed cert saved.
directory cert sign abc12345 client
# → my-app writes abc12345-client-key.pem and abc12345-client-cert.pem, prints the cert id
# Use your own CSR instead of generating one (no key is written):
directory cert sign abc12345 signing --csr my.csr --cert-out signing.pem
# Download a certificate by id (default filename comes from the server)
directory cert download <certificate-id> -o cert.pem
# Revoke a certificate (destructive, so --yes is required; this is a soft revoke)
directory cert revoke <certificate-id> --yescert sign takes the application identifier and the type (client or signing). The CA
forces the certificate subject to your organisation regardless of the CSR, so a generated
CSR's subject does not matter. Download the CA root/intermediate bundle with
directory ca download <client|signing>.
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | API or transport error (4xx/5xx, connection failure) |
| 2 | Usage error (no token, update with no fields, delete without --yes, login against an API that publishes no login configuration) |
Interactive login needs a public Cognito app client on the existing user pool:
- no client secret
- authorization-code grant with PKCE
- callback URL
http://localhost:8400/callback(the exact port must matchDIRECTORY_REDIRECT_PORT) - scopes
openid email
The API it logs in to must then be configured with:
COGNITO_ALLOWED_CLIENT_IDSincluding the client id, so the API accepts tokens it issuesCOGNITO_DOMAINandCOGNITO_CLI_CLIENT_ID, so the API publishes them at/.well-known/directory-clifor the CLI to discover
These are deploy/infra steps (AWS and the deployments repo), not part of the CLI.
pip install -e ".[dev]" # or: uv sync --extra dev
pytest # or: uv run pytest