Node & cluster secrets
This article refers to SDK version v0.5.0. The current SDK version is N/A.
Overview
In the product, a Global Secret is a Barbara-managed, encrypted key-value pair: Wi-Fi pre-shared keys, API tokens injected into a workload, and similar values, stored per node (or per cluster) and distributed to every workload there. This is the same data behind the Secrets card on the Node Details page; see Application configuration types for how a Global Secret differs from App Config or Global Config.
Global Secret. A sensitive value an operator adds by hand from the Secrets card in Barbara Panel, like a Wi-Fi password or an API key, kept out of the app itself and distributed to the node or cluster instead.
Learn moreThe SDK's naming makes three distinct kinds of secret worth telling apart before you start:
- A Global Secret (this guide,
client.nodes/client.clusters*_global_secretsmethods) is Barbara-managed and shared by every workload on the node or cluster. - App Secrets are a Marketplace app's own secrets, scoped to a single deployed workload (see Application configuration types).
- Swarm Secrets are Docker-native objects a cluster app declares itself via its
docker-compose.yamlsecrets:section (see Swarm Secrets and the cluster-scope section below).
The SDK base64-encodes and decodes Global Secrets for you: pass plain strings in, get plain strings back out.
Every method in this guide takes a node_id (or cluster_id), the internal id you'd get from client.nodes.resolve(node_name) or client.clusters.get(cluster_id). See Getting started if you haven't resolved one yet.
Create a secret
client.nodes.create_global_secrets(node.id, {"wifi-psk": "s3cr3t"})
create_global_secrets(node_id, secrets) takes secrets as a plain Dict[str, str], mapping the secret's name to its plain-text value, the same two fields you'd fill into Name and Value when clicking the + icon on the Secrets card. Pass as many key-value pairs as you like in a single call; each becomes its own independent secret on the node, exactly as if you'd added them one at a time in Panel. The SDK base64-encodes both the name and the value before sending the request, matching Barbara's API convention, and returns None, so a subsequent list_global_secrets or get_global_secret call is how you confirm the write.
List and read secrets
secrets = client.nodes.list_global_secrets(node.id)
secret = client.nodes.get_global_secret(node.id, "<secret-id>")
list_global_secrets(node_id) returns a List[GlobalSecret]. Each GlobalSecret has:
| Field | Type | What it is |
|---|---|---|
id | str | The secret's internal identifier, generated by the API when the secret is created; this is the secret_id every other method in this guide expects |
name | str | The secret's name, decoded from base64 automatically |
raw | Dict[str, Any] | The full untyped API response for this secret |
get_global_secret(node_id, secret_id) fetches a single one by its id from the list above and returns the same GlobalSecret shape.
Secret values are write-only. What comes back from list_global_secrets or get_global_secret is a GlobalSecret with just an id and a name, never the value you set, the same restriction that masks values behind a visibility toggle for non-admin roles on the Global Secrets card. If you need to recreate a secret elsewhere, you have to know its value from your own records, not from reading it back through the API.
Delete a secret
client.nodes.delete_global_secret(node.id, "<secret-id>")
client.nodes.delete_all_global_secrets(node.id)
delete_global_secret(node_id, secret_id) takes the id from list_global_secrets, not the secret's name, passing a name here fails, since the API expects the internal identifier. delete_all_global_secrets(node_id) takes no further arguments and removes every Global Secret on the node in one call, the scripted equivalent of Delete all Secrets in the Secrets card's header menu.
Deleting a secret does not affect a workload that's already running: it keeps the in-memory value it started with, the same behavior documented for the Panel card. The missing secret only takes effect the next time the workload restarts or updates, at which point an app that depends on it may fail to start. Plan secret deletions around a restart, not independently of one.
Manage secrets at cluster scope
Clusters have their own, independent Global Secrets, same shape as node secrets, mirroring the Global Secrets card at the cluster level:
client.clusters.create_global_secrets(cluster.id, {"db-password": "s3cr3t"})
secrets = client.clusters.list_global_secrets(cluster.id)
client.clusters.delete_global_secret(cluster.id, "<secret-id>")
create_global_secrets, get_global_secret, delete_global_secret, and delete_all_global_secrets on client.clusters take the exact same parameter shapes as their client.nodes counterparts above, just with a cluster_id in place of a node_id. The one difference is list_global_secrets: at cluster scope, the API wraps the array in an object rather than returning it directly, {"secretList": [...], "lastUpdated": ...}, so the SDK's client.clusters.list_global_secrets(cluster_id) returns List[Dict[str, Any]] (the raw entries from secretList) rather than a List[GlobalSecret]. Read each entry's name/_id keys directly from the dict, the same fields the GlobalSecret dataclass would otherwise expose.
client.clusters.export_global_secrets(cluster_id) has no node equivalent: it returns every secret in one call instead of paging through list_global_secrets, matching the Download button on the cluster-level Global Secrets card that exports every secret as a .txt file in one click.
Swarm Secrets are separate
A cluster app can also declare its own Swarm Secrets through its docker-compose.yaml secrets: section, Docker-native objects that live and die with the app that owns them, encrypted by Docker Swarm itself rather than by Barbara. The SDK exposes only cleanup for these, no create/read, since they're declared by the app, not by your script:
client.clusters.delete_swarm_secret(cluster.id, "<swarm-secret-id>")
client.clusters.delete_all_swarm_secrets(cluster.id)
delete_swarm_secret(cluster_id, secret_id) and delete_all_swarm_secrets(cluster_id) mirror the delete action on the Swarm Secrets card: only a secret not referenced by any running service can be deleted, an attempt against one still in use is rejected by the API rather than silently ignored. Use these two methods for cleanup after removing the app that declared the secret; use the *_global_secrets methods above whenever you want Barbara itself to manage and distribute a secret. Docker Swarm configs follow the same cleanup-only pattern; see Clean up Swarm-native configs and secrets in the Clusters guide.
Summary
You can now create, list, and remove Global Secrets at both node and cluster scope, without ever touching base64 yourself, and you know when to reach for the separate Swarm Secrets cleanup methods instead.
Because secret values can't be read back, treat your own scripts or a secrets manager as the source of truth: the SDK is a write path, not a backup.
For the full method list, see client.nodes and client.clusters in the Reference. For registry credentials and volumes, the other two node-level resources with the same base64 convention, continue to Docker credentials & volumes.