brama subscription list¶
Report the subscription pool this gateway routes over: every subscription, the state of its credential, its plan windows and whatever is standing in its way. Reading stored usage is read-only; refreshing usage is explicit.
This command runs where the vault and the usage ledger are, so what it proves is the installation and what it is answered about is the whole pool. The same capability answers an account holder about its own accounts and a signed agent about its own over GET /v1/subscription-pool; there is no second implementation per audience. The report sits in the brama subscription group beside the commands that act on one member, so the object has one name; the former top-level brama subscriptions no longer parses.
Invocation¶
brama subscription list [--json] [--refresh-usage]
brama subscription list --gateway http://127.0.0.1:17651 [--json] < console-bearer
brama subscription list --gateway-consumer <consumer> --bearer-role <console bearer role> [--json]
brama subscription list --apply --json < membership.json
brama subscription list --gateway-consumer <consumer> --bearer-role <console bearer role> --refresh-usage --json
| Input | Required/default | Meaning |
|---|---|---|
--json | optional; off | Print the complete report as JSON. |
--refresh-usage | optional; off | Read current free provider usage reports through Brama, renewing expired OAuth grants when possible. Never starts a sign-in or sends a model request. With a gateway destination, asks that gateway to refresh its own providers through an authenticated, empty-body POST to /v1/plan-usage. |
--gateway | optional | Read the pool of the gateway at this origin instead of this process's own, with the console's bearer on standard input. |
--gateway-consumer | optional | Read the pool of the gateway Stado's service directory gives this consumer, resolved at every run. |
--bearer-role | optional | Read the console's bearer from the token field of the vault item that plays this role instead of from standard input, so a service holds no secret, picks up a rotated token, and names no item. |
The gateway an operator asks about usually runs on another machine. --gateway, --gateway-consumer and --bearer-role read that gateway's own GET /v1/subscription-pool document: the origin comes from Stado's service directory and the console bearer from the vault, so neither a rotated token nor a moved port has to be typed.
--apply reads one JSON membership document from standard input and cannot be combined with --refresh-usage. It runs the same operation as POST /v1/subscription-pool, with the local vault-owning process's deployment scope. The document must name agent_id; secret values are never command-line arguments.
Pool membership
A bank document carries {"action":"bank","agent_id":"my-agent","provider":"openai","api_key":"…","account":"me@example.com"}. A retirement document carries {"action":"retire","agent_id":"my-agent","subscription_id":"<subscription id from the pool report>"}. Banking persists the credential and membership; retirement disables the subscription and journals the transition without deleting the encrypted vault item. A bank receipt proves storage, not a successful model request. The pool holds only real accounts' subscriptions: a new member's id is made from its provider and account, and banking without account is refused (account email address is required when adding a subscription); re-banking an existing subscription_id the vault holds with no account is refused the same way (account email address is required: the selected subscription names no account, and the pool holds only real accounts' subscriptions) unless the document names one.
The mutation prints its receipt as key: value lines, or as the JSON receipt with --json. Invalid JSON, an unknown field or action, a missing owner, an unknown subscription and vault failures exit 1 with the same error body as HTTP: on standard error, or on standard output with --json. A refused operation leaves membership unchanged. Brama Desktop's existing subscription controls use that same HTTP operation; there is no second lifecycle implementation.
Output and state¶
Text output begins <live> of <servable> servable subscription credentials are live; <burnt> burnt (renewed only by a sign-in) and <retired> retired credential(s) are listed and not counted: a credential the gateway reports retired, by its retired flag or a retired status, is listed with its cause but is not one of the subscriptions the pool holds, and a burnt one (state burnt) serves nothing until a sign-in renews it, so neither is counted among the servable credentials. A report without a subscriptions array says the report carries no subscription list instead. The output then states whether the usage report is complete, and prints each account's state, provider, subscription id, windows, percentages, reset times, observation times and latest usage attempt; a row whose report carries no window list says usage windows not reported, apart from one whose list is empty. JSON contains ok, observed_at_ms, scope (deployment here) and the subscriptions array; every row carries id, provider, status, state, expires_at, last_redeem_error, limits, measured, block, credential, usage_check, usage_source and stale. The four states are in subscription states.
Brama Desktop's Subscription Pool screen counts the same way: its Live, Unavailable, Expired and Unknown counts are of the members the pool holds, a separate Retired count shows the retired members the table still lists, and the no-subscriptions-available panel names how many held members need attention and how many are retired.
Beneath that line the command states how many accounts the pool holds, which accounts they are per provider, and how many members it could not attribute to one: 5 account(s): claude-code 3: <address>, <address>, <address>; codex 2: <address>, <address> followed by 6 member(s) declare no account, 1 not active (retired or disabled), 0 malformed (no id or provider). A member belongs to an account only where the account is recorded against it — the address on its vault item, written from what the provider signed into its grant — and nothing is read from a name: a member’s id, its label and the login row it signs in through are names, and one Google login row signs both the Claude Code and the Codex account of one person in. A member that records none is attributed from its own grant by brama subscription attribute. In JSON the same counts are the accounts object: total, per_provider, members_without_account, inactive_members and malformed_members (pool rows without the id or provider every member carries, shown as written), and every member row carries the account it records. The pool’s members are what the vault lists, plus retired ones; an id only the usage history remembers is no member, counted nowhere, and is printed apart as remembered by the usage history only, not pool members (N): … (JSON remembered, {id, provider}), each dropped with brama subscription forget.
Beneath the account lines the command states what each real account has left, read across every member that carries it, one line per account and one per plan window: claude-code <address>: available; live sessions: 2, then 5 hours: 0.62 of the window left; reset <instant>; read <instant> off <member> and 7 days: 0.41 of the window left; reset <instant>; read <instant> off <member>. Two members of one account draw on one quota, so the account’s reading of each window is the newest reading any of its members holds, and the line names which member it was read off. What is left is the whole limit less the provider’s own used fraction; a window whose reset instant has passed is printed rolled, not read since rather than as a number, because the provider’s clock says it rolled and the reading no longer describes it. available means a session put on the account now would be served: a member nothing in the ledger stands against exists and no current window is at its limit; not available otherwise, with every refused member printed beneath as refused member <id>: <the ledger’s reason> and a standing block as blocked until <instant>. An account no member has a reading for prints no plan window has been read for this account. In JSON this is accounts.capacity, one object per account: provider, account, members, usable_members, refused_members (member, refusal), blocked_until_ms, live_sessions (null when the lease register could not be read), windows (limit_id, label, window_label, used_fraction, remaining_fraction or null for a rolled window, resets_at_ms, read_at_ms, member, current), tightest_remaining_fraction (the least left across the current windows, null when none was read) and available. This is the reading the acquisition and a session-placing policy decide on, so that what they decide on is what the operator sees. A gateway older than this reading carries no accounts.capacity, and the CLI prints account capacity: not stated by this gateway; a capacity that does not read as this shape is reported on stderr with the decoding cause rather than shown as an empty pool.
Every member also says who refreshes its grant: refreshed by: brama for one the gateway renews itself, and the harness's name for a grant stored while Brama could still take one a coding harness held. In JSON it is credential.borrowed_from, that harness's name or null. Such a grant is never rotated here, because the machine it came from holds the same pair and a provider revokes the pair it replaces — which is why taking grants was removed and why brama subscription disown exists to give those members back.
Every member also says how many sessions run on it: live sessions: <n>, the live leases the gateway's register holds on it, 0 when none does. In JSON it is live_sessions. When the lease register cannot be read the count is null, the line is not printed, and the report's errors name brama.subscriptions.leases with the cause, so an unread register is never shown as a member carrying no sessions.
With --refresh-usage, the CLI reads the plan-usage capability locally or asks the selected gateway through POST /v1/plan-usage, the same operation Brama Desktop's Refresh usage uses. Provider grants remain on the owning gateway. Successful accounts remain visible if another fails. The last good reading survives a refusal with its original timestamp and stale: true; a missing or malformed metric never becomes zero. Without the flag, a remote read uses GET /v1/subscription-pool and does not request fresh provider observations.
Refusals¶
The command exits 1 when discovery, ledger access, or any active subscription's usage is unavailable or stale. Both output modes retain the partial result; text mode prints failure details to stderr, while JSON includes standard Wisent envelopes in errors. Each envelope names the failed operation and retains the actual cause, with the subscription or agent and attempt time in context. An inventory Brama could not read is reported as a failure with its reason — never as an empty pool, because an empty pool and an unread vault are the same picture and opposite repairs.
Remote transport failures identify the HTTP method, selected endpoint and underlying error. HTTP refusals retain their status and response body; unreadable responses and invalid successful JSON retain their actual cause. No failure is replaced with a local pool report. The real saved-reset report journey in tests/subscriptions/capacity.rs refreshes through this CLI path, checks a new provider-read attempt when a prior attempt exists, and reads the pool again to verify persisted observations without consuming a reset.
See the pool capability over HTTP, subscription states, or the CLI tree.