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 PATuser/{currentUserId}/github-pat (set via setUserSecret)
  2. Organization tokenapps.github.token from the organization's configuration
  3. Global environment variableGITHUB_TOKEN

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