clockworklabs/SpacetimeDB · critical

Unable to read public key for JWT token verification

Error message

Unable to read public key for JWT token verification

What it means

At startup the node reads its EC key pair for JWT signing/verification from the keychain files. The pair must both exist or both be absent (in which case a fresh pair is generated and written); if the private key file is readable but the public key file is missing or unreadable, startup aborts with this error before serving any traffic.

Source

Thrown at crates/core/src/auth/mod.rs:65

// Get the key pair if the given files exist. If they don't, create them.
// If only one of the files exists, return an error.
pub fn get_or_create_keys(certs: &CertificateAuthority) -> anyhow::Result<JwtKeys> {
    let public_key_path = &certs.jwt_pub_key_path;
    let private_key_path = &certs.jwt_priv_key_path;

    let public_key_bytes = public_key_path.read().ok();
    let private_key_bytes = private_key_path.read().ok();

    // If both keys are unspecified, create them
    let key_pair = match (public_key_bytes, private_key_bytes) {
        (Some(pub_), Some(priv_)) => EcKeyPair::new(pub_, priv_),
        (None, None) => {
            let keys = EcKeyPair::generate()?;
            keys.write_to_files(public_key_path, private_key_path)?;
            keys
        }
        (None, Some(_)) => anyhow::bail!("Unable to read public key for JWT token verification"),
        (Some(_), None) => anyhow::bail!("Unable to read private key for JWT token signing"),
    };

    key_pair.try_into()
}

// An Ec key pair in pem format.
pub struct EcKeyPair {
    pub public_key_bytes: Vec<u8>,
    pub private_key_bytes: Vec<u8>,
}

impl TryFrom<EcKeyPair> for JwtKeys {
    type Error = anyhow::Error;
    fn try_from(pair: EcKeyPair) -> anyhow::Result<Self> {
        JwtKeys::new(pair.public_key_bytes, pair.private_key_bytes)
    }
}

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Restore the matching public key from backup so the pair is consistent again.
  2. Or delete BOTH key files and restart — the node generates a fresh pair; note all previously issued tokens become invalid and users must re-login.
  3. Check file permissions on both key files in the keychain directory.
  4. Never regenerate only one side — EC keys must be a matched pair.
Defensive patterns

Strategy: validation

Validate before calling

#!/usr/bin/env bash
# pre-start: key pair must be all-present or all-absent
KEYDIR="$STDB_KEYCHAIN_DIR"
pub="$KEYDIR/public.pem"; priv="$KEYDIR/private.pem"   # adjust to your keychain layout
if [ -f "$priv" ] && [ ! -f "$pub" ]; then
  echo "JWT key pair incomplete — restore public key or delete BOTH keys to regenerate" >&2; exit 1;
fi
exec spacetime start

Try / catch

Wrap server startup and abort the deployment (fail fast) when stderr contains 'Unable to read public key'; alert the operator instead of looping restarts.

Prevention

When it happens

Trigger: Deleting, moving, or truncating only the public key file in the keychain directory; a partial backup restore that copied the private key but not the public key; permission or filesystem damage affecting just one of the two files.

Common situations: Hand-cleaning or rotating key directories; backup scripts that glob only private keys; copying keychain dirs between hosts incompletely; container volumes where one key file was overwritten.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/701955dd1c213a5f. Report an issue: GitHub.