astrid-runtime/astrid · error

gateway revocation namespace exceeds entry cap

Error message

gateway revocation namespace exceeds entry cap

What it means

Bounds guard in load_from_store: the number of principal/device revocation keys listed from the fixed control namespace exceeded the entry cap, so startup hydration aborts rather than trusting an oversized revocation set.

Solutions

  1. Audit the revocation namespace for runaway key growth (e.g. a bug writing unbounded keys)
  2. Prune expired/rotated revocation entries below the cap
  3. Raise the cap only after confirming the growth is legitimate
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at crates/astrid-gateway/src/revocations.rs:310 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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

Appendix: source

Thrown at crates/astrid-gateway/src/revocations.rs:310

    };
    Ok(publish_device_epoch(revoked_key_ids, key_id, durable_epoch))
}

/// Load all durable principal and device epochs from the fixed control
/// namespace. Every key/value is bounded and validated before publication.
pub async fn load_from_store(
    store: &dyn KvStore,
) -> anyhow::Result<(HashMap<PrincipalId, u64>, HashMap<String, u64>)> {
    let principal_keys = store
        .list_keys_with_prefix(REVOCATION_NAMESPACE, PRINCIPAL_PREFIX)
        .await
        .map_err(|error| anyhow::anyhow!("list principal revocations: {error}"))?;
    let device_keys = store
        .list_keys_with_prefix(REVOCATION_NAMESPACE, DEVICE_PREFIX)
        .await
        .map_err(|error| anyhow::anyhow!("list device revocations: {error}"))?;
    if principal_keys.len().saturating_add(device_keys.len()) > MAX_REVOCATION_ENTRIES {
        anyhow::bail!("gateway revocation namespace exceeds entry cap");
    }
    let mut principals = HashMap::with_capacity(principal_keys.len());
    for key in principal_keys {
        let alias = key
            .strip_prefix(PRINCIPAL_PREFIX)
            .filter(|alias| !alias.is_empty())
            .ok_or_else(|| anyhow::anyhow!("invalid principal revocation key {key:?}"))?;
        let principal = PrincipalId::new(alias).map_err(|error| {
            anyhow::anyhow!("invalid principal revocation key {key:?}: {error}")
        })?;
        let value = store
            .get(REVOCATION_NAMESPACE, &key)
            .await
            .map_err(|error| anyhow::anyhow!("read principal revocation {key:?}: {error}"))?
            .ok_or_else(|| {
                anyhow::anyhow!("principal revocation {key:?} disappeared during load")
            })?;
        principals.insert(principal, decode_epoch(&value, &key)?);

View on GitHub (pinned to affd8760f4)