astrid-runtime/astrid · error

invalid runtime key (replaces 'invalid signing key' in…

Error message

invalid runtime key (replaces 'invalid signing key' in wrapped keypair error: {error})

What it means

When loading or creating the node's runtime keypair, any error from astrid_crypto::load_or_generate_keypair is preserved but its message is rewritten: the first occurrence of 'invalid signing key' is replaced with 'invalid runtime key'. The io::Error keeps the original error kind and the amended message, so callers see context-appropriate wording for the runtime key file.

Solutions

  1. Delete the corrupt runtime key file and let the daemon regenerate it (note: this changes node identity)
  2. Restore the key file from backup, keeping correct private-file permissions
  3. Verify the file was produced by the same astrid_crypto version/format
  4. Check file permissions and ownership if the underlying error was access-related

Example fix

// before: corrupt key file at path
rm /var/lib/app/runtime.key
// after: daemon regenerates on next start
systemctl restart app-daemon   # load_or_generate_keypair creates a fresh keypair
Defensive patterns

Strategy: try-catch

Validate before calling

let md = std::fs::metadata(&key_path)?;
if md.len() == 0 || md.len() > 4096 { eprintln!("runtime key file size suspicious"); }
let md2 = std::fs::symlink_metadata(&key_path)?;
let mode = md2.permissions().mode();
if mode & 0o077 != 0 { eprintln!("runtime key too permissive"); }

Try / catch

match load_runtime_keypair(&path) {
    Err(e) if e.to_string().contains("invalid runtime key") => {
        // back up corrupt key, let daemon regenerate
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling the runtime-key setup routine (e.g. daemon startup that loads the keypair from disk) when the key file contains an invalid or corrupt signing key, or the underlying crypto loader fails for permission/format reasons.

Common situations: The runtime key file was truncated, hand-generated with the wrong key type, or created by a different/older crypto format; disk corruption; the file was swapped for an unrelated key.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at crates/astrid-kernel/src/lib.rs:4461

fn audit_mountpoint(_path: &Path) -> std::io::Result<bool> {
    Ok(false)
}

/// Load the runtime ed25519 signing key from disk, or generate and persist a new one.
///
/// The key file is 32 bytes of raw secret key material at `{keys_dir}/runtime.key`.
#[cfg(unix)]
fn load_or_generate_runtime_key(keys_dir: &Path) -> std::io::Result<KeyPair> {
    astrid_core::platform_fs::ensure_private_directory(keys_dir)?;
    let key_path = keys_dir.join("runtime.key");
    if key_path.exists() {
        astrid_core::platform_fs::validate_private_file(&key_path)?;
    }
    let keypair = astrid_crypto::load_or_generate_keypair(&key_path).map_err(|error| {
        let message = error
            .to_string()
            .replacen("invalid signing key", "invalid runtime key", 1);
        std::io::Error::new(error.kind(), message)
    })?;
    astrid_core::platform_fs::restrict_private_file(&key_path)?;
    Ok(keypair)
}

/// Spawns the persistent-daemon idle monitor.
///
/// Ephemeral shutdown is driven by reliable connection lifecycle accounting;
/// its never-connected fallback is armed by the daemon only after readiness.
/// Persistent mode remains idle-shutdown-free unless
/// `ASTRID_IDLE_TIMEOUT_SECS` is set.
/// Number of permanent internal event bus subscribers that are not client
/// connections: `KernelRouter` (`kernel.request.*`), `AdminRouter`
/// (`kernel.admin.*`), the synchronous `ConnectionTracker` (`client.*`),
/// `EventDispatcher` (all events), the bus activity monitor (all events,
/// storm diagnostics — see [`bus_monitor::spawn_bus_activity_monitor`]), and
/// the grant-on-first-use observer (`astrid.v1.approval` — see
/// [`grant_on_use::spawn_grant_on_use_handler`]).

View on GitHub (pinned to affd8760f4)