MATIH Platform is in active MVP development. Documentation reflects current implementation status.
8. Platform Services
Secret Management

Secret Management

The Secret Management system provides encrypted storage, rotation, and access control for sensitive configuration values. The SecretController at /api/v1/config/secrets handles API keys, database credentials, OAuth tokens, and other secrets with AES-256-GCM encryption, per-service access control, and automatic rotation policies.


SecretController

@RestController
@RequestMapping("/api/v1/config/secrets")
@Tag(name = "Secret Management", description = "Endpoints for managing encrypted secrets")
public class SecretController {
    private final SecretManagementService secretService;
}

Create a Secret

Create a new encrypted secret with optional rotation policy and service-level access control.

Endpoint: POST /api/v1/config/secrets

curl -X POST http://localhost:8888/api/v1/config/secrets \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000" \
  -H "X-User-ID: 550e8400-e29b-41d4-a716-446655440001" \
  -H "X-User-Email: admin@acme.com" \
  -d '{
    "secretKey": "openai-api-key",
    "value": "sk-proj-abc123...",
    "secretType": "API_KEY",
    "environment": "production",
    "rotationPolicy": "MANUAL",
    "rotationDays": 90,
    "description": "OpenAI API key for the AI service",
    "allowedServices": ["ai-service", "ml-service"],
    "tags": ["ai", "external-api"]
  }'

Response:

{
  "id": "sec-001",
  "secretKey": "openai-api-key",
  "secretType": "API_KEY",
  "environment": "production",
  "description": "OpenAI API key for the AI service",
  "version": 1,
  "rotationPolicy": "MANUAL",
  "lastRotatedAt": null,
  "nextRotationAt": "2026-05-13T10:00:00Z",
  "expiresAt": null,
  "tags": ["ai", "external-api"],
  "createdAt": "2026-02-12T10:00:00Z",
  "updatedAt": "2026-02-12T10:00:00Z"
}
⚠️

The secret value is never returned in the creation response. Only metadata is returned. The value is encrypted using AES-256-GCM before storage.

Secret Types

TypeDescription
API_KEYThird-party API keys
DATABASE_CREDENTIALDatabase connection credentials
OAUTH_TOKENOAuth access/refresh tokens
CERTIFICATETLS certificates and private keys
ENCRYPTION_KEYEncryption keys
WEBHOOK_SECRETWebhook signing secrets
CUSTOMAny other secret type

Rotation Policies

PolicyDescription
MANUALManual rotation only
AUTOMATICAuto-rotate on the configured schedule
ON_DEMANDRotation triggered by external events

Get Secret Value

Retrieve and decrypt a secret value. Requires service-level access control -- only services listed in allowedServices can access the secret.

Endpoint: GET /api/v1/config/secrets/value

curl "http://localhost:8888/api/v1/config/secrets/value?key=openai-api-key&environment=production" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000" \
  -H "X-User-ID: 550e8400-e29b-41d4-a716-446655440001" \
  -H "X-User-Email: admin@acme.com" \
  -H "X-Service-Name: ai-service"

Response:

{
  "key": "openai-api-key",
  "value": "sk-proj-abc123..."
}
🚫

Every secret value access is logged in the audit trail. Unauthorized access attempts (wrong service name, missing permissions) are denied and flagged.


List Secrets (Metadata Only)

List all secrets for a tenant. Only metadata is returned -- never the actual values.

Endpoint: GET /api/v1/config/secrets

curl "http://localhost:8888/api/v1/config/secrets?page=0&size=20" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000"

Update a Secret

Update a secret's value. The previous value is preserved in version history.

Endpoint: PUT /api/v1/config/secrets/{secretId}

curl -X PUT http://localhost:8888/api/v1/config/secrets/sec-001 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000" \
  -H "X-User-ID: 550e8400-e29b-41d4-a716-446655440001" \
  -H "X-User-Email: admin@acme.com" \
  -d '{
    "value": "sk-proj-xyz789...",
    "reason": "API key regenerated in OpenAI dashboard"
  }'

Rotate a Secret

Explicit rotation operation that updates the value and resets the rotation timer.

Endpoint: POST /api/v1/config/secrets/{secretId}/rotate

curl -X POST http://localhost:8888/api/v1/config/secrets/sec-001/rotate \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000" \
  -H "X-User-ID: 550e8400-e29b-41d4-a716-446655440001" \
  -H "X-User-Email: admin@acme.com" \
  -d '{
    "newValue": "sk-proj-new-key..."
  }'

Get Secrets for a Service

Retrieve all secrets that a specific service is authorized to access, in a given environment.

Endpoint: GET /api/v1/config/secrets/service/{serviceName}

curl "http://localhost:8888/api/v1/config/secrets/service/ai-service?environment=production" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000" \
  -H "X-User-ID: 550e8400-e29b-41d4-a716-446655440001" \
  -H "X-User-Email: admin@acme.com"

Response:

{
  "openai-api-key": "sk-proj-abc123...",
  "embedding-api-key": "emb-key-xyz..."
}

Secrets Needing Rotation

List secrets that have exceeded their rotation period.

Endpoint: GET /api/v1/config/secrets/rotation/pending

curl http://localhost:8888/api/v1/config/secrets/rotation/pending \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000"

Expiring Secrets

List secrets that will expire within a specified number of days.

Endpoint: GET /api/v1/config/secrets/expiring

curl "http://localhost:8888/api/v1/config/secrets/expiring?withinDays=30" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000"

Rotate Encryption Key

Re-encrypt all secrets in the tenant with a new master encryption key. This is used during key rotation for compliance.

Endpoint: POST /api/v1/config/secrets/encryption-key/rotate

curl -X POST http://localhost:8888/api/v1/config/secrets/encryption-key/rotate \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000" \
  -H "X-User-ID: 550e8400-e29b-41d4-a716-446655440001" \
  -H "X-User-Email: admin@acme.com" \
  -d '{
    "oldKeyId": "key-v1",
    "newKeyId": "key-v2"
  }'

Response:

{
  "secretsRotated": 42
}

Secret Statistics

Endpoint: GET /api/v1/config/secrets/stats

curl http://localhost:8888/api/v1/config/secrets/stats \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000"

Encryption Details

Secrets are encrypted using AES-256-GCM (Galois/Counter Mode):

PropertyValue
AlgorithmAES/GCM/NoPadding
Key Length256 bits
IV Length12 bytes (96 bits)
Tag Length128 bits
Key DerivationPBKDF2 with HMAC-SHA256

The encryption key is stored in Kubernetes secrets and referenced via environment variables. It is never stored in the database.