Credential profiles
A credential profile is a named set of AI credentials that Boat stores encrypted: API keys for Anthropic, OpenAI and other providers, secrets for any other HTTPS API, and the Claude or ChatGPT sign-ins you make on the dashboard. A sandbox gets the profile its environment names. Boat never returns a secret once it’s stored.
Declare a profile
Section titled “Declare a profile”import * as Boat from "@alchemy.run/boat";import * as Config from "effect/Config";
export const MyProfile = Boat.CredentialProfile("MyProfile", { anthropic: Config.Redacted("ANTHROPIC_API_KEY"), openai: Config.Redacted("OPENAI_API_KEY"),});Each provider has its own prop: anthropic, openai, openrouter, llmgateway, deepseek, moonshot, mistral, xai and opencode.
The props are the whole list. A deploy sets a key whose value changed, and removes an API key the props no longer name. Sign-ins and Bedrock are connected on the dashboard’s Agents & Proxying page, and a deploy leaves them alone.
Give sandboxes the profile
Section titled “Give sandboxes the profile”Name the profile on an environment, and start sandboxes on it:
import * as Boat from "@alchemy.run/boat";import { MyProfile } from "./MyProfile.ts";
export const MyEnv = Boat.Environment("MyEnv", { credentialProfile: MyProfile,});
export const Box = Boat.Sandbox("Box", { environment: MyEnv });Box starts with ANTHROPIC_API_KEY and OPENAI_API_KEY set. An environment that names no profile passes no AI credentials.
Boat refuses to delete a profile an environment still names, so a destroy deletes the environment first.
Add a secret for any other API
Section titled “Add a secret for any other API”apis holds a secret for any HTTPS API, with the base URL it’s sent to. The key of each entry is the variable the sandbox gets:
import * as Boat from "@alchemy.run/boat";import * as Config from "effect/Config";
export const MyProfile = Boat.CredentialProfile("MyProfile", { anthropic: Config.Redacted("ANTHROPIC_API_KEY"), apis: { STRIPE_KEY: { key: Config.Redacted("STRIPE_KEY"), baseUrl: "https://api.stripe.com/v1", }, },});A sandbox gets STRIPE_KEY and STRIPE_KEY_BASE_URL. Code in the sandbox sends its requests to $STRIPE_KEY_BASE_URL, never to the API’s own address:
curl "$STRIPE_KEY_BASE_URL/charges" \ -H "Authorization: Bearer $STRIPE_KEY"The name is A-Z, 0-9 and _, up to 64 characters. Provider names like ANTHROPIC_* and AWS_*, and names ending in _BASE_URL, are reserved. The base URL is HTTPS on the default port, with a public DNS name.
Keep real keys out of the sandbox
Section titled “Keep real keys out of the sandbox”A sandbox normally holds the real keys, and anyone with a shell in it can copy them. With Boat’s AI credentials proxy on, the sandbox gets a placeholder for each key and the proxy’s URL for each base URL. Boat swaps the real key in as the request passes through.
A placeholder is worth nothing outside Boat, and a copied one stops working when the sandbox stops: every start gets new ones. The proxy sends each provider key only to its own provider, and only in the Authorization or x-api-key header.
The proxy is one switch for a whole account. Boat.AiProxy turns it on:
import * as Boat from "@alchemy.run/boat";
export const Proxy = Boat.AiProxy("Proxy");Boat bills the proxy: $1 for each day it’s on, and $1 per 1,000 proxied requests. With it on, every sandbox on one of the account’s profiles gets placeholders from its next start. Destroying the Stack puts the switch back the way it was.
Bedrock credentials sign each request, so there’s nothing to swap. Boat keeps them out of sandboxes while the proxy is on.
Give users’ sandboxes AI without the keys
Section titled “Give users’ sandboxes AI without the keys”A safeForThirdParties environment passes nothing of your account, AI credentials included. proxyAiCredentials passes them anyway, as placeholders:
import * as Boat from "@alchemy.run/boat";import { MyProfile } from "./MyProfile.ts";import { Proxy } from "./Proxy.ts";
export const Tenants = Boat.Environment("Tenants", { safeForThirdParties: true, credentialProfile: MyProfile, proxyAiCredentials: Proxy,});Someone with a shell in a Tenants sandbox can run an agent, and can’t take your key. Passing the proxy itself means the switch is turned on before the environment asks for it: Boat rejects proxyAiCredentials while the proxy is off.
Use the profile you set up on the dashboard
Section titled “Use the profile you set up on the dashboard”Every account has a profile named default, which holds the sign-ins and keys you connect on the dashboard. lookup finds it by name, and the Stack never changes or deletes it:
import * as Boat from "@alchemy.run/boat";
export const MyEnv = Boat.Environment("MyEnv", { credentialProfile: Boat.CredentialProfile.lookup({ name: "default" }),});This is how a sandbox gets your Claude or ChatGPT subscription, which only a browser sign-in can connect.
Share a profile with an organization
Section titled “Share a profile with an organization”A profile is yours, or an organization’s. When the Stack works in an organization, the profiles it declares belong to the organization: every member’s sandboxes can use them, and only owners change them. Name one on an environment of the same organization.
The proxy follows the same rule. An organization has its own switch, which only an owner changes, and Boat.AiProxy in a Stack that works in the organization is that switch.
Pull keys from a secrets manager
Section titled “Pull keys from a secrets manager”Config.Redacted("ANTHROPIC_API_KEY") reads .env by default. A Stack’s secrets option reads the same name from a secrets manager like Doppler or Infisical instead, so a production key never sits in a file on anyone’s machine. See Secret providers.