Skip to content

Contract

Vocino edited this page Aug 20, 2026 · 4 revisions

Contract — wb1!

Single source of truth: warband-pro/addon docs/CONTRACT.md. Web side must match src/lib/warband-import.ts.

Format

wb1!<base64url(deflate(json))>
  • No newlines, no spaces, single line paste (EditBox high but avoid whitespace)
  • wb0! — Camp DNA share URLs, wb1! — inventory, wb2! reserved talent loadouts
  • One envelope for 1 char and 20, so decoder has no branch

v1 envelope

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

CharacterObject

{
  "guid": "Player-112-0A1B2C3D",
  "name": "Vocnar",
  "realm": "Wyrmrest Accord", "realmSlug": "wyrmrest-accord",
  "faction": "Horde", "class": "DRUID", "classId": 11,
  "level": 80, "xp":123456, "restXP":234567,
  "guild": {"name":"Moxes","rank":"Initiate","rankIndex":2},
  "gold": 8457392,
  "bags": [{"bagID":0,"size":30,"free":3,"items":[{"id":211493,"count":20,"link":"|Hitem:211493::::::::80:::::::::|h[Phial]|h","quality":2}]}],
  "bank": [...], "bankBags": [...], "reagentBank": {"size":98,"free":12,"items":[...]},
  "warbandBank": {"seenAt":1723999000,"seenByGuid":"...","tabs":[[{id,count}]]},
  "mail": {"countItems":2,"goldPending":120000,"soonestExpiryHours":12,"seenAt":null},
  "auctions": {"countActive":3,"goldHeld":500000,"seenAt":1723998000},
  "currencies": [{"id":2815,"name":"Resonance Crystals","quantity":4500,"maxQuantity":20000}],
  "professions": [{"id":171,"name":"Alchemy","skill":100,"maxLevel":100}],
  "instances": [{"name":"Nerub-ar Palace","instanceID":1273,"difficulty":3,"locked":true,"bosses":[...]}],
  "keystone": {"level":12,"dungeonID":503},
  "weeklyVault": {"raid":{"progress":1,"threshold":3,"unlocked":false},"mplus":{"progress":4,"threshold":8,"unlocked":true}},
  "consumables": {"phial":120,"healthPotion":80,"foodFeast":200},
  "seenAt": {"lastSeen":1724001000,"bag":1724001000,"bank":1723999000,"warbank":1723999000,"currency":1724001000,"instance":1723998000,"vault":1723997000}
}

Field rules

  • All _seen times = unix seconds UTC from time(), not ms (web converts). Null = never seen.
  • items = inventory only, no equips (API has it). Link optional for debug.
  • Bank never opened: free=null, items=[] → unknown vs empty (UI says "not opened" not "0 free", avoids wrong "clear bank" urge).
  • Warband bank seenByGuid → web "by Vocnar" credit line, valid across alts. Payload root + separate D1 table warband_bank_cache (one row per user) avoids repeating 5 tabs × 6 chars (~22KB saved, deflate 32KB window can't fold copies).
  • currencies.maxQuantity 0=no cap, weeklyMax 0=not weekly-capped.
  • consumables derived cache for Tonight Plan speed: count known consumable ids, not name regex. Absent ⇒ unknown, not zero (prevents hard-block on "0 phials" when we didn't look).
  • instances.bosses bool order matches encounter order, name included.

Implementation gotchas (corrected in code, doc preserved)

  1. Not LibDeflate:EncodeForPrint. That uses LibDeflate 6-bit alphabet — neither base64 nor base64url — swap +/ for -_ doesn't make it atob-readable. Addon carries real RFC4648 §5 encoder.
  2. Raw deflate, not zlib. CompressDeflate emits bare stream, no header → DecompressionStream('deflate') rejects. Must 'deflate-raw' or pako.inflateRaw. Same fix on both sides.

Capabilities

  • 20-char cap (DoS)
  • 1MB decoded cap (not 25KB — measured 1 full char 39KB JSON/8.6KB wire, 6 154KB/26KB, 20 474KB/73KB). Enforced chunk-by-chunk during inflate, not after (bomb mitigation).
  • Validated v==1, array 1..20, each has guid, name, seenAt.lastSeen.
  • Base64url deflated via LibDeflate lvl9 → EncodeForPrint→b64url strip = → prefix wb1!.

Absent vs Empty

  • Section absent ⇒ never seen ⇒ unknown ⇒ UI prints not opened / greys, doesn't delete.
  • Char missing from bundle ⇒ not gone ⇒ row keeps old snapshot aging red.
  • Consumables absent until table filled — absent ≠ zero.

Vectors

  • docs/contract/vectors/v1-min.json 630 bytes (min)
  • v1-min.wb1 test string Add vectors v1-full.json, v1-6.json planned for CI check-contract-vectors.

Warband bank split (one vault)

Root + warband_bank_cache table. Each char carries seenAt.warbank for dots. seenByName holds UI credit.

Versioning

  • Single wb1! version stringent. Any field change bumps prefix and needs web importer update same day (broken paste if sides disagree). Web does version check on prefix first.

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

Warband.pro Companion

Home

Start

Integration

Internals

Clone this wiki locally