AI Support Assistant
This example deploys an AI-powered support assistant that demonstrates three Riptides capabilities working together: transparent mTLS for database access, secretless credential injection for external APIs, and mixed credential sources (HashiCorp Vault and Kubernetes Secrets).
Architecture
Section titled “Architecture” ┌─────────────────┐ │ support- │ │ assistant │ │ (Next.js) │ └──┬─────┬─────┬──┘ │ │ │ mTLS │ │ │ credential injection ┌─────────────┘ │ └─────────────────┐ │ │ │ v v v ┌────────────────┐ ┌───────────────┐ ┌───────────────────┐ │ PostgreSQL │ │ Anthropic │ │ OpenAI API │ │ (internal) │ │ Claude API │ │ (external) │ │ │ │ (external) │ │ │ │ port 5432 │ │ port 443 │ │ port 443 │ │ MUTUAL TLS │ │ Bearer Token │ │ Vault-managed │ │ │ │ (K8s Secret) │ │ API key │ └────────────────┘ └───────────────┘ └───────────────────┘How the connections work:
- App to PostgreSQL — The kernel module performs a mutual TLS handshake. Both the app and database present SPIFFE certificates. No connection strings or TLS configuration in the application.
- App to Anthropic API — The kernel module intercepts the HTTPS connection to
api.anthropic.comand injects a Bearer token sourced from a Kubernetes Secret. The application makes a plain HTTPS call; the credential is added transparently. - App to OpenAI API — Same transparent injection, but the API key is fetched from HashiCorp Vault using JWT authentication. Riptides handles the Vault lease lifecycle automatically.
Resources
Section titled “Resources”WorkloadIdentity: support-assistant
Section titled “WorkloadIdentity: support-assistant”The main application identity.
apiVersion: core.riptides.io/v1alpha1kind: WorkloadIdentitymetadata: name: support-assistant namespace: riptides-systemspec: workloadID: support-assistant/app/assistant-api scope: daemonGroup: id: riptides/daemongroup/dev-eu-west-1/on-demand-workers selectors: - k8s:label:app: support-assistant k8s:pod:namespace: support-assistant process:cmdline: next-server (v - k8s:label:app: support-assistant process:name: curl connection: tls: mode: PERMISSIVE intercept: trueKey points:
- Two selector sets match the Next.js server process and
curlhealth checks running in the same pod. process:cmdline: next-server (vuses a prefix match on the process command line for precise workload attestation.connection.tls.intercept: truetells the kernel module to intercept outbound TLS connections so it can inject credentials into HTTP headers. Which credentials are injected for which service is controlled by thepropagation.injection.selectorsin each CredentialBinding.
WorkloadIdentity: support-assistant-postgres
Section titled “WorkloadIdentity: support-assistant-postgres”The database identity enforces strict mutual TLS. Only the support-assistant application can connect.
apiVersion: core.riptides.io/v1alpha1kind: WorkloadIdentitymetadata: name: support-assistant-postgres namespace: riptides-systemspec: workloadID: support-assistant/app/postgres scope: daemonGroup: id: riptides/daemongroup/dev-eu-west-1/on-demand-workers selectors: - k8s:label:app: postgres k8s:pod:namespace: support-assistant process:cmdline: postgres connection: tls: mode: MUTUALKey points:
MUTUALTLS mode ensures every connection to the database requires a valid SPIFFE identity. No anonymous or plaintext connections are possible.- The
process:cmdlineselector matches the exact PostgreSQL server process, preventing other binaries in the same pod from claiming this identity.
Services
Section titled “Services”PostgreSQL (internal)
Section titled “PostgreSQL (internal)”apiVersion: core.riptides.io/v1alpha1kind: Servicemetadata: name: support-assistant-postgres-svc namespace: riptides-systemspec: addresses: - address: postgres-service.support-assistant.svc.cluster.local port: 5432 tags: - postgres - database external: false labels: api: postgresAnthropic Claude API (external)
Section titled “Anthropic Claude API (external)”apiVersion: core.riptides.io/v1alpha1kind: Servicemetadata: name: anthropic-svc namespace: riptides-systemspec: addresses: - address: api.anthropic.com port: 443 tags: - anthropic - claude external: true labels: api: anthropicOpenAI API (external)
Section titled “OpenAI API (external)”apiVersion: core.riptides.io/v1alpha1kind: Servicemetadata: name: openai-svc namespace: riptides-systemspec: addresses: - address: api.openai.com port: 443 tags: - openai - chatgpt external: true labels: api: openaiNote: External services (external: true) represent endpoints outside the cluster. Riptides does not manage identities for these services but can intercept outbound connections to inject credentials.
CredentialSources
Section titled “CredentialSources”Credential sources define where secrets come from. This example uses two different backends to show that Riptides can federate credentials from multiple systems.
Vault-managed OpenAI API keys
Section titled “Vault-managed OpenAI API keys”apiVersion: core.riptides.io/v1alpha1kind: CredentialSourcemetadata: name: vault-openai-apikeys namespace: riptides-systemspec: vault: address: https://vault.example.com audience: - example-audience jwtAuthMethodPath: jwt_production path: openai/creds/riptides-role role: openai-apikeys type: token: source: api_keyHow it works:
- The Riptides control plane authenticates to Vault using a JWT signed with the workload’s SPIFFE identity.
- Vault validates the JWT against its configured OIDC provider (the Riptides trust domain).
- Vault returns a dynamic API key from the
openai/creds/riptides-rolepath. - Riptides extracts the
api_keyfield from the Vault response and uses it as a bearer token. - When the lease expires, Riptides automatically renews or fetches a new credential.
Kubernetes Secret for Anthropic API key
Section titled “Kubernetes Secret for Anthropic API key”apiVersion: core.riptides.io/v1alpha1kind: CredentialSourcemetadata: name: anthropic-bearer namespace: riptides-systemspec: kubernetes: secretRef: name: anthropic-api-key key: token type: BearerTokenThis reads the token key from the anthropic-api-key Kubernetes Secret and formats it as a Bearer token for HTTP Authorization headers.
CredentialBindings
Section titled “CredentialBindings”Credential bindings connect a credential source to a workload identity and define how the credential is delivered.
OpenAI credential binding
Section titled “OpenAI credential binding”apiVersion: core.riptides.io/v1alpha1kind: CredentialBindingmetadata: name: support-assistant-openai-binding namespace: riptides-systemspec: workloadID: support-assistant/app/assistant-api credentialSource: vault-openai-apikeys propagation: injection: selectors: - api: openai sysfs: {}Propagation methods:
injection— The kernel module injects the credential into HTTP requests destined for services matching the selector (api: openai). The application never sees or handles the API key.sysfs— The credential is also written to the kernel sysfs filesystem, making it available at a well-known path for workloads that need to read credentials directly (e.g., for non-HTTP protocols).
Anthropic credential binding
Section titled “Anthropic credential binding”apiVersion: core.riptides.io/v1alpha1kind: CredentialBindingmetadata: name: support-assistant-claude-binding namespace: riptides-systemspec: workloadID: support-assistant/app/assistant-api credentialSource: anthropic-bearer propagation: injection: selectors: - api: anthropicThis binding uses injection only. When the support assistant connects to api.anthropic.com:443, the kernel module adds the Authorization: Bearer <token> header before the request leaves the node.
How the pieces fit together
Section titled “How the pieces fit together”-
Identity issuance — The daemon attests the Next.js process using its Kubernetes labels, namespace, and command line. If the selectors match, the control plane issues a SPIFFE X.509 identity (
spiffe://example.com/support-assistant/app/assistant-api). -
Database connection — When the app opens a TCP connection to
postgres-service.support-assistant.svc.cluster.local:5432, the kernel module:- Intercepts the socket.
- Performs an mTLS handshake, presenting the app’s SVID.
- Validates that the database presents
spiffe://example.com/support-assistant/app/postgres. - Proxies data over the encrypted channel.
-
LLM API calls — When the app makes an HTTPS request to
api.anthropic.com:443, the kernel module intercepts the TLS connection. The daemon evaluates the connection context and determines that a credential should be injected for this destination. The kernel checks the eval context and injects the Bearer token into the HTTP Authorization header before forwarding the request to the external API.
The application code simply calls fetch("https://api.anthropic.com/v1/messages", ...) without any secrets in its environment, mounted volumes, or configuration files.
Security highlights
Section titled “Security highlights”- No secrets in application code or environment — API keys are managed by Riptides and injected at the kernel level. They never appear in pod specs, environment variables, or application logs.
- Mixed credential backends — Production secrets can live in Vault with dynamic rotation while development credentials use simpler Kubernetes Secrets. The application code is identical in both cases.
- mTLS for internal services — The PostgreSQL database rejects any connection that does not present a valid SPIFFE certificate, eliminating credential-based database authentication entirely.
- Credential scoping — Credentials are only injected for the three services configured in the CredentialBindings. Connections to any other destination carry no injected credentials.
Next steps
Section titled “Next steps”- Secretless cloud access guide — extend this pattern to AWS services
- Vault integration guide — detailed Vault configuration
- Credentials concepts — credential sources, bindings, and propagation
- Kubernetes bearer tokens guide — working with K8s Secrets