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
bootstrapcommand 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
| Command | Purpose |
|---|---|
login / logout | Browser sign-in (PKCE) and session teardown. |
whoami | Show the authenticated tenant, environment, and principal. |
bootstrap | One-shot least-privilege MCP credential (ssk_* + access profile + optional blueprint). |
blueprint init | Scaffold a new blueprint file to edit. |
blueprint validate | Structural parse + scope gate + lint (creds-free). |
blueprint plan | Validate, then preview what bootstrap or apply would provision (creds-free). |
blueprint apply | Provision a blueprint into a tenant WITHOUT minting a key (for CI). --tenant required; deletion only with --prune. |
blueprint list | List the bundled blueprint library. |
blueprint-test | Apply → assert → teardown a blueprint against a live environment. |
context create|list|get|destroy | Manage app contexts. destroy is the irreversible teardown — it asks you to re-type the context id before doing anything. |
identity create|list|get|delete|invite | Manage 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|rotate | Manage scoped API keys (ssk_*). |
role create|list|get|delete | Manage context-scoped roles. |
access grant|revoke|list|get|explain | Manage 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. |
assume | Re-mint your own token with one or more identity.<namespace> values switched — needs a role's assumable grant. |
scripts push|list|get|delete | Manage script objects: the versioned JS source a trigger rule runs. --context is required on all four. |
issuers register|update|list|get|delete|verify | Manage 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|get | Manage scope-namespace registrations (whether scope:<namespace> values resolve to identity entities). |
keyring list|use|show|doctor|prune|remove | Inspect 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
| Flag | Applies to | Meaning |
|---|---|---|
--env staging|production | creds-bearing verbs | Target environment (default production). |
--base-url <url> | creds-bearing verbs | Override the API base URL. |
--token <token> | creds-bearing verbs | Bearer token; else read from the environment. |
--json | read verbs | Emit machine-readable JSON. |
--blueprint <name|path> | bootstrap | A bundled name or a path to your own file. |
--blank | bootstrap | Credential only (read-only data-plane, no schemas). |
--no-merge | bootstrap | Still mints/stores — print the config snippet instead of merging it. |
--rotate | bootstrap | Revoke + re-mint this machine's key. |
--require-verified-issuers | bootstrap, blueprint apply | Exit 4 (after the apply completed) when an issuer is still pending_verification; without it a pending issuer still exits 0. |
--yes / -y | bootstrap, blueprint apply | Skip confirmations (agent / CI). apply requires it when there is no terminal. |
--seed | blueprint apply | Also write the blueprint's sample seed records. Off by default, in both tenants (the opposite of bootstrap, which seeds unless --no-seed). |
--prune | blueprint apply | Also 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-principal | bootstrap, blueprint apply | Authorize using a service principal that already exists in your tenant (standing consent in a pipeline). |
--keep | blueprint-test | Skip teardown; leave the graph for inspection. |
--force | context destroy | Skip the interactive re-type-the-id confirmation (required when stdin is not a terminal). |
--tenant test|live | blueprint-test, context destroy, blueprint apply | Which 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, bootstrap | Supply 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.
bootstrapneeds a bridge token from the developer portal (passed via--tokenorVECTROS_BOOTSTRAP_TOKEN). There is no way to mint one from the CLI alone. --set/--valuesapply to file blueprints only. Bundled blueprints are fixed and declare no inputs; supplying values against one is a usage error.blueprint-testnever 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.