CLI

Explanation — what & why

vectros is the provisioning command-line tool (@vectros-ai/cli). It is the terminal-and-CI counterpart to the developer portal: it stands up the things an app needs to exist — app contexts, identities, scoped keys, access bindings, roles — and it runs the blueprint lifecycle that turns a declarative app model into provisioned infrastructure.

Two ideas shape the CLI:

  • It is the trust boundary, not the blueprint. A blueprint is untrusted input — it may be forked, hand-written, or agent-authored. The CLI's scope gate refuses to mint anything outside a fixed data-plane allowlist. There is no override flag: control-plane access is provisioned deliberately in the portal, never through a tool an agent can drive.
  • Bootstrap without the root key. The bootstrap command provisions a least-privilege scoped key for an MCP agent using a short-lived bridge token from the developer portal — your root key never touches the flow.

The CLI bundles its own SDK and blueprint library into the binary, so it has no runtime dependency you have to install or authenticate separately.

How-to

Install

npm install -g @vectros-ai/cli
# or invoke without installing:
npx -y @vectros-ai/cli <command>

The binary is vectros. Run vectros --version to print the CLI version plus the bundled SDK and blueprint-library versions.

Sign in and confirm who you are

vectros login            # opens a browser for a PKCE sign-in, stores a session
vectros whoami           # shows the authenticated tenant, environment, and principal
vectros logout           # clears the stored session

login accepts --env staging|production (default production) and --no-browser to print the sign-in URL instead of opening one (useful on headless/CI/WSL hosts).

whoami confirms tenant, environment, and principal — it does not currently show which app context (the per-tenant data-partition every scoped credential is confined to) a scoped credential is bound to; that binding isn't surfaced by the underlying identity probe (GET /v1/ping) today. If the credential is one this CLI minted or joined itself (bootstrap, join), vectros keyring show reports the context you set it up for, from the CLI's own local record — but for a credential you didn't mint through this CLI (pasted from the developer portal, or handed to you by someone else), there's no discovery command yet; you have to already know which context it targets.

Bootstrap an MCP credential in one command

vectros bootstrap

