Bit-Me app API & the BMR1 / BME1 maps
The /bitme/* endpoints are a plain-JSON polling API built
for the Bit-Me mobile companion app — no SpacetimeDB
protocol, no authentication. Six GETs — plus one optional WebSocket,
the live resource change stream — cover the whole app:
name resolution, a ~1 Hz activity snapshot, a packed binary
resource map centered on the player (the BMR1
format, documented below), the same window anchored at arbitrary
world coordinates, a matching elevation plane (the
BME1 format), and the per-region dictionary needed
to decode BMR1.
The seven endpoints
| Endpoint | Returns | Cadence |
|---|---|---|
| GET /bitme/resolve?name= | Exact-match (case-insensitive) player name →
entity_id, identity, home region
(name, host, module), live signed_in. Covers all
25 regions. This is a lookup, not a search — use
GET /player?name= for substring search. |
Once at onboarding |
| GET /bitme/session/<entity_id> | One snapshot for the activity screens: position, current claim, stamina, live buffs, action lifecycle, action-target health, watched spawns (citric detection). | ~1 Hz while open |
| GET /bitme/session/<entity_id>/resources | Packed 400×400 map centered on the player — binary BMR1
(application/octet-stream, 320,024 bytes),
not JSON. A merged view: every resource type
in the region, plus player paving and a water layer. |
Only while the map screen is open |
| GET /bitme/session/<entity_id>/resources/ws | The one WebSocket: a live change stream over the same
400×400 window. After a subscribed ack,
every in-window resource/paving tile change arrives as a
small binary BMD1 delta frame; the server
follows the player's anchor server-side. Read-only; the
client keeps convergence by re-fetching the BMR1 window
(details below). |
One per player, while the map screen is open |
| GET /bitme/world/<x>/<z>/resources | The same BMR1 window anchored at explicit world tile coordinates — the region is derived from the coordinates, so the map can pan anywhere without following a player. No player involved, so no session keep-alive. | Map panning / arbitrary views |
| GET /bitme/world/<x>/<z>/elevation | Packed super-hex terrain plane covering the same 400×400 footprint — binary BME1, 134–137 × 136 super-hexes (145,818–149,082 bytes; the odd-r shear makes the width vary with the window's row alignment): current and original (pre-terraform) elevation, water level, and water body type per super-hex. | With the map; refetch when generation changes |
| GET /bitme/region/<region>/resource-dictionary | Tile-word index → resource_id + name +
gamedata + harvestable flag, for expanding BMR1
payloads client-side. |
Once per region; refetch when dict_version changes |
Conventions
| Convention | Detail |
|---|---|
| Entity ids | JSON strings — upstream ids are u64 and
exceed JS Number.MAX_SAFE_INTEGER. Never parse
them as numbers. |
| Positions | world_x/world_z are float world
units (one tile = 1.0); tile_x/tile_z
are integer odd-r tiles — the same coordinate space as the
/roads endpoints. |
| Clocks | *_ms fields are unix milliseconds; buff
start_timestamp/duration are unix
seconds; last_decrease_at is RFC 3339. |
| Nulls & errors | Present-but-null means unknown/not applicable,
never an error. 400 for bad input,
404 with {"found": false, …} for
misses. Every response carries
Cache-Control: no-store; CORS is *. |
Coordinates: world ↔ tiles ↔ in-game N/E
Three coordinate systems show up across the relay APIs. They agree on axis directions — x grows east, z grows north — and differ only in scale and origin:
| System | Shape | Where you meet it |
|---|---|---|
World x/z |
Float world units (upstream milli-units ÷ 1000; one tile = 1.0) | session.position.world_x/world_z, building &
player lookups |
Tile tile_x/tile_z |
Integer odd-r small-hex coordinates
(floor(world)) |
session.position.tile_x/tile_z, BMR1 window
origins, all /roads/* endpoints,
location_state |
In-game N/E |
Integer super-hex coordinates — the game's terrain lattice: a true hex grid at 3× the tile scale (odd-r; odd super rows sheared one tile east) | The in-game map/HUD, hexite deposit claim names
(N: 5593, E: 9647),
/deposits north/east |
Conversions (the same formulas the game's own claim names use):
Notes:
- No sign flips — but there is shear. The super
lattice is the tile lattice scaled 3× in axial space and
rounded, so odd super rows sit one tile east. A hexite deposit
named
N: 5593, E: 9647is super(E, N) = (9647, 5593), whose center tile is(28942, 16779)— the deposit itself can be any tile of the super's flower, which lands in region 14 (origin(23040, 15360)). - The inverse lands on the super's center tile. The super owns the 7-tile flower around it and shares its six corners with the neighbouring supers — for map pins and path planning the center tile is already sub-HUD-pixel.
- Region layout: a 5×5 grid of 7680×7680-tile regions,
id 1 at
(0, 0), id 25 at(30720, 30720); live origins come fromGET /roads/regions. Details: Region map format. - BMR1 windows are already in tiles — header
origin_world_x/zplus the relative(lx, lz)of a word gives you(origin + offset)directly; apply the N/E formula only when displaying HUD-style coordinates. - BME1 elevation cells are already super-hexes — cell
(r, c)of a window is super offset(origin_super_x + c, origin_super_z + r), so display it as HUDE = origin_super_x + c,N = origin_super_z + r(exact — recoverorigin_superfrom the header's center tile, see below).
Converting in practice (Python)
import json, math, struct, urllib.request
BASE = "https://relay.bitcraftsync.app"
def world_to_tile(wx, wz):
return math.floor(wx), math.floor(wz)
def tile_to_ne(x, z):
q = x - (z - (z & 1)) // 2 # odd-r tile → axial
Q, N = (q + 1) // 3, (z + 1) // 3 # round each to nearest third
return N, Q + (N - (N & 1)) // 2 # axial → odd-r super (north, east)
def ne_to_tile(north, east):
return east * 3 + (north & 1), north * 3 # super's center tile
# session snapshot → HUD-style coordinates
snap = json.load(urllib.request.urlopen(
f"{BASE}/bitme/session/504403158290646123", timeout=30))
pos = snap["position"]
x, z = world_to_tile(pos["world_x"], pos["world_z"])
north, east = tile_to_ne(x, z)
print(f'tile ({x}, {z}) → in-game N {north}, E {east}')
# hexite deposit name → super center tile for /roads or a BMR1 window check
x, z = ne_to_tile(5593, 9647) # → (28942, 16779), region 14
# BMR1 window: word (lx, lz) → world tile → HUD coords
raw = urllib.request.urlopen(
f"{BASE}/bitme/session/{snap['player_entity_id']}/resources",
timeout=30).read()
_, _, width, ox, oz, _, _ = struct.unpack_from("<4sHhiiII", raw, 0)
tiles = struct.unpack_from(f"<{width*width}H", raw, 24)
i = next(i for i, t in enumerate(tiles) if t & 0x3FF)
lx, lz = i % width, i // width
north, east = tile_to_ne(ox + lx, oz + lz)
print(f"first resource at world tile ({ox + lx}, {oz + lz})"
f" → N {north}, E {east}")
Session snapshot in one look
Top-level fields of GET /bitme/session/<id> —
full field-by-field reference with real payloads lives in
crates/relay-cache/BITME-API.md in the relay repo:
| Field | Meaning | Gotcha |
|---|---|---|
| position | Last known world position + destination + dimension. |
Row persists after logout — judge freshness by
age_ms and signed_in.
dimension > 1 is a building interior. |
| claim | Claim under the player's tile (name, owner, neutral). | null in wilderness or interiors. |
| stamina | current/max +
last_decrease_at. |
Regen is not simulated server-side; project it client-side
from the passive rate anchored at last_decrease_at. |
| buffs | Live buffs only (placeholders filtered). | Expired entries can linger until the next upstream flush — check the countdown, not just presence. |
| actions | Action lifecycle per layer (Base,
UpperBody): type, start, duration,
ends_at_ms, result. |
Rows persist after completion. In progress iff
server_time_ms < ends_at_ms. |
| target | The acted-on entity, enriched: name, health/max, respawn
gamedata, growth_ends_at_ms. |
health is tracked server-side only while a
session polls — expect null on the first poll
after targeting. |
| activity_spawns | Watched spawns in the player's claim/wilderness: destroy-yield chains + the three Citric Giant bushes. | The citric-detection signal: alert when an entry with a
citric resource_id appears. Entries expire after
30 min or on upstream delete. |
The BMR1 resource window
GET /bitme/session/<id>/resources serves a
400×400 window out of the region's dense tile map — one
u16 per odd-r tile across the whole 7680×7680
region, merging three layers:
- Resources — stamped server-side from
resource_desc.footprint×directionfor everyresource_staterow (trees, ore, clay, sand, mushrooms, berry bushes, fiber, flowers, event resources — all of them). - Paving — player-paved tiles
(
paved_tile_state), stamped at their joined location into otherwise-empty tiles using the dictionary's separate paving namespace. - Water — one flag per tile derived from the terrain seed (elevation below water level, at terrain super-hex resolution: a water super wets its 7-tile flower, and corner tiles — where three supers meet — are water when any of the three is). Terrain never flips water→land, so it is filled once and rides along on every word — including otherwise-empty tiles.
The player's tile sits at relative (200, 200); the
window covers world tiles origin … origin+399 per axis.
- Size: exactly 320,024 bytes = 24-byte header +
160,000 little-endian
u16tile words (row-major, 800 bytes per row). - Row order: row
rof the payload is world z =origin_world_z + r; columncis world x =origin_world_x + c. - Layer priority: a tile holds a resource or paving, never both — paving fills only empty tiles and the resource wins the rare collision (observed on ~24 of 416k sampled tiles, always multi-hex formations over pavement). Paving words carry no origin/direction bits.
- Water-only tiles: word
0x8000(bit 15 alone) is plain water with nothing on it. - Outside the region: tile word
0— windows overhang region borders and there is no cross-region stitching. - Session keep-alive: the window poll refreshes the session TTL exactly like the snapshot poll.
Header (24 bytes, little-endian)
| Offset | Type | Meaning |
|---|---|---|
| 0–3 | [u8; 4] | Magic BMR1 |
| 4–5 | u16 | Format version (1) |
| 6–7 | u16 | Window width in tiles (400) |
| 8–11 | i32 | origin_world_x — odd-r world tile of relative column 0 |
| 12–15 | i32 | origin_world_z — odd-r world tile of relative row 0 |
| 16–19 | u32 | Region id |
| 20–23 | u32 | dict_version — pairs the payload with a resource-dictionary |
Tile word (u16 LE)
Every bit is now allocated:
| Bits | Meaning |
|---|---|
| 0–9 | Dictionary index
(0 = empty tile; capacity 1023). With bit 14 clear
this is a resource index; with bit 14 set it is
a paving index — the two live in separate
dictionary namespaces, so equal indices never collide. |
| 10 | Origin flag — this tile is the resource's anchor tile (resources only; never set on paving) |
| 11–13 | direction_index
(0–5), repeated on every tile of the resource footprint
(resources only) |
| 14 | Paving flag — the
tile is player-paved; bits 0–9 hold a paving index (dictionary
entries with "paving": true) |
| 15 | Water flag — the
tile's terrain sits below its water level (terrain super-hex
resolution: a water super wets its 7-tile flower, and corner
tiles — where three supers meet — are water when any
of the three is). Rides on every word, including
otherwise-empty tiles (0x8000 alone = water,
nothing on it). |
Two rendering styles fall out of this layout. Per-tile:
paint every occupied tile from the dictionary — no footprint math
needed, the server already stamped each tile of every multi-hex
shape. Per-instance: render one icon per resource by
anchoring on tiles with the origin flag set and rotating the
resource's resource_desc.footprint by
direction; adjacent same-type resources are disambiguated
by their origin tiles. Natural layer order for a map: water (bit 15)
as the background, paving (bit 14) as the underlay, resources on top.
A ready-made hex-grid viewer that does exactly this lives in the relay
repo at crates/relay-cache/debug/window.html.
The BME1 elevation window
GET /bitme/world/<x>/<z>/elevation serves the
terrain half of the same footprint: the super-hexes covering the
400×400 window anchored at world tile (x, z).
One u64 per super-hex, sliced straight from the
resident terrain grid. Super-hexes are the game's terrain lattice
— a true hex grid at 3× the tile scale,
addressed in odd-r offset coordinates the same way small tiles are,
not a rectangular 3×3 blocking of tile indices:
- Lattice: super
(X, Z)(HUDE = X,N = Z) has its center tile at(3X + (Z&1), 3Z)— odd super rows sit one tile right, the odd-r shear at 3× scale. It exclusively owns the 7-tile flower around that center and shares the six corner tiles where three supers meet. - Tile → super: convert to axial
(
q = x − (z − (z&1))/2,r = z), round each component to the nearest third ((v+1)/3floored), convert back to odd-r at the super scale. Corner tiles blend three supers — water at a corner is any-of-the-three, matching the game'sis_submerged, and that is exactly the rule behind BMR1's bit 15. - Size:
26 + width·height·8bytes — a 400-tile window covers 134–137 × 136 supers (the shear makes the column count depend on the window's row alignment; 145,818–149,082 bytes). Cells are little-endianu64, row-major in super offset space. - Coverage: every super owning or blended by a
tile of the resource window, padded one super on each side so
edge corner tiles can still blend. Cell
(r, c)is the super at offset(origin_super + (c, r)); recoverorigin_superfrom the header's center tile(x, z)viaX = (x − (Z&1))/3,Z = z/3(floored division). - Outside the region: cell
0, the same zero-fill convention as BMR1. - Water: a cell is underwater when
elevation < water_level(missing water data arrives asi16::MIN, which never classifies as water). This is the source of BMR1's bit 15, at full resolution here. - Generation: the header carries the grid's update counter and terraform bumps it. Elevation is the only field that ever changes after seed — refetch when it moves.
The cell layout is the roads terrain cell unchanged — see the region map format page for the full terrain layer:
| Cell bits (u64 LE) | Field |
|---|---|
| 0–15 | elevation (i16 stored as u16) |
| 16–31 | original_elevation (i16) — pre-terraform |
| 32–47 | water_level (i16) |
| 48–55 | water_body_type (u8) |
| 56–63 | unused (zero) |
BME1 header (26 bytes, little-endian)
| Offset | Type | Meaning |
|---|---|---|
| 0–3 | [u8; 4] | Magic BME1 |
| 4–5 | u16 | Format version (2) |
| 6–7 | u16 | width — super-hex columns (134–137) |
| 8–9 | u16 | height — super-hex rows (136) |
| 10–13 | i32 | origin_center_world_x — world tile of cell (0, 0)'s center tile |
| 14–17 | i32 | origin_center_world_z |
| 18–21 | u32 | Region id |
| 22–25 | u32 | generation — grid update counter; terraform bumps it |
Decoding pairs naturally with a BMR1 fetch at the same anchor:
# terrain plane for the same anchor as a BMR1 window
raw = urllib.request.urlopen(
f"{BASE}/bitme/world/{x}/{z}/elevation", timeout=30).read()
magic, version, width, height, cx, cz, region, generation = struct.unpack_from(
"<4sHHHiiII", raw, 0)
assert magic == b"BME1" and version == 2 \
and len(raw) == 26 + width * height * 8
cells = struct.unpack_from(f"<{width * height}Q", raw, 26)
def elevation(cell): # i16 stored as u16
return struct.unpack("<h", struct.pack("<H", cell & 0xFFFF))[0]
# header origin is cell (0, 0)'s CENTER tile → super offset
X0 = (cx - (cz & 1)) // 3
Z0 = cz // 3
def tile_to_super(x, z): # world tile → super offset
q = x - (z - (z & 1)) // 2 # odd-r → axial
Q, N = (q + 1) // 3, (z + 1) // 3 # round to nearest thirds
return Q + (N - (N & 1)) // 2, N # axial → odd-r super
def cell_at(tile_x, tile_z): # world tile → covering cell
sx, sz = tile_to_super(tile_x, tile_z)
return cells[(sz - Z0) * width + (sx - X0)]
hi = max(elevation(c) for c in cells)
print(f"region {region}: highest elevation {hi}, generation {generation}")
Resource dictionary
The 10-bit tile index is assigned in first-sighting order and is
stable per deploy but not across deploys. Resource
and paving entries use separate index namespaces —
a tile word's bit 14 says which list an index refers to, so equal
indices never collide. dict_version covers both.
Fetch the dictionary once per region and refetch it when a window
header's dict_version changes:
GET /bitme/region/7/resource-dictionary
{
"region": 7,
"ready": true,
"dict_version": 4018046328,
"entries": [
{
"index": 1,
"resource_id": 815748648,
"name": "Rough Quarry Rock",
"harvestable": false,
"max_health": 5000,
"despawn_time_secs": 0.0,
"respawn_time_secs": 0.0,
"paving": false
},
{
"index": 1,
"paving_type_id": 59838,
"name": "Cobblestone Road",
"paving": true
}
]
}
| Field | Meaning |
|---|---|
| index | The 10-bit value carried in tile words (index 0
is the empty sentinel and is not listed). |
| paving / paving_type_id | true on paving entries, which carry the raw
paving_type_id instead of
resource_id (names from the global
paving_tile_desc catalog). |
| name / max_health / … | resource_desc gamedata on resource entries
(null if the desc row is missing). |
| harvestable | The old roads-side forestry/mining/clay/sand classification — use it to filter the map client-side; the window includes all resource types. |
The resources/ws change stream (BMD1)
GET /bitme/session/<entity_id>/resources/ws (a
WebSocket — wss://relay.bitcraftsync.app/…) pushes
every resource/paving tile change whose world tile falls inside
the player's 400×400 window. The server keeps only the
window coordinates per listener — no window copy, no
diffing — and the client owns convergence:
initial state, reconnect recovery, and resync recovery are all
"re-fetch the BMR1 window". Frames are never silently dropped to a
live socket; backpressure closes the connection instead, so the
delivered stream is totally ordered. The stream is read-only
(client→server frames are ignored; WS pings are answered).
Pre-upgrade errors are byte-identical to the HTTP window's (the
table below), plus 503 when the fleet-wide stream cap
— 2048 connections, shared with the dim-buildings stream — is
reached. One stream per player; heartbeats every 5 s also
refresh the session TTL.
Server → client frames:
| Frame | Payload | Meaning |
|---|---|---|
| text (first) | {"type":"subscribed", "player_entity_id":"…", "region":14, "anchor":{"x":7690,"z":7700}, "width":400, "dict_version":123456789} | Ack after the upgrade. Note region and
anchor; pair dict_version with
the dictionary. |
| binary | BMD1 — one per applied upstream batch, only in-window changed tiles (layout below) | Apply each entry onto your window:
word → tile at (x, z) when in bounds.
Tiles are world odd-r coordinates, not
window-relative. A 0 word (or water-bit-only
word) means the tile emptied. |
| text | {"type":"resync","reason":"reseed"|"live"} | The region grid was replaced (upstream disconnect) or
finished re-seeding. Refetch the BMR1 window;
refetch the dictionary too if the next frame's
dict_version differs from the snapshot
header's. |
| text | {"type":"moved","region":7,"anchor":{…}} | The player crossed into another region and the stream re-anchored there. Refetch the BMR1 window (different area of the world). |
| text | {"type":"gone","player_entity_id":"…"} | No live position in any mirrored region for ~60 s (logged out, or a deploy/reseed outlasting the window). The server closes right after. |
| text | {"ts":1753296000123} | Heartbeat every 5 s. Treat ~15 s without a frame as dead. |
BMD1 frame layout
| Offset | Type | Meaning |
|---|---|---|
| 0–3 | [u8; 4] | Magic BMD1 |
| 4–5 | u16 | Format version (1) |
| 6–9 | u32 | Region id |
| 10–13 | u32 | dict_version — pair with /resource-dictionary |
| 14–15 | u16 | Entry count n (batches larger than 65535 tiles split into consecutive frames) |
| 16+ | n × 10 bytes | i32 world_x, i32 world_z, u16 tile word (the same bit layout as BMR1, water bit included) |
Duplicate tiles within a frame never occur; writes within one batch collapse to the final word. While walking, keep the movement-triggered BMR1 refetch — the server follows the anchor at ~1 s granularity, so trailing-edge events for a few tiles around a fast-moving player are intentionally trimmed until the next full fetch.
Consuming the stream (Python)
# pip install websocket-client (stdlib has no WebSocket client)
import json, struct, urllib.request, websocket
BASE = "https://relay.bitcraftsync.app"
PLAYER = "504403158290646123"
# 1. fetch the BMR1 window first — connect order is safe either way:
# replaying an already-applied tile word is idempotent, and frames
# that race the fetch carry the same final word.
raw = urllib.request.urlopen(
f"{BASE}/bitme/session/{PLAYER}/resources", timeout=30).read()
# 2. open the stream; on "subscribed" note region/anchor
ws = websocket.create_connection(
f"wss://relay.bitcraftsync.app/bitme/session/{PLAYER}/resources/ws")
ack = json.loads(ws.recv())
assert ack["type"] == "subscribed", ack
def refetch_window(): # 4. the universal recovery move
return urllib.request.urlopen(
f"{BASE}/bitme/session/{PLAYER}/resources", timeout=30).read()
while True:
try:
op, data = ws.recv_data()
except websocket.WebSocketException: # closed, any reason
raw = refetch_window(); break # …then reconnect with backoff
if op == websocket.ABNF.OPCODE_TEXT:
msg = json.loads(data)
if msg.get("type") in ("resync", "moved"):
raw = refetch_window(); break # + dictionary on dict_version change
# {"type":"gone", …} → server closes right after; {"ts":…} = heartbeat
elif op == websocket.ABNF.OPCODE_BINARY:
# 3. apply each BMD1 entry onto the window
magic, ver, region, dict_version, n = struct.unpack_from(
"<4sHIIH", data, 0)
assert magic == b"BMD1" and ver == 1
for x, z, word in struct.iter_unpack("<iiH", data[16:16 + n * 10]):
apply_word(x, z, word) # word → tile at (x, z) when in bounds;
# 0 (or water-only) = tile emptied
Decoding a window (Python)
import json, struct, urllib.request
BASE = "https://relay.bitcraftsync.app"
PLAYER = "504403158290646123" # entity_id string from /bitme/resolve
# --- fetch the window (binary!) -------------------------------------------
raw = urllib.request.urlopen(
f"{BASE}/bitme/session/{PLAYER}/resources", timeout=30).read()
magic, version, width, ox, oz, region, dict_version = struct.unpack_from(
"<4sHhiiII", raw, 0)
assert magic == b"BMR1" and version == 1 and len(raw) == 24 + width*width*2
tiles = struct.unpack_from(f"<{width*width}H", raw, 24)
def word(t):
return {
"index": t & 0x3FF, # bits 0-9
"origin": bool(t & 0x400), # bit 10
"direction": (t >> 11) & 0b111, # bits 11-13
"paving": bool(t & 0x4000), # bit 14
"water": bool(t & 0x8000), # bit 15
}
# --- resolve names via the dictionary (refetch on dict_version change) -----
d = json.load(urllib.request.urlopen(
f"{BASE}/bitme/region/{region}/resource-dictionary", timeout=30))
assert d["dict_version"] == dict_version
# separate namespaces: (paving?, index) → entry
names = {(e["paving"], e["index"]): e for e in d["entries"]}
# --- classify every tile: water / paving / resource -------------------------
water = paving = resource = 0
for t in tiles:
w = word(t)
if w["water"]: water += 1
if w["paving"]: paving += 1
elif t & 0x3FF and not w["paving"]: resource += 1
print(f"water tiles: {water}, paved: {paving}, resource tiles: {resource}")
# --- list resource instances (one per origin tile; paving has no origin) ----
for i, t in enumerate(tiles):
w = word(t)
if w["origin"]:
lx, lz = i % width, i // width
e = names[(False, w["index"])]
print(f'{e.get("name", "?")} dir={w["direction"]} '
f'at world tile ({ox + lx}, {oz + lz})')
# paving underlay: tiles where word["paving"] is set —
# names[(True, w["index"])]["name"] gives e.g. "Cobblestone Road"
With curl:
curl -s 'https://relay.bitcraftsync.app/bitme/resolve?name=whisper' | jq .
curl -s "https://relay.bitcraftsync.app/bitme/session/$ENTITY_ID" | jq .
curl -s "https://relay.bitcraftsync.app/bitme/session/$ENTITY_ID/resources" -o window.bmr1
ls -l window.bmr1 # 320024 bytes
curl -s "https://relay.bitcraftsync.app/bitme/world/$X/$Z/resources" -o pan.bmr1
curl -s "https://relay.bitcraftsync.app/bitme/world/$X/$Z/elevation" -o pan.bme1
ls -l pan.bme1 # 145818–149082 bytes (width varies with alignment)
curl -s "https://relay.bitcraftsync.app/bitme/region/$REGION/resource-dictionary" | jq .
# the change stream is a WebSocket — try it with any WS client, e.g.:
# websocat wss://relay.bitcraftsync.app/bitme/session/$ENTITY_ID/resources/ws
# (first frame: the {"type":"subscribed",…} ack; Ctrl-C to leave)
Window error modes
| Status | Meaning |
|---|---|
| 200 | 320,024-byte BMR1 payload. |
| 202 | Empty body — the region grid is still seeding (right after a deploy). Retry with backoff. |
| 400 | {"error": "entity_id must be a u64"}; the
world windows instead say
{"error": "x and z must be i32 world tile coordinates"}. |
| 404 | {"found": false, …} — player not present in any
mirrored region, not on the overworld (interiors have no tile),
or the region has no resource map. A player resolves globally
even when their region is not mirrored, but a session requires
presence in a mirrored one. The world windows instead 404 with
{"found": false, "x": …, "z": …} when the
coordinates fall outside the 5×5 region grid. |
| 503 | Roads cache not enabled (not the case in production). |