Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,18 @@ The absolute budget from socket acceptance until the peer proves itself: 10 s on
even when its blocking task has not started. Proof completion clears it before post-auth traffic.
_Avoid_: pre-auth read deadline, read timeout, idle timeout (for this limit)

**Logon Limiter**:
The Gateway's in-memory caps on the logon port: three attempts per connection, ten failed logons
per address per minute, eight open logon connections per address, and a 200 ms pause before a
failed proof is answered. A refusal closes the socket. Per gateway process; a restart forgets it.
_Avoid_: rate limiter, throttle, brute-force lockout

**Session Expiry**:
The one hour a `game_session` row stays valid after its logon. The world handshake refuses an
expired row like an absent one, and the Module's `reap_sessions` deletes it. Every logon rewrites
the row, so a returning Account always starts a fresh hour.
_Avoid_: session timeout, TTL (in prose)

### Sharding and transfer

**Transfer**:
Expand Down
10 changes: 6 additions & 4 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -473,8 +473,8 @@ Rules `--lan` enforces for you:
- A **running** gateway is never silently rebound — switching modes is refused with the `dev down`
to run first, rather than reported as "already up".

This is a LAN convenience for a contributor fixture, **not a deployment mode**: no rate limiting, no
TLS, 2004-era password hashing, and a fixture account whose password is written in this document.
This is a LAN convenience for a contributor fixture, **not a deployment mode**: no TLS, 2004-era
password hashing, and a fixture account whose password is written in this document.
Only put it on a network you trust, and read the next section.

---
Expand All @@ -497,8 +497,10 @@ are concrete, not precautionary:
identity gate at all (`debug_set_health(any_guid, 0)`, `set_level`, `kill_nearest`, `teleport`, …).
One anonymous call owns any character. This build is for the loopback harness; it must never reach
a reachable node.
- **No rate limiting, no TLS, no brute-force lockout.** SRP6 proof attempts are unlimited, and the
logon tier has no per-IP cap. That is acceptable on loopback and nowhere else.
- **No TLS.** The logon port has the Logon Limiter (three attempts per connection, ten failed
logons per address per minute, eight open logon connections per address). The stored `game_session`
key expires one hour after logon and prevents later world handshakes. Existing World Sessions
continue. The SRP6 exchange itself is 2004-era and unencrypted.

If you ever want to run this for real players, understand that the localhost → public-VPS delta
is real hardening work (a plain build, a firewalled `:3000`, DoS guards, the realm address, launch
Expand Down
13 changes: 8 additions & 5 deletions docs/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,10 @@ holds the Account's Alpha Test Tools authority. Existing Accounts receive that a
end-appended default. New Accounts copy `game_alpha_test_tools_enrollment`, which starts enabled;
changing enrollment never changes an existing Account.
`game_session` holds the SRP6 session key **K**, which is what makes gateways stateless: the logon
flow writes it, and any world gateway can read it to complete the handshake. `game_operator` is a
singleton holding the one trusted operator identity, captured (not derived) by `claim_operator`.
flow writes it, and any world gateway can read it to complete the handshake. A row is valid for
one hour from its logon (`expires_at`): the handshake refuses an expired row like an absent one,
and `reap_sessions` deletes expired rows every five minutes. `game_operator` is a singleton
holding the one trusted operator identity, captured (not derived) by `claim_operator`.

On a realm-core deployment these live on realm-core, and the world shards' copies are downgraded to
a **write-through cache that is never read for auth**: a world shard's own `game_account`/
Expand Down Expand Up @@ -401,11 +403,11 @@ Two constraints survive the removal and bind any filter added later.

## 6. Scheduled tables

**23 scheduled tables** drive every periodic and deferred effect in the game. Nothing on a gateway
**24 scheduled tables** drive every periodic and deferred effect in the game. Nothing on a gateway
timer decides gameplay. Recount and re-list them with:

```bash
grep -rn 'scheduled(' module/src --include='*.rs' # 23 tables plus 4 comment lines, 2026-09-03
grep -rn 'scheduled(' module/src --include='*.rs' # 24 tables plus 4 comment lines, 2026-09-03
```

| Scheduled table | Reducer | Cadence | Where |
Expand All @@ -424,6 +426,7 @@ grep -rn 'scheduled(' module/src --include='*.rs' # 23 tables plus 4 comment l
| `game_pet_care_schedule` | `tick_pet_care` | 7.5 s | `creatures/pet_care.rs:18` |
| `game_gateway_lease_reaper_schedule` | `reap_gateway_leases` | 15 s | `gw.rs:71` |
| `game_instance_reaper_schedule` | `reap_instances` | 60 s | `instance.rs:312` |
| `game_session_reaper_schedule` | `reap_sessions` | 5 min, armed lazily by `establish_session` | `auth.rs:127` |
| `game_weather_schedule` | `tick_weather` | 10 min | `weather.rs:153` |
| `game_pending_cast` | `fire_pending_cast` | one-shot at cast completion | `spell/tables.rs:643` |
| `game_pending_spell_impact` | `fire_spell_impact` | one-shot at projectile landing | `spell/tables.rs:689` |
Expand All @@ -435,7 +438,7 @@ grep -rn 'scheduled(' module/src --include='*.rs' # 23 tables plus 4 comment l
| `game_creature_ai_relay_arrival` | `resume_relay_arrival` | one-shot on movement completion | `creatures/eventai/relay.rs:309` |

The interval rows are inserted by `init` (`seed_scheduler_arming`, `module/src/seed.rs`), except the
transfer and mail-escrow reapers, which their own entry points arm lazily and idempotently.
transfer, mail-escrow and session reapers, which their own entry points arm lazily and idempotently.
Scheduled reducers self-gate on `ctx.sender() == ctx.database_identity()` so they cannot be driven
externally.

Expand Down
Loading
Loading