For builders

The protocol

Everything a moeba is lives on the ledger in a form anyone can read: one URI, a handful of token Remarks, and thirty-two bytes of genome. This page lays the bytes out so a wallet, an explorer or a marketplace can read them without asking us — and gives the API for those who would rather ask.

Written for a reader who writes code. The same material for a keeper is in the whitepaper and How it works. Where this page and the ledger disagree, the ledger is right.

A rehearsal. This species runs on Xahau testnet before its release: the population may be wiped, the issuing account replaced and the rules changed without notice, and nothing born here carries over. Why →

Where a moeba is

A moeba is a URIToken on Xahau issued by one account, the species: rMmx5nHzoVJAoMTFWkgCCMrZryTaD682tr on Xahau testnet. A URIToken from any other issuer is not a moeba, whatever its URI says; that check comes before any other. Three places hold everything:

One genetic model has ever been minted on chain: v0.1-32-deep, model byte 1. The URI reserves a byte for the model so a wider genome can be added later without touching a creature that exists; a reader that meets a model byte it does not know should say so rather than guess. Everything below describes v0.1-32-deep.

Numbers are big-endian throughout. Hex is upper-case where the ledger shows it; base64url is the RFC 4648 §5 alphabet, no padding, and a decoder must reject a string that does not re-encode to itself.

The identity URI

urn:creature:birth:2: followed by 52 bytes as base64url: 91 characters in all. A URIToken's URI is stored as hex on the ledger; decode the hex to ASCII first.

BytesFieldMeaning
0version02. Version 1 tokens (44-byte payload, prefix urn:creature:birth:1:) exist only on earlier testnet populations and carry no deadline or serial.
1model1-based index into the model table: 1 = v0.1-32-deep, 2 = v0.1-64 (reserved, never minted). 0 or above the table is invalid.
2kind0 founder, 1 born of two parents.
3generation0 for a founder; otherwise 1 + the greater parent's generation, saturating at 255.
4–7birthLedgeru32. The ledger in which the birth's randomness was fixed and its genome settled. The ledger whose hash was the randomness is named in c.audit/1; it closed a moment earlier.
8–9fertilityu16. Births this creature may parent in total. The count used so far is in c.use/1.
10–11rulesu16. The rules version; the profile's hash is in c.rules/1.
12–15maturityAtu32, ledger close time (seconds since the Ripple epoch, 2000-01-01). Mature when the current ledger's close time is at or past it. A founder's is in the past.
16–19serialu32. The admission number; monotonic, never reused, so a burned creature cannot be re-minted.
20–51commitment32 bytes. The hash of the validated request that committed this birth; never all zero. One birth per commitment.

The token id is Xahau's URIToken keylet — a SHA-512-half over the ledger-space prefix, the issuer's AccountID and the URI bytes — so the id is fixed the moment the URI is, and the species can retry a failed mint without producing a second token.

The records

Six Remarks by name. Each value begins with a version byte; a reader that meets a version it does not know reports it as unknown rather than parsing on assumption. Values are at most 256 bytes. The three immutable records and the URI are covered by the token's Digest.

RemarkWrittenLayout
c.birth/1at mint, immutable[0] version 02 · [1–2] development u16 · [3] parent count, 0 or 2 · [4–23] breeder AccountID, 20 zero bytes for a founder · [24–55], [56–87] the parents' token ids when count is 2 (P then Q; P supplies the child's first copy, Q its second) · then the genome, 32 bytes. 56 bytes for a founder, 120 for a birth. A version-1 record without the breeder exists only on earlier testnet populations.
c.audit/1at mint, immutable[0] version 01 · [1] source: 0 laboratory (never on a species deployment), 1 retired and never to be reused, 2 ledger-beacon · [2–5] the source ledger, u32 · [6–37] the entropy, 32 bytes. 38 bytes. For ledger-beacon the entropy is that ledger's hash; you can fetch the ledger and compare.
c.rules/1at mint, immutable[0] version 01 · [1–32] the profile hash. 33 bytes. The profile is the text that describes the rules of birth; the hash is in the Hook's own state too.
c.use/1by the species, after each birth it parents[0] version 01 · [1–2] births used, u16. Absent means zero on a token whose three immutable records are present; on an incomplete token it means unknown, and unknown is not fertile.
c.breed/1by the species at the keeper's request (F=05); removed at F=04[0] version 01 · [1–20] the owner who published, AccountID · [21–24] price per birth in drops, u32 · [25–28] expiry ledger, u32, 0 for none. 29 bytes. The terms bind only while the account named is the token's owner: a transfer closes the creature with no further write.
c.name/1by the species at the keeper's request (F=06), once[0] version 01 · [1–24] UTF-8, NUL-padded to 24 bytes. Trim trailing NULs. Not in the Digest: it says nothing about what the creature is.

