Round 45: launch hardening — TLS-port reject, ref-length lint, transfer-audit detectability (#486/#483/#443)
- #486 TLS-only port plaintext reject: Sysadmin-Reference (the "no login prompt"
section now describes the ACTIVE reject, not the old silent hang — with a note
that a still-hanging connection means a stale gate binary) + Edge-and-Protocol
(the first-byte sniff: 0x16 replays via peekConn into tls.Server, else a
one-line "use TLS" + close; the sniff runs off the accept loop, and the
handshake is driven under an explicit HandshakeContext deadline, closing the
resource-exhaustion vector a deadline-less lazy handshake left open).
- #483 identity-token LENGTH bound: Content-Loading (ref length added to the
lint list) + Pack-Authoring (a ref is a store btree PRIMARY KEY, ~2704-byte
page ceiling; RefMaxLen=256; also covers the two content-authored store keys
the charset lint skips — pack name and formula-override name — since an
over-long value fails the import transaction identically; boot warn + reload
hard-reject).
- #443 cross-character transfer audit: Persistence-and-Durability — new
item_transferred event (a transient Released marker records the releaser; a
DIFFERENT saved player's pickup emits the row) reusing the #350 audit trail;
covers drop->get and put->get-from-container; self-pickup/floor-item/mob-move
record nothing; marker cleared on pickup; bound items never reach it.
DETECTION ONLY (no prevention, no conservation invariant). Threaded into the
#432 owner_epoch residual note as the first half ("we cannot know" -> "we can
know") of the cross-row dupe close-out deferred to Launch.
Round 43: the three review-bundle follow-ups (#397/#399/#401)
Refinements to systems already documented (spawn-protection, comms history,
the audit trail); each thread lands in its existing home.
- Abilities-and-Effects (#397): the sourceless-ambient exemption. A room field
with no applier ticks at actor==target, which guardHarmful treats as an exempt
self-effect, so it kept damaging a just-respawned occupant. A sourcelessAmbient
flag fires ENFORCEMENT even at actor==target while CANCELLATION stays
actor!=target-gated, so the field never drops the occupant's own shield;
threaded into all four such harm sites.
- Combat-System (#397): a swing at a spawn-protected target is short-circuited
BEFORE the pipeline, not zeroed inside it. Narration ran before dealDamage, so
the target saw a phantom hit each round; the pre-gate also drops a still-
protected attacker's own shield.
- Builder-Commands + Pack-Entity-Reference (#401): a sent tell is recorded at the
CONFIRMED-echo point, so no phantom entry on a resolve-miss or publish-failure;
history channel appends a shard-local partial-view footer on a multi-shard
fleet; the channel history ring is reaped on channel removal, and the schema
now warns that loosening hear_access retroactively exposes buffered lines.
- Persistence-and-Durability (#399): the async auditor now coalesces up to 64
events into one pgx.Batch round-trip, with per-row fallback under the same
bounded ctx; newest-first reads order by at DESC, seq DESC for true insertion
order incl. within a shared-now batch; and staff audit-by-name now folds in the
target account tier_changed rows, scoped to the owning account.
Round 38 milestone: owner_epoch — ownership as a durable fence (#432 + #413/#379/#384)
CORRECTS A FALSE SAFETY CLAIM. Three pages asserted that state_version
"fences stale writes, so a mis-fired handoff or a zombie owner can never
clobber a newer record". It never did: state_version is contention control,
and every caller answered a CAS loss by rebasing, so a stale writer rebased
exactly as a legitimate one did.
- Persistence-and-Durability: "The state_version guard" reworked into "Two
guards" — state_version (contention) vs owner_epoch (ownership, at the
SINK). Documents the rollback primitive it closed (a stale shard 60s-old
logout snapshot force-writing over the live owner, repeatable), why the
fence had to be a SECOND column (a rebase moves state_version and cannot
reach a separate conjunct), why the mint is atomic (a read-then-bump would
look fixed), the three-outcome SaveResult, terminal epoch loss on both save
paths, the max(directory,row) floor, fail-closed login, the checkpoint tier
ownership guard (it had NO CAS and was a full bypass one rung up; detects a
double-own ~6x sooner), the (owner_epoch, state_version) load ordering, and
the honest residual (an epoch on row X cannot fence a write to row Y).
- Distributed-Systems-Model + Overview: same correction; adds a "do not confuse
the two epochs" invariant (directory epoch fences ROUTING and is evictable;
owner_epoch fences WRITES and is durable).
- Cross-Shard-Handoff: handoff mints the epoch AFTER destination resolution
(minting first evicted legitimate players mid-move); an unreadable store now
fails the handoff closed; concurrent claims derive distinct epochs hence
distinct handoff tokens, closing the shared-pending race structurally.
- Zone-Runtime: #413 extends the resolve-then-deliver claim to login attach and
Prepare (incl. why neither may reuse claimTransferTarget, and Prepare needing
a release on post failure); #379 refuses a reconnect racing an in-flight
transfer in TWO layers, since a probe reproduced a dupe the resolve-time
check alone cannot see.
- #384: documents why the drain-reservation margin is not bound by step 1
duration (the hold is rebased per accumulate and anchored to the redirect it
covers) and the WARN added to falsify it; corrects the Round 31 "stamped once
and never refreshed" wording on three pages.
Round 36 milestone: instanced zones (#72)
New deep-dive page Instanced-Zones, plus corrections to two pages that
asserted "instancing is deferred / not implemented".
- NEW Instanced-Zones: template-vs-id identity and the ownsZoneRef locality
chokepoint (with the build-failing AST lint); why the id is <template>#<128-bit
random> (# outside the ref charset, unguessable so it is not a farming oracle);
unleased by construction; async 3-hop mint + quiescence reaper; the
instanceable content opt-in; the isolation exclusions (persistent resets fail
closed vs a durable item dupe, per-mint RNG salting, withheld director
schedule events, refused signal_region/world, drain/reload/off-box-ingress);
THE ANCHOR (template projection was killed because placement is the gate ROUTING
key); exit paths; and the security posture.
- Distributed-Systems-Model + Running-at-Scale (x2): instancing now exists but is
ISOLATION, not transparent load-sharding — the public hot-zone ceiling stands.
- Zone-Runtime: quiescence gains the third `incoming` counter for in-flight
intra-shard transfers, claimed under the same mutex teardown checks.
- Cross-Shard-Handoff + RPC-and-Protobuf: signed-snapshot optionals now written
UNCONDITIONALLY — a second append-if-non-empty optional made
digest(tier="",account=X) == digest(tier=X,account="") (length prefixes solve
boundaries, not presence); instances rejected at every off-box ingress.
- Persistence: an instance is never a durable location; the anchor + COALESCE.
- Pack-Entity-Reference: zone `instanceable` flag, start_room respawn semantics.
- Pack-Lua-Scripting: mud.zone(), mud.send_to_instance (self-only).
- Orchestration: instances withheld from reserved schedule fan-out.
- Sidebar + Engine-Developer-Reference index entries.
Round 34 milestone: spawn-protection window, audit trail, comms history
- Combat-System + Abilities-and-Effects: the actor-agnostic post-respawn
spawn-protection window shipped (#394) — updates the #318 "deferred" note.
respawnPlayer opens a pulse-deadline protectedUntil; guardHarmful refuses
harmful ops aimed at a protected player AHEAD of the !isPlayer no-op so a MOB
attacker is covered (safe-room cannot be); drops when the player acts.
- Persistence-and-Durability (new section) + Accounts-and-Auth-Internals: the
append-only character_audit trail (#350) — world emits died/attr/track via an
async per-shard auditor (saver twin, drains on shutdown), account emits
create/tier_changed in-transaction; idempotent on (subject,kind,dedup_key)
with a DURABLY-UNIQUE key (died = per-death UUID, not the transient deaths
counter that collided across relogs); audit self-view scoped by stable pid,
audit <name> staff-only.
- Builder-Commands + Player-Reference: tells/replay session tell history (#349,
structural pair-privacy, skips ignored authors, resets on relog) and
history <channel> scrollback (#348, fetch-time canHear gate, shard-local
partial view); Pack-Entity-Reference channel_defs.history row enriched.
Round 31 milestone: handoff phase-2 auth, checkpoint tie-break, drain-reservation TTL
- Cross-Shard-Handoff + RPC-and-Protobuf: document that Handoff.Commit/Abort
are now Ed25519-signed under the cluster keypair with the digest bound to the
destination shard id (#314) — the deterministic handoff_token over public
inputs was forgeable, and destination binding stops a captured signed Abort
from being replayed against a second destination in a split-brain race.
- Persistence-and-Durability + Distributed-Systems-Model: the load freshness
check breaks a state_version tie toward the Redis checkpoint (#322); a
checkpoint is dumped at the pre-CAS version so it ties the row while carrying
newer content — the strict > left the tier inert, so the tie-break is what
makes the crash window the ~10s cadence and not the ~60s flush.
- Cross-Shard-Handoff + Running-at-Scale + Distributed-Systems-Model: the
drain-target reservation TTL now outlasts the whole drain (deadline + one
presence-reflect window) so a slow-but-alive drain keeps its hold while
players migrate; only a crashed drainer lingers the full TTL (#334).
Round 30 milestone: reconnect/placement/drain correctness (#321/#324/#329/#331/#336) + RID re-home (#373)
Round did not self-document. Closed:
- Persistence-and-Durability: a live reconnect routes by an in-memory residency
index BEFORE the lagging durable zone_ref (prevents a double-owned character
when a link-dead resume beats the async flush of an intra-shard walk) (#321);
the placement writer gets the saver's shutdown-barrier treatment so a logout
tombstone isn't lost on graceful shutdown (else the tell/mail oracle reports a
player on a dead shard) (#331)
- Distributed-Systems-Model: the placement tombstone now carries a per-session
nonce, fencing a same-shard/same-epoch relog that shard+epoch alone couldn't
distinguish (#329)
- Edge-and-Protocol: a fresh login bounced by a draining shard (Unavailable) is
re-resolved + re-dialed to the peer instead of dropped; token-bearing re-dials
never retried (extends the zero-drop drain to ARRIVING players) (#324)
- Cross-Shard-Handoff: a gate-wedged player is skipped by the drain and reclaimed
(client-fault straggler), not miscounted zero-drop Redirected (#336)
- Entity-Component-Model: an intra-shard transfer re-homes the entity + carried
subtree to fresh per-zone RIDs (the overworld 'map on some rooms' bug) (#373)
Round 26: the placement record becomes the reconnect-routing spine
Track-6 residuals. Round self-documented only #276 (catch-up metrics); the
rest of the engine/ops surface was stale.
- Distributed-Systems-Model: NEW 'The placement record' section — zone is the
routing key (ShardForZone resolves the current owner, so an offline rebalance
is transparent), epoch is the fence AND the tell/mail existence key, shard
routes nothing. Every residency registers a placement (incl. the intra-shard
zone walk); registerPlacement accepts an equal epoch where the handoff CAS
demands a strictly greater one. Clean logout writes a fenced compare-and-delete
tombstone, not a delete (#320 slice 2, #70). Also: lease renewal restarts on
re-adoption, closing an A->B->A split-brain (#288).
- Persistence-and-Durability: close the shard-not-zone 'Known gap'; add the
saver shutdown sentinel barrier (#282).
- Edge-and-Protocol: world-side Send watchdog — keepalive can't see a gate whose
transport acks PINGs while its application stopped reading (#274).
- Orchestration-and-Directors: a runtime-adopted zone's scope replica is seeded
before the zone is exposed (a full-map replace would clobber newer state) (#280).
- Cross-Shard-Handoff / Running-at-Scale / DistSys: drain-target reservations now
carry per-field, server-timed expiry (#284).
- Running-at-Scale: drop the self-contradicting 'deliver_lag_ms silent' gap; the
gate row now describes zone-keyed reconnect routing.
Round 25: durable-zone login fix + engine-side notes for #69/#262/#266
The round self-documented #69 (Abilities), #262 and #266 (Sysadmin). Closing the
engine-page gaps and the one undocumented item:
- Persistence-and-Durability: NEW 'Where a returning player lands' — characters.zone_ref
is the login routing key (#320). Documents the data-loss bug it fixed (an intra-shard
walk + logout silently reset you to the home zone's start room), the link-dead
reconnect side-fix, the room_ref warn, the checkpoint drift guard, and the remaining
shard-not-zone placement gap.
- Combat-System: the death generation (Living.deaths) + die() entry re-entrancy latch
(an item dupe) + why position/location can't carry the 'did it die' signal (#69).
- Scoped-Event-Bus: paced durable NAK redelivery + the tri-state ack decision (#266).
- RPC-and-Protobuf: AdoptZone is signed on keyed shards (#262).
- Edge-and-Protocol: the connect diagram binds the durable zone.
Engine Dev: persistence/durability + content loading/hot reload
- Persistence-and-Durability: the memory->Redis->Postgres ladder, state_version
CAS + reconcile paths, flyweight item delta, ephemeral resets, handoff bypass
- Content-Loading-and-Hot-Reload: boot load, validate-before-broadcast, the
applier + zone reconcile version guard, content_version authority, reconcile-on-join
Scaffold wiki + author About and Getting Started
- 48-page skeleton across all audiences (flat files + grouped _Sidebar)
- Every page carries an Audience + Status banner (Ready/Draft/Placeholder)
- About: Overview, History of MUDs (Ready)
- Getting Started: Getting-Started, Running-Locally, First-Admin-Setup,
Content-Packs-Intro (Ready) — dev bypass, local GitHub OAuth, bootstrap
admin, promote/demote, content-pack model, all grounded in code
- SRD5/WoW/D6 pack pages are honest placeholders (packs not yet built)