astrid-runtime/astrid · critical

OS CSPRNG unavailable while generating invite token

Error message

OS CSPRNG unavailable while generating invite token

What it means

Panic from `SysRng.try_fill_bytes(&mut bytes).expect("OS CSPRNG unavailable while generating invite token")` in `generate_token` (crates/astrid-kernel/src/invite/mod.rs:675). The library refuses to generate an invite token from anything but the OS cryptographically secure RNG (rand::rngs::SysRng); if the operating system's entropy source (/dev/urandom, getrandom) is unavailable, it panics rather than fall back to a weak source.

Solutions

  1. Restore OS entropy access: ensure /dev/urandom exists and getrandom(2) is permitted by seccomp/AppArmor/sandbox policy.
  2. If running in a container, add the device and adjust the syscall allowlist (e.g. docker run with proper /dev mounts).
  3. Retry at process start after entropy is initialized (wait for rngd / kernel crng ready).
  4. Replace expect with error propagation (try_fill_bytes returns Result) so callers can degrade gracefully instead of panicking.

Example fix

// before
SysRng.try_fill_bytes(&mut bytes).expect("OS CSPRNG unavailable while generating invite token");
// after
SysRng.try_fill_bytes(&mut bytes)
    .map_err(|e| InviteError::RngUnavailable(e))?;
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight: verify OS CSPRNG works before generating tokens
use rand::{TryRng, rngs::SysRng};
let mut probe = [0u8; 1];
let rng_ok = SysRng.try_fill_bytes(&mut probe).is_ok();

Try / catch

// wrap token generation so panics don't take down the request
let token = std::panic::catch_unwind(generate_token)
    .map_err(|_| InviteError::RngUnavailable)?;

Prevention

When it happens

Trigger: Calling `generate_token()` on a system where the OS CSPRNG fails: exhausted/fileless entropy setups, containers without /dev/urandom, seccomp/sandbox rules blocking getrandom(2), or extremely early boot before entropy initialization.

Common situations: Hardened containers with restricted syscalls, chroot/jail environments missing device nodes, custom Linux builds without getrandom, or VM snapshots resumed with RNG misconfiguration.

Related errors


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

Appendix: source

Thrown at crates/astrid-kernel/src/invite/mod.rs:675

struct PersistedFile {
    #[serde(default)]
    schema_version: u32,
    #[serde(default)]
    invite: Vec<Invite>,
}

/// Generate a typed token with a random URL-safe-base64 secret. Uses the OS CSPRNG.
///
/// # Panics
///
/// Panics if the OS CSPRNG is unavailable.
#[must_use]
pub fn generate_token() -> String {
    use rand::{TryRng, rngs::SysRng};
    let mut bytes = [0u8; TOKEN_RAW_LEN];
    SysRng
        .try_fill_bytes(&mut bytes)
        .expect("OS CSPRNG unavailable while generating invite token");
    format!(
        "{TOKEN_PREFIX}{}",
        base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(bytes)
    )
}

/// Derive a token identifier for storage and lookup.
#[must_use]
pub fn hash_token(token: &str) -> String {
    IdentifierHash::derive(TOKEN_HASH_CONTEXT, token.as_bytes()).to_prefixed_hex()
}

/// Constant-time hash comparison. Both inputs must be `blake3:<hex>`
/// identifiers. Returns `false` on any length mismatch
/// without leaking the position via short-circuit.
#[must_use]
pub fn ct_hash_eq(a: &str, b: &str) -> bool {
    if a.len() != b.len() {

View on GitHub (pinned to affd8760f4)