Skip to main content
A vault holds the credentials your agents use to reach outside services: a GitHub token, a Discord bot token, an LLM API key. Vaults exist at three levels, and a credential is resolved from the most specific level outward. When an agent needs a credential for a provider, JoyStream walks these in order and uses the first match: jstm connection resolve <provider> --workspace <id> prints that walk tier by tier, so you can see exactly which credential an agent will get and why.

Org credential audit

An org owner or admin can see every service credential connected anywhere in their organization, across all workspaces — including workspaces they are not personally a member of. This is a read-and-inspect surface: you see that a credential exists and its metadata, never the secret value. An organization is made of workspaces — the org itself, plus its teams — and each workspace can hold its own credentials. Without the audit there is no single place to answer “what is connected across my whole org, and where?” The audit groups credentials by availability scope:
  • Org-wide — credentials in the org vault that every workspace can use.
  • Selected workspaces — credentials restricted to specific workspaces. A credential restricted to two workspaces appears under each of those workspace groups.
For each credential you see metadata only: provider such as github or discord, type (SERVICE or LLM), status (ACTIVE, NEEDS_REAUTH, or REVOKED), its scope, the owning workspace, and who connected it.
Values are never exposed. No surface in this feature returns token material — not the API, not the CLI, not the UI.Personal vaults are excluded. Credentials a user stored in their personal vault are never visible to org admins.Only an org owner or admin can audit. A non-admin org member sees only credentials for workspaces they belong to.
Use the organization’s Vault Settings view in the web UI, or jstm vault audit from the CLI.

Credential scope

Every credential in the org vault has a scope that decides which workspaces in the org can use it:
  • Org-wide (all_workspaces) — available to every workspace in the org.
  • Selected workspaces (selected_workspaces) — available only to the workspaces you pick.
The labels Org-wide and Selected workspaces are what you see in the web UI and in CLI human output. The underlying values all_workspaces and selected_workspaces appear in --json and API payloads.
Selected requires at least one workspace. Choosing Selected workspaces without naming any is rejected up front — the UI keeps the picker open, and the CLI fails with a clear message instead of saving an empty selection. Scope supersedes the previous per-workspace sharing model. The older vault grant and vault revoke commands have been removed. To read a credential’s current scope and its selected workspaces, use jstm vault grant list. Only an org owner or admin can set a credential’s scope, and only credentials in the org vault are scopeable. Attempting to scope a workspace or personal vault credential is rejected.

How a credential reaches a service

Most tools an agent uses reach their provider through the connector gateway — one destination for every tool call, rather than a separate server per provider. The gateway is the only component that ever holds a provider secret. Three properties of this are worth knowing when you build agents: JoyStream never holds your provider secret. When you connect a service, the OAuth grant is stored by the connection provider. JoyStream records only that the connection exists, which vault owns it, its provider key, and its status. Nothing in a vault row is a token. The connector’s schema is authoritative. Parameter names come from the provider, not from a skill’s own documentation — which routinely disagrees with it, channel_id where Slack actually wants channelId. The action’s schema is captured at build time and frozen into the package, so an agent built today keeps sending exactly the fields it was tested with. At dispatch, parameters are narrowed to the fields that schema declares, and an unexpected field is rejected rather than quietly ignored. Execution credentials are minted, never stored. A dispatch gets a token scoped to a single service for that one call. Authoring gets a different, weaker token: it can browse the catalog of providers and actions, and it cannot execute anything or enumerate your connections.

Skills backed by a stdio MCP server

A skill backed by an MCP server that runs as a subprocess — rather than through the gateway — is handed its credential in an environment variable, whose name the server itself dictates: DISCORD_TOKEN, GITHUB_PERSONAL_ACCESS_TOKEN, and so on. JoyStream resolves that name from the server’s manifest first — a well-formed MCP server declares its variables and marks which one is the secret. Failing that, it uses a curated provider overlay, a small map of provider to variable name such as discord → DISCORD_TOKEN. If a server matches neither, JoyStream fails with an error naming the service rather than guessing, and it does the same when a server declares more than one secret variable. A server’s declared variables are stored in this shape and rendered on its detail page:
type: "secret" marks the variable that receives the credential. A secret variable carries no value or default — it is supplied at run time from your vault, never stored in the catalog. env_vars: null means the manifest declared no variables; it does not mean “no authentication”, only that JoyStream falls through to the overlay. Whether a skill needs a credential at all is decided by the services it declares it requires, never by whether env_vars is populated. Only an empty required-services set means a skill is genuinely no-auth.

See also