Skip to content

Contract

Vocino edited this page Aug 21, 2026 · 4 revisions

Contract wb1!

Law for addon Lua and web D1. Any field change bumps version and requires web importer update same day.

Format

wb1! + base64url( raw-deflate( json ) )

No newline, single line. WoW EditBox is large but avoids whitespace.

  • wb0! Camp DNA share URL, existing
  • wb1! Inventory, lockouts, gear, talents — this repo v1
  • wb2! Reserved, unused

One envelope for 1 and 20 characters so decoder has no branch.

Envelope v1

{
  "v":1,
  "addon":"1.0.0",
  "exportedAt":1724001234,
  "gameVersion":"12.1.0",
  "interface":120100,
  "bundle":{"count":3,"freshestSeenAt":1724001200,"oldestSeenAt":1723980000},
  "characters":[ ... ]
}

Character fields: guid, name, realm slug, faction, class, race, level, gold, bags[], bank[], reagentBank, Warband bank root, mail, auctions, currencies[], professions[], cooldowns[], instances[], bosses killed boolean in encounter order plus name, world bosses[], keystone{}, runs[], score, weekly Vault map, consumables rolled up phial/potion/food/rune, gear[], talents{}, seenAt stamps.

All _at times are Unix seconds UTC from time(), not milliseconds. Null if never seen. Web converts.

  • Bank never opened: free=null, items=[] -> unknown versus empty. UI prints "Not opened" not "0 free".
  • Warband bank seenByGuid -> web "by Vocnar" credit line. Payload root plus own D1 table avoids repeating 5 tabs across 6 characters (about 22 KB saved, deflate 32 KB window cannot fold copies).
  • currencies.maxQuantity 0 = no cap, weeklyMax 0 = not weekly-capped.
  • Consumables derived cache for Tonight Plan speed. Absent = unknown not zero (prevents hard block on 0 Phials when we did not look).
  • Items inventory only, no bonus IDs, no enchant, no sockets (API has full equips). gear[] carries that detail. Link optional.

Gear And Talents (1.1.0+)

Equipped gear needs no addon — Blizzard's Profile API already returns bonus IDs, sockets, enchants, crafted stats. gear[] fills the gap the API cannot see: bag and bank items, plus equipped items again so both halves compare from the same snapshot.

{"slot":1,"where":"equipped","id":212018,"ilvl":639,"s":"item:212018::::::::80:250::9:6:...:::"}
  • s is the item string verbatim, |H...|h, same substring SimC's own addon exports. Not decomposed — bonus IDs, enchant, gems, crafted stats, drop level all ride positionally. Full field order in docs/CONTRACT.md.
  • where: equipped | bag | bank | warbank. Never reagent.
  • slot: 1-19, shirt(4) and tabard(19) excluded everywhere. Bag/bank items collapse pairs to one representative slot — any ring to 11, any trinket to 13, off-hand family to 17 — because a generic equip location cannot say which of the two.
  • Duplicates the {id,count} stack already in bags[]/bank[] on purpose. Cheap after deflate, keeps both consumers simple.
  • talents{activeSpecID, specs[]}. Only the active spec's loadout is readable, so specs[] accumulates across a spec switch rather than replacing. loadout is C_Traits.GenerateImportString — same string the in-game Copy button and the site's talent_loadout_code already use.
  • /warband gear on\|off toggles export only. Stored data untouched either way.

Implementation Gotchas Fixed In Code

  1. Not LibDeflate:EncodeForPrint. That uses own 6-bit alphabet neither Base64 nor Base64url, swap +/ for -_ cannot make it atob readable. Addon carries real RFC 4648 Section 5 encoder.
  2. Raw deflate not zlib. CompressDeflate emits bare stream no header, DecompressionStream('deflate') rejects. Must use 'deflate-raw' or pako.inflateRaw.

Caps

  • 20 characters (DoS protection)
  • 1 MB decoded not 25 KB. Measured: 1 full 39 KB JSON 8.6 KB wire, 6 154 KB / 26 KB, 20 474 KB / 73 KB. Enforced chunk-by-chunk during inflate not after (bomb mitigation).
  • Validated v==1, array 1..20, each guid + name + seenAt.lastSeen.

Wire: LibDeflate level 9 -> base64url strip = -> prefix.

Absent Versus Empty

  • Section absent -> never seen -> unknown -> grey, not delete
  • Character missing from bundle -> not gone -> old snapshot aging red
  • Consumables absent until ID table filled -> absent not zero

Vectors

  • docs/contract/vectors/v1-min.json / v1-min.wb1
  • v1-gear.json / v1-gear.wb1 — one character, gear[] and 2-spec talents
  • Both checked by node tools/vector.mjs, regenerated with --write

Warbank Split

Root plus warband_bank_cache one row per user. Each character seenAt.warbank for dots. seenByName credit line.

Versioning

Single wb1! version strict. Any field change bumps prefix and breaks paste if sides disagree. Web checks prefix first.

Spec lives in addon repo, enforced in app warband-import.ts.

Warband.pro Companion

Home

Start

Integration

Internals

Clone this wiki locally