repos bind/update permanently fails once a repo's last announcement is >15 min old

Open Beginner friendly
#7,541 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
rust
Domain
backend, cli

Research direction

Start with crates/buzz-cli/src/commands/repos.rs:147-154 and compare its timestamp handling with the drift check in crates/buzz-relay/src/handlers/ingest.rs:1976-1983. Reproduce the update after a repo announcement is over 15 minutes old, then verify that bind and protect updates succeed while preserving the observed-head ordering invariant.

Written by the indexing model from the issue text.

Description

Describe the bug
buzz repos bind (and any other repo-metadata update going through the same helper, e.g. repos protect) can permanently fail with:

relay error 400: invalid: event timestamp too far from server time

This is not a client-clock issue — it's a logic bug in how the CLI advances the repo announcement's created_at, combined with the relay's timestamp-drift check.

crates/buzz-cli/src/commands/repos.rs:147-154 computes the updated event's created_at as existing_repo.created_at + 1 (never wall-clock time), specifically to stop a delayed writer from clobbering a newer concurrent update:

// Advance only the observed head. Using wall-clock time here would let a
// delayed writer leapfrog an intervening update and silently erase metadata.
let next_created_at = existing
    .created_at
    .as_secs()
    .checked_add(1)
    ...

crates/buzz-relay/src/handlers/ingest.rs:1976-1983 rejects any incoming event whose created_at is more than MAX_TIMESTAMP_DRIFT_SECS = 900 (15 minutes) away from the relay's own clock:

const MAX_TIMESTAMP_DRIFT_SECS: i64 = 900; // ±15 minutes
let now = chrono::Utc::now().timestamp();
let event_ts = event.created_at.as_secs() as i64;
if (event_ts - now).abs() > MAX_TIMESTAMP_DRIFT_SECS {
    return Err(IngestError::Rejected(
        "invalid: event timestamp too far from server time".into(),
    ));
}

If a repo's most recent announcement is already older than 15 minutes, existing_created_at + 1 is also older than 15 minutes, so the relay rejects it every time. The repo is then permanently stuck: no bind/protect update can ever succeed again through this code path, regardless of who signs it or how many times it's retried.

Steps to reproduce

  1. Announce a repo with buzz repos create.
  2. Wait more than 15 minutes without touching it again.
  3. Run buzz repos bind --id <id> --channel <uuid> (or repos protect).
  4. See error: non-retryable relay error 400: invalid: event timestamp too far from server time.

Expected behavior
The update should succeed regardless of how old the repo's last announcement is, since the caller's request is well within the relay's acceptable clock drift.

Suggested fix: use max(now, existing_created_at + 1) instead of existing_created_at + 1 alone in repos.rs. This still satisfies the "advance past the observed head" invariant the comment describes, while keeping the timestamp within the relay's accepted drift window.

Version and platform

  • Buzz version: buzz-cli workspace 0.1.0 (commit 538e5e1, 2026-08-10)
  • OS: macOS 15.6 (24G84)

Logs / additional context
None beyond the inline snippets above; no screenshots (CLI-only repro).

Dominant language
Rust
Stars
33.7k
Forks
4.4k
Avg merge
1d 21h
Merged PRs (30d)
239

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from block/buzz

All issues in block/buzz

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.