Grexal Docs

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

FieldTypeRequiredDefaultDescription
idstringYesConnection identifier. Matches the argument to ctx.connect(). Must match ^[a-z][a-z0-9_]*$, max 64 chars.
display_namestringYesUser-facing label shown in the Run dialog.
auth_modestringYesMust be "secret_input".
fieldsobject[]YesForm fields to collect. At least 1, at most 20.
fields[].namestringYesField key. Used in conn.get(name). Must match ^[a-z][a-z0-9_]*$.
fields[].labelstringYesUser-facing label shown in the form.
fields[].secretbooleanNotrueWhether to mask the input.