Skip to content

Try the plaintext MSSP-REQUEST once per address that never gave option 70 - #198

Merged
HarryCordewener merged 1 commit into
mainfrom
claude/project-thread-ab8vfx
Oct 6, 2026
Merged

HarryCordewener merged 1 commit into
mainfrom
claude/project-thread-ab8vfx

Conversation

@HarryCordewener

@HarryCordewener HarryCordewener commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

Requested by Grave · project thread

Before: the probe never sent the plaintext MSSP-REQUEST. A game that only serves MSSP that way (the older SMAUG-family form) showed no MSSP at all.

After: every address that has never reported over telnet option 70 is asked MSSP-REQUEST once. If it answers, its report is stored like any other MSSP report and it is asked again every crawl. If it doesn't, it is never asked again. Addresses that have given MSSP over option 70 are never sent it.

The exchange itself is TelnetNegotiationCore 4.0's MSSPPlaintextProtocol; this PR only decides when to ask and remembers the answer.

How

  • Memory per address: crawl_target.mssp_route (migration 0042): NULL unknown, telnet, plaintext, none. Backfilled to telnet for every game that already has mssp/mssp_roster fields.
  • Policy: MsspRoutes.Ask maps the route to ProbeTarget.PlaintextMssp (Trial, Ask, Never; default Never). MsspRoutes.Learn maps a probe result to the next route. Option 70 wins from any state. An unanswered trial is final. A former answerer that misses once goes back to unknown for one more trial, so one slow night doesn't drop its only MSSP route.
  • Trial on its own dial: for an unknown address, after an ordinary session that got no MSSP and no WILL MSSP, the probe dials again, settles the banner, answers pre-screen prompts, sends MSSP-REQUEST and nothing else. This keeps the measuring session clean: a game that reads the line as a name and moves to a password prompt (the playdecay shape) would otherwise get WHO typed as a password. The trial can only add to the result; any failure on that dial returns the measurement unchanged.
  • Known answerers: asked in-session at the connect screen, before WHO, so a PLAYERS in the reply spares the game the WHO through the existing PublishedCountAsync path. The reply's side effects land in the discarded slice between cursors.Banner and cursors.Flush.
  • Not a negotiation: a plaintext report sets MsspTransport.PlaintextRequest and does not add MSSP to OfferedOptions, so capability.mssp.measured is not written as true for a game that never offered option 70.
  • mui-probe --mssp-request (trial) and --mssp-request=ask (in-session) for trying it against a live server by hand.

Not measured live: raw TCP to games is blocked from the environment this was built in. Running mui-probe <host> <port> --mssp-request against coffeemud.net 2327 or a SMAUG game would be a good check before merge.

Tests: PlaintextMsspTests (fake servers: plaintext-only answerer, name-reader with a password prompt, hang-up on unknown name, option-70 server), MsspRoutesTests, MsspRoutePostgresTests. Crawl 613, Discovery 361, Crawler 363 pass locally against Postgres.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YK1nuQCYc9TnnDnWEjcNpS


Generated by Claude Code

Summary by CodeRabbit

  • New Features
    • The crawler can now request plaintext MSSP reports when a server’s behavior supports it. Unknown servers receive one trial; known answerers can be asked again, while servers that use Telnet option 70 are not sent plaintext requests.
    • Plaintext MSSP results are recorded separately from Telnet-negotiated reports, and the crawler remembers each server’s discovered route.
    • The probe command now supports options to request plaintext MSSP in trial or repeat-request mode. Without either option, it does not send a request.

…n 70

TelnetNegotiationCore 4.0 ships MSSPPlaintextProtocol, which is what the
probe was waiting for. The crawl loop now remembers, per address, how
MSSP arrives (crawl_target.mssp_route, migration 0042):

- telnet: reported over option 70. Never sent MSSP-REQUEST. Backfilled
  for every game that already has mssp or mssp_roster fields.
- NULL: unknown. After an ordinary session that got no MSSP, the probe
  makes one short second dial that sends MSSP-REQUEST and nothing else,
  so a name prompt that turns it into a password prompt never gets the
  measuring session's WHO typed into it.
- plaintext: answered. Asked in-session every crawl, before WHO, so a
  PLAYERS in the reply spares the game the WHO.
- none: did not answer. Never asked again.

