BBramaDocs
Browse documentation

Brama CLI command reference

brama subscription lease take

Take a lease for one session: the gateway records which subscription the session runs on, choosing the usable member of the provider that carries the fewest live sessions, so a machine's sessions are spread over the pool instead of all spending one account. The program that starts the session takes the lease before the harness starts (Oko does, for every session it starts or resumes) and releases it when the terminal ends. A session that already holds a live lease on the provider gets that lease back: a harness restarted by its own runtime is the same session.

Invocation

brama subscription lease take <PROVIDER> --session-id <ID> --holder <PROGRAM> (--gateway <ORIGIN> | --gateway-consumer <NAME>) [--bearer-role <ROLE>] [--agent <AGENT> --signing-role <ROLE#FIELD>] [--json]
InputRequired/defaultMeaning
<PROVIDER>requiredThe provider the session will run on, for example claude-code.
--session-id <ID>required, nonemptyThe session that will run on the lease, as its runtime names it.
--holder <PROGRAM>required, nonemptyThe program taking the lease for the session (oko).
--gateway <ORIGIN> or --gateway-consumer <NAME>one requiredThe gateway that holds the pool (POST /v1/subscription-pool/leases); a lease lives nowhere else. The console bearer is read from stdin, or from the vault item playing --bearer-role.
--agent <AGENT> --signing-role <ROLE#FIELD>optional pair; neither defaultsProve the agent that takes the lease. The signing secret is read through Stado credentials at the moment of the request; only the signature, agent id and timestamp cross the wire. Without the pair, the bearer uses its console or account scope.
--jsonoptional; offPrint the lease document instead of lines.

The choice

The limit is the operator's own number that the provider's declaration in src/subscription_dispatch/acquire/providers.json names (sessions_per_subscription_max for claude-code), in src/subscription_dispatch/acquire/numeric-provenance.json with his words beside it. The usable members are the provider's active, non-retired members the ledger holds nothing against: no grant awaiting a sign-in, no standing block, and no current plan window at its whole limit. A window counts while its own reset instant is ahead or unstated; a member whose window is spent is named plan window <window> is at its whole limit (<fraction> used); it resets at <instant> (or the provider named no reset), because a session leased onto a spent plan would run on an account the provider refuses. Among the usable members the one carrying the fewest live leases is chosen; among equals, the one whose plan is least used. A member at the limit is passed over. A provider whose every member is spent or at the session limit is the shortage an acquisition buys for.

When both live-session counts and plan readings are equal, each tied available member has equal selection probability. Vault ordering and member names do not grant priority. The same unbiased shuffle is used for inference ties; it does not overrule lower session load or better plan headroom. A standing session returns its existing lease without a new draw, and members at capacity do not participate.

Output and state

One line per lease — <lease id> <provider> <subscription> (<account>) session <id> held by <holder> since <instant> — then taken: <n> of <limit> sessions on <subscription>, or already held: for a session's standing lease. With --json the document is {lease: {id, provider, subscription_id, account, session_id, holder, taken_at_ms}, live_on_subscription, limit, new}. The gateway writes the lease to its register (BRAMA_SUBSCRIPTION_LEASES_FILE, or leases.json under BRAMA_STATE_DIR), and brama subscription list shows each member's live_sessions.

The serving gateway serializes standing-session lookup, member selection, capacity checking and lease persistence against the same register lock. Concurrent requests for the same provider and session return the same lease; requests for different sessions cannot reserve a member from an outdated lease count. Vault inventory is read before entering this transaction, so the lock is not held across a network request. CLI and Desktop pool counts read the same register.

Refusals

A pool that cannot give the lease starts an acquisition before it answers: every usable member at the limit runs it with trigger sessions_full, and a provider with no usable member at all with trigger no_usable_member. When the acquisition bought an account, the gateway takes the lease again and answers it as a lease, with the purchase verdict in acquisition, so the session starts on the bought account. When it did not, the refusal below carries the verdict. Either way the answer waits for the purchase, which runs a real browser on Weles and can take minutes.

Every refusal exits 1 and prints the HTTP status, request path and full response body, including a non-JSON error; an unreadable body is reported as such rather than as a lease result. 409 pool_full: every usable member carries the limit; details.refusal.members lists each with its count, and details.acquisition is the verdict of the acquisition the gateway started for that shortage (refused with its code, or failed); after an acquisition that bought an account and still could not lease, details.first_refusal is the refusal that started it. 400 invalid_request: no provider, session or holder. 503 no_usable_member: the provider has no usable member, each named with what the ledger holds against it, and details.acquisition is the verdict of the purchase this started. 503 limit_unstated: providers.json declares no such provider (naming the ones it declares), or numeric-provenance.json states no number the declaration names, ending so no lease can be shared out. 503 dependency_unavailable: the inventory or the register could not be read or written, with the cause. Without a gateway: a lease lives on the gateway that holds the pool: name --gateway or --gateway-consumer.

Signing requires both --agent and --signing-role ROLE#FIELD. The CLI refuses an incomplete pair or a role without a field before sending a request, and names the failed Stado credential lookup when the secret cannot be read. The gateway verifies the signature against the exact request body and refuses an invalid or unauthorized agent rather than taking a console-scoped lease.

If operating-system randomness cannot be read for a tied choice, admission returns 503 dependency_unavailable with the failed randomness operation and its actual cause. No lease is persisted and no account purchase is started for that refusal. A pool with no free capacity still reports pool_full, not a randomness failure.

The concurrent CLI journey is tests/subscriptions/leases/concurrency.rs, registered in the subscription capacity test target. It requires an isolated gateway with free capacity for LEASE_PROVIDER, LEASE_HOLDERS naming concurrent callers, and the shared BRAMA, CONSUMER, BEARER_ROLE and GATEWAY_SOURCE_REVISION inputs. It submits one fresh session concurrently, verifies identical returned and persisted leases plus pool counts, then releases that session and verifies its absence. Command results and gateway identity are retained under target/real-tests/subscriptions; it does not release another session's leases.

Set CLI_SOURCE_REVISION for the tested binary too. Each caller's setup checks and retains brama version before gateway access, so a stale CLI cannot qualify the declared candidate merely by reaching the intended gateway.

The distribution journey tests/subscriptions/leases/fairness.rs requires LEASE_MEMBERS, the space-separated IDs of every live member of LEASE_PROVIDER, in addition to those shared inputs. Use an isolated pool with multiple usable members and no existing provider leases. The provider must have no authorized account-purchase cap, and the gateway revision must equal the qualification source revision: an unexpected shortage must not buy an account. The journey fills every member to the declared sessions-per-subscription limit in balanced rounds: no member is reused in a round while another carries fewer sessions. It repeats each standing-session admission to verify unchanged account and capacity, compares returned counts with persisted leases, then requests one more session. That request must refuse with pool_full and leave the complete persisted lease list unchanged. Finally it releases only its own sessions and verifies their absence. It makes no inference request. This qualifies least-loaded distribution, standing-session identity and the capacity boundary, not statistical randomness. The command below runs the journey; its presence is not an execution result.

cargo test --test subscription-capacity lease_distribution -- --ignored --nocapture

The spent-pool journey tests/subscriptions/leases/spent.rs requires LEASE_SPENT_PROVIDER, a declared provider without an authorized account-purchase cap whose every pool member has a current plan window at its whole limit, plus the shared inputs. It takes a lease for a fresh session and expects 503 no_usable_member naming every member with its window at the limit and carrying details.acquisition, and the persisted lease list unchanged; a lease given instead is released and fails the journey. Run it with cargo test --test subscription-capacity spent_pool_leases -- --ignored --nocapture.

The signed-ownership journey tests/subscriptions/leases/ownership.rs additionally requires two independently provisioned identities and signing vault coordinates: LEASE_AGENT_A, LEASE_SIGNING_ROLE_A, LEASE_AGENT_B and LEASE_SIGNING_ROLE_B, with an isolated gateway that can grant LEASE_PROVIDER. It checks the missing-signature refusal, takes a fresh lease as the first agent, observes that only that agent can list it, checks a second agent's release is refused without changing the lease, then releases it as its owner and observes its absence. Run it with cargo test --test subscription-capacity lease_ownership -- --ignored --nocapture; the run retains commands, exit statuses, build identities and final state under target/real-tests/subscriptions.

The real test is tests/subscriptions/journeys/leases.sh. See the command group or the CLI tree.