astrid-runtime/astrid · error

OS CSPRNG unavailable while generating default keypair name

Error message

OS CSPRNG unavailable while generating default keypair name

What it means

default_name generates a random key-<hex> name using the OS CSPRNG via getrandom (SysRng.try_fill_bytes). If the operating system randomness source is unavailable, the function panics with this expect message instead of returning a keypair name. The randomness is considered a hard requirement for key generation.

Solutions

  1. Fix the environment so getrandom works (restore /dev/urandom, relax seccomp rules blocking getrandom(2))
  2. Pass an explicit keypair name so default_name is never invoked
  3. Retry after the system RNG is available (e.g. after boot completes)
  4. Report/upgrade if running on a platform whose RNG syscall is unsupported

Example fix

// before
astrid keypair new
// after
astrid keypair new --name my-key
Defensive patterns

Strategy: fallback

Validate before calling

// probe RNG availability before invoking keypair commands
let mut b = [0u8; 1];
getrandom::getrandom(&mut b).map_err(|_| "OS CSPRNG unavailable")?;

Try / catch

// the library panics, so guard the entry point
let out = Command::new("astrid").args(["keypair", "new"]).output()?;
if !out.status.success()
    && String::from_utf8_lossy(&out.stderr).contains("OS CSPRNG unavailable") {
    eprintln!("RNG unavailable; pass an explicit --name or fix entropy");
}

Prevention

When it happens

Trigger: Calling commands that need an auto-generated keypair name (e.g. astrid keypair new without --name) on a system where /dev/urandom or getrandom(2) fails — exhausted entropy blocking, seccomp/container policies blocking getrandom, or a broken /dev/urandom.

Common situations: Hardened containers/jails that seccomp-filter getrandom; early-boot environments before the RNG is seeded; chroots missing /dev/urandom; VMs with unusual entropy configuration.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/25b762b4bedaf5cd. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-cli/src/commands/keypair.rs:600

            name.len()
        );
    }
    // Lowercase letters, digits, dash. Same posture as principal ids;
    // also avoids path-separator / shell-meta surprises.
    if !name
        .chars()
        .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
    {
        bail!("keypair name {name:?} contains invalid chars; only a-z, 0-9, '-' are allowed");
    }
    Ok(())
}

fn default_name() -> String {
    let mut bytes = [0u8; 4];
    SysRng
        .try_fill_bytes(&mut bytes)
        .expect("OS CSPRNG unavailable while generating default keypair name");
    format!("key-{}", hex::encode(bytes))
}

fn fingerprint_pubkey(hex_pub: &str) -> Result<String> {
    PublicKeyFingerprint::from_ed25519_hex(hex_pub)
        .map(PublicKeyFingerprint::into_inner)
        .map_err(|e| anyhow::anyhow!("fingerprint Ed25519 public key: {e}"))
}

/// Convert a 64-char hex ed25519 public key into the `ed25519:<base64>`
/// wire form that `[distro.signing].pubkey`, `astrid distro seal`, and
/// the distro trust store consume. Reuses `astrid-crypto`'s encoder so
/// the base64 variant matches the verifier byte-for-byte.
fn pubkey_hex_to_wire(pub_hex: &str) -> Result<String> {
    let pk = astrid_crypto::PublicKey::from_hex(pub_hex.trim())
        .map_err(|e| anyhow::anyhow!("decode public key hex: {e}"))?;
    Ok(format!("ed25519:{}", pk.to_base64()))
}

View on GitHub (pinned to affd8760f4)