Tenant Secrets - EyevinnOSC/community GitHub Wiki

Tenant Secrets

A tenant secret is a named credential stored at the tenant level, shared across all services and instances in your workspace. Unlike service secrets, which are scoped to a single service and must be recreated for each new instance, a tenant secret exists once and can be injected into any service instance through the materialize API.

Choosing between tenant secrets and service secrets? See Secrets vs Parameter Store for a decision guide.

Key namespaces

Every tenant secret key must start with either system. or user..

user.* keys are created and managed by you. You can list, read, set, and delete these keys using the /mytenantsecrets API. Use this namespace for credentials you own: API keys, tokens, or any value you want to reuse across multiple service instances without copying it separately for each one.

system.* keys are written by the Open Source Cloud platform. You cannot overwrite or delete them. The platform uses this namespace for credentials it manages on your behalf, such as the Gitea admin token used by the Agentic SDLC pipeline and the SDLC webhook secret. You can read a system.* key to inspect its value, and you can inject it into a service instance via the materialize endpoint — but you cannot write to it.

Managing tenant secrets via the API

All endpoints require a valid personal access token in the x-pat-jwt: Bearer <token> header. Replace $OSC_PAT_JWT with your OSC Personal Access Token from Settings → API.

Base URL: https://deploy.svc.prod.osaas.io

List your tenant secrets

GET /mytenantsecrets

Returns a list of key names and their metadata (key, prefix, updatedAt). Values are never returned in the list.

curl -s "https://deploy.svc.prod.osaas.io/mytenantsecrets" \
  -H "x-pat-jwt: Bearer $OSC_PAT_JWT"

Get a single secret value

GET /mytenantsecrets/:key

Returns { "key": "...", "value": "..." }.

curl -s "https://deploy.svc.prod.osaas.io/mytenantsecrets/user.my-api-key" \
  -H "x-pat-jwt: Bearer $OSC_PAT_JWT"

Set a tenant secret

Use PUT to create or update a user.* key (upsert):

PUT /mytenantsecrets/:key
Content-Type: application/json

{ "value": "sk-..." }
curl -X PUT "https://deploy.svc.prod.osaas.io/mytenantsecrets/user.my-api-key" \
  -H "x-pat-jwt: Bearer $OSC_PAT_JWT" \
  -H "Content-Type: application/json" \
  -d '{"value":"sk-..."}'

PUT is an upsert: it creates the key if it does not exist and replaces the value if it does. Use PUT for token rotation: write the new credential under the same key name and re-materialize into any services that use it.

To create a key only when it does not yet exist, use POST (returns 409 if the key already exists):

POST /mytenantsecrets/:key
Content-Type: application/json

{ "value": "sk-..." }

Attempting to PUT or POST a system.* key returns HTTP 403.

Delete a tenant secret

DELETE /mytenantsecrets/:key
curl -X DELETE "https://deploy.svc.prod.osaas.io/mytenantsecrets/user.my-api-key" \
  -H "x-pat-jwt: Bearer $OSC_PAT_JWT"

Permanently removes the tenant secret. Any service instances that previously had this value materialized as a service secret retain the materialized copy — they are not affected by the deletion.

Attempting to delete a system.* key returns HTTP 403.

Injecting a tenant secret into a service instance

Tenant secrets cannot be referenced directly in service instance parameters. To inject a tenant secret into a service instance, you first materialize it as a service secret.

POST /mytenantsecrets/:key/materialize
Content-Type: application/json

{
  "serviceId": "eyevinn-my-service",
  "secretName": "apikey"
}
curl -X POST "https://deploy.svc.prod.osaas.io/mytenantsecrets/user.my-api-key/materialize" \
  -H "x-pat-jwt: Bearer $OSC_PAT_JWT" \
  -H "Content-Type: application/json" \
  -d '{"serviceId":"eyevinn-my-service","secretName":"apikey"}'

The platform copies the value server-side into a service-scoped secret named apikey for eyevinn-my-service. The API returns:

{
  "reference": "{{secrets.apikey}}"
}

Pass this reference string as the value of the corresponding parameter when creating or updating a service instance. The orchestrator resolves it to the stored value at pod-creation time — the raw credential never passes through any API argument or MCP call.

This mechanism works for both user.* and system.* keys. For system.* keys, materialization is one way to consume the credential server-side without the raw value appearing in an API response.

When you rotate the tenant secret (PUT a new value), call the materialize endpoint again with the same serviceId and secretName to propagate the updated value to the service.

When to use tenant secrets vs service secrets

Use tenant secrets when:

  • The same credential applies to multiple services or instances (for example, a shared API key for an external provider).
  • You want to manage the credential in one place and push updates to all instances by re-materializing.
  • The credential is managed by the platform (system.* keys).

Use service secrets (via the UI or POST /mysecrets/:serviceId) when:

  • The credential is specific to a single service instance.
  • You do not plan to share it with other services.
  • You are working in the dashboard UI where tenant secrets are not yet exposed as a direct configuration option.

Both types are encrypted at rest and never returned in plaintext by the API after the initial write.

API reference summary

Method Path Description
GET /mytenantsecrets List all secret key names and metadata (no values)
GET /mytenantsecrets/:key Get the value of a single key
POST /mytenantsecrets/:key Create a key (409 if already exists)
PUT /mytenantsecrets/:key Upsert a key (403 if system.*)
DELETE /mytenantsecrets/:key Delete a key (403 if system.*)
POST /mytenantsecrets/:key/materialize Copy value into service-secret chain; body: { serviceId, secretName }

Resources

  • My Apps: Git Credentials — credential management for private git repositories
  • Developer Guide: Environment Variables — injecting secrets and config into My Apps
  • Settings → API — your OSC Personal Access Token
⚠️ **GitHub.com Fallback** ⚠️