Ramble
Ramble is a proximity extension: you leave marks (notes pinned to a place) and caws (short-lived presence pings), and you see the ones other people left near you. It ships as an installable bundle with an MCP server, a dashboard panel with a map, and a Nostr transport that runs inside the gateway. A blue dot with an accuracy ring follows you on the map; Around you toggles follow mode (dragging the map turns it off).
Phase 1 is deliberately narrow:
- Geo only. The privacy grid has
bleandlanchannels, but onlygeois wired. - Contacts and groups travel as DMs. Marks with visibility
contactsorgroup:<uid>are sealed for each recipient (NIP-44, one DM per contact) and never touch a public relay in the clear — see below. Gifts and swaps use the same door. - Public
lockedmarks are a client-side teaser gate, not cryptography. The content is on the relay in the clear; the panel simply declines to show it until you prove proximity. Do not put a secret in one.
Privacy
Everything is off by default. Nothing leaves the box until you turn on both:
- the master switch ("I'm visible"), and
- the specific (audience × channel) cell — for phase 1 that is
public×geo.
Both live on the panel's privacy grid, stored as flat rows in ramble_settings (master, grid.<audience>.<channel>). They are user state, so they replicate across your own instances.
The public identity level picks which key signs what you publish:
| Level | What goes on the wire |
|---|---|
rotating (default) | Marks are signed by a stable per-seed world pseudonym; caws get a fresh key each boot, so presence pings are not linkable across restarts. |
pseudonym | Both marks and caws use the stable world pseudonym — consistent identity, no link to your real Crow id. |
real | Your instance's own key and crow_id. Anything you publish is attributable to this Crow. |
World name. In the Visible sheet you can set a world name (up to 24 characters) that strangers see on your public marks and caws instead of a bare key — but only while your Name level is pseudonym or real; at rotating nothing but the short key goes out. It is unverified, so a stranger's name is always shown with the first four characters of their key ("Casey · f665"). Contacts never see it: they see your Crow name, or the name they saved for you. Your own marks read "your mark".
How publishing works
Authoring is local and synchronous; the wire is not. A new mark is stored with publish_state = 'pending', and the gateway's transport publishes it on the next drain tick (every 15 s, or immediately when authored in-process).
publish_state values:
| Value | Meaning |
|---|---|
pending | Waiting for a drain tick, or the grid gate is closed, or (contacts/group) at least one recipient's DM has not been accepted by a relay yet. |
published | At least one relay accepted the event — or, for a contacts/group mark, every recipient's DM was accepted (a mark with nobody to send to is published at once). |
synced | Written by instance sync from another of your own Crows. |
remote | Received from a relay — someone else's mark. |
failed | Parked after 20 rejected attempts, so one poison row cannot occupy a drain slot forever. |
A failed row is terminal until an operator re-arms it:
UPDATE ramble_marks SET publish_state='pending' WHERE mark_id=?;Deletes are asynchronous too. Deleting a published mark writes a ramble_tombstones row; the transport turns it into a NIP-09 kind-5 event on the next tick and removes the tombstone only once a relay accepted it.
Expiry
Marks and caws carry a TTL, and defaults differ by kind:
- public marks: 24 h
- caws: 1 h
contacts/groupsmarks: no expiry
The sweep runs on the same 15 s drain tick. Expired rows are deleted locally and the delete is emitted to instance sync, so your other Crows drop them too. An expired mark cannot be unlocked even in the window before the sweep catches it — ramble_unlock refuses it.
Your egg and your bird
You are the egg. Real-world activity credits warmth toward it; at the hatch threshold you hatch into a bird, and the incubating egg after that is the next you.
Eggs come from three places, and none of them is free:
- Nests — walk to one on the map, one claim per local day, within 75 m.
- Gifts and swaps from contacts.
- Laying — while you hold no egg at all, every local day you end happy counts one. At
lay.days(default 14) you lay one yourself. Days need not be consecutive, and the count only runs while you are eggless, so this is a floor rather than a faucet.
Whenever your incubating slot is empty and an egg becomes yours to warm, the oldest egg on your shelf moves in automatically — so you are only ever eggless when you genuinely hold none. That happens when an egg hatches, when a gift lands, when a swap completes, when a swap is declined or withdrawn and hands your egg back, and when you claim a nest. It happens on the Crow that received the egg, and your other Crows simply copy the result.
Two cases wait instead, and the pet page offers a one-tap Warm it for them:
- An offer that lapses after seven days frees its egg without warming it. The other side's reply can still arrive and complete the swap; if it does, the egg you offered leaves even if you have started warming it in the meantime.
- If one of your Crows hatches an egg at the same moment another one receives a gift, the two can settle with the slot empty and the gift on your shelf. Both Crows agree; they just leave the choice to you.
Warmth earned with an empty slot vanishes, even if you are holding eggs on your shelf — so warm a waiting egg before you go walking.
| Setting | Default | Governs |
|---|---|---|
lay.days | 14 | Happy days while eggless before you lay one yourself |
| Event | Warmth |
|---|---|
| Visiting a new place | 20 |
| Leaving a mark | 15 |
| Unlocking a mark | 10 |
| Meeting a nearby crow | 20 |
| Checking in | 8 |
| Hatch threshold | 100 |
Each event is idempotent per its own key, so repeating the same real-world action never stacks warmth:
- Visiting a place credits once per geohash-7 cell per ISO week, and only from your real position (
here, from the browser's geolocation) — panning the map to a new cell never credits. - Checking in credits once per local calendar day.
- Meeting a crow credits once per (persona, ISO week) pair.
- Leaving a mark and unlocking a mark have no repeat key — every one is credited.
Meeting crows is additionally capped at 5 credits per local calendar day (MEET_CROW_DAILY_CAP in bundles/ramble/server/eggs.js) — a persona is just a pubkey anyone can mint, so without that ceiling a flood of spoofed personas could force hatch after hatch; meetings past the cap credit nothing and leave no ledger row.
When warmth reaches the hatch threshold, a species and a seed are rolled server-side (crypto.randomInt, never Math.random, so the roll can't be predicted or replayed); the bird's look is unique to that seed. If an egg is waiting on your shelf it moves into the slot; otherwise nothing new starts, and the next one has to be found, given, or laid.
Your active bird rides on your public caws and marks — the wire JSON carries bird: { species, seed } so other people see it on your pins. Contacts and "Just me" marks never reach the Nostr wire (see below), so the bird is omitted from the wire only: those rows still store bird_species / bird_seed locally and replicate, bird and all, to your own linked instances.
Once a bird has hatched, the Nest header crow becomes it: its face reflects the pet's energy (see Chores), while the "!" alert badge is unrelated and still means host health, not pet mood.
Render any bird from its species and seed with:
GET /api/ramble/bird/:species/:seed.svg?mood=happy|tired|alarmedDashboard-authenticated, returns image/svg+xml, cached privately for a day. An unknown species or a malformed/unknown seed answers 400.
Chores
Three chores — feed, preen, play — are yours to do once each per local day. Each completion gives the pet +8 energy; none of them touch the egg's warmth.
POST /api/ramble/pet/chore { kind: "feed" | "preen" | "play" } completes one. A repeat for a kind already done today is a no-op (done: false); either way the response carries the pet's current state.
Walking
Set a daily step goal on the pet page (6,000 to start; anywhere from 2,000 to 30,000). Walking toward it tops up your bird's energy as you go — up to +30 a day at the goal — and reaching it pays 3 bird seed. A day you miss costs nothing.
- In the Crow Android app (1.6.0 or later) tap Count my steps once and allow Physical activity. The app reads the phone's own step counter whenever you open Ramble; nothing runs in the background. The first day may start at zero ("counting from now") unless the phone was restarted today.
- Anywhere else (a phone browser, an iPhone, a phone without a step counter) tap I walked today. It cheers your bird up (+15) but pays no seed, since there is no count behind it.
Who sees what. Your step count stays on your own Crows. Contacts see only a small "walked today" mark on your bird's profile picture — never a number. The mark appears when you check in or pass 2,000 steps, and clears after midnight. Because it appears when you walk, it does say roughly when you walked.
The evening nudge. If you have been walking with Ramble this week and by 6 pm you are under half your goal, your bird sends one gentle reminder through Crow notifications. Never more than one a day. Turn it off, or keep weekends quiet, under Goal and reminders on the walking card.
API: GET /api/ramble/steps, POST /api/ramble/steps/reading { device_id, counter, elapsed_ms, boot_count }, POST /api/ramble/steps/walked, PUT /api/ramble/steps/settings { goal?, nudge?, nudge_weekends? }.
Nests and the egg shelf
Nests are spawn points in the world. Each ISO week, every geohash-7 cell (about 150 m square) either has a nest or not, decided by a public formula — sha256("ramble-nest-v1:" + cell + ":" + week), a nest when the first 32 bits mod nest.rate (default 24) is 0 — so everyone sees the same nests with no server involved and nothing about people is revealed. The map shows them as egg pins once you zoom in (zoom 15 or closer), fetched from GET /api/ramble/nests?bbox=south,west,north,east.
Walk within 75 m of a nest and tap Take the egg (POST /api/ramble/nests/claim): a new egg lands on your shelf (unhatched, warmth 0, marked with the cell and week it was found in), or goes straight into the incubating slot if nothing is warming. Limits: one claim per local day and a shelf cap of 5 (shelf.cap); both refusals come back as a friendly reason, not an error. Claiming the same nest twice returns the same egg. A claim credits no warmth and feeds no energy — the egg is the reward. Claims are recorded per instance (ramble_nest_claims) and never replicate; the egg itself does.
Exactly one egg incubates at a time. From the Flock screen you can incubate any shelf egg (POST /api/ramble/eggs/:id/incubate); the one it replaces goes to the shelf keeping its warmth. Instance sync distinguishes an egg you parked (shelf_origin = 'user') from one the sync layer shelved while reconciling two instances ('sync'): only the latter is ever pulled back into the incubating slot automatically.
The map unlocks as you walk. Ground you have actually stood in stays unlocked for good: you can read the marks and caws left there and claim any nest. A few blocks further out is the frontier, where you can see that something is waiting without seeing what it is. Everything beyond that is fog until you go there. Only the public map works this way — a contact's mark always reaches you wherever you are. Walking ground you have already unlocked turns up bird seed, which regrows after a day. Seed appears in about one cleared cell in four, at a spot inside it, so a walk has a handful of places worth heading for rather than one in every square; you collect it by walking there, not by tapping. Zoom out to see the whole shape of the ground you have cleared. Now and then a new place also holds a heart container, which permanently lengthens your bird's energy bar — about one place in three the first time you enter it, and much more rarely in ground you have already cleared. Hearts are the only thing that raises the maximum; they buy your bird a longer stretch between walks before it droops, and nothing else. A heart you have not collected shows on the map wherever it is waiting, so places you cleared before hearts existed are worth walking again. You collect one by walking to it, the same way you collect seed.
Your flock
Every hatched bird stays in your flock. The Flock screen (GET /api/ramble/flock) lists them with the active one tagged — that is the bird on your map, in the Nest header and on your public caws — and tapping another bird activates it (POST /api/ramble/birds/:id/activate). The score is species found out of the 8 kinds; a second bird of a kind you already have is still a bird, just not a new kind.
Wardrobe
Bird seed buys things to wear: hats (bow, leaf, beanie), scarves (knitted, striped) and glasses (round, shades). Open Wardrobe from your bird's page. Buy something once and any of your birds can wear it; each bird remembers its own outfit, so switching birds switches clothes. Taking a hat off brings back whatever hat the bird hatched with.
What you wear shows on your bird's page, your flock, your marker on the map, the AR view, the bird in the dashboard header, and your profile picture if you use your bird as one — which is how contacts see it. Strangers never do: public marks always carry the plain bird you hatched, because a chosen outfit would tie your rotating map identities together.
Your profile picture also shows how you are doing. A bird left without walks or chores looks tired, then rattled, to your contacts — nothing worse than that ever happens. Changes to the picture are gathered up and sent once things settle (about twenty seconds after your last change), and a quiet stretch is noticed within half an hour, so trying on four hats sends your contacts one picture, not four.
Purchases are recorded the same way seed pickups are, as a ledger on each of your Crows that syncs between them. If two of your Crows buy while out of touch with each other, both purchases stand once they reconnect, and your balance can briefly read below zero; it climbs back as you collect seed, and nothing can be bought until it does. A Crow still running a Ramble older than 0.13 does not know about purchases and shows the balance as it was before them until it is updated.
| Item | Price (seed) |
|---|---|
| Bow, Leaf | 8 |
| Beanie | 12 |
| Knitted scarf | 15 |
| Striped scarf, Round glasses | 20 |
| Shades | 25 |
API: GET /api/ramble/wardrobe, POST /api/ramble/wardrobe/buy { item }, POST /api/ramble/birds/:id/outfit { slot, item | null }.
Contacts and groups
A mark for Contacts goes to every full contact (unblocked, not a bot, not a pending request) as one NIP-44 DM each, signed by this instance's key — the same door every Crow DM uses. A mark for a Group goes to the members of that contact group (group:<group_uid>, the groups from the Contacts panel; the Ramble panel only shows the Group audience when you have one). The DM's only tag is the recipient; the mark's text, place and bird are ciphertext. Nothing about a contacts or group mark reaches a relay in the clear.
Delivery is queued, not immediate: authoring writes one ramble_outbox row per recipient, and the gateway transport sends them on its drain tick (every 15 s, or at once when you author from the panel). A contacts mark is gated by the privacy grid like a public one — the contacts (or groups) × geo cell and the master switch must be on, or it waits in the queue. The row flips to published once every recipient's DM has been accepted by a relay (or dropped because that contact is gone). A recipient stores it as a persistent contacts mark, named after the contact, and it counts as meeting their bird for warmth.
A contact's marks are bounded by retention: you keep the newest 50 from each contact and older ones are pruned as new ones arrive (their created_at comes from the sender, so a per-day cap would be their clock to game). Blocking stays the hard stop.
Contacts delivery is contact-only in both directions: a DM of this kind from someone who is not a contact is discarded and never becomes a message request. A stranger's pin on your map offers Share an invite, which opens the Contacts panel — become contacts first, then trade.
The queue is per instance: only the Crow you authored on sends a mark, a gift or an offer. All your Crows share one Nostr identity, so each of them receives what a contact sends and applies it; the rows then agree through instance sync. A swap step a contact answers is completed on each of your Crows, and each sends the confirmation — the contact simply ignores the copies.
Gifts and swaps
Any unhatched egg on your shelf — claimed from a nest or received from someone — can be gifted to a contact (POST /api/ramble/eggs/:id/gift { crow_id }, tool ramble_gift_egg). The egg leaves your shelf as gifted and arrives on theirs as received, still unhatched: the wire carries only { egg_id, warmth, found_cell, found_week }, never a species or seed — whoever hatches it rolls the bird. A received egg shows "A gift · from <name>" and can be incubated, gifted on, or offered in a swap. Received eggs do not use one of the five nest-claim spots. A gift delivered twice is stored once; an egg you gave away that comes back to you simply returns to your shelf.
A swap is an offer of one of your eggs for one of theirs (POST /api/ramble/trades { egg_id, crow_id }, tool ramble_propose_swap). The contact sees the offer on their Flock screen and answers with an egg of their choice (Accept, POST /api/ramble/trades/:id/accept { egg_id }) or Decline; you can Withdraw an unanswered offer. Eggs change hands only when the swap completes — on each side, atomically — and an egg named by an open offer is locked (it cannot be incubated, gifted or offered again until the offer closes). Offers lapse after seven days on each side; a lapsed offer releases the egg. If your answer arrives after the offer lapsed on their side, they reply with a decline and your egg is released. Accept and decline are panel actions (there is no MCP tool for them).
Everything here is contact-only and encrypted, and bounded: a contact can have at most 20 open offers with you and give you at most 20 eggs a day; anything past that is ignored. An offer, an answer or a completion that names an egg you still hold is ignored. There is no market, no ledger of value and no scarcity: if the two sides disagree at the very moment an offer lapses, the worst case is a duplicated egg, never a lost one. Meeting a contact through a mark counts toward warmth the same way meeting a stranger does (per key, per week).
The AR view
Tap Look around on the map to open the AR view: the rear camera fills the screen and every mark, caw and nest within about 500 m gets a label placed by direction and distance. Labels within 35° of where you face sit on the picture, nearer ones lower and larger; the rest park at the left or right edge with an arrow, stacked by distance. Locked marks are dashed labels with a walking distance measured to their cell centre (they carry no exact position, same as on the map). Tapping any label opens the same actions as its map pin — unlock, take the egg, read the text, share an invite. Your active bird sits at the bottom, hops when a label comes into view, and says what is nearest. A caw that only carries its coarse publish cell is listed as "somewhere in this area" and never given a direction. Within 75 m of a nest its label turns gold and shows the egg itself; tap it for the sheet, press Take the egg, and the label, the pin and the button pulse while your position is checked, then the egg flies down toward your bird when it is yours (the map pin pops the same way).
Position comes from watchPosition; heading from deviceorientationabsolute (Safari reports webkitCompassHeading on the plain event, and iOS asks once for motion access from the button tap). No fix, no camera or no compass falls back to the radar strip — a bearing ring (north up, or heading up when there is a compass) and a distance list — so the screen is never blank; the first open explains the limits (compass accuracy, the iOS prompt, no surface placement, the camera stays on the phone). The camera picture never leaves the device: the view is client-side rendering with no capture, canvas or upload, and the stream stops when you close the view or switch away from the tab (it restarts when you come back).
The panel fetches GET /api/ramble/around?lat=&lon=&radius_m= (radius 50–1000 m, default 500): marks as stored (a locked mark as its cell-centre teaser, a contact's mark named, a stranger's world name with its key tail), each with distance_m, plus this week's nests, nearest first. The panel lists what the map lists (public, contacts and your own "Just me" marks). It refreshes after you move 50 m, once a minute, when you close a tapped label, and on every live event. It is a read and credits nothing. The view needs a secure context: open the Nest over its HTTPS Tailscale Serve address, not a raw-IP http:// URL, or the browser refuses the camera and the compass and you get the radar strip.
Just me marks
visibility: "private" marks are for you alone: they never leave the instance over Nostr, so no relay or contact ever sees them. Unlike public and contacts marks, they're persistent by default (no TTL) and open by default (no proximity gate).
"Just me" still means you, everywhere: a private mark replicates to your own linked instances via instance sync, so it follows you across your Crows — it just never crosses onto the Nostr wire. On the map, a private mark carries a "Just me" tag.
Map tiles
The map never talks to a tile host directly. It loads /ramble/tiles/{z}/{x}/{y}.png, a same-origin, dashboard-authenticated proxy, so the tile provider never sees your browser or your session. The upstream is the ramble_settings key tile_url; the default is OpenStreetMap (https://tile.openstreetmap.org/{z}/{x}/{y}.png). The attribution line under the map comes from tile_attribution.
There is no UI for either yet — set them with SQL against crow.db:
INSERT INTO ramble_settings (key, value) VALUES ('tile_url', 'https://tiles.example.org/{z}/{x}/{y}.png')
ON CONFLICT(key) DO UPDATE SET value = excluded.value;The proxy only accepts an http(s) template containing {z}, {x} and {y}, and only serves image responses.
Configuration
| Env var | Default | Effect |
|---|---|---|
RAMBLE_DEFAULT_GEOHASH_PRECISION | 5 | Geohash precision of the cell tag published with each mark (5 ≈ a 4.9 km cell). Clamped to 1–12. Lower is coarser and more private. |
Warmth weights
Every weight from the table above is also a ramble_settings override, read live on every credit — no restart needed. An override must be a non-negative integer; hatch_at must additionally be >= 1 (anything lower would hatch every fresh, zero-warmth egg on read). A missing, non-numeric, or out-of-range override falls back to the default.
| Key | Default |
|---|---|
warmth.visit_place | 20 |
warmth.mark_left | 15 |
warmth.unlock_mark | 10 |
warmth.meet_crow | 20 |
warmth.checkin | 8 |
warmth.hatch_at | 100 |
Nests and shelf
| Key | Default | Effect |
|---|---|---|
nest.rate | 24 | About one nest per this many geohash-7 cells per week (integer ≥ 1). Replicates with your settings, so your own instances agree; it is an operator knob, and a changed rate no longer matches other people's nests. |
shelf.cap | 5 | How many unhatched eggs the shelf holds (integer ≥ 0; 0 turns claiming off). |
frontier.depth | 3 | How many blocks ahead of your unlocked ground you can see. |
seed.rate | 4 | About one cleared cell in this many carries seed (integer ≥ 1). Lower means denser. |
seed.respawn.hours | 24 | How long before bird seed regrows in a place. |
seed.per.pickup | 1 | How much seed a place gives. |
heart.rate | 3 | About one place in this many holds a heart container the first time you enter it (integer ≥ 1). |
heart.wild.days | 30 | How long before a heart may reappear in ground you have already cleared. |
heart.wild.rate | 40 | About one cleared place in this many holds that reappearing heart (integer ≥ 1). |
energy.max.base | 100 | The energy bar's length with no heart containers. |
energy.max.per.heart | 10 | How much each heart container lengthens it. |
energy.max.cap | 300 | The longest the bar can ever get, however many hearts you find. |
unlock.max.accuracy.m | 100 | How sharp your location has to be before a place counts as visited. |
Steps
| Key | Default | Effect |
|---|---|---|
steps.goal | 6000 | Daily step goal (2,000–30,000). Set on the pet page. |
steps.max.day | 40000 | Most steps credited per day across all your devices. |
steps.max.per.min | 250 | Most steps credited per minute elapsed since the last reading (or since boot). |
steps.devices.per.day | 4 | Most distinct devices credited per day. |
steps.energy.full | 30 | Energy for a day at goal. |
steps.energy.chunk | 5 | Smallest step-energy payment (fewer, larger feeds). |
steps.checkin.energy | 15 | Energy for the manual "I walked today" check-in. |
steps.goal.seed | 3 | Bird seed paid for reaching the goal. |
steps.badge.min | 2000 | Steps (capped at the goal) that earn the "walked today" mark. |
steps.nudge | 1 | Evening nudge on (1) or off (0). Set on the walking card. |
steps.nudge.weekends | 1 | Nudge on weekends too (1) or not (0). |
steps.nudge.hour | 18 | Earliest local hour for the nudge. |
steps.nudge.until | 21 | Latest local hour for the nudge (exclusive). |
steps.nudge.below | 50 | Nudge only if today's steps are below this percent of the goal. |
MCP tools
| Tool | Purpose |
|---|---|
ramble_leave_mark | Leave a mark at a location. |
ramble_caw | Broadcast a short-lived public presence marker. |
ramble_query_world | List nearby marks and caws in the cell containing a location; each row carries a label ("your mark", "mark by <contact>", "mark by <world name> · <key4>", or "mark by <key8>"). |
ramble_unlock | Unlock a locked mark by proving proximity to its anchor. |
ramble_pet_state | The companion pet's mood, energy, weekly counters, active bird, and egg progress. |
ramble_egg_state | The incubating egg's warmth progress and hatch checklist. |
ramble_checkin | Record today's check-in, crediting warmth toward the egg. |
ramble_chore | Complete a daily chore (feed, preen, or play) for the companion pet. |
ramble_block | Block a persona by x-only pubkey and purge its stored marks. |
ramble_unblock | Remove a persona from the block list. |
ramble_flock | Your flock: birds, the shelf, the incubating egg, species found. |
ramble_nests | Nests near a location this week, nearest first, with your claims marked. |
ramble_claim_nest | Claim the nest you are standing at (or a named cell within 75 m) for a shelf egg. |
ramble_gift_egg | Gift an unhatched shelf egg to a contact (queued as one encrypted DM). |
ramble_propose_swap | Offer a shelf egg to a contact for one of theirs; they choose what to give back. |
Group audiences are the contact groups from the Contacts panel (visibility: "group:<group_uid>"); there are no ramble-specific group tools. ramble_leave_mark reports recipients for a contacts or group mark.
A hatch triggered through these tools never sends a live update to an open dashboard panel — they run over the stdio MCP process, not through the panel's request path, so there is nothing to push a ramble-hatched event through. The panel still picks up the new bird on its next refresh (after any action).
Operating notes
The transport lives in core (servers/gateway/boot/ramble-transport.js), not in the bundle, because it must reuse the gateway's one live Nostr manager. It starts only on a gateway where the Ramble bundle is installed (~/.crow/bundles/ramble/server exists) and a Nostr manager is up. A gateway without the bundle installed creates the Ramble tables, so your other Crows' replicated Ramble state still lands there, but it never sends or processes Ramble messages. Boot prints one of:
[ramble] transport started
[ramble] transport not started: no nostrManager (sharing disabled or boot order)
[ramble] transport not started: bundle not installed on this instance (tables only)The second line means the bundle is installed but nothing will ever be published or received — check that sharing/Nostr is enabled on that instance. The third is normal on a Crow where you have not installed Ramble. No Ramble failure can block gateway boot; every problem is a warning.
The one-claim-per-day limit and the shelf cap are checked per instance (claims do not replicate), so a user with two Crows can claim once per day on each.
The map of places you have unlocked, and your seed and heart balances, replicate to your own linked Crows, and they never go to a contact.
The cap only gates claims. Incubating an egg the sync layer had parked (shelf_origin='sync') moves the egg it replaces to your own shelf without anything leaving, so the shelf can briefly read 6 of 5; it settles as you hatch.
Two instances can disagree for one sync cycle about which egg incubates: if you swap eggs on one Crow while the other is still crediting warmth to the old egg, the older egg wins on both sides and your swap is undone (consistently). The same can happen if a gift arrives, or you claim a nest, on the other Crow before your swap has reached it: that Crow refills its slot with your oldest egg, and the egg you chose ends up back on the shelf. Swap again once both are in sync.
During the upgrade that introduced lamport_origin, one more case can leave two of your Crows briefly disagreeing about an egg: a change made on one Crow just before it was upgraded, still waiting to reach the other, can tie with a change the other made just after its upgrade. The older change has no recorded origin, so each Crow may keep a different one. Nothing is lost, and the next change to that egg on either Crow (warmth, a hatch, a swap) brings them back into step. It cannot happen once every Crow runs the new code and its backlog has drained.
Contacts delivery, gifts and swaps need every gateway on the new code: a gateway running phase 2 stores a ramble envelope as a chat message. Restart all of them before anyone sends. The transport logs dropping <kind> delivery to <crow_id>: not a deliverable contact when a queued recipient was deleted or blocked, and gave up after 20 attempts when no relay accepts a DM. During a rolling restart, a gateway still on phase 2 silently drops incoming ramble_trades sync ops (an unknown table advances its checkpoint without applying); a swap row emitted in that window reaches that Crow only when a later op touches the same trade. Restart all gateways back-to-back to keep the window to seconds.
The AR view is client-only: the gateway sees GET /api/ramble/around and nothing else new. A phone that shows "Radar · no compass" on Android usually has no deviceorientationabsolute support in that browser; "Radar · no camera" on any device means the permission was refused or the page is not a secure context. A caw published at a geohash precision of 4 or less (a non-default RAMBLE_DEFAULT_GEOHASH_PRECISION on the sender) is shown on the map but not in the AR view.