01Documentation
Credential Sources
Credential sources store the secret material that later gets projected into outbound auth flows.
Sources are created per team in a region and then referenced by sourceRef
from sandbox or template network policy in that region. The same source name
in another team or region is a separate object.
The API surface is:
| Method | Path | Purpose |
|---|---|---|
GET | /api/v1/credential-sources | List sources |
POST | /api/v1/credential-sources | Create a source |
GET | /api/v1/credential-sources/{name} | Get source metadata |
PUT | /api/v1/credential-sources/{name} | Replace a source |
DELETE | /api/v1/credential-sources/{name} | Delete a source |
POST creates a new source and returns 409 Conflict when the name already exists. PUT rotates an existing source and returns 404 Not Found when the name does not exist.
Source specs are write-only. Read APIs return metadata such as name, resolverKind, currentVersion, and timestamps, but not the raw secret values.
CLI examples assume you already ran s0 auth login and selected a current team with s0 team use <team-id>.
Resolver Kinds#
| Resolver kind | Key spec fields | Typical use |
|---|---|---|
static_headers | spec.staticHeaders.values | Bearer tokens and header fragments |
static_tls_client_certificate | spec.staticTLSClientCertificate.certificatePem, privateKeyPem, optional caPem | mTLS client authentication |
static_username_password | spec.staticUsernamePassword.username, password | Username/password based outbound auth |
static_ssh_private_key | spec.staticSSHPrivateKey.privateKeyPem, optional passphrase | SSH transparent proxy upstream authentication |
Resolver kind is immutable for a source name. Rotate the secret material within the same resolver kind. To change from one resolver kind to another, create a new source and update the affected bindings to reference that new source.
The current CLI file parser accepts the first three resolver kinds in the
table. Use an SDK or raw HTTP for static_ssh_private_key; passing it to
s0 credential create or update is currently rejected.
Storage Backends#
Credential source storage is selected by the platform, not by the credential source API caller.
Sandbox0 supports encrypted PostgreSQL storage and Vault-compatible storage backends. API users keep sending the same write-only spec in both modes. Sandbox0 Cloud manages backend configuration. See Deployment Options for current service availability.
Create A Source#
/api/v1/credential-sources
gogithubToken := os.Getenv("GITHUB_TOKEN") if githubToken == "" { log.Fatal("GITHUB_TOKEN is required") } source, err := client.CreateCredentialSource(ctx, apispec.CredentialSourceWriteRequest{ Name: "github-source", ResolverKind: apispec.CredentialSourceResolverKindStaticHeaders, Spec: apispec.CredentialSourceWriteSpec{ StaticHeaders: apispec.NewOptStaticHeadersSourceSpec(apispec.StaticHeadersSourceSpec{ Values: apispec.NewOptStaticHeadersSourceSpecValues( apispec.StaticHeadersSourceSpecValues{ "token": githubToken, }, ), }), }, }) if err != nil { log.Fatal(err) } fmt.Println(source.Name)
List Sources#
/api/v1/credential-sources
Read APIs return metadata only. Use list to discover source names, resolver kinds, status, and current versions.
gosources, err := client.ListCredentialSources(ctx) if err != nil { log.Fatal(err) } for _, source := range sources { fmt.Printf("- %s (%s) version=%d\n", source.Name, source.ResolverKind, source.CurrentVersion.Or(0)) }
Get Source Metadata#
/api/v1/credential-sources/{name}
Use get when you know the source name and want the current metadata record without listing everything.
gosource, err := client.GetCredentialSource(ctx, "github-source") if err != nil { log.Fatal(err) } fmt.Printf("%s version=%d status=%s\n", source.Name, source.CurrentVersion.Or(0), source.Status.Or(""))
Update Or Rotate A Source#
Use PUT /api/v1/credential-sources/{name} to replace the source contents while keeping the same source name and resolver kind. Existing bindings continue to point at that source name.
Sources are reusable. Rotate the source once, then keep bindings and credential
rules stable by continuing to reference the same sourceRef. Rotation advances
current bindings and invalidates ctld's resolved-material cache. Pending
network updates retain the source identity and resolve the current version
when they publish, so a concurrent update does not pin an obsolete version.
If invalidation delivery is interrupted by a restart or notification outage,
cache expiry is the fallback for subsequent credential resolution. The default
resolved-material TTL is 5 minutes, or the binding's cachePolicy.ttl when set.
Rotation does not disconnect an already authenticated SSH, TLS, or other
long-lived upstream session; reconnect when authentication must use the new
material.
/api/v1/credential-sources/{name}
gorotatedToken := os.Getenv("ROTATED_GITHUB_TOKEN") if rotatedToken == "" { log.Fatal("ROTATED_GITHUB_TOKEN is required") } source, err := client.UpdateCredentialSource(ctx, "github-source", apispec.CredentialSourceWriteRequest{ Name: "github-source", ResolverKind: apispec.CredentialSourceResolverKindStaticHeaders, Spec: apispec.CredentialSourceWriteSpec{ StaticHeaders: apispec.NewOptStaticHeadersSourceSpec(apispec.StaticHeadersSourceSpec{ Values: apispec.NewOptStaticHeadersSourceSpecValues( apispec.StaticHeadersSourceSpecValues{ "token": rotatedToken, }, ), }), }, }) if err != nil { log.Fatal(err) } fmt.Println(source.Name, source.CurrentVersion.Or(0))
Delete A Source#
/api/v1/credential-sources/{name}
Current sandbox bindings and pending Nomad network mutations prevent source
deletion with 409 Conflict. Canceling a pending update releases its source
pins; deleting a sandbox releases current bindings only after physical runtime
cleanup reaches the durable claim-cleanup boundary.
Template sourceRef strings do not themselves prevent deletion. Remove or
replace those references before deleting the source, or later claims using
that template can fail when they resolve the missing source. Deleting an
already absent source succeeds.
go_, err := client.DeleteCredentialSource(ctx, "github-source") if err != nil { log.Fatal(err) } fmt.Println("deleted github-source")