Interactively, it asks what to set up — a blank read-only data-plane credential, or a bundled blueprint (which also provisions that blueprint's schemas and seed) — signs you in once with a portal-issued bridge token, previews the plan, mints a narrow ssk_*, stores it in this machine's OS credential store (or an encrypted file where none is available — never plaintext-in-repo), and merges an entry naming only that non-secret alias into your MCP client config. The vectros MCP server resolves the actual secret at startup via vectros keyring show --format raw — the config file itself discloses nothing if leaked.

For scripted or agent runs, set the bridge token in the environment and pass the blueprint plus --yes:

export VECTROS_BOOTSTRAP_TOKEN=<token from the developer portal>
vectros bootstrap --blueprint task-management --yes

Useful flags: --blank (credential only, no schemas), --no-merge (still mints/stores — this only prints the config snippet instead of merging it), --rotate (revoke and re-mint this machine's key), --env staging|production, and --blueprint <name|./path.yaml> (a bundled name or a path to your own blueprint file — both pass the identical scope gate).

The minted key is per-machine and independently rotatable. The access profile is data-plane only: the command hard-refuses any control-plane scope, so even a community-authored blueprint cannot escalate the credential it provisions.

Apply a blueprint without minting a key

vectros bootstrap also mints a per-machine key and merges MCP client config. To re-apply a changed blueprint into an existing context (from CI, say) use blueprint apply, which runs the same pipeline with the key step off:

vectros blueprint apply ./my-app.blueprint.yaml --tenant test
vectros blueprint apply ./my-app.blueprint.yaml --tenant live --yes --confirm-existing-principal   # a CI re-apply

It creates or reconciles everything the blueprint declares, but mints no key, opens no keyring and touches no client config. --tenant test|live is required (no default), and before it changes anything it prints the account, tenant, context and API it is pointed at, then the plan, then asks. With no terminal it needs --yes. It uses the same credential and gates as bootstrap and refuses on them before any change. An existing service principal needs --confirm-existing-principal (standing consent in a pipeline, and required on every apply once the principal exists, on any machine; every run prints which principal it reused). Sample seed records are opt-in: unlike bootstrap, apply writes none unless you pass --seed, in either tenant (a blueprint's seeds are illustrative, not real data, and a re-apply from a pipeline must not write them by omission); the plan and the summary say plainly whether they will be or were written (when the blueprint declares any), and --no-seed is refused on apply (it already does not seed). Deletion is opt-in: without --prune nothing is deleted; with it, a trigger the file no longer declares is removed when its stored provisionedBy marker equals the blueprint's name and its principal's external id is the one that blueprint would derive. Those are stored markers, not verified identities, and the name comes from the blueprint file (after any ${{ inputs.* }} substitution, so --set and --values can change it), so a blueprint that reuses another one's name and omits some of its triggers will remove them. If a removal it matched fails, or a row carrying this blueprint's marker has no id or principal to remove it by, apply exits 1. Apply exit codes: 0 applied (also --help, and declining) · 1 failed while applying, or --prune could not remove a trigger it matched · 2 refused, before anything was changed (usage, no --yes without a terminal, no credential, an unknown or malformed blueprint, an invalid base URL, the scope or capability gate, a tenant-wide namespace without the opt-in), or an identity the blueprint references that could not be resolved (after the context and principal were provisioned) · 3 an existing principal was not confirmed · 4 applied in full but --require-verified-issuers was given and an issuer is pending verification.

Author and inspect blueprints (no credentials needed)

The authoring commands of the blueprint group are creds-free and offline (apply, above, is the one that needs credentials) — they are your authoring inner loop:

vectros blueprint list                          # the bundled library
vectros blueprint init my-app                   # scaffold ./my-app.blueprint.yaml to edit
vectros blueprint init my-app --from clinical-intake   # scaffold from a bundled exemplar
vectros blueprint validate ./my-app.blueprint.yaml     # structural + scope-gate + lint
vectros blueprint plan ./my-app.blueprint.yaml         # validate, then preview what bootstrap or apply would provision

validate runs three checks in order: structural parse, the data-plane scope gate, and the authoring linter. plan runs the same validation and then prints a provisioning preview (it mints nothing). A blueprint may declare an inputs: block; supply values with --set name=value (repeatable) or --values <file>.

Test a blueprint end-to-end against a live environment

vectros blueprint-test ./my-app.blueprint.yaml
vectros blueprint-test task-management --keep   # leave the provisioned graph in place

blueprint-test runs the full lifecycle: apply → assert → teardown. It applies the blueprint to the target environment, asserts the minted key actually works (it pings the API as that key and checks the provisioned graph is readable), then tears the graph back down — leaving a shared environment clean. The harness key is isolated in an ephemeral store so a test run never touches your real per-machine key. --keep skips teardown for inspection. The app context itself is intentionally never deleted — it is idempotent and reused, so leaving it is harmless.

Provision the pieces directly

When you do not want a whole blueprint, the verb groups provision individual resources. Every creds-bearing verb shares the auth flags --env, --base-url, --token, and (on read verbs) --json.

App contexts — the namespace all data lives in:

vectros context create my-app --name "My App"
vectros context list
vectros context get my-app

Identities — tenant-wide principals: --type user, or an entity namespace (org / client are reserved names, registered the same way as any other — see identity-access/reference.md; there's no dedicated vectros command for registration, call POST /v1/namespaces directly):

vectros identity create --type user --external-id alice --email alice@example.test
vectros identity create --type org  --external-id acme --name "Acme Inc."
vectros identity list   --type user
vectros identity get    --type user --id <vectrosId>
vectros identity delete --type user --id <vectrosId>
vectros identity invite --context my-app --role editor --email bob@example.test \
  --accept-url https://my-app.example.test/accept

identity invite onboards a person into a context by email (POST /v1/users/invite) — your vectros login session already carries enough scope, no root key required. --role names an existing Role in that context; the invitee inherits its scopes. --accept-url is required unless --no-send-email is given, in which case the response carries the raw inviteToken/acceptLink for you to deliver yourself.

Scoped keys (ssk_*):

vectros key issue  --principal usr_<id> --context my-app --name laptop
vectros key list   --context my-app
vectros key get    <keyId>
vectros key rotate --principal usr_<id> --context my-app --name laptop
vectros key revoke <keyId>

key issue and key rotate emit the secret; choose the output shape with --format human|raw|env|json. A revoke is a soft-delete and takes a few minutes to take effect.

key rotate matches the key to revoke by (context, principal, name) — --name defaults to "default". If that default doesn't match but another active key exists for the same (context, principal) — e.g. a bootstrap-minted key, named ${blueprint.name}-${machineId} — rotate will not mint a new key silently: it names the other active key(s) it found and asks for an explicit --name (or --force-new to proceed anyway). Read the exact name off vectros key list --context <ctx> (the NAME column) when rotating a key you didn't mint yourself with key issue.

Roles — reusable, identity-agnostic scope rules in a context:

vectros role create --context my-app --role-id editor --name Editor \
  --actions records:cru,search:r
vectros role list   --context my-app
vectros role get    --context my-app --role-id editor
vectros role delete --context my-app --role-id editor

Access bindings — bind a principal to a context, via a role or inline actions:

vectros access grant  --principal usr_<id> --context my-app --role editor
vectros access grant  --principal key_<id> --context my-app --actions records:r,search:r
vectros access list   --context my-app
vectros access get    --principal usr_<id> --context my-app
vectros access revoke --principal usr_<id> --context my-app

Reference

Command catalog

CommandPurpose
login / logoutBrowser sign-in (PKCE) and session teardown.
whoamiShow the authenticated tenant, environment, and principal.
bootstrapOne-shot least-privilege MCP credential (ssk_* + access profile + optional blueprint).
blueprint initScaffold a new blueprint file to edit.
blueprint validateStructural parse + scope gate + lint (creds-free).
blueprint planValidate, then preview what bootstrap or apply would provision (creds-free).
blueprint applyProvision a blueprint into a tenant WITHOUT minting a key (for CI). --tenant required; deletion only with --prune.
blueprint listList the bundled blueprint library.
blueprint-testApply → assert → teardown a blueprint against a live environment.
context create|list|get|destroyManage app contexts. destroy is the irreversible teardown — it asks you to re-type the context id before doing anything.
identity create|list|get|delete|inviteManage tenant-wide principals — --type user, or an entity namespace (org/client, or one you register) — plus invite, which onboards a person into a context by email without a root key.
key issue|list|get|revoke|rotateManage scoped API keys (ssk_*).
role create|list|get|deleteManage context-scoped roles.
access grant|revoke|list|get|explainManage principal-to-context bindings. explain shows what a binding actually grants — its clauses plus a concrete access matrix.
join <context>Enroll a principal in a context (grant + key) in one step, and keep the key in the keyring.
assumeRe-mint your own token with one or more identity.<namespace> values switched — needs a role's assumable grant.
scripts push|list|get|deleteManage script objects: the versioned JS source a trigger rule runs. --context is required on all four.
issuers register|update|list|get|delete|verifyManage registered third-party IdP issuers — the token-exchange trust anchors. register and get print a pending_verification registration's challenge; verify answers it with a JWT from a real login (--idp-token-file, stdin or --idp-token, never --token), activating the issuer (platform 0.45.0+).
namespace register|list|getManage scope-namespace registrations (whether scope:<namespace> values resolve to identity entities).
keyring list|use|show|doctor|prune|removeInspect and manage the local multi-principal keyring. switch is a top-level shortcut for keyring use.

The scope gate (data-plane allowlist)

The bootstrap and blueprint paths will only mint scopes on these resources:

records · schemas · search · documents · folders · inference · entities

entities grants ordinary per-namespace identity-entity CRUD (creating/reading/ updating/deleting org/client/or any namespace you registered), gated by the same ownership rules as records. It does not grant any authority over the namespace registry itself.

profiles is a partial exception, and admission differs by op letter: profiles:r is admitted only unqualified — any qualifier rejects the whole entry, since the platform resolves no qualifier for r. profiles:u and profiles:d are admitted with or without a qualifier, including a literal principal id. profiles:c is admitted when qualified with the literal self (profiles:c:self) — the platform resolves self at the handler from the minted credential's own bound identity, never from anything the pack supplies — or, bare and unqualified, when the SAME clause also declares capabilities: [member-lifecycle] (the platform's own context-bounded, allowlisted mechanism for a role that invites/removes members without raw tenant-wide users:*). A bare profiles:c with no such capability, or a literal-principal qualifier on c (profiles:c:usr_<id>) with or without one, still hard-rejects — the capability unlock covers the bare form only. profiles:* hard-rejects outright — a wildcard carries no qualifier by construction. keys, app-contexts, users, billing, admin, namespaces, scripts, triggers, any unknown resource, or a bare * wildcard — all of these are hard-rejected with no partial exception, and the command mints nothing and exits non-zero. There is no override flag. Registering, updating, or deleting a namespace (POST/PUT/DELETE /v1/namespaces) is root-key-only regardless of any scope a token carries — namespaces is not, and will never be, a resource the bootstrap loader mints. scripts and triggers are refused too — not as root-key-only surfaces, but because they are not data-plane resources; the gate names them explicitly rather than as unrecognized ones: script authoring and trigger declaration are deliberately admin-shaped, design-time surfaces, and the platform already puts them out of reach of an ordinary confined role. (The gate is an allow-list: it accepts only the data-plane resources above, so a resource it has never heard of is rejected by default.)

Scope grammar

A scope token is resource:operations[:qualifier]. Operations are the combinable single characters c (create), r (read), u (update), d (delete) — e.g. records:cru. Author explicit resource:op forms: coarse verbs and the ops-wildcard resource:* do not grant access at runtime. A trailing qualifier (e.g. records:r:intake_form) only narrows access.

Common flags

FlagApplies toMeaning
--env staging|productioncreds-bearing verbsTarget environment (default production).
--base-url <url>creds-bearing verbsOverride the API base URL.
--token <token>creds-bearing verbsBearer token; else read from the environment.
--jsonread verbsEmit machine-readable JSON.
--blueprint <name|path>bootstrapA bundled name or a path to your own file.
--blankbootstrapCredential only (read-only data-plane, no schemas).
--no-mergebootstrapStill mints/stores — print the config snippet instead of merging it.
--rotatebootstrapRevoke + re-mint this machine's key.
--require-verified-issuersbootstrap, blueprint applyExit 4 (after the apply completed) when an issuer is still pending_verification; without it a pending issuer still exits 0.
--yes / -ybootstrap, blueprint applySkip confirmations (agent / CI). apply requires it when there is no terminal.
--seedblueprint applyAlso write the blueprint's sample seed records. Off by default, in both tenants (the opposite of bootstrap, which seeds unless --no-seed).
--pruneblueprint applyAlso remove the triggers this blueprint provisioned earlier and its file no longer declares (matched by a stored marker, not a verified identity). Off by default.
--confirm-existing-principalbootstrap, blueprint applyAuthorize using a service principal that already exists in your tenant (standing consent in a pipeline).
--keepblueprint-testSkip teardown; leave the graph for inspection.
--forcecontext destroySkip the interactive re-type-the-id confirmation (required when stdin is not a terminal).
--tenant test|liveblueprint-test, context destroy, blueprint applyWhich of your tenants the command targets (default live; blueprint apply has no default and requires it).
--set name=value / --values <file>blueprint validate|plan|apply, bootstrapSupply install-time input values (file blueprints only).

Bootstrap exit codes

(blueprint apply has its own set, above.)

0 applied (also --help, and answering no at "Apply this plan?") · 1 failed: a bad flag, an unknown or malformed blueprint, a missing or invalid --set input, a declared issuer scope the platform did not apply, or any error while applying · 2 refused by a gate: the scope gate or the capability gate rejected the blueprint, an identity it references could not be resolved, or it declares a tenant-wide namespace without the opt-in (the message says what, if anything, was already provisioned) · 3 a confirmation it did not get (an existing service principal, a key that cannot be recovered) · 4 applied in full, but --require-verified-issuers was given and an issuer is still pending_verification.

Notes & limits

  • SDK pin. The CLI bundles a matching 0.45.0 SDK build into its binary; you do not install or pin the SDK separately to use the CLI.
  • A bridge token is a human prerequisite. bootstrap needs a bridge token from the developer portal (passed via --token or VECTROS_BOOTSTRAP_TOKEN). There is no way to mint one from the CLI alone.
  • --set/--values apply to file blueprints only. Bundled blueprints are fixed and declare no inputs; supplying values against one is a usage error.
  • blueprint-test never deletes the app context. It tears down the data it created, but the context is idempotent and intentionally left in place.
  • No control-plane provisioning through bootstrap/blueprints. Keys, profiles, billing, and admin scopes are portal-only by design.

Where to go next

  • blueprints.md — the blueprint format the lifecycle commands operate on.
  • mcp.md — what the bootstrapped credential plugs into.
  • sdk.md — the programmatic equivalent of the verbs above.
  • The blueprint walkthroughs — narrated, end-to-end builds that use init → bootstrap → blueprint-test.