Replies: 2 comments
-
|
We support secured MCP Proxies through the AI Workspace. The scope of this feature should extend to cover the MCP related scenarios as well. |
Beta Was this translation helpful? Give feedback.
0 replies
-
|
I have some problems with the design.
|
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Secrets Management — Platform API
Platform administrators need to configure sensitive credentials across multiple domains — primarily LLM provider API keys — when creating and managing LLM providers through the platform. Currently, there is no mechanism to handle these securely. The problem has two distinct dimensions:
No secure storage for secrets - The platform has no dedicated secret storage layer. Sensitive values such as LLM provider API keys (e.g., sk-xxx for OpenAI) have nowhere safe to live. If they were stored today, they would land as plain TEXT in the database — the same way client_cert and client_key are already stored in api_mtls_config and backend_endpoints. A database dump would expose all credentials in full.
No safe way to reference secrets in deployment artifacts - When an LLM provider is configured (e.g., auth.header.value: "Bearer sk-xxx"), the raw API key would end up embedded inside the deployment artifact pushed to the gateway controller. This means:
The core gap: there is no placeholder/reference mechanism that keeps the credential out of deployment artifacts while still making it available at runtime. This will be handled as explained below. The proposed architecture would be as,
1. Overview
The Platform API provides administrator-only CRUD endpoints for managing encrypted secrets, scoped to organizations (and optionally projects). Secrets are encrypted at rest and referenced in resource definitions — LLM providers, API backend configs, mTLS configs — via a placeholder syntax. The raw secret value never appears in any stored artifact or API response after the initial creation response.
Placeholder syntax used in resource definitions:
Example — LLM provider stored configuration:
2. API Endpoints
All endpoints are administrator-only and JWT-authenticated. Organization scope is derived from the JWT token — the same pattern used by the existing gateway and LLM provider endpoints.
POST/api/v1/secretsGET/api/v1/secretsGET/api/v1/secrets/:idPUT/api/v1/secrets/:idDELETE/api/v1/secrets/:id2.1 Create Secret
Request
projectIdis optional. When omitted, the secret is org-wide. When set, the secret is scoped to that project and the samenamecan coexist at org-level and project-level independently.Response — 201 Created (value is returned exactly once)
{ "id": "a1b2c3d4-...", "name": "wso2-openai-key", "displayName": "WSO2 OpenAI API Key", "description": "API key for WSO2 OpenAI integration", "type": "API_KEY", "provider": "IN_HOUSE", "value": "sk-xxx", "createdAt": "2026-01-12T10:00:00Z", "createdBy": "admin@wso2.com" }After this response, the
valuefield is never included in any response again.2.2 List Secrets
Response — 200 OK (metadata only)
{ "list": [ { "id": "a1b2c3d4-...", "name": "wso2-openai-key", "displayName": "WSO2 OpenAI API Key", "type": "API_KEY", "provider": "IN_HOUSE", "environment": "__ALL_ENV", "projectId": null, "createdAt": "2026-01-12T10:00:00Z", "updatedAt": "2026-01-12T10:00:00Z" } ], "count": 1 }2.3 Get Secret by ID
Same shape as a single list item — no
valuefield.2.4 Rotate Secret (PUT)
Re-encrypts and stores the new value. The
nameandidremain unchanged, so all{{ secret "..." }}placeholders across resources remain valid without modification.Request
{ "value": "sk-new-value", "displayName": "WSO2 OpenAI API Key (rotated)" }Response — 200 OK (new value returned once)
{ "id": "a1b2c3d4-...", "name": "wso2-openai-key", "value": "sk-new-value", "updatedAt": "2026-06-01T08:00:00Z" }2.5 Delete Secret
Before deletion, the service queries all tables that can reference a secret by placeholder:
llm_providers(configuration JSON)api_backend_endpointsapi_mtls_configIf any reference exists,
409 Conflictis returned:{ "error": "secret is referenced by active resources", "references": [ { "type": "llm_provider", "id": "...", "name": "OpenAI Provider" } ] }A soft alternative is available: set
status: DEPRECATEDon the secret instead of deleting it. The GW controller will continue to resolve it but new resources cannot reference a deprecated secret.3. Storage and Encryption
3.1 Encryption
EncryptSubscriptionToken/DecryptSubscriptionTokenpattern inutils/subscription_token_crypto.go)DeriveEncryptionKey(existing utility) — 32-byte key from 64-char hex or base64PLATFORM_SECRET_ENCRYPTION_KEYenv var (consistent withAUTH_JWT_SECRET_KEY)3.2 Vault Provider Interface
Encryption is behind a pluggable interface to allow future KMS backends without changing the service layer:
Implementations to provide at launch:
providercolumn valueIN_HOUSEPLATFORM_SECRET_ENCRYPTION_KEYAWS_KMSHASHICORP_VAULTThe
providercolumn persists which vault encrypted the secret so rotation and decryption use the correct backend even after a backend migration.Research items for AWS KMS and HashiCorp Vault:
PUT3.3 Hashing and Change Detection
Every secret stores a
hashof its plaintext value (SHA-256 hex). The GW controller uses this hash to determine whether a secret has changed since its last sync, avoiding unnecessary re-deployment.Hash versioning: The
hashcolumn stores a prefixed value to allow algorithm changes:The prefix lets the GW controller (and future code) detect the hashing algorithm in use and handle multi-algorithm transitions during rollouts.
GW controller flow:
/api/v1/secrets(metadata + hash, no value).hashagainst the locally cached hash.3.4 Database Schema
Note on uniqueness: The
PRIMARY KEY (organization_id, handle)enforces org-level uniqueness. Project-scoped secrets use the samehandlebut a different scope, so an additional unique constraint covers this:This allows the same secret name (e.g.
openai-key) to coexist as org-wide and within a specific project simultaneously.4. Scope Resolution
4.1 Access Control
All queries include
WHERE organization_id = ?extracted from the JWT token — the same pattern as gateways and LLM providers. Cross-organization requests return404 Not Found(not403) to avoid leaking existence.4.2 Project vs Org Scope
A secret can be org-wide (
project_id IS NULL) or project-scoped (project_id = <uuid>). Scope resolution for placeholder lookup follows a most-specific-wins rule:(organization_id, project_id, handle).(organization_id, NULL, handle)(org-wide).400 Bad Requestat resource save time.This allows teams to override org-level secrets per project without renaming placeholders.
4.3 Placeholder Validation at Resource Save
When creating or updating any resource that contains
{{ secret "..." }}references (LLM providers, APIs, backend configs), the service:400 Bad Requestand the list of unresolvable placeholders if any are missing.5. Key Behaviours
5.1 Write-once Value Semantics
The
ciphertextcolumn is never mapped to any GET or LIST response DTO. This is enforced at the DTO struct level (novaluefield onSecretResponse), not by convention. OnlySecretCreateResponseandSecretRotateResponsecarry thevaluefield.5.2 Rotation
PUT /api/v1/secrets/:idre-encrypts and stores the new value. Thehandleandidare immutable, so no resource definition changes are required after rotation. Thehashis also updated, triggering GW controller reconciliation on next poll.5.3 Delete Protection
See §2.5. Reference scanning covers all tables that store configuration blobs. The implementation uses SQLite JSON functions to query for placeholder patterns within configuration columns.
5.4 Soft Delete / Deprecation
status: DEPRECATEDallows an admin to mark a secret as retired without breaking existing GW deployments. The GW controller continues resolving deprecated secrets but the platform API rejects new resource definitions that reference a deprecated secret.6. Gateway Controller Integration
6.1 Background — Compatibility
{{ secret "..." }}placeholders to plaintext before generating Envoy/Nginx config. Encrypts where applicable.The new GW release should be backward-compatible: if a resource definition contains no
{{ secret "..." }}placeholders, it behaves identically to 1.1.0.6.2 Secret Resolution Flow at Deploy Time
Internal decryption endpoint (not in the public API surface — GW-to-platform internal):
Authenticated via GW service account JWT. Returns decrypted plaintext. Audited separately.
6.3 Hash-driven Incremental Updates
The GW controller polls
/api/v1/secrets?updatedAfter=<timestamp>. The platform API returns only secrets whoseupdated_at > updatedAfter. If no secrets changed, no decryption calls are made.7. Configuration
PLATFORM_SECRET_ENCRYPTION_KEYPLATFORM_SECRET_VAULT_PROVIDERIN_HOUSE|AWS_KMS|HASHICORP_VAULTIN_HOUSE)For AWS KMS and HashiCorp Vault, additional provider-specific env vars will be specified in the respective research deliverables.
8. Error Responses
"secret with this name already exists in scope"9. AI Workspace — Auto-Encryption on LLM Provider Creation
9.1 Overview
When an LLM provider is added or updated via the AI Workspace UI, any plain-text API key value in the upstream auth configuration is automatically encrypted via the Platform API secrets endpoint before the provider is persisted. The raw API key never reaches the stored LLM provider artifact.
9.2 Flow
The same flow applies to
updateProvider(PUT) — if the user provides a new plain-text key the same handle is used, effectively rotating the secret.9.3 Secret Handle Naming Convention
Secret handles are derived deterministically from the provider ID:
Examples:
wso2-openai-providerwso2-openai-provider-api-keyanthropic-claudeanthropic-claude-api-keyazure-openai-eastazure-openai-east-api-keyThe handle is lowercased and non-alphanumeric characters are collapsed to hyphens. This makes the secret discoverable by handle if an admin needs to rotate it independently via the secrets API.
9.4 Idempotency Guard
Before calling the secrets API, the context checks whether the auth value already contains a
{{ secret "substring. If it does (e.g. the provider config is being re-submitted with the existing placeholder), the secret creation step is skipped entirely and the payload is passed through unchanged. This prevents duplicate secrets being created on re-save.9.5 Error Behaviour
If the
POST /secretscall fails, the entire provider creation is aborted — no partial state is written. The error propagates to the existing snackbar error handler inServiceProviderNew.tsx, which displays the Platform API error message to the user.For more details refer https://docs.google.com/document/d/1I-ls5QUvDpGLmYPe39ai_i3AEL_awv8vQbi83jOnoRg/edit?usp=sharing
Beta Was this translation helpful? Give feedback.
All reactions