The Digest. SHA-512-half( "KIN/digest/1\0" ‖ len(uri) ‖ uri ‖ len(birth) ‖ birth ‖ len(audit) ‖ audit ‖ len(rules) ‖ rules ), each length a u32, the URI as ASCII. It is fixed before the mint, so it cannot name the mint transaction; what it commits to is the identity and the three immutable records in one fixed order.

The genome

Thirty-two bytes: two haplotypes of 16 bytes, the first from parent P, the second from parent Q. Within a haplotype the 36 loci are packed at the bit offsets below, MSB-first — bit 0 of the haplotype is the top bit of its first byte — using 128 of 128 bits. A locus's value on haplotype h is the bits-wide unsigned integer at h × 128 + offset.14 loci are discrete and choose an allele from a table; the rest are quantitative and add up into 22 traits.

#OffsetBitsLocusKindAlleles (index: name, dominance, founder weight /256) or trait
Body (LG1)
003BODY_PLAN
Body plan
discrete0 elongate d3 w1121 compact d2 w802 discoid d1 w483 serpentine d0 w164 bifid (bifid (forked)) d5 mutation-only5 colonial (colonial (lobed)) d6 mutation-only6 polyp d7 mutation-only7 reserved
133SYMMETRY
Symmetry
discrete0 bilateral d2 w1601 radial3 (threefold radial) d1 w402 radial4 (fourfold radial) d3 w403 asymmetric d0 w164 radial5 (fivefold radial) d4 mutation-only5 spiral d5 mutation-only6 radial6 (sixfold radial) d6 mutation-only7 reserved
263REG_SEGMENT
Segment rule
discrete0 linear d1 w1281 doubled d2 w642 paired d3 w483 suppressed d0 w164 hyper d4 mutation-only5 fused d5 mutation-only6 reserved7 reserved
394SEGMENT_COUNT
Segment count
quantitativetrait SEGMENT_COUNT (Segment count)
4134ELONGATION
Elongation
quantitativetrait ELONGATION (Elongation)
5174TAPER
Taper
quantitativetrait TAPER (Taper)
6214BODY_SIZE
Body size
quantitativetrait BODY_SIZE (Body size)
7253HEAD_FORM
Head form
discrete0 rounded d2 w1201 pointed d3 w722 flat d1 w483 crested d0 w164 horned d4 mutation-only5 double d5 mutation-only6 reserved7 reserved
8284HEAD_SIZE
Head size
quantitativetrait HEAD_SIZE (Head size)
Limbs and senses (LG2)
9323APPENDAGE_FAMILY
Limb type
discrete0 fins d2 w961 legs d3 w802 tentacles d1 w483 none d0 w324 spines d4 mutation-only5 wings d5 mutation-only6 frills d6 mutation-only7 reserved
10353REG_APPENDAGE
Limb placement
discrete0 per_segment (one per segment) d2 w1121 fixed (fixed count) d1 w962 terminal (at the ends) d3 w323 alternating d0 w164 radial_all (all around) d4 mutation-only5 none_reg (none) d5 mutation-only6 reserved7 reserved
11384APPENDAGE_COUNT
Limb count
quantitativetrait APPENDAGE_COUNT (Limb count)
12424APPENDAGE_LENGTH
Limb length
quantitativetrait APPENDAGE_LENGTH (Limb length)
13464APPENDAGE_SPREAD
Limb spread
quantitativetrait APPENDAGE_SPREAD (Limb spread)
14504BRANCHING
Branching
quantitativetrait BRANCHING (Branching)
15543TAIL_FORM
Tail form
discrete0 tapered d2 w1281 forked d3 w642 blunt d1 w483 none d0 w164 fan d4 mutation-only5 split3 (three-way split) d5 mutation-only6 reserved7 reserved
16574TAIL_LENGTH
Tail length
quantitativetrait TAIL_LENGTH (Tail length)
17613EYE_FORM
Eye arrangement
discrete0 paired d2 w1441 single d1 w482 multiple d3 w483 none d0 w164 stalked d4 mutation-only5 ring d5 mutation-only6 reserved7 reserved
Colour and pattern (LG3)
18644PIGMENT_A
Base hue
quantitativetrait PIGMENT_A (Base hue)
19684PIGMENT_B
Accent hue
quantitativetrait PIGMENT_B (Accent hue)
20724SATURATION
Saturation
quantitativetrait SATURATION (Saturation)
21763BRIGHTNESS
Brightness
quantitativetrait BRIGHTNESS (Brightness)
22793PIGMENT_CLASS
Pigment class
discrete0 cool d3 w961 warm d2 w802 deep d1 w483 pale d0 w324 iridescent d4 mutation-only5 albino d5 mutation-only6 melanic d6 mutation-only7 magenta d7 mutation-only
23823PATTERN_FAMILY
Pattern
discrete0 plain d0 w961 stripes d2 w642 spots d3 w643 gradient d1 w324 rings d4 mutation-only5 mottled d5 mutation-only6 labyrinth d6 mutation-only7 eyespots d7 mutation-only
24853REG_PATTERN
Pattern rule
discrete0 global (whole body) d1 w1281 segmental (per segment) d2 w802 appendage_only (limbs only) d3 w323 sparse d0 w164 fractal d4 mutation-only5 masked d5 mutation-only6 reserved7 reserved
25884PATTERN_DENSITY
Pattern density
quantitativetrait PATTERN_DENSITY (Pattern density)
26924PATTERN_CONTRAST
Pattern contrast
quantitativetrait PATTERN_CONTRAST (Pattern contrast)
Movement and surface (LG4)
27963MOVEMENT_FAMILY
Movement
discrete0 undulate (undulating) d2 w1121 pulse (pulsing) d1 w642 crawl (crawling) d3 w483 drift (drifting) d0 w324 jitter (jittering) d4 mutation-only5 spiral_swim (corkscrew swimming) d5 mutation-only6 glide (gliding) d6 mutation-only7 reserved
28993ACCENT_PLACEMENT
Accent placement
discrete0 none d1 w1121 appendages (on the limbs) d2 w802 head_tail (head and tail) d3 w483 alternate (alternating) d0 w164 inverted d4 mutation-only5 gradient d5 mutation-only6 reserved7 reserved
291024MOVE_AMPLITUDE
Movement amplitude
quantitativetrait MOVE_AMPLITUDE (Movement amplitude)
301064MOVE_FREQUENCY
Movement speed
quantitativetrait MOVE_FREQUENCY (Movement speed)
311104MOVE_STIFFNESS
Stiffness
quantitativetrait MOVE_STIFFNESS (Stiffness)
321143MOVE_PHASE
Movement phase
quantitativetrait MOVE_PHASE (Movement phase)
331173SURFACE_TEXTURE
Surface
discrete0 smooth d1 w1281 scaled d2 w642 ridged d3 w483 velvet (velvety) d0 w164 plated d4 mutation-only5 translucent d5 mutation-only6 crystalline d6 mutation-only7 bristled d7 mutation-only
341204SURFACE_RELIEF
Surface relief
quantitativetrait SURFACE_RELIEF (Surface relief)
351244CURVATURE
Curvature
quantitativetrait CURVATURE (Curvature)

