Skip to main content

Secrets

The Secrets API provides encrypted key-value storage for sensitive configuration values such as API keys, tokens, and credentials. Secrets are scoped to organizations and encrypted at rest using AES-256.

Overview​

  • Encryption: AES-256 with per-value random IV, stored as base64 in PostgreSQL
  • Scoping: Secrets are automatically scoped to the organization via qualified naming (org/{organizationId}/{secretName})
  • Authorization: Users must be a member of the target organization
  • Integration: Secrets can be referenced from Organization Configuration values for automatic resolution

GraphQL Schema​

Mutations​

setSecret​

Creates or updates an encrypted secret for an organization.

mutation SetSecret($input: SetSecretInput!) {
setSecret(input: $input) {
setSecretResult {
secretName
}
}
}
input SetSecretInput {
organizationId: Int!
secretName: String!
secretValue: String!
}

Response Type:

type SetSecretPayload {
setSecretResult: SetSecretResult
}

type SetSecretResult {
secretName: String! # Qualified name: "org/{organizationId}/{secretName}"
}

Example:

mutation {
setSecret(
input: {
organizationId: 42
secretName: "CARRIER_API_KEY"
secretValue: "sk-abc123..."
}
) {
setSecretResult {
secretName
}
}
}

Response:

{
"data": {
"setSecret": {
"setSecretResult": {
"secretName": "org/42/CARRIER_API_KEY"
}
}
}
}

deleteSecret​

Deletes a secret from the organization's secret store.

mutation DeleteSecret($input: DeleteSecretInput!) {
deleteSecret(input: $input) {
boolean
}
}
input DeleteSecretInput {
organizationId: Int!
secretName: String!
}

Example:

mutation {
deleteSecret(
input: {
organizationId: 42
secretName: "CARRIER_API_KEY"
}
) {
boolean
}
}

Response:

{
"data": {
"deleteSecret": {
"boolean": true
}
}
}

User-Scoped Secrets​

In addition to organization-scoped secrets, each user can store personal secrets that are invisible to other users. User secrets are keyed as user/{currentUserId}/{secretName} and are derived entirely from the JWT — no organizationId argument is required or accepted.

The primary use case is storing a personal GitHub Personal Access Token (PAT) for the publish flow. When a user PAT is present, the GitHub client uses it in preference to the organization-level token or the global GITHUB_TOKEN.

Mutations​

setUserSecret​

Creates or updates a secret scoped to the currently authenticated user.

mutation {
setUserSecret(
secretName: String!
secretValue: String!
): SetUserSecretResult!
}

Response Type:

type SetUserSecretResult {
secretName: String! # Qualified name: "user/{currentUserId}/{secretName}"
}

Example:

mutation {
setUserSecret(
secretName: "github-pat"
secretValue: "ghp_abc123..."
) {
secretName
}
}

Response:

{
"data": {
"setUserSecret": {
"secretName": "user/7/github-pat"
}
}
}

deleteUserSecret​

Deletes a secret scoped to the currently authenticated user.

mutation {
deleteUserSecret(
secretName: String!
): Boolean!
}

Example:

mutation {
deleteUserSecret(secretName: "github-pat")
}

Queries​

hasUserSecret​

Returns true if the currently authenticated user has stored a secret with the given name.

query {
hasUserSecret(secretName: String!): Boolean!
}

Example:

query {
hasUserSecret(secretName: "github-pat")
}

Response:

{
"data": {
"hasUserSecret": true
}
}

Validation Rules​

FieldRule
organizationId (org operations)Must be greater than 0
secretNameMust not be empty
secretValue (set operations)Must not be empty

Authorization​

  • Organization secrets: The authenticated user must have access to the specified organizationId. Returns 401 Unauthorized if the user does not belong to the target organization.
  • User secrets: No organization membership check is required. The user ID is derived from the JWT; a user can only read or write their own secrets.

Usage with Organization Config​

Secrets can be referenced in organization configuration values using secret reference syntax. The OrganizationConfigService automatically resolves these references at runtime, enabling secure storage of sensitive configuration without exposing plaintext values in the configuration store.

GitHub PAT Resolution Order​

When the system builds a GitHub client, it resolves a token using the following priority:

  1. User PAT — user/{currentUserId}/github-pat (set via setUserSecret)
  2. Organization token — apps.github.token from the organization's configuration
  3. Global environment variable — GITHUB_TOKEN

This allows individual users to authenticate GitHub operations with their own PAT without changing organization-level settings.