Skip to main content

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).

TypeRequired FieldsSecret FieldsOptional Fields
postgresqldsndsn--
redisaddrpassworddb
nginxurlpasswordusername
dockersocket_path or registry_urlregistry_passwordregistry_user
generic--all fieldsany key-value pair
info

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:

  1. Credential data (a map[string]string) is JSON-marshaled
  2. The JSON bytes are base64-encoded
  3. Vault Transit encrypts the encoded payload, returning a ciphertext string (vault:v1:...)
  4. The ciphertext is stored in the credentials table alongside the credential metadata

Decryption flow (on read or credential resolution):

  1. The ciphertext is sent to Vault Transit for decryption
  2. Vault returns the base64-encoded plaintext
  3. The plaintext is decoded and unmarshaled back to the original key-value map
warning

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:

PermissionRequired For
credentials:readList credentials, get credential details
credentials:writeCreate credentials, update credentials, test credentials
credentials:deleteDelete 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:

MethodPathDescription
POST/api/v1/clients/{id}/credentialsCreate a credential
GET/api/v1/clients/{id}/credentialsList 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}/testTest a credential
tip

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:

  1. The new data is validated and encrypted
  2. last_rotated_at is set to the current timestamp
  3. revision is incremented
  4. 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.

warning

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).

info

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_monitor is a built-in PostgreSQL role that bundles pg_read_all_stats, pg_read_all_settings, and pg_stat_scan_tables. It provides read-only access to all the system views the agent queries.
  • The agent only runs SELECT queries 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:

PermissionPurpose
+infoServer info and stats
`+configget`
`+clientlist`
+dbsizeDatabase key count
`+slowlogget`
note

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_status module 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 htpasswd and 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 /test endpoint.
  • 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.