An API key is a value your backend needs but your source repository and browser should not receive. A secret resource gives that value a managed home. Your runtime asks for a version of the secret when it needs to authenticate to the external service.
Declaring the resource, allowing a runtime to read it, and supplying its value are three separate steps. A deployment can successfully create a secret resource that does not yet contain a usable version.
#Understand secret versions
A secret is the named resource. A version contains a particular value. Rotation adds or selects a newer value while your application continues referring to the same declared resource.
ReadLatest asks for the current latest version on each call. It does not push a rotated value into a variable your program already holds. ReadVersion asks for an exact version when a workflow needs stable input. Your application decides whether to read for each operation or cache the result for a bounded time.
The read grant generates both methods. The separate list grant generates ListVersions; being able to enumerate versions does not allow their values to be read.
#A missing version is not missing permission
| State | Consequence |
|---|---|
| Runtime has no secret grant | No generated secret client for that runtime |
| Runtime has read access, but no version is available | A read can return NotFound |
| Runtime reads a version with an empty value | Decide whether an empty value is valid for the integration |
| A new version is supplied | A subsequent latest read can see it; cached bytes do not update themselves |
| A user calls your HTTP handler | Your handler uses the secret internally; it does not return the value to that user |
How access works explains the same declaration/grant boundary with an interactive bucket example.
#Configure secret access
#Declare the secret and permission
Add a secret named api-key to fluffy-chainsaw.yaml and grant an existing API runtime read access:
secrets:
api-key: {}
runtimes:
api:
uses:
secrets:
- name: api-key
actions: [read]Keep the runtime's artifact and other fields. Workspace policy selects Secret Manager for a configured GCP environment:
resourceTypes:
secrets:
secret-manager:
product: gcp.secret-manager
environments:
prod:
secrets:
defaults:
type: secret-manager
provider: applicationThe application provider must already specify the intended project and region. This snippet chooses the product; it does not contain the secret value.
#Supply a secret version
After deploying the resource, an authorized operator supplies a version to the exact Secret Manager resource using the provider's workflow. Keep the value out of application YAML, logs, source control, and command output that might be recorded.
Fluffy's public runtime API offers read/list access. It does not provide a method for authoring new versions. That separation lets the runtime consume a credential without also granting it permission to rotate or replace the credential.
For local development, the local secret service stores its own versions. A fresh resource may have no version. The current public CLI and local manifest do not provide a secret-value population command or field. Use a provisioned development version for successful reads; use an empty local resource to check missing-secret handling. Keep production secrets out of local fixtures.
#Read from your runtime
Generate the runtime's SDK. For an application named example-app:
fluffy-chainsaw generate --application example-app --runtime api --language typescriptUse the sidecar setup for your selected language. These examples use example-app and module example.com/app; adjust the imports to your application. TypeScript uses address and transport; Python uses channel. Go passes its connection and a deadline-bearing context to the function:
import { Metadata } from '@grpc/grpc-js'
import * as runtimev1 from './.fluffy/generated/runtimes/api/typescript/fluffy/runtime/example-app/api.js'
import * as secretsv1 from './.fluffy/generated/runtimes/api/typescript/fluffy/secrets/v1/secrets.js'
const client = new runtimev1.SecretsApiKeyClient(address, transport)
const latest = await new Promise<secretsv1.ReadLatestResponse>((resolve, reject) => {
client.readLatest(
secretsv1.ReadLatestRequest.fromPartial({}),
new Metadata(),
{ deadline: Date.now() + 5000 },
(error, value) => {
if (error) reject(error)
else resolve(value)
},
)
})
const apiKey = latest.value
if (apiKey.length === 0) throw new Error('api-key is empty')Pass apiKey directly to the server-side integration. If your application logs an authentication failure, record the operation and safe error context, not the key. Use a deadline for the read and decide whether a temporary read failure should stop startup or fail that integration request.
#Verify rotation and failure behavior
With a development version available, confirm the integration authenticates successfully and that logs and HTTP responses omit the value. Supply a new development version, let the application's refresh policy run, and verify the integration uses it.
Also test a missing version. The application should report a controlled unavailable integration or startup failure instead of continuing with an accidental empty credential. A valid empty value and a NotFound response are different states.
#Reference: permitted operations and limits
| Grant | Generated operation | Purpose |
|---|---|---|
read | ReadLatest, ReadVersion | Read a latest or exact version's value |
list | ListVersions | Enumerate version metadata |
An unknown action, empty action list, unresolved secret name, or duplicate edge is rejected. There is no write or rotate runtime secret grant. Version listing uses a page limit from 1 to 100; preserve the returned cursor rather than inventing or reusing one for another resource.
#Native access
The client also includes Unwrap and Credential. Their native behavior depends on the product. In GCP, a Secret Manager native credential is the runtime service account's ordinary OAuth token, whose authority includes that identity's IAM grants; it is not necessarily limited to this one secret's read action. See Generated SDKs.
#Diagnose the failing boundary
A missing client points to the grant or generation target. NotFound can mean the secret has no available version. A cloud permission error needs the runtime identity and resource policy checked; copying a service-account key into YAML does not fix the declaration model. A downstream API rejecting the value may indicate the external credential is invalid even though the secret read itself succeeded.
Continue with Cache for values that can be recomputed, or Authentication for identifying the user behind a request.