A plaintext report is stored as mssp like an option-70 one, and is not
noted as an option-70 negotiation in the handshake fields. MsspRoutes
holds both the ask and the transition, as pure functions. mui-probe
gains --mssp-request and --mssp-request=ask for trying it by hand.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YK1nuQCYc9TnnDnWEjcNpS
@HarryCordewener HarryCordewener self-assigned this Oct 6, 2026
@coderabbitai

coderabbitai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Essentials
  • Run ID: 8a3ab44a-1f66-4270-b498-568a54267f02
📥 Commits

Reviewing files that changed from the base of the PR and between 3853b47 and 4ef4afb.

📒 Files selected for processing (20)
  • CLAUDE.md
  • docs/codebase-survey-2026-07-30.md
  • migrations/0042_mssp_route.sql
  • src/MUI.Crawl/Telnet/ProbeOptions.cs
  • src/MUI.Crawl/Telnet/ProbeResult.cs
  • src/MUI.Crawl/Telnet/Probing.cs
  • src/MUI.Crawl/Telnet/TelnetProbe.cs
  • src/MUI.Crawler/Crawl/CrawlCycle.cs
  • src/MUI.Crawler/Crawl/FieldObservations.cs
  • src/MUI.Crawler/Persistence/NpgsqlCrawlTargetRepository.cs
  • src/MUI.Discovery/Scheduling/CrawlTarget.cs
  • src/MUI.Discovery/Scheduling/MsspRoutes.cs
  • src/MUI.Probe/Program.cs
  • tests/MUI.Crawl.Tests/Telnet/PlaintextMsspTests.cs
  • tests/MUI.Crawl.Tests/Telnet/ProbeRestraintTests.cs
  • tests/MUI.Crawler.Tests/Crawl/MsspRoutePostgresTests.cs
  • tests/MUI.Crawler.Tests/Support/FakeStores.cs
  • tests/MUI.Discovery.Tests/Scheduling/MsspRoutesTests.cs
  • tests/MUI.Discovery.Tests/Support/InMemoryCrawlTargetRepository.cs
  • tests/MUI.Web.Tests/StoredCrawlerPulseTests.cs
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@HarryCordewener
HarryCordewener marked this pull request as ready for review October 6, 2026 15:54
@HarryCordewener
HarryCordewener merged commit c3ba5ac into main Oct 6, 2026
2 of 3 checks passed
@HarryCordewener
HarryCordewener deleted the claude/project-thread-ab8vfx branch October 6, 2026 16:00
HarryCordewener added a commit that referenced this pull request Oct 7, 2026
…nt (#199)

* Refuse an MSSP PLAYERS too large to be an online count

Tapestries MUCK answers the plaintext MSSP-REQUEST with PLAYERS = 26842
beside DBSIZE 174343, ROOMS 50509, EXITS 77079 and OBJECTS 19623: the
four add to within 290 of the database, so PLAYERS is its player objects.
Once #198 started asking, that stated count spared the game its WHO,
which had been counting 300 to 500, and the site published 26842 as the
number connected.

MsspPresence.Stated now refuses a PLAYERS above BannerCount.Implausible,
the ceiling the connect-screen rung already uses. The probe and
PresenceChoice both read through it, so WHO is asked again and its count
is what gets published. ReasonFor no longer calls such a value
players_not_numeric: it is a number, and the refusal is ours.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Ciq6ySnuNyVtkEBnVceLU

* Ask WHO where WHO is known to answer, rather than capping PLAYERS

Replaces the 10,000 ceiling on MSSP PLAYERS. A stated count is not
always the number online, and no threshold tells the two apart: what
does is whether WHO works at that address.

crawl_target.who_answers_at (migration 0043) records when a WHO typed
there last came back counted. WhoAnswers.Learn sets it on a counted WHO
and clears it when the login prompt takes WHO for a character name; an
unreadable WHO leaves it alone. The crawl loop passes it to the probe as
ProbeTarget.WhoAnswers, and where it is set the probe asks WHO even when
the game has stated a count. PresenceChoice already ranks WHO above
MSSP, so WHO's count is published and MSSP remains the fallback.

The migration credits every single-address game with a counted `who`
presence row, dated by the newest. Tapestries MUCK is one of them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Ciq6ySnuNyVtkEBnVceLU

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants