Protocol Stack
Architecture/protocol decisions only. Product requirements live in Vision. Delivery status lives in Execution Tracker.
This document describes an actionable architecture that keeps WEFA local-first while enabling global play, verifiable outcomes, and progressive decentralization using:
- EAS (Ethereum Attestation Service) for verifiable receipts/claims
- AT Protocol for identity + discovery + social distribution
- Unlock Protocol for membership/access primitives (Locks/Keys)
This is intentionally layered so we can swap providers and reduce centralized infrastructure over time.
Design Goals
- Local-first core loop: the game works offline; sharing/proving is opt-in.
- Progressive decentralization: start with offchain proofs and optional social, then selectively anchor/timestamp onchain, then evolve toward an autonomous world.
- Youth-safe defaults: do not leak location, faces, or plant photos publicly by default.
- Replaceable infra: any relay/indexer/backend should be treated as plumbing, not a hard dependency.
- Ship-first constraints: Base + Base Sepolia to start; avoid custom servers unless they unlock a core loop or safety requirement.
Layered Architecture
1) Realtime Session Layer (Gameplay State)
Purpose: low-latency match state + AR scene state for active players.
- Transport: Yjs (Local = WebRTC; World = hybrid WebRTC + websocket relay).
- Scope: keep documents small. Sync moves and a few anchor transforms, not per-frame physics.
2) Proof Layer (Receipts + Achievements + Identity Bindings)
Purpose: portable, verifiable claims about what happened.
- Offchain EAS attestations: signed by a wallet/smart-account key; shareable; verifiable by anyone.
- Optional onchain timestamping: batch-anchor UIDs at milestones for immutability.
- Trust level depends on who signs (self, peer, witness, issuer), not on whether it is offchain vs onchain.
3) Social/Discovery Layer (AT Protocol)
Purpose: account portability + distribution of proof references + invites and discovery.
- Use AT OAuth for user authorization and portable identity (DID).
- Store WEFA records in the user’s repo (custom lexicons) or post references in standard feeds.
4) Access/Membership Layer (Unlock Protocol)
Purpose: memberships, passes, and event access (including time-bound access).
- Locks (contracts) mint Keys (NFT memberships) with expiration/renewal semantics.
- In-app checks should use
getHasValidKey(address)(or equivalent) rather than onlybalanceOf. - Unlock is optional: do not block the core loop on membership checks.
Identity Model (What We Persist)
WEFA should treat identity as a graph, not a single account type.
Primary local identity
Player.id(Dexie) remains the local-first anchor.
Linked identities (optional)
- EVM address (wallet or smart account)
- AT DID + handle
- Unlock memberships (by lock address + chain id + validity)
Recommended rule:
- Login supports: passkey, wallet, or AT Protocol (Bluesky).
- EAS + Unlock require an EVM identity. AT-only users can play locally, then optionally “upgrade” by linking an EVM identity later (wallet or passkey smart account).
- AT and EVM can be linked with an explicit
wefa.identityLinkproof (see schemas).
What We Prove With EAS (Schemas)
Keep schemas stable, compact, and privacy-aware. Prefer “event receipts” that reference local event IDs and content hashes.
Minimal V1 schemas (offchain)
-
wefa.careEventplayerId(string) orplayerDid(DID string)plantId(string)actionMask(uint32) oractions(bytes32 hash)photoHash(bytes32) (hash of the photo file or perceptual hash)streakDays(uint16)energyEarned(uint16)timestamp(uint64)
-
wefa.gameResultsessionId(string)gameType(uint8 enum)mode(uint8 enum: device/local/world)players(two addresses or DIDs)winner(uint8 or 255=draw)joinCodeHash(bytes32) (never store raw join codes in attestations)timestamp
-
wefa.evolutioncreatureId(string)fromStage/toStage(uint8)xpSpent(uint32)timestamp
Optional binding schema (identity link)
wefa.identityLinkplayerId(string)evmAddress(address)atDid(string)timestamp
This allows portable “same person” linking across EVM and AT without relying on a centralized account database.
Trust Model (Make Claims Meaningful)
EAS is a receipt layer; meaning comes from who signs.
Recommended WEFA trust levels:
- L0: local-only (Dexie), no attestation
- L1: self-attested (player signs)
- L2: peer-attested (both players sign matching
wefa.gameResultreceipts) - L3: witnessed (a “Shrine host” or event organizer signs a witness attestation referencing the same match UID)
- L4: issuer verified (Green Goods / partner org signs after checking evidence)
You can show this in UI as “Proof Strength”.
AT Protocol Integration (Actionable Records)
AT Protocol should primarily reference proof artifacts, not store sensitive raw evidence.
Suggested WEFA record types (custom lexicons)
-
com.wefa.profile- element, biome, creature IDs (safe fields only)
- optional “public mode” toggles
-
com.wefa.proofRefeasChainIdschemaUidattestationUidkind(careEvent/gameResult/evolution/identityLink)createdAt
-
com.wefa.matchInvite(private-by-default)gameType,mode- encrypted payload containing join code + relay hint (avoid public broadcasting)
Discovery strategy (v1)
- Friends/family: share a code directly in-app (QR/code).
- Community discovery: optional AT post that says “Match open” but requires explicit consent and safe defaults.
- Growth strategy: allow “Sign in with AT” so Bluesky-native users can join WEFA immediately, then link EVM later for proofs/memberships.
Unlock Protocol Integration (Membership + Events)
Unlock is best treated as an access primitive for:
- tournaments
- “Shrine” events (physical spaces)
- seasonal campaigns
- spectator access for livestreams (optional)
WEFA decision: deploy a WEFA-managed Lock (Base + Base Sepolia) and customize via metadata + hooks.
Customization targets (recommended):
- Metadata: brand, event/shrine identity, terms.
- Pricing/expiration: time-bound passes for events (day/week/season).
- Hooks:
- On key purchase/renewal, emit a WEFA-specific event and/or create a WEFA receipt attestation reference.
- Optional “referral” or “team” attribution stored as lock data (avoid storing PII).
How the app checks membership
Given a configured lockAddress + chainId:
- read
getHasValidKey(userAddress)(or equivalent) from the lock - optionally read
keyExpirationTimestampFor(userAddress)for UI (“expires in X days”)
Cache the result locally with a timestamp; allow degraded offline behavior:
- do not block “Device” or “Local” gameplay if offline
- only require fresh membership check for “World” events that explicitly require it
Unlock purchase UX (optional)
If a player lacks membership, offer:
- “Get a Pass” flow that opens an Unlock checkout
- keep this behind a consent gate and clearly explain any costs
End-to-End Flows
Flow A: Match Result -> Proof -> Social Share
- Match is played (Yjs realtime state).
- App writes local
gameSessions+gameResults+eventLog. - Job queue creates
wefa.gameResultoffchain EAS attestation (L1). - If opponent is present, request opponent signature for L2 proof (optional).
- Optionally batch timestamp UIDs onchain (milestone or “sync now”).
- If AT is linked, write
com.wefa.proofRefrecord (or a post referencing the UID).
Flow B: Plant Care -> Proof -> Optional Issuer Verification
- Player submits care with required photo.
- App writes local
careEvents+plantsupdates +eventLog. - Create
wefa.careEventoffchain attestation withphotoHashonly. - Optional: if user opts in, upload evidence to a user-controlled store (IPFS/AR) and reference the CID hash (not raw URL).
- Optional: issuer attests verification (L4) referencing the original UID.
Flow C: Membership-gated Tournament
- Player attempts to join a “World Tournament”.
- App checks Unlock
getHasValidKey(address)for the configured lock. - If valid: allow join; optionally attest “participation” referencing lock + match UID.
- If not valid: show “Get a Pass” (Unlock checkout) and allow non-tournament play modes.
Mapping To Current Codebase (Where This Fits)
Current state in /packages/app:
- Local-first persistence: Dexie in
src/modules/db.ts - Job queue:
src/modules/job-queue/*(handlers not implemented yet) - Attestation stubs:
src/modules/attestation/*(currently pseudo/offchain) - Config toggles:
src/config.ts(hasfeatures.onchainAttestations = false)
Minimal implementation steps (next)
- Replace pseudo-attestations with real EAS offchain signatures:
- new module
src/modules/attestation/eas-offchain.ts - use
@ethereum-attestation-service/eas-sdk
- new module
- Add job handlers:
- register handlers for
care-event,nurture-event,evolution-event,game-result - handlers read local DB rows, encode schema data, sign, persist to
db.attestations
- register handlers for
- Add AT OAuth “link account”:
- new module
src/modules/atproto/* - store DID + handle and refresh tokens in IndexedDB
- new module
- Add Unlock membership checks:
- new module
src/modules/unlock/* - call
getHasValidKeyand cache results
- new module
Autonomous World (MUD) — Optional But High-Leverage
MUD is valuable if WEFA is evolving into a composable public world with shared global state (Shrines, tournaments, seasonal events, map/spaces) where other builders can integrate without permission.
MUD is not valuable (or should be delayed) if:
- the main outcomes are private/local and don’t benefit from shared, canonical state
- you need high-frequency state changes (daily care for many players) onchain
- you need to store large evidence (photos, models), which does not belong onchain
Recommended hybrid:
- Dexie remains the local source for raw events and evidence references.
- EAS is the cross-app proof plane (append-only receipts).
- MUD holds only “world-facing aggregates” and “gatekeeping state”:
- tournament ladder / scores
- shrine memberships, shrine state (season progress)
- milestone flags (care streak 7, first tournament win)
Concrete integration point:
- Use an onchain “accept proof” system that takes:
- an EAS UID (or a tuple of {schemaUid, payloadHash, signature(s)})
- verifies validity rules (signature, optional witness, optional Unlock membership)
- writes to MUD tables (e.g.,
Match,Energy,Care,Members)
This is the “no centralized server” bridge: anyone can submit the proof transaction, and the world verifies it.
Local Execution -> Onchain Verification (ZK Roadmap)
Zero knowledge helps when you need:
- correctness without trusting a server (anti-cheat) AND
- you can’t (or won’t) reveal the full transcript publicly
For WEFA, ZK is most realistic for deterministic board games (Tic-Tac-Toe, Mancala) and least realistic for real-world plant care without sensors/oracles.
Game results proof (in increasing sophistication)
-
Peer signatures (recommended first)
- Both players sign the same
wefa.gameResultpayload (EIP-712). - Anyone can submit it onchain later to update a scoreboard/world state.
- This is highly decentralized and cheap, and it scales well.
- Both players sign the same
-
Onchain replay (simple, public)
- Submit the move list; contract replays rules to compute the result.
- Best for very small transcripts (Tic-Tac-Toe).
- Still needs a way to prove the opponent participated (e.g., both sign the match start).
-
ZK correctness proof (private or compressed)
- Submit a proof that “given committed transcript, the winner is X”.
- Contract verifies proof, updates world state.
- Tooling options:
- Circom/Plonk/Groth16 (classic, smallest verifiers)
- Noir (developer-friendly circuits)
- zkVM (SP1/Risc Zero) if you want to write verification logic in Rust and generate EVM proofs
Important: ZK does not prove “a real opponent existed” by itself. You still need opponent consent/commitment (signature or onchain join).
Plant care proof (reality/oracle problem)
Plant care is a physical-world claim; ZK alone cannot prove you watered a plant unless you introduce a trusted measurement source.
Recommended “proof ladder”:
- L1 self attestation: photoHash + timestamp + streak in EAS (local-first, opt-in sharing).
- L3 witness attestation: a shrine host or friend signs an attestation referencing the same care UID.
- L4 issuer attestation: partner org verifies evidence offchain and signs a verification attestation.
ZK is still useful for privacy/anti-sybil around plant care:
- ZK geofencing: prove “I’m within this region” without revealing exact coordinates (future).
- ZK membership: prove “I hold a valid WEFA pass” without revealing which address (future; Semaphore-style).
UGC + Autonomous World Composition
UGC is the growth engine. Autonomous world state is the coordination engine. They should connect through proofs, not through a centralized CMS.
What goes where
- Local device/Dexie:
- raw draft content
- private evidence
- editing history
- Content-addressed store (IPFS/Arweave or equivalent):
- immutable artifacts (model files, packs, challenge definitions)
- AT Protocol:
- identity-linked publish metadata
- social discovery and distribution
- moderation and curation signals
- EAS:
- provenance and moderation receipts (
publish,remix,flag,canonize)
- provenance and moderation receipts (
- Autonomous world registry (MUD-compatible):
- canon set for each season/shrine
- tournament/ruleset references
- accepted milestone pointers (EAS UIDs)
Canonization pipeline
- Creator (human or AI-assisted) produces UGC artifact and content hash.
- Publish metadata to AT record (opt-in visibility).
- Emit EAS provenance attestation (
wefa.ugc.publish). - Collect curation signals and moderation outcomes.
- Promote to canon candidate.
- Canon decision recorded as
wefa.ugc.canonize. - Autonomous world registry references accepted content hash + proof UID.
This makes canon state reproducible by third parties from public proofs.
AI agent usage model
- Agents can draft content and suggest remixes.
- Agents can run policy checks and quality checks.
- Agents should not directly canonize content.
- Human approvers or explicit governance logic issue canonization proofs.
- All agent-generated public content should carry provenance labeling.
High-value use cases
- Community creature packs and shrine challenge packs with transparent provenance.
- Seasonal tournament rulesets where accepted game variants are publicly auditable.
- Education partners publish local ecology modules that can be canonized by region.
- Creator economy where remix lineage and contribution credits are preserved.
Decisions (Confirmed)
- Target chains: Base (8453) and Base Sepolia (84532).
- Unlock: deploy a WEFA-managed Lock and customize via metadata + hooks.
- AT Protocol: enable “Sign in with AT” (not link-only) to onboard Bluesky users, then optionally link EVM for proofs and memberships.