A reserved allele index is invalid: a genome that carries one is malformed, and no birth produces one. Mutation-only alleles have founder weight 0 and appear only through the mutation pathways at birth.

Expression

What the two copies come to, per locus. This is all a reader needs to name a moeba and list its traits; the body that is drawn from the traits is presentation and may change, the traits do not.

Asking the species

Every request except the poke is an XAH Payment to the species account with hook parameters: one-letter names (as hex: F is 46, P is 50) and values as below. The species' Hooks read them and nothing else; memos and destination tags mean nothing to it. No partial payments, no SendMax, no paths: the amount must be the amount delivered.

FAsks forOther parametersAmountWho
01A founderfounder price + budgetanyone
02A birthP, Q: the parents' token ids, 32 bytes eachbirth fee + budget + Q's or P's price if it is another keeper'sthe keeper of at least one parent; the other must be theirs too or open under valid terms
04Withdraw termsP: the token idenough for one SetRemarks (the site sends 0.1 XAH)its keeper
05Publish termsP: token id; A: price per birth, u32 drops; X: expiry ledger, u32, optionalas aboveits keeper
06Give a nameP: token id; N: UTF-8, 1–24 bytes, printable, no leading or trailing spacename fee + record writeits keeper, once
Poke: sweep a stuck birth, restart a stopped clockan Invoke with Destination the species account; no parameters, no amountthe transaction fee onlyanyone

