Credentials — Encrypted Secret Storage
Credentials let you store encrypted secrets for external services in one place and reference them from agents, skills, and integrations. Instead of scattering API keys across your configuration, you define a credential once and FleetQ injects it at execution time — never exposing the raw secret in logs or agent prompts.
Scenario: A research agent calls the OpenAI API, a CRM connector, and a Slack webhook. Each service needs its own secret. With Credentials, you configure each secret once and reference them by name — rotation, expiry, and access auditing happen automatically.
Credential types
| Type | Use case |
|---|---|
| api_token | Single API key or bearer token for a service (OpenAI, Anthropic, SendGrid, …). Stored in secret_data.token. |
| basic_auth | Username and password for HTTP Basic Authentication. Required fields: username, password. |
| ssh_key | SSH private key material for git and shell tools. Required field: private_key. |
| oauth2 | OAuth 2.0 tokens. Stores access_token and optionally refresh_token, client_id, client_secret. |
| proxy |
HTTP/HTTPS/SOCKS5 proxy definition with optional username and password. Assign to a
Tool via transport_config.proxy_credential_id
to route that tool's outbound traffic through the proxy — used with the browser sandbox
headful mode for residential-proxy anti-bot scenarios. Chromium SOCKS5 with auth is
handled by a local gost forwarder.
|
| custom_kv | Arbitrary key-value pairs in secret_data. Use for services with non-standard auth formats. |
Security & encryption
FleetQ uses per-team envelope encryption to protect all credential secrets at rest.
- Each team has a unique Data Encryption Key (DEK) generated automatically on team creation.
- The DEK itself is wrapped with the platform's
APP_KEY(AES-256-CBC via Laravel's encryption layer). - Secrets are encrypted using XSalsa20-Poly1305 (libsodium) with the team's DEK — a different nonce per secret.
- Secrets are decrypted only at execution time, in memory, and are never written to logs or stored in plaintext.
- Every decryption is recorded in the audit log (
audit_entriestable) for compliance.
APP_KEY requires re-encrypting team DEKs. Use the
credentials:re-encrypt Artisan command to batch-migrate all secrets to a new key.
Optional KMS wrappers. For teams with stricter compliance requirements, the DEK can
be wrapped by an external Key Management Service instead of the platform's APP_KEY.
FleetQ ships adapters for AWS KMS, Azure Key Vault, and
Google Cloud KMS via app/Infrastructure/Encryption/KMS.
Enable a KMS wrapper by pointing the team at a managed key ARN/URI in Team Settings — existing
credentials are re-wrapped on the next rotation.
Credential statuses
Active
Credential is valid and will be injected at execution time.
Disabled
Manually disabled. Will not be injected until re-enabled.
Expired
Past the expires_at date. Auto-transitioned by the scheduler.
Revoked
Permanently invalidated. Cannot be re-enabled — create a new credential instead.
Creating credentials
Navigate to Credentials → New Credential in the sidebar, or use the API. Required fields:
- Name — human-readable label (e.g., OpenAI Production Key).
- Type — one of the six credential types above.
- Secret data — JSON object with the actual secret values (see example below).
- Description (optional) — notes about the credential's purpose or scope.
- Expires at (optional) — ISO 8601 date. FleetQ auto-expires the credential when this date passes.
// api_token
{ "token": "sk-abc123..." }
// basic_auth
{ "username": "admin", "password": "hunter2" }
// ssh_key
{ "private_key": "-----BEGIN OPENSSH PRIVATE KEY-----\n..." }
// oauth2
{
"access_token": "ya29.abc...",
"refresh_token": "1//0g...",
"client_id": "my-app",
"client_secret": "secret"
}
// custom_kv
{ "account_id": "ACC-9001", "api_secret": "xyz", "region": "us-east-1" }
// proxy
{
"protocol": "socks5",
"host": "proxy.example.net",
"port": 1080,
"username": "fleetq",
"password": "s3cret"
}
secret_data for sensitive values.
Secret rotation
Use Rotate Secret from the credential detail page (or POST /api/v1/credentials/{id}/rotate)
to update the secret without deleting and recreating the credential. The RotateCredentialSecretAction:
- Atomically replaces
secret_datawith the new value. - Re-encrypts under the team's current DEK.
- Logs the rotation to the audit trail (old value is never stored).
- Preserves the credential's UUID — existing agent/skill references remain valid.
curl -X POST https://your-instance.com/api/v1/credentials/cred-uuid-here/rotate \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "secret_data": { "key": "sk-new-key-here" } }'
Project credentials
Credentials can be scoped to specific projects. The ResolveProjectCredentialsAction
collects all credentials associated with a project and injects them into agent executions as named
environment variables. This allows different projects to use different API keys for the same
service without modifying agent configuration.
Attach credentials to a project from the project detail page under the Credentials tab. At execution time, FleetQ decrypts and injects only the credentials assigned to that project — other team credentials remain inaccessible.
BYOK — Bring Your Own LLM Keys
Teams can supply their own LLM provider API keys instead of relying on platform defaults. Configure BYOK keys in Team Settings → Provider Credentials.
The ProviderResolver selects the active LLM key using the following priority hierarchy:
- A skill-level override takes highest precedence (set per skill version).
- An agent-level override applies to all skills executed by that agent.
- Team BYOK keys apply across the team when no skill/agent override is set.
- The platform default (configured via
.env) is used as the fallback.
TeamProviderCredential records, encrypted with the same
per-team XSalsa20-Poly1305 scheme as regular credentials.
MCP tools
The FleetQ MCP server exposes the following tools for credential management — accessible to any LLM agent with a valid session.
| Tool | Description |
|---|---|
| credential_list | List all credentials for the team with optional status filter. |
| credential_get | Retrieve credential metadata by ID (secret values are never returned). |
| credential_create | Create a new credential with name, type, secret_data, and optional expiry. |
| credential_update | Update credential metadata (name, description, expires_at, status). |
| credential_rotate | Replace the secret_data atomically. Preserves all references to the credential. |
| credential_oauth_initiate | Begin an OAuth2 authorization code flow — returns the redirect URL. |
| credential_oauth_finalize | Exchange the OAuth2 callback code for tokens and store them encrypted. |
API endpoints
All endpoints require a Sanctum bearer token and respect team scope. Full OpenAPI schema available at
/docs/api.
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/credentials | List credentials. Filter by status or type. Cursor-paginated. |
| GET | /api/v1/credentials/{id} | Get a single credential. Secret values are omitted from the response. |
| POST | /api/v1/credentials | Create a new credential. Pass name, type, secret_data, and optionally expires_at. |
| PUT | /api/v1/credentials/{id} | Update credential metadata. Use /rotate to change secrets. |
| DELETE | /api/v1/credentials/{id} | Delete a credential. This is permanent and cannot be undone. |
| POST | /api/v1/credentials/{id}/rotate | Replace secret values atomically. Pass the full new secret_data object. |