Skip to main content

Secrets

Orunbase secrets manage sensitive values at the same three scopes as settings — workspace, project, and environment. The API surface is deliberately asymmetric: secret values are write-only. You supply a value on create and rotate; the platform encrypts it before persistence, and no response, event, or audit payload ever contains the value, its ciphertext, or a hash of it. Everything you can read back is metadata — key, status, version, rotation timestamps.

Encryption at rest​

Values are encrypted in the worker before they touch storage, using AES-256-GCM (authenticated encryption) with a random 12-byte IV per value. The 256-bit key is sourced from a deployment environment binding — it is never stored alongside the data. What persists is a versioned ciphertext envelope ({ alg: "AES-256-GCM", v: 1, iv, ct }); plaintext is never written.

Resource shape​

{
"secret": {
"id": "sec_9c8d7e6f5a4b",
"orgId": "org_1f2e3d4c5b6a",
"projectId": "prj_5e6f7a8b9c0d",
"environmentId": null,
"scopeKind": "project",
"secretKey": "stripe.webhook_signing",
"displayName": "Stripe webhook signing secret",
"status": "active",
"version": 2,
"rotationPolicy": null,
"lastRotatedAt": "2026-06-20T10:03:11.482Z",
"expiresAt": null,
"createdBy": "usr_2b3c4d5e6f7a",
"createdAt": "2026-05-02T08:41:07.909Z",
"updatedAt": "2026-06-20T10:03:11.482Z"
}
}

Endpoints​

All paths are relative to a scope base — /v1/organizations/{orgId}/config, …/projects/{projectId}/config, or …/environments/{envId}/config:

MethodPathDescription
GET/secretsList secret metadata at this scope
GET/secrets?scope=allWorkspace route only: list metadata across every rung the workspace owns
POST/secretsCreate a secret (optional write-only value)
POST/secrets/{secretId}/rotateReplace the value; bumps version, sets lastRotatedAt
DELETE/secrets/{secretId}Revoke — soft delete, sets status: "revoked"

Listing requires organization.config.read (workspace scope) or project.config.read; create, rotate, and revoke require the corresponding …config.write action. ?scope=all is valid only on the organization-scope route — on project or environment scope it is rejected with 422.

Create a secret​

curl -X POST "https://api.orunbase.com/v1/organizations/org_1f2e3d4c/projects/prj_5e6f7a8b/config/secrets" \
-H "Authorization: Bearer $ORUN_CLOUD_TOKEN" \
-H "Content-Type: application/json" \
-d '{"secretKey": "stripe.webhook_signing", "displayName": "Stripe webhook signing secret", "value": "whsec_..."}'
import { OrunCloud } from "@saas/sdk";

const client = new OrunCloud({
baseUrl: "https://api.orunbase.com",
auth: { kind: "bearer", token: process.env.ORUN_CLOUD_TOKEN! },
});

const scope = { kind: "project", orgId: "org_1f2e3d4c", projectId: "prj_5e6f7a8b" } as const;

const { secret } = await client.config.createSecretMetadata(scope, {
secretKey: "stripe.webhook_signing",
displayName: "Stripe webhook signing secret",
value: process.env.STRIPE_WEBHOOK_SECRET!,
});

The 201 response carries metadata only. value may be omitted to register metadata first and supply the value on a later rotate.

warning

Once written, a value cannot be read back through any Orunbase API. Keep your own copy in a system of record if you need to reference it — the only recovery path is rotating in a new value.

Rotate and revoke​

Rotate replaces the value in place: the metadata row keeps its id and key, version increments, and lastRotatedAt is set. Consumers referencing the secret by id or key pick up the new value without re-wiring.

curl -X POST "https://api.orunbase.com/v1/organizations/org_1f2e3d4c/projects/prj_5e6f7a8b/config/secrets/sec_9c8d7e6f5a4b/rotate" \
-H "Authorization: Bearer $ORUN_CLOUD_TOKEN" \
-H "Content-Type: application/json" \
-d '{"value": "whsec_new..."}'
await client.config.rotateSecret(scope, "sec_9c8d7e6f5a4b", {
value: process.env.STRIPE_WEBHOOK_SECRET_V2!,
});

Revoke is the end of the lifecycle — a soft delete that flips status to revoked while preserving the metadata row for audit:

await client.config.revokeSecret(scope, "sec_9c8d7e6f5a4b");

Create, rotate, and revoke all land in the audit log under the config category — with the value redacted by construction, since it never enters any event payload.

The secret.value.use permission​

Reading metadata and writing values are gated by the config permissions above. A separate action, secret.value.use, governs the runtime use of a secret's decrypted value. It is held by the owner, admin, and builder workspace roles but not viewer, so read-only members can see that a secret exists without ever being able to exercise its value.

The surface that consumes it is live: the lease-bound run secrets resolve — the only value-returning machine route on the platform — requires secret.value.use as its first gate, alongside a live job lease as its second.

How CI publishes secrets​

CI never writes through this API's create/rotate routes. When a job produces values (Terraform outputs, provisioned resource ids), it publishes them through the lease-bound job output secrets channel, which lands them at the project/environment rung of this store: the first publish of a key creates the secret (as the system actor, source: "static"), and later publishes append versions — a rotate, never an overwrite. Those secrets then look and behave like any other row on this page.

Orunbase secrets vs. platform infrastructure secrets​

Two different kinds of secret exist in an Orunbase deployment — keep them separate:

  • Orunbase secrets (this page) are tenant data: values your workspaces, projects, and environments manage through the API — third-party API keys, webhook signing secrets, tokens your runs consume. This now includes deployment wiring: Terraform outputs and wiring documents that a tenant's jobs produce publish into this API at the project/environment rung, through the run-lease channel.
  • Platform infrastructure secrets are configuration of the platform itself — its database credentials, the secret-encryption key, its own provider tokens. These never live in this API; they belong in the operating cloud's secret manager and are injected as worker bindings when you run your own instance. See Self-hosting architecture.