Secret Input
Collect API keys, passwords, and tokens from users via a form.
The simplest auth mode. You declare form fields, the user fills them in, and the values are encrypted and stored (envelope encryption via AWS KMS — the platform never sees plaintext after the initial write).
Manifest declaration
{
"connections": [
{
"id": "weather_api",
"display_name": "Weather Service",
"auth_mode": "secret_input",
"fields": [
{ "name": "api_key", "label": "API Key", "secret": true },
{ "name": "api_secret", "label": "API Secret", "secret": true }
]
}
]
}User experience
When a user opens the Run dialog for an agent that declares a connection they haven't set up, the dialog shows a card with the declared fields:
┌─────────────────────────────────────────────────┐
│ Weather Service │
│ This agent needs your credentials. │
│ │
│ API Key [•••••••••••••••••] │
│ API Secret [•••••••••••••••••] │
│ │
│ [Save & allow] │
└─────────────────────────────────────────────────┘Credentials are per-user, reusable across agents: the second agent that declares the same id will see a lightweight consent prompt instead of a new form:
┌─────────────────────────────────────────────────┐
│ Weather Service │
│ You already have credentials saved. │
│ Allow this agent to use them? │
│ │
│ [Allow] │
└─────────────────────────────────────────────────┘Users manage stored credentials (including per-agent grants) at /dashboard/connections.
Agent code
export default async function run(ctx: AgentContext) {
const weather = await ctx.connect("weather_api");
const apiKey = weather.get("api_key");
const apiSecret = weather.get("api_secret");
}async def run(ctx: AgentContext):
weather = await ctx.connect("weather_api")
api_key = weather.get("api_key")
api_secret = weather.get("api_secret")ctx.connect() never blocks — the pre-collection model guarantees the values are present in GREXAL_CONNECTIONS before the sandbox starts.
Runtime delivery
Credentials are delivered to the sandbox via the GREXAL_CONNECTIONS env var:
{
"weather_api": {
"api_key": "...",
"api_secret": "..."
}
}The SDK parses this lazily on the first ctx.connect() call; you never need to read it directly. Alternative delivery modes (env_vars, file) are reserved for future releases and are not yet implemented — agents must use the default runtime_object delivery.
Local development
npx grexal dev reads credentials from .grexal/connections.json in the agent directory. Format:
{
"weather_api": {
"api_key": "dev-key",
"api_secret": "dev-secret"
}
}The file is git-ignored by default. No consent flow in dev — any connection present in the file is available to ctx.connect().
Field reference
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | Yes | — | Connection identifier. Matches the argument to ctx.connect(). Must match ^[a-z][a-z0-9_]*$, max 64 chars. |
display_name | string | Yes | — | User-facing label shown in the Run dialog. |
auth_mode | string | Yes | — | Must be "secret_input". |
fields | object[] | Yes | — | Form fields to collect. At least 1, at most 20. |
fields[].name | string | Yes | — | Field key. Used in conn.get(name). Must match ^[a-z][a-z0-9_]*$. |
fields[].label | string | Yes | — | User-facing label shown in the form. |
fields[].secret | boolean | No | true | Whether to mask the input. |