Release notes · Developer walkthrough
Trust from your terminal, Go, Rust and Ansible: a practical guide to OIDC and scoped access
The deployment job needs a password. Your Go service needs a certificate. An engineer needs to check why yesterday's automation failed. Those are three different jobs, and handing all three the same credential makes the next problem harder to untangle.
We have been joining up the ways developers and operators use Trust outside the browser: a management CLI, Go and Rust clients, and the Ansible collection we use for deployment secrets. The OIDC work connects those tools to workload identity, so a job can prove what it is and receive a scoped Trust token.
This is a walkthrough of those pieces together. Start with one test secret, get a successful read, then change how the application authenticates. That gives you a small, understandable path to debug before you put it into a production deployment.
What shipped, and what the release number means
The Trust 1.1.26 service release brings account-scoped Gateway APIs, CLI browser login with PKCE, device OIDC federation and account authorization checks into the release. The CLI has its own version: trust v0.1.0, with Linux, macOS and Windows downloads for amd64 and arm64, plus checksums.
The Go client is available through its Go module path. The Rust client is available from Git, with a crate archive produced by CI; we are not claiming a crates.io publication. The Ansible collection is certlocker.secrets version 0.2.3, installable from Git or its built archive. A source repository, a CI artifact and a package-registry release are different distribution paths.
All four client repositories are public under certlocker-io. The deployment tooling is maintained separately in a private repository. It uses the same explicit legacy/account route selection, but it is not a public SDK download.
Choose the client for the job
| Tool | Use it for | Authentication |
|---|---|---|
| Trust CLI | Inspect and manage hosts, secrets, certificates, tokens, probes and agents; read audit events; run existing automations. | Browser login, including 2FA, followed by an Edge user session. |
| Go / Rust SDK | Read secret contents, certificate records, PEMs, bundles and token metadata from an application. | A scoped Gateway API token or OIDC exchange. |
| Ansible collection | Resolve secret references during a playbook run; create secrets and detect matching existing records. | A Gateway token supplied on the controller. |
The CLI's session belongs to the signed-in user and carries that user's permissions. A Gateway token belongs to an automation access policy. Putting CERTLOCKER_TOKEN into your shell does not sign the CLI in, and copying the CLI's saved browser session into a build job is not the SDK authentication flow.
There is another distinction worth learning early: get returns a record; fetch returns its contents. A secret record lets you inspect metadata. Fetching a password gives the application the actual password. Certificate fetching has a similar split between the public PEM and a bundle containing private-key material.
Start in the terminal: sign in and inspect
Install a release binary from GitHub, or use Go 1.25 or newer. If you use go install, ensure the Go binary directory is on your PATH. This example pins the CLI release so the commands match the version being discussed.
go install github.com/certlocker-io/trust-cli/cmd/trust@v0.1.0
trust auth --url https://trust.certlocker.io
trust auth whoami
trust hosts list --status ACTIVE --limit 10
trust certificates list
trust --output json probes list
trust audit list --outcome FAILURE --limit 20 trust auth opens the deployment's login page. Sign in and complete 2FA there. The browser returns to a loopback listener on 127.0.0.1, and the CLI saves the session and deployment URLs locally. PKCE binds the authorization result to the CLI login attempt; you are not pasting an account password into a terminal command.
For a terminal where automatic browser launch is inconvenient, use:
trust auth --url https://trust.certlocker.io --no-browser --timeout 10m Open the printed link in a browser on the same machine while the command waits. A browser on your laptop cannot reach a remote SSH machine's loopback listener without additional routing. The flag suppresses browser launch; it does not turn this into an unattended device enrollment.
whoami shows the identity and permissions saved at login. Edge still authorizes each operation. A JSON 403 from Edge means the operation was denied; an HTML error may instead have come from a proxy before Edge saw the request. On a 401 the CLI attempts one session renewal and retry. Use trust auth logout when you are finished.
Follow the certificate through to the service
The useful operator workflow goes beyond listing certificates. Inspect a saved automation, run it against a certificate record, then wait for a probe to check the endpoint. Create and configure the automation in the UI first; the CLI currently provides list, get and run for automations.
# Replace these with IDs from your deployment.
trust automations list --status ACTIVE
trust automations get '<automation-id>'
trust automations run '<automation-id>' --type certificate \
--certificate '<certificate-record-id>' --wait --timeout 2m
trust probes run '<probe-id>' --wait --timeout 2m These commands execute real deployment steps, so use an automation and target you intend to change. --wait waits for a recorded result; an automation failure or failed probe exits with an error. A renewed certificate in inventory, a successful deployment script and a successful endpoint check are separate pieces of evidence.
For management updates, the CLI also supports update ID --body file.json on its six supported asset areas. Supply the full update form, not a JSON merge patch. The put alias still calls Edge's POST .../modify contract. Keep secret-bearing input files private and outside source control.
Read a password from Go
The Go SDK uses the standard library and requires Go 1.22 or newer. Create a small module:
mkdir trust-example
cd trust-example
go mod init example.com/trust-example
go get github.com/certlocker-io/trust-go-client@latest Save the following as main.go. Create a disposable PASSWORD secret in your deployment and a scoped SECRET token permitted to read it. The example takes configuration from the process environment and uses a 30-second context deadline.
package main
import (
"context"
"fmt"
"log"
"os"
"time"
trustclient "github.com/certlocker-io/trust-go-client"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
service, err := trustclient.NewBuilder().
URL(os.Getenv("TRUST_URL")).
AccountID(os.Getenv("CERTLOCKER_ACCOUNT_ID")).
APIToken(os.Getenv("CERTLOCKER_TOKEN")).
BuildService(ctx)
if err != nil {
log.Fatal("Trust client configuration failed")
}
defer service.Close()
password, err := service.GetSecretAsPassword(ctx, os.Getenv("TRUST_SECRET_ID"))
if err != nil {
log.Fatal("Trust secret fetch failed")
}
if password.Password == "" {
log.Fatal("Expected a non-empty password")
}
// Pass password.Password to your database client here.
fmt.Println("Password retrieved; value omitted")
} export TRUST_URL=https://trust.certlocker.io
export CERTLOCKER_ACCOUNT_ID='<your-account-id>'
export TRUST_SECRET_ID='<password-secret-id>'
# Supply CERTLOCKER_TOKEN through your secret injection mechanism.
# The examples deliberately do not echo it.
go run . The program confirms that it received a non-empty value without printing it. In an application, pass the value directly to the client that needs it. Environment injection is the input mechanism in this first example; it does not make a long-lived token short-lived. The OIDC example below replaces that token input.
TRUST_URL is the deployment base URL, without /rest. The SDK adds its API path. CERTLOCKER_ACCOUNT_ID must be the account that issued the token. For a token assigned to one secret, an empty secret ID lets Gateway select the assigned secret. A group-scoped token needs the ID of the secret you intend to read.
After dependency resolution, commit your application's go.mod and go.sum where present. That records the selected module version instead of fetching whatever @latest means during every build.
The same read from Rust
The Rust crate is called trust-client; the import is trust_client. Start with cargo new trust-example-rust, enter that directory, and add this dependency to Cargo.toml. It pins the reviewed source revision.
[dependencies]
trust-client = { git = "https://github.com/certlocker-io/trust-rust-client", rev = "448fa3a47b69b236c5d94228df333c16f259640d" } Replace src/main.rs with:
use std::{env, error::Error, time::Duration};
use trust_client::{TrustClient, TrustService};
fn run() -> Result<(), Box<dyn Error>> {
let service = TrustService::from_builder(
TrustClient::builder()
.url(env::var("TRUST_URL")?)
.account_id(env::var("CERTLOCKER_ACCOUNT_ID")?)
.api_token(env::var("CERTLOCKER_TOKEN")?)
.timeout(Duration::from_secs(30)),
)?;
let password = service.get_secret_as_password(&env::var("TRUST_SECRET_ID")?)?;
if password.password.is_empty() {
return Err("Expected a non-empty password".into());
}
// Pass password.password to your database client here.
println!("Password retrieved; value omitted");
Ok(())
}
fn main() {
if run().is_err() {
eprintln!("Trust configuration or secret fetch failed");
std::process::exit(1);
}
} Set the same four environment variables as the Go example, then run cargo run once to resolve dependencies. Commit the resulting lockfile for your application and use cargo run --locked in subsequent checks.
This client uses blocking HTTP calls. In an async service, keep them off the async executor's worker threads, for example by using your runtime's blocking-task facility. The request timeout here bounds the Gateway call. Linux source builds need the platform TLS development dependencies used by reqwest; on Debian or Ubuntu these normally include pkg-config and libssl-dev.
The repository tests its pinned dependency graph with Rust 1.84. A consuming application's Git dependency resolves its own graph, so its resolved dependencies may require a newer compiler. Use a current stable toolchain for this walkthrough and retain the application lockfile.
Replace the stored CI token with workload identity
A static token gets the first read working. For a recurring build job, the next question is why that job needs a reusable Trust credential stored beside its workflow at all.
With OIDC, the workload obtains a signed identity token from its provider. Trust checks that token against a configured principal and exchanges it for a Trust access token. The job then uses the Trust token on Gateway requests. The provider identity token and the resulting Trust token have different jobs and separate expiries.
- 1. IdentifyGitHub, cloud identity or an enrolled device supplies a signed JWT.
- 2. AuthorizeTrust checks the issuer, audience, claims and principal policy.
- 3. ExchangeGateway returns a scoped Trust token, account and expiry.
- 4. FetchThe application uses that token to read its permitted asset.
In the OIDC screen, create a principal for the actual workload. Bind it to the repository, cloud identity or enrolled device that should be accepted. Set explicit allowed groups and the permitted mint types. An empty allowed-groups list means unrestricted group access in the current implementation. It is not a deny-all setting.
| Mint type | What to configure |
|---|---|
SECRET | The secret groups this workload may access. |
CERTIFICATE | The certificate binding required by the principal, alongside its permitted scope. |
MCP | The required agent binding and the access that agent should have. |
A requested type must be allowed by the principal. Omitting it selects the principal's default. Do not treat an MCP token, a certificate token and a secret token as interchangeable just because all three are returned by the same exchange endpoint.
The Go and Rust clients implement GitHub Actions, GCP, Azure, Azure DevOps and device exchange helpers. Their ambient discovery order is GitHub Actions, GCP, Azure, then Azure DevOps. Device exchange is configured explicitly. You can also choose a provider explicitly, which makes a CI job's intended identity easier to inspect.
| Identity source | Practical setup detail |
|---|---|
| GitHub Actions | Allow OIDC token requests in the job. Bind Trust to the intended repository and any additional claim restrictions. |
| GCP | Use the workload's metadata identity and bind the principal to the intended service account. |
| Azure | Configure the tenant and managed identity. Select a user-assigned identity explicitly when needed. |
| Azure DevOps | Expose the job's OIDC request URI and access token; its audience is api://AzureADTokenExchange. |
| Device | Enroll an Ed25519 public key and retain the private key on the machine that signs requests. |
GitHub, GCP, Azure and device helpers use the configured Trust audience, normally https://trust.certlocker.io/oidc/<accountId>. Azure DevOps uses its fixed audience and issuer/subject binding. Follow the provider-specific setup in the SDK documentation rather than applying the GitHub settings to every provider.
A GitHub Actions job without a stored Trust token
First create a GITHUB_ACTIONS principal in Trust. Bind the exact repository, choose SECRET, and set explicit allowed groups that contain your disposable password secret. Add the claim restrictions your deployment policy requires. The current UI exposes the repository binding; more specific restrictions such as ref or subject may require the API.
Put this Go program in cmd/read-secret/main.go in an application module that depends on the Go SDK:
package main
import (
"context"
"fmt"
"log"
"os"
"time"
trustclient "github.com/certlocker-io/trust-go-client"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
exchanged, err := trustclient.ExchangeGitHubActions(
ctx, os.Getenv("TRUST_URL"), os.Getenv("TRUST_OIDC_AUDIENCE"),
trustclient.TokenTypeSecret,
)
if err != nil {
log.Fatal("GitHub identity exchange failed")
}
service, err := trustclient.NewBuilder().
URL(os.Getenv("TRUST_URL")).
AccountID(exchanged.AccountID).
APIToken(exchanged.TokenID).
BuildService(ctx)
if err != nil {
log.Fatal("Trust client configuration failed")
}
defer service.Close()
password, err := service.GetSecretAsPassword(ctx, os.Getenv("TRUST_SECRET_ID"))
if err != nil || password.Password == "" {
log.Fatal("Trust secret fetch failed")
}
// Use the password inside this process; never print the JWT or token.
fmt.Println("OIDC exchange and secret retrieval succeeded")
} Then add .github/workflows/read-secret.yml. Replace both placeholders, configure the GitHub production environment with the approvals or branch restrictions your team requires, and commit the application module files.
name: Read a deployment secret
on: workflow_dispatch
permissions:
contents: read
id-token: write
jobs:
read-secret:
runs-on: ubuntu-latest
environment: production
env:
TRUST_URL: https://trust.certlocker.io
TRUST_OIDC_AUDIENCE: https://trust.certlocker.io/oidc/<your-account-id>
TRUST_SECRET_ID: <password-secret-id>
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7
with:
go-version: '1.25'
- name: Exchange identity and read the secret
run: go run ./cmd/read-secret id-token: write lets the job request an OIDC identity token. It does not give it permission to modify your infrastructure. The Go helper obtains that identity and exchanges it with Trust; no CERTLOCKER_TOKEN secret is required in this workflow. See GitHub's OIDC configuration guide for the permission and environment-protection model.
If you bind a principal to sub, use the actual subject format issued for your repository. GitHub documents different default formats for newer repositories; copying a subject from an old example can produce a correct-looking policy that never matches. Check the claims without placing the JWT in a public log. GitHub's identity-token documentation explains the claims and current subject-format distinction.
Test a denied case too: a different repository or a claim that your principal does not allow should fail before it reads the secret. A successful read demonstrates connectivity. A rejected identity demonstrates that the intended boundary is doing something.
The sample uses explicit GitHub exchange and supplies no static fallback. The SDK builders can retain a supplied API token if ambient exchange fails, which may be useful for a deliberate fallback policy. Decide whether you want that behavior; do not add a fallback accidentally while trying to prove OIDC works.
Enroll a build machine that has no cloud identity
A laptop, a bare-metal build host or a VPS may not have a cloud metadata identity. Device OIDC covers that case with a keypair. It removes the need to keep a reusable Trust API token on the machine, but the device's private signing key remains a credential that needs protection.
- Create an OIDC principal with provider Device.
- Choose a stable device ID, explicit allowed groups and permitted token types.
- Generate the Ed25519 keypair in the enrollment UI, or supply the supported public key from your own enrollment process.
- Keep the private key on the device. The SDK expects an unencrypted PKCS#8 Ed25519 PEM; an OpenSSH private-key file is a different format.
- If you set a key ID during enrollment, configure that same key ID in the client.
Trust stores the public key. The device signs a short-lived JWT locally, and Gateway verifies it against the enrolled principal. The helpers default to a five-minute device JWT. That is the assertion lifetime, not a promise that every exchanged Trust token lasts five minutes.
Using the same Rust project, replace src/main.rs with this device-authenticated version:
use std::{env, error::Error, path::PathBuf, time::Duration};
use trust_client::{oidc, TrustClient, TrustService};
fn run() -> Result<(), Box<dyn Error>> {
let url = env::var("TRUST_URL")?;
let config = oidc::DeviceOidcConfig::builder()
.audience(env::var("TRUST_OIDC_AUDIENCE")?)
.device_id(env::var("TRUST_OIDC_DEVICE_ID")?)
.private_key_path(PathBuf::from(env::var("TRUST_OIDC_DEVICE_PRIVATE_KEY")?))
.key_id(env::var("TRUST_OIDC_DEVICE_KEY_ID").unwrap_or_default())
.build()?;
let exchanged = oidc::from_device(
&url, &config, Some(trust_client::model::TokenType::Secret),
)?;
let service = TrustService::from_builder(
TrustClient::builder()
.url(&url)
.account_id(exchanged.account_id.ok_or("Missing exchange account")?)
.api_token(exchanged.token_id)
.timeout(Duration::from_secs(30)),
)?;
let password = service.get_secret_as_password(&env::var("TRUST_SECRET_ID")?)?;
if password.password.is_empty() {
return Err("Expected a non-empty password".into());
}
println!("Device exchange and secret retrieval succeeded");
Ok(())
}
fn main() {
if run().is_err() {
eprintln!("Device identity exchange or secret fetch failed");
std::process::exit(1);
}
} export TRUST_URL=https://trust.certlocker.io
export TRUST_OIDC_AUDIENCE='https://trust.certlocker.io/oidc/<your-account-id>'
export TRUST_OIDC_DEVICE_ID=build-box-1
export TRUST_OIDC_DEVICE_PRIVATE_KEY="$HOME/.trust/device.pem"
# If you enrolled a key id, supply the exact same id:
export TRUST_OIDC_DEVICE_KEY_ID=build-box-1-key
export TRUST_SECRET_ID='<password-secret-id>'
cargo run --locked Keep the key readable only by the account running the workload; on Unix, a private directory and mode 0600 on the key are a sensible starting point. Do not commit it or copy it into a container image. The program reports success without printing the private key, signed JWT, exchanged token or retrieved password.
The Go client has the equivalent FromDeviceOIDC and ExchangeDeviceOIDC helpers. Both languages also accept an identity token acquired by another trusted component.
Plan for expiry and suspension
These SDKs exchange identity while constructing the client or when you explicitly call an exchange helper. They do not implement a background token-refresh loop. Short jobs can exchange at startup; longer-running applications need to observe the returned expiry, acquire fresh identity and replace the client deliberately.
Suspending a principal prevents further exchanges. Do not assume that action immediately invalidates every previously issued token: treat an existing token's expiry and revocation as a separate part of incident handling. The live device test checked rejection during suspension and successful exchange after resume.
Retrieve the certificate your service should load
Once the identity path works, certificate retrieval uses the same SDK with the appropriate token. There is one easy mistake here: Gateway certificate operations take a CERTIFICATE access-token ID, not a certificate record ID. The CLI automation example earlier uses a record ID. They are different API contracts.
Configure a Go service with a certificate-bound token, obtained directly or through a principal allowed to mint CERTIFICATE. Inside that program:
// Use a client configured with a CERTIFICATE access token.
// An empty ID uses that configured token.
certificate, err := service.GetCertificate(ctx, "")
if err != nil {
log.Fatal("Certificate metadata fetch failed")
}
leaf, err := service.FetchCertificate(ctx, "")
if err != nil {
log.Fatal("Certificate PEM fetch failed")
}
// Inspect metadata or pass leaf to your TLS configuration.
// FetchCertificateBundle also returns private-key material.
_ = certificate
_ = leaf In Rust, use get_assigned_certificate() and fetch_assigned_certificate() on the service. The bundle methods return certificate and private-key material, so feed the result to the intended TLS consumer or write it with suitable permissions. Do not use a debug print to prove that the download worked.
Fetching a PEM does not reload Nginx or change an IIS binding. If your deployment writes files, validate them, activate the service configuration and probe the endpoint afterward. The certificate Automations walkthrough covers those service-specific steps.
Resolve deployment secrets where the playbook runs
The Ansible collection keeps references in your inventory and resolves values on the controller. Managed hosts do not need the Trust token unless your playbook deliberately gives it to them.
Create requirements.yml with the reviewed collection revision:
collections:
- name: https://github.com/certlocker-io/ansible-certlocker.git
type: git
version: 8c20ee58f42abe6efc80417bee5512377e96c776 ansible-galaxy collection install -r requirements.yml
export CERTLOCKER_API_URL=https://trust.certlocker.io/rest
export CERTLOCKER_API_MODE=account
export CERTLOCKER_ACCOUNT_ID='<your-account-id>'
# Inject CERTLOCKER_TOKEN securely on this controller.
ansible-playbook -i inventory.ini deploy.yml Notice the URL difference: Ansible's CERTLOCKER_API_URL includes /rest. The Go and Rust base URLs omit it. Set the account and route mode explicitly for the deployment you are testing.
For example, store the application's complete configuration as a CONFIGURATION secret named ansible.prod.app.runtime_config. Create the destination directory on your target first. Save this playbook as deploy.yml, with an app group in your inventory:
- name: Deliver the application's configuration
hosts: app
become: true
gather_facts: false
tasks:
- name: Write configuration fetched from Trust
ansible.builtin.copy:
content: >-
{{ lookup('certlocker.secrets.secret',
name='ansible.prod.app.runtime_config') }}
dest: /etc/myapp/runtime.conf
owner: root
group: root
mode: '0600'
no_log: true
diff: false no_log keeps the value out of task output; diff: false prevents a configuration diff from revealing it. The final file still exists on the target because the application needs it. Add the reload or restart handler appropriate to your service after validating the configuration.
The collection also provides a PEM-key lookup and a module for creating secrets. Repeating a creation with matching metadata and contents reports changed: false. Updating a differing existing secret needs a deployment-provided modify path; there is no assumed default Gateway modify route. Use the management CLI when an operator needs an Edge secret update.
Ansible does not acquire or refresh OIDC identity itself. A separate trusted launcher can exchange identity, then pass the returned token and account into the playbook process. Keep that handoff in memory, suppress credential output and ensure the run fits within the token lifetime. A lookup plugin cannot rescue a deployment that outlives its authentication.
For migration from existing inventory files, the earlier Ansible Vault migration article explains stable names and reference files. The current collection README is the authority for account-route configuration.
Move an existing deployment without guessing its API
The clients support legacy Gateway routes as well as the account-scoped routes introduced by this release. Choose the mode intentionally. The SDKs default to account routes; use LegacyAPI(true) in Go or legacy_api(true) in Rust for a legacy installation.
Ansible supports CERTLOCKER_API_MODE=legacy or account. Its default auto chooses account routes when an account ID is configured, and legacy routes otherwise. It is a configuration choice, not a network probe. The deployment helpers use the same mode names.
A denied request does not trigger a retry against a different route family. That matters when checking tenant isolation: the account in the path must match the token's account, and changing a URL must not become an escape route around a denial.
| Symptom | Check first |
|---|---|
| CLI login fails before opening a browser | The deployment's CLI authorize/token endpoints, UI compatibility and proxy routing. |
| Gateway returns 404 | The configured legacy/account route mode and whether the base URL has the correct /rest handling. |
| 401 or 403 | Token expiry, token type, account, group access and any required access-code step-up. Do not switch tenants to bypass it. |
| OIDC exchange denied | Issuer, audience, exact claims, principal status and allowed mint type; for devices, the key and key ID too. |
| Certificate fetch fails with an inventory ID | Use the certificate access-token ID or the configured assigned token. |
| Long-running job stops reading | The exchanged Trust token's expiry and whether your application reacquires identity. |
What we tested, and what remains deployment-specific
The public repositories contain a sanitized 4 November live dev verification record. That run exercised real browser 2FA and CLI PKCE login; token and secret creation, reads and updates; account-scoped Gateway access and cross-account denial; the actual Go and Rust smoke executables; and Ansible creation, idempotent repeat and lookup.
The device OIDC run enrolled a key, exchanged a real signed assertion and used the resulting token. It checked rejection for the wrong key, key ID, claims, audience, expiry and mint type, then exercised modify, suspend and resume. Certificate checks covered metadata, the public PEM and private-key bundle, including cross-account bundle denial. Test fixtures were revoked afterward.
On 5 November, the local CLI suite passed 88 tests, the Go SDK passed 23, the Rust suite passed 32, and the Ansible suite passed 32. Go vet, Rust Clippy and the CLI executable smoke also passed. The smoke uses a local HTTP fixture; it checks the compiled CLI's login callback, credential permissions, asset lifecycle and logout without claiming a fresh live human login.
GitHub Actions, GCP, Azure and Azure DevOps exchange helpers have implementation and test coverage. The device dev run is not evidence of a live exchange with every public cloud provider. Validate the provider, claim policy and network path you will actually use. CLI cross-builds for other operating systems likewise do not replace running that binary on your target OS.
Start with one secret and one identity
Pick a small workflow: one password read from a Go service, one Rust build host, or one configuration lookup in Ansible. Check that the intended identity can read it, that another identity cannot, and that your output contains no credential material. Then add certificate delivery, more environments or longer-running jobs once that first path is understood.
The source, installation instructions and verification records are here:
- Trust CLI — Browser sign-in and management commands.
- Go SDK — Gateway reads and OIDC exchange.
- Rust SDK — Gateway reads and OIDC exchange.
- Ansible collection — Runtime lookups and secret creation.
For the wider picture, see the developer integrations and MCP access for AI agents. If you want to walk through your own deployment pattern, book a technical walkthrough. Bring the job that currently has a credential copied into three places.