[FEATURE] Server-signed /buddy manifests for rarity integrity

Resolved 💬 2 comments Opened Apr 5, 2026 by Swordsman Closed Apr 9, 2026

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

The current /buddy system generates companions deterministically on the client from the user's identity plus a hardcoded salt. Within roughly 72 hours of launch, multiple public tools shipped that defeat this:

  • Tools that binary-patch the salt in the Claude Code binary, with auto-patch hooks that re-apply after updates
  • Tools that brute-force userID values in ~/.claude.json to land on target seeds
  • Public writeups documenting the "accountUuid trap" where team plans share one identity

The existing "bones regenerated from hash on every read" logic indicates the intent was already that users shouldn't be able to edit their way to legendary. Client-side generation cannot enforce that intent against a target audience that is, by definition, capable of reverse engineering.

This matters more now than it did at launch because shareable artifacts are emerging. The community buddy-card skill generates trading cards from buddies, which means rarity signals are leaving the local machine. Once rarity is shared, forgery becomes fraud rather than self-expression.

Related, not duplicate: #42894 reports buddies changing across Claude Code versions on the same account. Different root cause, but user-visible evidence that the current identity model has integrity issues.

Proposed Solution

Move buddy minting to the server. Sign the result with Ed25519. Client verifies before render or share.

Scope note

