Skip to content

fix(cli): emit structured JSON warning when archive/unarchive owner-auth extraction fails - #4824

Open
wpfleger96 wants to merge 2 commits into
mainfrom
wpfleger/cli-archive-auth-diagnostics
Open

fix(cli): emit structured JSON warning when archive/unarchive owner-auth extraction fails#4824
wpfleger96 wants to merge 2 commits into
mainfrom
wpfleger/cli-archive-auth-diagnostics

Conversation

@wpfleger96

@wpfleger96 wpfleger96 commented Aug 5, 2026

Copy link
Copy Markdown
Member

emit structured JSON diagnostics when NIP-OA owner-auth extraction fails during buzz agents archive/unarchive

Problem

When owner-auth extraction returned None, the CLI silently sent a bare request. The relay replied with 400: missing auth tag and the caller had no way to know why extraction failed.

Solution

Extract resolve_auth_from_profile — a sync function that owns all three warning branches and the success path. resolve_auth reduces to: self-check → fetch kind:0 → delegate.

  • Four distinct diagnostics: no kind:0 profile / no tags array / classify_owner_auth_tag failure (typed AuthFailure enum: NoAuthTag, AmbiguousAuthTag, WrongArity, NonStringElement, InvalidOwnerHex, InvalidSigHex, OwnerMismatch)
  • JSON format: each fallback emits exactly one {"warning":"..."} line to stderr, matching the CLI's documented structured-stderr contract and the precedent in channels.rs:597
  • Relay-supplied values (target pubkey, actual owner pubkey) pass through serde_json serialization — no unescaped text
  • Admin bare path preserved: request is always sent after the warning; bare non-self requests are legitimate for relay admins
  • Self path unchanged: silent, no relay query

Boundary tests

Tests call resolve_auth_from_profile directly with &mut Vec<u8>. Each of the three production writeln! calls is covered: deleting any one fails at least one test. Success path asserts zero bytes written.

Changes

crates/buzz-cli/src/commands/agents.rs only:

  • AuthFailure enum with message() formatter
  • classify_owner_auth_tag returning Result<[String;4], AuthFailure>
  • extract_owner_auth_tag reduced to #[cfg(test)] .ok() wrapper
  • resolve_auth_from_profile sync helper (testable without BuzzClient)
  • resolve_auth reduced to self-check + fetch + delegate
  • 9 new boundary tests replacing the prior test-local helper

…n fails

When `buzz agents archive` or `buzz agents unarchive` runs for a
non-self target, `resolve_auth` fetches the target's kind:0 and looks
for a NIP-OA `auth` tag. If the tag is absent or invalid the CLI was
silently sending a bare request, which caused the relay to respond with
the opaque error `400: missing auth tag` — exactly what Will hit when
archiving duplicate agent instances.

Four distinct causes can prevent attestation:

1. No kind:0 profile found for the target pubkey.
2. Target kind:0 has no `tags` array.
3. kind:0 has zero or multiple `auth`-labelled tags (ambiguous ownership).
4. Sole `auth` tag is structurally malformed, or its owner pubkey does
   not match the signer (the diagnostic prints the actual owner so the
   user can identify the mismatch).

Each cause now emits a distinct warning to stderr naming what was wrong,
then falls through to send the bare request — relay admins can still
succeed without owner attestation. The self path (target == signer)
is unchanged and silent.

Also fixes the `resolve_auth` doc-comment, which incorrectly described
the degradation as silent after the non-silent network-error behaviour
was added.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 requested a review from a team as a code owner August 5, 2026 02:52
@wpfleger96 wpfleger96 changed the title fix(cli): emit diagnostic when archive/unarchive owner-auth extraction fails fix(cli): emit structured JSON warning when archive/unarchive owner-auth extraction fails Aug 5, 2026
Addresses three IMPORTANT findings from Thufir's pass-1 review:

1. JSON warnings, not prose
   Replace eprintln! calls with writeln!(warn_sink, ...) emitting exactly
   one {"warning":"..."} JSON object per fallback — consistent with the
   CLI's documented structured-stderr contract (README.md, VISION.md) and
   the precedent in channels.rs:597. relay-supplied values (target pubkey,
   actual owner pubkey) pass through serde_json serialization so no
   unescaped text reaches the output stream.

2. Injected warn-sink seam
   Thread 'warn_sink: &mut dyn std::io::Write' through resolve_auth (same
   pattern as finalize_roster_resolution in channels.rs). Production callers
   pass &mut std::io::stderr(); tests pass &mut Vec<u8>. New boundary tests
   prove each of the four fallback causes emits exactly one parseable JSON
   warning, and that the success path emits nothing.

3. Single typed classifier
   Replace the parallel describe_auth_failure mirror with AuthFailure enum
   + classify_owner_auth_tag returning Result<[String;4], AuthFailure>.
   extract_owner_auth_tag is reduced to a #[cfg(test)] .ok() wrapper for
   the existing option-style tests; the unreachable 'not a JSON array'
   branch is eliminated.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96
wpfleger96 force-pushed the wpfleger/cli-archive-auth-diagnostics branch from db80d7c to c17eead Compare August 5, 2026 03:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant