01
How It Works
Two players on separate nodes, syncing state via CRDTs. Ship placements stay private until hit.
Lobby Service
lobby.wasm
Game Service
game.wasm
Shared State
CRDT-synced
Private Board
NOT synced
Calimero Runtime · merod
Lobby Service
lobby.wasm
Game Service
game.wasm
Shared State
CRDT-synced
Private Board
NOT synced
Calimero Runtime · merod
gossipsub
✕
✕
◆
Shared state replicates via CRDT deltas over gossipsub. Both nodes converge automatically.
✕
Private boards never leave the node. Shots are resolved locally, only hit/miss results are shared.
02
Namespaces & Access Control
A namespace scopes identity, governance, and app instances. Each battleships lobby is a namespace.
NAMESPACE
root group · identity + governance + app binding
Lobby Context
service: lobby
All members
Player Pool
Ed25519 identities
Match Subgroup
2 players only
Match Context
service: game
P1 + P2 only
Default Capabilities (bitmask = 11)
CREATE_CONTEXT
INVITE_MEMBERS
MANAGE_MEMBERS
Bit 0 · create match contexts
Bit 1 · invite to namespace
Bit 3 · add to subgroups
03
Multi-Service Bundle
One .mpk bundle, two WASM services. Each context picks its service at creation time.
battleships-0.3.0.mpk
multi-service application bundle
Lobby Service
lobby.wasm
6 methods
create_match, set_match_context_id,
get_matches, get_player_stats,
get_history, on_match_finished
3 events
Game Service
game.wasm
9 methods
place_ships, propose_shot,
acknowledge_shot, get_own_board,
get_shots, get_active_match_id, ...
5 events
manifest.json
lobby-abi.json
game-abi.json
battleships-types
GameError, PublicKey (no SDK dep)
04
Private Storage
Ship placements are the core secret. Each player's board is stored locally and never replicated.
PRIVATE
Ship positions
+ cell count
SHARED (CRDT)
Match state
Current turn
Shot results (hit/miss)
placed_p1 / placed_p2
Winner
Pending shots
CRDT
SHARED (CRDT)
Match state
Current turn
Shot results (hit/miss)
placed_p1 / placed_p2
Winner
Pending shots
PRIVATE
Ship positions
+ cell count
05
Cross-Context Calls (xcall)
When a game ends, the game context notifies the lobby to update stats and history.
Game Context
All ships sunk → winner determined
calimero_sdk::env::xcall(...)
JSON payload
{ match_id,
winner, loser }
Lobby Context
on_match_finished()
Update player_stats
Append to history
06
Complete Game Flow
Eight steps from namespace creation to final score update.
1
Create Namespace
Host creates a namespace with the battleships app. Sets default capabilities (bits 0+1+3 = 11).
2
Create Lobby
Lobby context created in namespace root group with service_name: lobby
3
Invite Player
Recursive namespace invitation covers root + all subgroups.
4
Player Joins
joinNamespace → auto-gets identity → joins lobby context.
5
Create Match
Lobby allocates match ID. Create subgroup → add P2 → create game context with service_name: game
6
Place Ships
Both players place ships in private storage . placed_p1/placed_p2 flags synced via CRDT.
7
Take Turns
propose_shot → event triggers acknowledge_shot_handler on target node → resolves against private board → result synced.
8
Game Ends
All ships sunk → winner set → xcall to lobby → stats and history updated.
07
CRDT State Sync
All shared state replicates automatically between nodes. Each mutation creates a delta broadcast via gossipsub.
DAG-based
Deltas form a causal DAG. Concurrent writes merge automatically without coordination.
Encrypted
Deltas are encrypted with the group's shared key before broadcast over the network.
Eventual
Nodes converge to the same root hash. No central coordinator needed.
The sync layer is service-aware : when applying a delta from a peer, the node loads the correct WASM service (lobby or game) based on the context's service_name to execute __calimero_sync_next.
08
Project Structure
■ battleships/
├─ app/ React + TS
│ ├─ src/hooks/ — useBattleshipsLobby, useNamespaceBootstrap
│ ├─ src/api/lobby/ — LobbyClient (codegen)
│ └─ src/api/game/ — GameClient (codegen)
├─ logic/ Cargo Workspace
│ ├─ crates/types/ — GameError, PublicKey (no SDK dep)
│ ├─ crates/lobby/ — LobbyState + 6 methods → lobby.wasm
│ ├─ crates/game/ — GameState + 9 methods → game.wasm
│ └─ build-bundle.sh — builds both + packages .mpk
└─ e2e/ merobox — E2E workflow
09
Calimero Platform Features
Namespaces Identity scoping, recursive invitations, subgroup-based access control
Multi-Service Bundles Two WASM services in one .mpk, service_name-based context creation
Private Storage #[app::private] for ship boards — never replicated between nodes
CRDT Sync Automatic state replication with DAG-based causal ordering
xcall Cross-context calls from game → lobby for match results
Event System app::emit! for real-time UI updates via SSE subscriptions
Capabilities Fine-grained permission bits for member actions
Governance DAG Membership, roles, and context registration via signed ops
Subgroups Per-match access isolation — only 2 players per match group