This proposal concerns buddy creation only. After the initial signed mint, the buddy lives entirely in the local cache and requires no ongoing server interaction. Features that track state over time (XP, evolution, personality drift, see #42389) can layer on top without modification, because the signed manifest only binds the creation moment. This is a compatible fix, not an alternative architecture.

Identity model

Two UUIDs per user, both server-only, neither ever exposed to the client:

  • buddy_base_uuid: globally unique per user, defines the user's buddy-space. Generated cryptographically at account creation (backfilled once for existing accounts).
  • buddy_roll_uuid: current draw within that space. Defaults to buddy_base_uuid, which produces a stable forever-buddy unless rotated.

Generation seeds from both:

seed = SHA256(base_uuid || roll_uuid)
prng = CSPRNG(seed)
=> species, rarity, eyes, hat, stats, shiny

The existing generation algorithm (species tables, rarity tiers, cosmetic rolls, stat formulas) is preserved; only the seeding source changes.

Signing

Server signs a canonical manifest with Ed25519. Manifest includes an opaque user reference (not base_uuid) so signatures are bound to a specific user without leaking the identity seed.

POST /api/buddy/mint   (authenticated)
  server looks up (base_uuid, roll_uuid)
  computes seed, runs generation
  returns { manifest, signature, public_key_id }

Client

Ed25519 verification via Node's stdlib crypto module. Verification runs before render and before any share tool includes the buddy in its output. Cached signed envelopes survive offline use. Share tools (including community tools like buddy-card) include the full signed envelope in their output format, enabling third-party verification against the public key.

Properties

  • Forgery resistance is anchored on the Ed25519 signature. Forging a legendary requires the private key.
  • Precomputation is eliminated. No client-visible input to the seed, so offline seed-searching is impossible.
  • Team plan identity trap is solved incidentally. Identity is keyed to the user, not the OAuth account object, so each Team/Pro user gets their own buddy.
  • Rotation stability. Key rotations, email changes, and plan transitions don't touch buddies.
  • Offline display still works. The client caches the signed envelope and verifies against its local public key registry.

What does not change

  • Determinism: same user gets the same buddy
  • 18 species, 5 rarities, 1% shiny roll
  • Client-side rendering, sprites, animations, reaction bubbles
  • Offline display of cached manifests
  • Existing generation logic: only the seeding source changes

Scope not being requested

To be explicit: I am not asking for re-rolls, friend combos, seasonal events, self-variants, or any of the forward-compatibility features noted in Additional Context. Those are listed only to justify why the v1 UUID shape is what it is. The v1 ask is: two UUID columns, one mint endpoint, one signing key, client-side verification.

Alternative Solutions

Implementation roadmap

A possible phasing for this work, sized for incremental review. Happy to flesh any of these out further, or leave implementation entirely to whoever owns /buddy internally — whichever is more useful.

Phase 1: identity foundation

  • Add buddy_base_uuid and buddy_roll_uuid columns (nullable → backfilled → non-nullable)
  • One-time backfill migration generating cryptographically random bases for existing accounts, roll_uuid = base_uuid by default
  • Populate on account creation for new users
  • No behavioral change yet — columns exist but aren't read

Phase 2: server minting and signing

  • POST /api/buddy/mint endpoint (authenticated via existing Claude Code auth surface)
  • Ed25519 key pair loaded from existing secrets infrastructure
  • Canonical manifest serialization (RFC 8785 JCS or custom deterministic serializer, whichever matches existing patterns)
  • Existing generation function refactored to accept a seed parameter instead of computing it from identity + hardcoded salt

Phase 3: client verification

  • Public key registry shipped with the CLI (supports key rotation via public_key_id lookup)
  • Ed25519 verification on render and before share-tool inclusion
  • Cache handling for signed envelopes in ~/.claude.json or wherever buddies currently persist
  • Graceful degradation: "unverified buddy" state for failed verification rather than hard crash

Phase 4: share format propagation

  • Signed envelope format documented publicly so community share tools (like buddy-card) can adopt it
  • Existing unsigned share artifacts are not retroactively verifiable — acceptable since the install base is small and the feature is still in its teaser window

Rough order of magnitude: Phase 1 is a migration and two lines in account creation. Phase 2 is an endpoint and canonical JSON. Phase 3 is verification logic, cache handling, and UX for failure states. Phase 4 is documentation. Total feature footprint is meaningful but bounded.

Forward compatibility

The v1 design deliberately enables several future features without schema changes. None of these are being requested now. They are listed only to justify specific v1 shape decisions.

  • Re-rolls. Rotate roll_uuid, keep base_uuid. User gets a fresh draw from their personal space while preserving their identity anchor.
  • Seasonal events. The generation function can accept an optional third input derived from a per-user value, substituted server-side during event windows. No per-user event state; events are pure server configuration.
  • Friend combos. A future endpoint could accept a friend identifier and mint a combo buddy using the friend's base_uuid as the requesting user's roll_uuid. Both bases stay server-side; the client never sees either. Consent, discovery, and rate-limiting are product questions deferred to that future design pass.
  • Self-buddy variants. Additional buddies unique to the user via roll_uuid = HMAC(base_uuid, index) for small integer indices.
  • Pity and collection mechanics. Because roll_uuid generation is server-side, tracking, saving favorites, and pity systems become straightforward.

None of these require changes to the v1 schema or signing pipeline. Choosing the base/roll split now is nearly free; retrofitting it later would require either a schema migration or a one-time buddy reset.

Timing

This is structurally easier to ship during the April 1-7 teaser window than after. The install base is small, users expect volatility from an explicitly experimental feature, and a one-time re-hatch during the window costs less than a migration story later. Every day of delay makes the churn harder to justify.

Open questions

I've deliberately left these unanswered because they depend on Claude Code internals I don't have visibility into:

  1. Does the CLI already authenticate against an /api/* surface the mint endpoint could piggyback on, or would this require a new auth path?
  2. Is the existing buddy generation code isolated enough to refactor cleanly into a seed-accepting function?
  3. Does Anthropic already use Ed25519 signing patterns elsewhere? The release manifest signing documented in the setup docs uses GPG, which is a different story — I'm asking about application-level signing.
  4. Canonical JSON: does existing signed-payload infrastructure exist this should match, or is this introducing the first such payload?

Answers to any of these would let me sharpen the proposal further.

Offer

This is a proposal looking for a signal on whether the direction is worth pursuing. Two paths forward, whichever is more useful:

  • If the direction is interesting and internal implementation makes sense, I'm happy to iterate on the design in this thread, answer questions, and refine any part of the proposal against constraints I can't see from outside. Implementation stays with whoever owns /buddy.
  • If a public reference implementation would help ground the discussion, I can build one against placeholder interfaces (mock user store, mock auth, stubbed generation function) in a separate permissively-licensed repository. It would demonstrate the crypto, serialization, verification, and failure-state UX end-to-end, and would be freely usable or adaptable by whoever implements internally.

Happy with either. I'd rather know the preferred shape before investing in the reference implementation than guess wrong.

Priority

Low - Nice to have

Feature Category

Other

Use Case Example

Happy path: legitimate mint and share

  1. User runs /buddy for the first time after updating to the version with this feature.
  2. Claude Code authenticates against Anthropic's API and calls POST /api/buddy/mint.
  3. Server looks up (or lazily generates) the user's (base_uuid, roll_uuid), computes seed = SHA256(base || roll), runs the existing generation function on the seed, signs the resulting manifest with Ed25519, returns {manifest, signature, public_key_id}.
  4. Claude Code verifies the signature against its embedded public key registry, caches the signed envelope in the user's config, and renders the buddy with the normal hatching animation.
  5. Later, the user runs buddy-card (or any future share tool) to generate a trading card. The share tool includes the full signed envelope, not just the buddy fields.
  6. The user posts the card in a Discord or subreddit. Another user, or an automated verifier, checks the signature against Anthropic's public key and confirms the buddy is authentic.

Forgery path: existing reroll tools

  1. A user runs one of the existing reroll tools (any-buddy, ccbuddyy, buddy-reroll) to brute-force a legendary and patch their Claude Code binary.
  2. The binary patch changes the local generation output, but there is no way to produce a valid Ed25519 signature for the forged manifest without the server's private key.
  3. On next render, Claude Code verifies the signature and it fails. The buddy displays in an "unverified" state rather than rendering as legitimate.
  4. If the user shares the forged card, any verifier checking the signature sees the mismatch and rejects it as forged.
  5. The existing reroll tools stop working as rarity shortcuts. They become cosmetic-only local display modifications with no ability to affect sharing.

Offline path

  1. User has already minted and cached a signed buddy.
  2. User goes offline (plane, coffee shop with no wifi, whatever).
  3. User runs /buddy. Claude Code reads the cached signed envelope, verifies the signature against the local public key registry, and renders normally.
  4. No network required for display. Network is only required once, at initial mint.

Additional Context

Implementation roadmap

A possible phasing for this work, sized for incremental review. Happy to flesh any of these out further, or leave implementation entirely to whoever owns /buddy internally — whichever is more useful.

Phase 1: identity foundation

  • Add buddy_base_uuid and buddy_roll_uuid columns (nullable → backfilled → non-nullable)
  • One-time backfill migration generating cryptographically random bases for existing accounts, roll_uuid = base_uuid by default
  • Populate on account creation for new users
  • No behavioral change yet: columns exist but aren't read

Phase 2: server minting and signing

  • POST /api/buddy/mint endpoint (authenticated via existing Claude Code auth surface)
  • Ed25519 key pair loaded from existing secrets infrastructure
  • Canonical manifest serialization (RFC 8785 JCS or custom deterministic serializer, whichever matches existing patterns)
  • Existing generation function refactored to accept a seed parameter instead of computing it from identity + hardcoded salt

Phase 3: client verification

  • Public key registry shipped with the CLI (supports key rotation via public_key_id lookup)
  • Ed25519 verification on render and before share-tool inclusion
  • Cache handling for signed envelopes in ~/.claude.json or wherever buddies currently persist
  • Graceful degradation: "unverified buddy" state for failed verification rather than hard crash

Phase 4: share format propagation

  • Signed envelope format documented publicly so community share tools (like buddy-card) can adopt it
  • Existing unsigned share artifacts are not retroactively verifiable — acceptable since the install base is small and the feature is still in its teaser window

Rough order of magnitude: Phase 1 is a migration and two lines in account creation. Phase 2 is an endpoint and canonical JSON. Phase 3 is verification logic, cache handling, and UX for failure states. Phase 4 is documentation. Total feature footprint is meaningful but bounded.

Forward compatibility

The v1 design deliberately enables several future features without schema changes. None of these are being requested now. They are listed only to justify specific v1 shape decisions.

  • Re-rolls. Rotate roll_uuid, keep base_uuid. User gets a fresh draw from their personal space while preserving their identity anchor.
  • Seasonal events. The generation function can accept an optional third input derived from a per-user value, substituted server-side during event windows. No per-user event state; events are pure server configuration.
  • Friend combos. A future endpoint could accept a friend identifier and mint a combo buddy using the friend's base_uuid as the requesting user's roll_uuid. Both bases stay server-side; the client never sees either. Consent, discovery, and rate-limiting are product questions deferred to that future design pass.
  • Self-buddy variants. Additional buddies unique to the user via roll_uuid = HMAC(base_uuid, index) for small integer indices.
  • Pity and collection mechanics. Because roll_uuid generation is server-side, tracking, saving favorites, and pity systems become straightforward.

None of these require changes to the v1 schema or signing pipeline. Choosing the base/roll split now is nearly free; retrofitting it later would require either a schema migration or a one-time buddy reset.

Timing

This is structurally easier to ship during the April 1-7 teaser window than after. The install base is small, users expect volatility from an explicitly experimental feature, and a one-time re-hatch during the window costs less than a migration story later. Every day of delay makes the churn harder to justify.

Open questions

I've deliberately left these unanswered because they depend on Claude Code internals I don't have visibility into:

  1. Does the CLI already authenticate against an /api/* surface the mint endpoint could piggyback on, or would this require a new auth path?
  2. Is the existing buddy generation code isolated enough to refactor cleanly into a seed-accepting function?
  3. Does Anthropic already use Ed25519 signing patterns elsewhere? The release manifest signing documented in the setup docs uses GPG, which is a different story — I'm asking about application-level signing.
  4. Canonical JSON: does existing signed-payload infrastructure exist this should match, or is this introducing the first such payload?

Answers to any of these would let me sharpen the proposal further.

Offer

This is a proposal looking for a signal on whether the direction is worth pursuing. Two paths forward, whichever is more useful:

  • If the direction is interesting and internal implementation makes sense, I'm happy to iterate on the design in this thread, answer questions, and refine any part of the proposal against constraints I can't see from outside. Implementation stays with whoever owns /buddy.
  • If a public reference implementation would help ground the discussion, I can build one against placeholder interfaces (mock user store, mock auth, stubbed generation function) in a separate permissively-licensed repository. It would demonstrate the crypto, serialization, verification, and failure-state UX end-to-end, and would be freely usable or adaptable by whoever implements internally.

Happy with either. I'd rather know the preferred shape before investing in the reference implementation than guess wrong.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