Credentials
Overview
Credentials store secrets that collector plugins need to connect to monitored services (databases, web servers, container runtimes, etc.). Every credential is:
- Encrypted at rest via HashiCorp Vault's Transit secrets engine
- Client-scoped — credentials belong to a client and optionally an environment
- RBAC-controlled — access requires explicit
credentials:*permissions - Type-aware — each credential type defines required, optional, and secret fields
Agents never see raw credentials in their configuration. When a host config references a credential_id, the backend's credential resolver decrypts the secret fields at config-push time and injects them into the collector parameters sent over NATS.
Architecture
The Provider Registry is a pluggable abstraction. The current implementation uses Vault Transit for envelope encryption, but the Provider interface supports alternative KMS backends:
type Provider interface {
Get(ctx context.Context, in SecretRef) (map[string]string, error)
Set(ctx context.Context, ref string, data map[string]string) (SecretRef, error)
Delete(ctx context.Context, ref string) error
Type() string
}
Credential Types
Each credential type defines which fields are allowed, which are required, and which contain secrets (redacted in API responses).
| Type | Required Fields | Secret Fields | Optional Fields |
|---|---|---|---|
postgresql | dsn | dsn | -- |
redis | addr | password | db |
nginx | url | password | username |
docker | socket_path or registry_url | registry_password | registry_user |
generic | -- | all fields | any key-value pair |
The generic type accepts any key-value pairs with no field restrictions. All fields are treated as secret and redacted in API responses.
Encryption
Credential data is encrypted using Vault's Transit secrets engine with the key named proxima-creds.
Encryption flow:
- Credential
data(amap[string]string) is JSON-marshaled - The JSON bytes are base64-encoded
- Vault Transit encrypts the encoded payload, returning a ciphertext string (
vault:v1:...) - The ciphertext is stored in the
credentialstable alongside the credential metadata
Decryption flow (on read or credential resolution):
- The ciphertext is sent to Vault Transit for decryption
- Vault returns the base64-encoded plaintext
- The plaintext is decoded and unmarshaled back to the original key-value map
Vault must be unsealed and reachable for any credential operation. If Vault is unavailable, credential create, read, update, and resolution all fail. The backend logs these failures and returns 500 Internal Server Error.
RBAC
Credential endpoints require the following permissions, scoped to the client that owns the credential:
| Permission | Required For |
|---|---|
credentials:read | List credentials, get credential details |
credentials:write | Create credentials, update credentials, test credentials |
credentials:delete | Delete credentials |
Super admins bypass all permission checks.
Service Accounts
Service accounts can create, list, and manage credentials via API key authentication (X-API-Key header). When a service account creates or updates a credential, the created_by and updated_by fields are set to null (since service account subjects reference the service_accounts table, not the users table referenced by the FK constraint).
API Endpoints
All credential endpoints are nested under a client:
| Method | Path | Description |
|---|---|---|
POST | /api/v1/clients/{id}/credentials | Create a credential |
GET | /api/v1/clients/{id}/credentials | List credentials for a client |
GET | /api/v1/clients/{id}/credentials/{credentialID} | Get a credential |
PUT | /api/v1/clients/{id}/credentials/{credentialID} | Update a credential |
DELETE | /api/v1/clients/{id}/credentials/{credentialID} | Delete a credential |
POST | /api/v1/clients/{id}/credentials/{credentialID}/test | Test a credential |
Secret fields are redacted in GET responses (replaced with "***"). The raw values are never returned after creation.
Credential Lifecycle
1. Create
Create a credential with a name, type, and data fields:
POST /api/v1/clients/{clientID}/credentials
{
"name": "Production DB",
"credential_type": "postgresql",
"provider": "local",
"environment_id": "env-uuid",
"data": {
"dsn": "postgres://user:pass@db-host:5432/mydb"
}
}
The data is validated against the credential type's field definitions, encrypted via Vault Transit, and stored in PostgreSQL.
2. Use in Collector Config
Reference the credential in a host's collector configuration by its credential_id. When the backend pushes config to the agent, the Credential Resolver decrypts the secret fields and injects them into the collector parameters.
3. Rotate
Update the credential's data to rotate secrets:
PUT /api/v1/clients/{clientID}/credentials/{credentialID}
{
"data": {
"dsn": "postgres://user:new-pass@db-host:5432/mydb"
}
}
4. Delete
Remove a credential when it is no longer needed. Deletion is blocked if any host configs reference it.
Rotation
When a credential's data is updated:
- The new data is validated and encrypted
last_rotated_atis set to the current timestamprevisionis incremented- Affected hosts (those with collector configs referencing this credential) receive a config re-push via NATS
This ensures agents pick up rotated secrets without manual intervention or restarts.
Reference Guarding
Credentials cannot be deleted while host configurations reference them. A DELETE request for a referenced credential returns:
Response: 409 Conflict
{
"error": {
"code": "conflict",
"message": "credential is referenced by 2 host(s)",
"request_id": "req_abc123"
}
}
The response includes the host_ids that reference the credential, so operators can update or remove those references before retrying the delete.
Always check for host config references before attempting to delete a credential. Use the error response to identify which hosts need updating.
Connectivity Testing
POST /api/v1/clients/{clientID}/credentials/{credentialID}/test
The test endpoint validates credential fields against the type definition (required fields present, no unknown fields for typed credentials).
Testing currently performs field validation only -- it does not attempt a live connection to the target service. Live connectivity testing is planned for a future release.
Least-Privilege Setup
Each credential should grant the minimum permissions the agent needs to collect metrics. Below are practical setup instructions for each credential type.
PostgreSQL
-- Create a dedicated monitoring user (do NOT use postgres/root/admin)
CREATE USER proxima_mon WITH PASSWORD 'your_secure_password';
-- Grant the pg_monitor role (read-only access to pg_stat_* views)
GRANT pg_monitor TO proxima_mon;
-- Grant CONNECT on each database you want to monitor
GRANT CONNECT ON DATABASE mydb TO proxima_mon;
Why this works:
pg_monitoris a built-in PostgreSQL role that bundlespg_read_all_stats,pg_read_all_settings, andpg_stat_scan_tables. It provides read-only access to all the system views the agent queries.- The agent only runs
SELECTqueries on system views -- no write access is needed. - Never use a superuser (
postgres,root,admin) for monitoring. If the credential is compromised, the blast radius should be limited to read-only stats access.
Redis
ACL SETUSER proxima on >your_password ~* +info +config|get +client|list +dbsize +slowlog|get
This creates a Redis 6+ ACL user with the minimum permissions the agent needs:
| Permission | Purpose |
|---|---|
+info | Server info and stats |
| `+config | get` |
| `+client | list` |
+dbsize | Database key count |
| `+slowlog | get` |
For Redis < 6 (no ACL support), use requirepass and ensure the server is not exposed to untrusted networks. There is no way to restrict commands without ACL, so network-level isolation is critical.
Nginx
# Enable stub_status on a restricted endpoint
location /stub_status {
stub_status;
allow 127.0.0.1; # Only localhost
allow 10.0.0.0/8; # Or your internal network
deny all;
}
Key points:
- The
stub_statusmodule provides basic connection and request metrics. It is compiled into most Nginx builds by default. - Restrict access to localhost or internal networks only -- do not expose this endpoint publicly.
- No authentication is needed if the endpoint is properly firewalled.
- If basic auth is required (e.g., the agent runs on a different host), create a dedicated read-only user with
htpasswdand reference the username/password in the Nginx credential.
Docker
# Read-only socket mount (recommended)
-v /var/run/docker.sock:/var/run/docker.sock:ro
# Or add the agent to the docker group
usermod -aG docker proxima-agent
Key points:
- Mount the Docker socket as read-only (
:ro) when possible. The agent only reads container metadata and stats. - For container registries, use a token or service account with pull-only access. Never grant push or admin permissions to a monitoring credential.
- Never expose the Docker socket over TCP without TLS. An unencrypted, network-accessible Docker socket is equivalent to root access on the host.
Generic
Generic credentials are custom key-value pairs where all values are treated as secrets. Use these for integrations not covered by the built-in types.
When creating generic credentials, follow the principle of least privilege for whatever service you are connecting to:
- Create a dedicated service account or API token for monitoring.
- Grant only the read or stats permissions the agent needs.
- Avoid reusing credentials that have write or admin access.
- Rotate the credential on a regular schedule using the rotation API.
Management UI
Credentials can be managed through the Console frontend in two places:
- Client Detail → Credentials Tab — create, edit, and delete credentials scoped to a single client. The form dialog dynamically renders required/optional/secret fields based on the selected credential type and shows security warnings (e.g., least-privilege setup links). After creation, the credential is automatically tested via the
/testendpoint. - Admin → Credentials Page (
/admin/credentials) — cross-client view for administrators. Lists credentials across all accessible clients with search filtering.
See the Frontend Overview for full component details.