ServicesWorkloads

Sign in with a private key

Let a workload sign in with a key pair, and replace a legacy client secret with a private key.

Available

If your job runs somewhere without its own identity tokens, such as an on-premises server, give its workload a key pair. You keep the private key in your secret manager, and Spiral stores only the public key, so nothing that can sign in as your job ever leaves your hands. For each sign-in, the job signs a short-lived assertion with the private key.

If the job runs on GitHub Actions, GCP, Kubernetes, AWS, or Modal, use a platform identity instead: there is no key to store or rotate.

Prerequisites

  • You created a workload and granted it a Project role.
  • You have the Project admin role to manage the workload's keys. A key lets its holder act as the workload, with all of the workload's grants.
  • You have a secret manager for the job's environment.

Add a key pair

Create the key pair on a computer that you control:

spiral workloads keys add work_01GHI

The command creates a P-256 key pair and registers the public key with the workload. It writes the private key, as a JWK, to work_01GHI.jwk. Only your user can read the file. Move the private key to your secret manager, then delete the file. To write the private key to standard output instead, add --output -.

Add --kid deploy-1 to give the key a descriptive ID. Studio lists this value in the workload's Credentials section. Keys have a key ID (kid) rather than a separate display name.

Set the workload ID and the private key in the job's environment:

export SPIRAL_CLIENT_ID=work_01GHI
export SPIRAL_PRIVATE_KEY="$(cat work_01GHI.jwk)"

In Studio, open the workload and select Register public key, then open Generate key pair and select Generate key pair & download private key. Key generation runs entirely in your browser. Studio immediately starts a download of the private JWK and fills the public key field. Store the downloaded private key securely, then select Register public key to submit only its public half. Studio does not save the private key in browser storage.

When you set up your own Workers from a Project's Tasks page in Studio, Studio also creates the workload's key pair in your browser and registers only the public key.

To register an existing SSH public key, open Import public key and paste its .pub contents into Public key, drop the file there, or select Choose file…. Studio accepts RSA, ECDSA (P-256, P-384, P-521), and Ed25519 SSH keys, as well as public JWK files, up to 16 KiB. It converts SSH keys to JWKs in your browser and previews the public key before you select Register public key. SSH certificates and hardware security-key formats are unsupported.

SSH keys default to their SHA-256 fingerprint as the key ID. Enter a Key ID to choose a descriptive ID instead. A JWK keeps its existing kid, or takes the ID you enter if it has none. Use that same ID in the JWT header when signing assertions. SDK sign-in requires a private JWK with the matching kid; the SSH private-key file stays on your computer.

Generate a key pair with ssh-keygen

Generate a P-256 key pair in your terminal. The private key uses PEM format without a passphrase so the CLI can read it:

ssh-keygen -t ecdsa -b 256 -m PEM -N '' -f workload-private.pem

Drop workload-private.pem.pub into Studio's public-key form or paste its contents, then select Register public key. Keep workload-private.pem private.

Alternatively, register the key with the CLI, which converts it locally and sends only the public half. The output file holds the private JWK that the SDK accepts:

spiral workloads keys add work_01GHI --key workload-private.pem --output workload-private.jwk

Move the private files into the job's secret manager.

Register a key that you already have

Pass the key file with --key. The file can hold a public or a private RSA, EC, or Ed25519 key, as a JWK or in PEM. Spiral receives only the public key. SPIRAL_PRIVATE_KEY takes a JWK that names the kid of the key. For a private key in PEM, add --output to write that JWK:

spiral workloads keys add work_01GHI --key workload.pem --output workload.jwk

Over HTTP, register a public key as a JWK that names its kid. In this example, public-key.jwk holds that JWK:

curl -X POST https://api.spiraldb.dev/api/workloads/work_01GHI/keys \
  -H "Authorization: Bearer $SPIRAL_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"jwk\": $(cat public-key.jwk)}"

Verify and rotate keys

List the workload's keys:

spiral workloads keys ls work_01GHI

A workload can have more than one key. To rotate a key, add the new key, deploy the new private key, and then remove the old key with spiral workloads keys rm. Assertions signed with a removed key no longer sign the workload in.

Exchange a key assertion over HTTP

A client without the SDK signs a client assertion with the private key (private_key_jwt client authentication, RFC 7523, section 3). The workload ID is also the workload's OAuth client ID. Set these claims:

ClaimValue
iss and subThe workload ID.
audhttps://auth.spiraldb.com/oauth/token
jtiA unique value for each assertion.
iat and expThe issue time, and an expiry a few minutes later.

Put the kid of the key in the JWT header. Then send the assertion with the client_credentials grant:

curl -X POST https://auth.spiraldb.com/oauth/token \
  -d grant_type=client_credentials \
  -d client_id=work_01GHI \
  -d client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
  -d client_assertion="$CLIENT_ASSERTION"

The server metadata at https://auth.spiraldb.com/.well-known/oauth-authorization-server lists the accepted signing algorithms.

Replace a client secret

Spiral no longer issues client secrets. An existing secret, set as SPIRAL_CLIENT_ID=spw_... and SPIRAL_CLIENT_SECRET, still works. Unlike a private key, though, a secret is something both you and Spiral hold a copy of, and it does not expire. Replace it with a private key:

  1. Find the workload that owns the secret. This command lists the Project's workloads:

    spiral workloads ls demo
  2. Add a key pair to the workload:

    spiral workloads keys add work_01GHI
  3. In the environment of the workload, replace the client secret with the private key:

    export SPIRAL_CLIENT_ID=work_01GHI
    export SPIRAL_PRIVATE_KEY="$(cat work_01GHI.jwk)"
    unset SPIRAL_CLIENT_SECRET
  4. Run the workload, and make sure that it signs in.

  5. Revoke the client secret:

    spiral workloads revoke-secret spw_01MNO

Troubleshooting

ProblemCauseFix
the private key JWK must name the kid it was registered withSPIRAL_PRIVATE_KEY has no kid.Write the JWK again with spiral workloads keys add --output.
The token endpoint returns invalid_clientThe kid in the assertion header names no key of the workload, or the key was removed.Compare the kid with spiral workloads keys ls.

Where next: the Workloads API lists the key operations.

Found a problem? Edit this page or report an issue.

On this page