*** title: Scopes & Inheritance description: >- How AI credentials, storage, and vault access flow from workspaces to runtimes. --------- When you create a workspace, you mount the resources it needs — AI credentials, vault secrets, and storage. Every runtime launched inside that workspace automatically inherits those mounts. This means you configure access once and every runtime just works. ## How inheritance works ``` Workspace (mounts: ai, vault, storage) ├── Runtime "crm" ← inherits all mounts ├── Runtime "email" ← inherits all mounts └── Runtime "research" ← inherits all mounts ``` You set the ceiling at the workspace level. Runtimes inherit everything by default. When needed, you can **narrow** scopes for a specific runtime or agent execution — but you can never **widen** beyond what the workspace allows. ## AI credentials AI credentials give runtimes access to LLM providers (OpenAI, Anthropic, Google, etc.) for agent-based automation like Stagehand and browser-use. ### Managing credentials ```ts // Create a credential await bctrl.aiCredentials.create({ id: "openai-prod", provider: "openai", label: "Production OpenAI", apiKey: "sk-...", defaultModel: "gpt-4o", }); // List all credentials const creds = await bctrl.aiCredentials.list(); // Test connectivity await bctrl.aiCredentials.test("openai-prod"); ``` ### Mounting to a workspace ```ts const workspace = await bctrl.workspaces.create({ name: "agent-workspace", mounts: { ai: { allow: ["openai-prod", "anthropic-prod"], defaults: { openai: "openai-prod", anthropic: "anthropic-prod", }, }, }, }); ``` * `allow` — which credential IDs this workspace can use * `defaults` — the default credential per provider (used by agents automatically) Any runtime in this workspace can use Stagehand or browser-use agents, and they'll automatically pick up the correct AI credentials. ## Vault The vault stores secrets like login credentials, API keys, and TOTP seeds. Runtimes use vault entries to authenticate into websites. ### Managing secrets ```ts // Store a credential await bctrl.vault.set("prod/crm/salesforce", { username: "bot@company.com", password: "...", totp: "JBSWY3DPEHPK3PXP", origins: ["https://login.salesforce.com"], }); // Retrieve const cred = await bctrl.vault.get("prod/crm/salesforce"); // Generate a TOTP code const code = await bctrl.vault.totp("prod/crm/salesforce"); // List keys const keys = await bctrl.vault.list("prod/crm/"); ``` ### Mounting to a workspace ```ts mounts: { vault: { allow: ["prod/crm/", "prod/email/"], deny: ["prod/admin/"], allowRawReads: true, }, } ``` * `allow` — key prefixes the workspace can access * `deny` — key prefixes explicitly blocked (takes precedence over allow) * `allowRawReads` — whether plaintext secret values can be read directly Runtimes in the workspace can only access secrets that match the allowed prefixes. An agent running in this workspace could auto-login to Salesforce using `prod/crm/salesforce` but couldn't touch `prod/admin/` secrets. ## Storage Storage gives runtimes a place to save files — screenshots, downloads, exports, CSV files. It's scoped by workspace namespace. ### Using storage ```ts // Upload a file await bctrl.storage("my-workspace").upload("exports/report.csv", csvBuffer); // Download const data = await bctrl.storage("my-workspace").get("exports/report.csv"); // Browse files const files = await bctrl.storage("my-workspace").browse({ prefix: "exports/" }); // Get a download URL const url = await bctrl.storage("my-workspace").getDownloadUrl("exports/report.csv"); ``` ### Mounting to a workspace ```ts mounts: { storage: { workspace: "shared-exports", }, } ``` This connects the workspace to the `shared-exports` storage namespace. All runtimes in the workspace can read and write files there. Multiple workspaces can share the same storage namespace. ## Narrowing scopes Scopes can be narrowed at the runtime or execution level. This is useful when a workspace has broad access but a specific agent task should be restricted: ```ts // Workspace has access to all prod/ vault keys const workspace = await bctrl.workspaces.create({ name: "multi-agent", mounts: { vault: { allow: ["prod/"] }, ai: { allow: ["openai-prod", "anthropic-prod"] }, }, }); // This runtime can only use anthropic credentials // (narrowed from the workspace's full set) ``` The principle: **workspaces define the ceiling, runtimes can lower it, nothing can raise it.** ## Putting it all together ```ts import { Bctrl } from "@bctrl/sdk"; const bctrl = new Bctrl({ apiKey: process.env.BCTRL_API_KEY! }); // 1. Set up credentials (one-time) await bctrl.aiCredentials.create({ id: "openai-prod", provider: "openai", label: "Production", apiKey: process.env.OPENAI_API_KEY!, }); await bctrl.vault.set("prod/crm/salesforce", { username: "bot@company.com", password: process.env.SF_PASSWORD!, totp: process.env.SF_TOTP_SEED!, origins: ["https://login.salesforce.com"], }); // 2. Create workspace with all mounts const workspace = await bctrl.workspaces.create({ name: "sales-pipeline", mounts: { ai: { allow: ["openai-prod"], defaults: { openai: "openai-prod" } }, vault: { allow: ["prod/crm/"] }, storage: { workspace: "sales-exports" }, }, }); // 3. Launch runtimes — they inherit everything const runtime = await workspace.runtimes .browser("crm") .stagehand({ mode: "profile", profileId: "sf-bot" }); // 4. Agent can auto-login using vault creds and use OpenAI for reasoning await runtime.stagehand.act("Log in to Salesforce and export Q1 pipeline"); ``` ## Related * [Workspaces](/sdk/concepts/workspaces) — creating and managing workspaces * [Runtimes](/sdk/concepts/runtimes) — launching and controlling runtimes * [API Reference: AI Credentials](/api/ai-credentials) — HTTP endpoints for credential management * [API Reference: Vault](/api/vault) — HTTP endpoints for secret management