block/buzz · error

invalid repo identifier: must be [a-zA-Z0-9._-]{1,64}, no le

Error message

invalid repo identifier: must be [a-zA-Z0-9._-]{1,64}, no leading dots, no '..'

What it means

After extracting the `d` tag from a kind:30617 announcement, the relay validates the repo identifier with validate_repo_id. This error means the identifier contains characters outside [a-zA-Z0-9._-], exceeds 64 characters, starts with a dot, or contains the '..' sequence — any of which could enable path traversal or ambiguous repo naming. The announcement is rejected.

Source

Thrown at crates/buzz-relay/src/handlers/side_effects.rs:2571

pub(crate) struct GitRepoAnnouncementGate {
    pub(crate) reached: tokio::sync::Notify,
    pub(crate) resume: tokio::sync::Notify,
}

pub(crate) async fn handle_git_repo_announcement_inner(
    tenant: &TenantContext,
    event: &Event,
    state: &Arc<AppState>,
    hooks: &GitRepoAnnouncementHooks,
) -> anyhow::Result<()> {
    #[cfg(not(test))]
    let _ = hooks;
    // Extract repo identifier from d tag (required for NIP-33 parameterized replaceable events).
    let repo_id =
        extract_tag_value(event, "d").ok_or_else(|| anyhow::anyhow!("kind:30617 missing d tag"))?;

    if !validate_repo_id(&repo_id) {
        return Err(anyhow::anyhow!(
            "invalid repo identifier: must be [a-zA-Z0-9._-]{{1,64}}, no leading dots, no '..'"
        ));
    }

    let owner_hex = hex::encode(event.pubkey.to_bytes());

    // The relay holds no persistent per-repo disk state: runtime reads and
    // writes hydrate an ephemeral bare repo from object storage per request
    // (see `api::git::hydrate`). Announce only (1) reserves the repo name and
    // (2) seeds the empty-manifest pointer that makes the repo clone-able.
    //
    // The `git_repo_names` table (Postgres) is the relay's name registry,
    // keyed `(community_id, repo_id)`. It serves three jobs at once inside the
    // server-resolved community boundary:
    //   - uniqueness: `INSERT … ON CONFLICT DO NOTHING` is atomic, so
    //     concurrent kind:30617 events for the same community/name can't both
    //     claim it (TOCTOU-free — the DB PK is the race guard);
    //   - idempotent re-announce: a reservation owned by the same pubkey is an

View on GitHub (pinned to dad5a33865)

Solutions

  1. Rename the repo identifier to contain only [a-zA-Z0-9._-], 1-64 chars.
  2. Strip leading dots and collapse/remove any '..' sequence in the id before signing.
  3. Re-publish the kind:30617 event with the corrected `d` tag value.

Example fix

// before
[["d", "team/../etc"]]
// after
[["d", "team-etc"]]  // or "team.etc"
Defensive patterns

Strategy: validation

Validate before calling

fn valid_repo_id(id: &str) -> bool {
    !id.is_empty() && id.len() <= 64 && !id.starts_with('.')
        && !id.contains("..")
        && id.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '.'|'_'|'-'))
}

Try / catch

let repo_id = extract_tag_value(&event, "d").context("missing d tag")?;
if !valid_repo_id(repo_id) {
    return Err(anyhow!("rejecting announcement: invalid repo id {repo_id:?}"));
}

Prevention

When it happens

Trigger: Publishing a kind:30617 announcement whose `d` tag value is e.g. "my repo", "repo/name", ".hidden", "a..b", or a >64-char name.

Common situations: Repo names containing slashes or spaces copied from git remote URLs; users trying hierarchical repo ids like "team/project"; legacy repos with dots-leading names; automated scripts interpolating full URLs instead of bare repo names.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of block/buzz@dad5a33865 (2026-08-30). Data as JSON: /api/errors/545ac9e821087bbc. Report an issue: GitHub.