Credentials
Agent tasks frequently need to authenticate with external APIs. The credential system provides secure, encrypted storage with split-trust key management and automatic injection at execution time.
Storage Model
Credentials are stored as a Firestore subcollection under each task:
agent_tasks/{task_id}/credentials/{credential_key}Each credential document contains:
credential_key
string
Credential name, referenced by the agent in api_call tool invocations (e.g., LUNARCRUSH_API_KEY)
service
string
The integration service this credential belongs to (e.g., lunarcrush, slack)
status
string
Connection status (connected)
credentials_encrypted
bytes
KMS-encrypted ciphertext blob
kms_key_version
string
Full KMS key resource name used for encryption
auth_type
string
One of bearer, header, query_param
auth_header_name
string
Custom header name (e.g., Authorization, X-Api-Key)
scopes
array
OAuth scopes, if applicable
token_expires_at
timestamp
Token expiry for OAuth credentials (null for non-expiring keys)
created_at
timestamp
When the credential was stored
updated_at
timestamp
Last update timestamp
The plaintext credential value is never stored in Firestore. Only the KMS-encrypted ciphertext is persisted.
Credential Types
Bearer Token
The credential value is injected as an Authorization: Bearer header on outbound requests.
Use this for APIs that accept standard OAuth2 bearer tokens or API keys in the Authorization header. Most modern APIs (Slack, Notion, Airtable, LunarCrush) use this pattern.
Custom Header (API Key)
The credential value is injected as a custom HTTP header. The auth_header_name field specifies which header to set.
Use this for APIs that expect keys in non-standard headers (e.g., CoinMarketCap uses X-CMC_PRO_API_KEY, NewsAPI uses X-Api-Key, CoinGecko uses x-cg-demo-api-key).
Query Parameter
The credential value is appended as a URL query parameter named api_key.
Use this for APIs that accept keys as URL parameters (e.g., Alpha Vantage, OpenWeather). Note that query parameter auth is less secure than header-based auth because credentials may appear in server access logs on the target API side. Prefer bearer or header when the API supports it.
OAuth (Future)
OAuth credential support is planned for Phase 3. This will enable agents to authenticate with services that require OAuth flows (e.g., Google Sheets, GitHub) using refresh tokens stored alongside the encrypted access token. The executor will handle token refresh automatically when access tokens expire.
KMS Encryption (Split Trust)
Credentials are protected by Google Cloud KMS with a split-trust model that ensures no single service account can both encrypt and decrypt credential material.
Service Account Permissions
Bridge SA ([email protected])
inference-platform
cloudkms.cryptoKeyVersions.useToEncrypt
Encrypts credentials during storage via Cloud Function
Executor SA ([email protected])
inference-agents
cloudkms.cryptoKeyVersions.useToDecrypt
Decrypts credentials during task execution
Neither service account has the other's permission. The bridge SA (running in the main platform project) cannot decrypt credentials, and the executor SA (running in the agents project) cannot encrypt or overwrite them. This separation means:
A compromised bridge service cannot read existing credentials.
A compromised executor cannot inject new credentials.
Both services would need to be compromised simultaneously to both write and read credential material.
KMS Key Configuration
Key Ring
agent-keys
Key Name
credentials
Location
us-central1
Full Resource Name
projects/inference-agent/locations/us-central1/keyRings/agent-keys/cryptoKeys/credentials
Protection Level
Software (HSM available for upgrade)
Rotation Period
90 days (automatic)
Encrypted Payload Format
The plaintext credential is a JSON object that is serialized, encrypted, and stored as a binary blob:
The encrypt_credentials() function serializes this JSON, encrypts it via kms.encrypt(), and returns the ciphertext bytes. The decrypt_credentials() function reverses the process, calling kms.decrypt() and deserializing the JSON.
Credential Lifecycle
End-to-End Flow: Web UI to Execution
Creating Credentials
Credentials are created through a Cloud Function (encryptCredential) triggered from the frontend:
User enters the credential value in the web UI.
Frontend calls the Cloud Function with the task ID, credential key, auth type, and plaintext value.
The Cloud Function (running as bridge SA) encrypts the value via Cloud KMS.
The encrypted ciphertext is written to Firestore at
agent_tasks/{task_id}/credentials/{key}.The plaintext value is discarded from memory; it never reaches Firestore.
The frontend never sends the plaintext credential to Firestore directly. The Cloud Function acts as a secure intermediary.
Updating Credentials
Updating a credential follows the same encrypt-and-write flow. The old ciphertext is overwritten with the new encrypted value. There is no version history for credential values.
Deleting Credentials
Credentials are deleted through a Cloud Function (deleteCredential) that removes the document from the subcollection. When a task is deleted, all credentials in its subcollection are cascade-deleted as well.
Rotation
To rotate a credential, update it with the new value. The old ciphertext is overwritten. KMS key rotation (every 90 days) is handled automatically by Cloud KMS and does not require re-encryption of existing credentials -- Cloud KMS retains prior key versions for decryption.
Runtime Injection
At execution time, the agent executor:
Loads all credential documents from the task's subcollection via
load_task_credentials().Decrypts each encrypted blob using Cloud KMS via
decrypt_credentials().Merges the decrypted values with metadata from the credential document (
service,auth_type,auth_header_name).Holds the complete decrypted map in memory for the duration of the execution.
Builds a credential context for the LLM's system prompt listing available credential names (not values).
When the agent invokes
api_callwith acredentialparameter, the executor looks up the credential name, retrieves the decrypted value, and injects it into the outbound HTTP request according to theauth_type.
Credential Context
The LLM never receives raw credential values. It receives a list of available credential names so it knows which credential parameter to pass to api_call:
The agent references these names in tool calls, and the executor handles the actual injection:
Security Model
Credential Isolation
Credentials are per-task, not shared across tasks. Even if the same user creates multiple tasks that access the same API, each task has its own copy of the credential in its own subcollection. This prevents cross-task credential leakage and allows fine-grained revocation.
Memory-Only Decryption
Decrypted credentials exist only in executor memory during a single execution. They are:
Never written to disk.
Never written to logs (the executor uses redacted logging throughout).
Never persisted in any intermediate storage.
Garbage collected when the execution completes and the in-memory dict goes out of scope.
Exfiltration Prevention
Outbound request bodies, URLs, and custom headers are scanned for credential values before transmission. The scanner checks for plaintext, base64-encoded, and URL-encoded forms of every decrypted credential. If a credential value is detected in a location where the agent placed it (as opposed to the executor's legitimate injection point), the request is blocked. See Security for full details.
Safety Preamble
The executor prepends a safety preamble to every LLM call that explicitly instructs the model:
Never include credential values in request bodies, URLs, or custom headers.
Credentials are injected automatically via the auth header.
Only make API calls that directly serve the task described in the user message.
Last updated