The amounts are quoted from the ledger at the moment of the request and refused below the quote; an overpayment is kept as budget. The protocol fee for a birth, the founder curve and the name fee are Hook parameters on the species account and are read for you at /v1/issuer (birthFeeDrops, founderCurve, nameFeeDrops, tickDrops). The budget a birth carries is 16 × tickDrops for the clock plus 37 emitted transactions at the network's emission price; what a birth does not spend stays with the species. The founder price is FFEE × (FMUL/100)^⌊n/FTRN⌋ with n the founders admitted so far, all from founderCurve. The site's own arithmetic for this is in its request pages; a wallet can copy it or over-quote slightly.

A request is accepted when the Payment succeeds and the species' Hooks return accept; a refusal is a tecHOOK_REJECTED with the reason in the Hook execution's return string, in plain words. After admission nothing more is signed: the species' own clock fixes the randomness, breeds, mints and delivers by Remit. A receiving account with lsfDisallowIncomingRemit set is refused at admission, since the child could never land.

The API

Everything above is what the ledger says; the API says the same in words and pictures, from an index that re-reads the ledger every few seconds. It is read-only, allows any origin, and every JSON answer carries headLedger so you know what ledger you are looking at. This deployment's is at https://api.xahmoeba.com.

RouteAnswer
GET /v1/moebas/{id}The record — id, model, genome, kind, generation, parents, breeder, owner, fertility and use, maturity, entropy, rules hash, digest, terms, offer — with name (taxon, variant, fingerprint, given, display) and traits (every expressed locus with its label, group, allele and value) already worked out.
GET /v1/moebas/{id}/metadata.jsonToken metadata in the shape NFT readers expect: name, description, image, external_url, attributes as { trait_type, value }. The protocol's facts sit under xahmoeba.
GET /v1/moebas/{id}.svg, .png?size=Its structure diagram, drawn from the genome; a still, on paper, with nothing interactive. size is the height, 64–2048. Immutable for a day.
GET /v1/moebas?owner=&kind=&living=&forSale=&open=&limit=&cursor=A page of records in mint order; ?ids=A,B,C for several.
GET /v1/moebas/{id}/history, /ownersEvery transaction that touched it and what each did; who has held it, with prices.
GET /v1/populationThe whole population in one document, gzipped, with an ETag.
GET /v1/issuer, /v1/queue, /v1/serverThe species' parameters and prices; the birth queue as it stands; the network's reserves and base fee.
GET /v1/ledgersServer-sent events, one per ledger close.

Each client has a budget — a few hundred requests a minute, a fresh picture costing several — and gets 429 with Retry-After over it. Pictures and the population are cacheable; please cache them. A marketplace that shows moebas needs only metadata.json and the picture, and can verify either against the ledger with this page.

Try it on a real one

Choose a moeba, or paste a token id. Its URI is decoded byte by byte on this device by the rules above, the genome is unpacked locus by locus, and each API link opens what the index says about it.

If this page and the ledger disagree, the ledger is right and this page has a fault. Tell us.