---
mvp: no
subsystem: admin
status: anti-port-reference
---

# Admin Anti-Port Reference (server side)

> **WARNING — DO NOT PORT.** This document catalogues the original BNO server admin model AS A FORCING FUNCTION, not as a specification. Per [CLAUDE.md hard rule #3](../../CLAUDE.md), the original "Ctrl+E run clipboard as superuser" admin pattern is a **remote code execution vulnerability in shipped product form** and must NEVER appear in the rebuild. Phase 7 PAR-07 replaces all admin functionality with a separate, authenticated web UI.

This is the server-side companion to [../extracted-engine/admin-anti-port.md](../extracted-engine/admin-anti-port.md). Most original admin keybinds live on the client side (the operator's running game window) — the server's contribution to the admin model is opcode 12 (`server_receive` case 12) which accepts an `execute_string` payload broadcast and runs it inside the game-state authority. That is the worst single feature of the original.

## Source files

- **`legacy/open-source-release/,ServerCommands.txt`** — top-level command summary that ships with the open-source release. Lists 7 keybinds (operator-side).
- **`legacy/servers/enlyzeam-current/Ctrl+O Codes.txt`** — operator code snippets. Free-form; each entry is a runnable GML expression that operators stuffed into the clipboard before pressing Ctrl+E.
- **`extracted/server-5-4/scripts/0364-scontrolmenu.gml`** — the 4-option `show_menu` admin pop-up wired into the server-operator's keybindings; each option emits a GML-source string that gets sent over the wire as opcode 12.
- **`extracted/server-5-4/scripts/0008-ChtCmdRec.gml`** — the backtick-command parser; benign commands belong in [./chat.md](./chat.md), this MD owns the REJECTED-AS-PORTED ones.

## Server-side admin command rows (5-column disposition table per D-20)

| Command | Original behaviour | REJECTED-AS-PORTED reason | Modernized intent name | TS intent shape |
|---------|-------------------|--------------------------|------------------------|-----------------|
| **Ctrl+E** | Server operator presses Ctrl+E; their clipboard contents are run as GML in the server process. Composes with Ctrl+9 (snippets) to mass-produce admin actions. | RCE-as-feature. Trivially weaponisable; the clipboard is a shared OS resource, anything writing to it (browser autocomplete, password manager, virus) becomes admin. **No equivalent exists; do not invent one.** | _none — surface deleted_ | `// Intentionally absent. Phase 7 PAR-07 admin UI exposes only the typed intents below.` |
| **Ctrl+Q** | Show the value of the variable currently named in the chat input field. | Exposes server internals; relies on clipboard-style GML eval. | `view-audit-log` | ```typescript\ninterface AdminViewAuditLogIntent {\n  command: 'view-audit-log';\n  payload: {\n    sinceMinutes?: number;\n    actorAccountId?: string;\n    eventTypes?: Array<'login' \| 'logout' \| 'kick' \| 'mute' \| 'ban' \| 'mb-moderate'>;\n    limit?: number;\n  };\n}\n``` |
| **Ctrl+9** | Pop a context menu of code-snippet templates that operate on the currently-selected player. Each option auto-fills the clipboard, which the operator then Ctrl+E's. The 5 server-side options (from `0364-scontrolmenu.gml`) are: Send Server Message, Change Running Speed, Change Input Text, Move Player, Force Log Out. | Composes with Ctrl+E into a fully-templated RCE; if anything *worse* than raw Ctrl+E because new operators don't realise the snippet executes with full game-state authority. | `kick`, `mute` (typed replacements for the destructive operations) | ```typescript\ninterface AdminKickIntent {\n  command: 'kick';\n  payload: { targetAccountId: string; reason: string; durationSec?: number; };\n}\n\ninterface AdminMuteIntent {\n  command: 'mute';\n  payload: { targetAccountId: string; reason: string; durationSec: number; };\n}\n``` |
| **Arrow Up / Arrow Down** | Navigate the player list in the server-operator's command screen (`Online_Command_Screen` room — see object 0045-commandob). | Not REJECTED — pure navigation. The admin UI just doesn't live in the game client anymore. | _navigation; HTML table_ | _none required — Phase 7 admin UI is a sortable table_ |
| **Ctrl+???** | Send the operator's clipboard GML to a *highlighted* player to be executed on **their** client. (This is opcode 12 — the `execute_string` payload broadcast.) | **DOUBLY REJECTED** — this is **client-side RCE inflicted by the server**. Anyone with admin keys can ship arbitrary code into another player's running game. Worst single feature of the original system. | `assign-role`, `ban` (the legitimate behaviours buried under this knob) | ```typescript\ninterface AdminAssignRoleIntent {\n  command: 'assign-role';\n  payload: { targetAccountId: string; role: 'player' \| 'moderator' \| 'admin'; };\n}\n\ninterface AdminBanIntent {\n  command: 'ban';\n  payload: { targetAccountId: string; reason: string; permanent: boolean; durationDays?: number; };\n}\n``` |
| **Ctrl+M** | View any of the existing message boards by number; visual themes ignored, only text shown. | Mild content-bypass risk (admin sees raw text without UI filtering); not a security primitive. | `mb-moderate` | ```typescript\ninterface AdminMbModerateIntent {\n  command: 'mb-moderate';\n  payload: {\n    boardId: number;\n    action: 'view' \| 'delete-topic' \| 'delete-reply' \| 'pin' \| 'unpin' \| 'lock' \| 'unlock';\n    topicId?: number;\n    replyId?: number;\n    reason: string;\n  };\n}\n``` |
| `` `end session`` (chat-input backtick) | Sends opcode 12 with payload `show_message('The server is restarting - try logging in again soon!'); global.online = 0; game_restart();` to every connected client; sets `pnum = 0`; sets `operations.alarm[1] = 150` (server self-shutdown timer). | The **mechanism** (broadcasting executable GML to all clients) is the RCE surface. The **intent** (graceful restart) is legitimate. | _server-side restart-broadcast; opcode 12 wire path is deleted_ | ```typescript\n// Server-side restart is an admin-API call, not a chat-input command.\n// Clients receive a typed `server-restart-warning` event, NOT executable code.\ninterface ServerRestartWarningEvent {\n  type: 'server-restart-warning';\n  payload: { reason: string; expectedDowntimeSec: number; };\n}\n``` |

## Account-recovery snippets (`Ctrl+O Codes.txt`-derived)

The Ctrl+O codes file is a free-form operator notebook. Recurring patterns:

- **Stash / restore location** (`global.awesomefunction = "..."`) — operator self-teleport via stash/restore of `server.x, server.y` strings stuffed into `global.helpmsg`. Composes with Ctrl+E (`execute_string`).
- **Hexport-out / -in animation** (`server.sprite_index = HexportOut`) — operator self-animation (skip the in-game hexport flow).

| Pattern | Original behaviour | REJECTED-AS-PORTED reason | Modernized intent name | TS intent shape |
|---------|-------------------|--------------------------|------------------------|-----------------|
| **Stash / restore location** | Operator teleport. | The mechanism (storing GML source in a global, then `execute_string`-ing it) is the entire RCE surface. | _admin teleport endpoint (Phase 7 PAR-07; not in v1 scope as a typed intent — admin ops can use the typed `mb-moderate` / `kick` flows for player-management; admin teleport is operator-self-only and lives behind admin auth)_ | _none — admin self-teleport is a UI form, not a wire intent_ |
| **Hexport-out animation** | Operator self-animation. | Uses `execute_string`. | _drop entirely_ | _none — cosmetic-bypass not in v1 scope_ |

## Old-Account-Updater binaries (informational)

Three sibling executables in `legacy/open-source-release/` — `Old Account Updater.exe`, `Old Accounts Reloader.exe`, `Account Updater.exe` — share a name with admin tooling but **no source GML for them is present in the open-source release**. Per RESEARCH §"Open Question 4", we cannot reconstruct their behaviour from the data we have.

> **Original behavior unknown — no source GML; modernized replacement: `account-recover` web-UI endpoint per Phase 7 PAR-07.**

The intent is roughly "operator-driven account password reset / re-link" since the names suggest account-table maintenance. The rebuild's typed replacement:

```typescript
interface AdminAccountRecoverIntent {
  command: 'account-recover';
  payload: {
    targetAccountId: string;
    action: 'reset-password-email' | 'unlock' | 'merge-duplicate' | 'force-reauth';
    reason: string;
  };
}
```

## All 7 modernized command names (lint contract)

For machine-grep determinism, the 7 modernized server admin commands are: `kick`, `mute`, `ban`, `assign-role`, `view-audit-log`, `mb-moderate`, `account-recover`.

## Consolidated TS intent definitions (lint contract — one interface per line)

The same intents that appear inline in the disposition tables above, restated here without the table-cell `\n`-encoding so machine grep can count them line-for-line. Phase 7 PAR-07 should pull these into `packages/admin-protocol/src/intents.ts` verbatim.

```typescript
interface AdminKickIntent {
  command: 'kick';
  payload: { targetAccountId: string; reason: string; durationSec?: number };
}

interface AdminMuteIntent {
  command: 'mute';
  payload: { targetAccountId: string; reason: string; durationSec: number };
}

interface AdminBanIntent {
  command: 'ban';
  payload: { targetAccountId: string; reason: string; permanent: boolean; durationDays?: number };
}

interface AdminAssignRoleIntent {
  command: 'assign-role';
  payload: { targetAccountId: string; role: 'player' | 'moderator' | 'admin' };
}

interface AdminViewAuditLogIntent {
  command: 'view-audit-log';
  payload: {
    sinceMinutes?: number;
    actorAccountId?: string;
    eventTypes?: Array<'login' | 'logout' | 'kick' | 'mute' | 'ban' | 'mb-moderate'>;
    limit?: number;
  };
}

interface AdminMbModerateIntent {
  command: 'mb-moderate';
  payload: {
    boardId: number;
    action: 'view' | 'delete-topic' | 'delete-reply' | 'pin' | 'unpin' | 'lock' | 'unlock';
    topicId?: number;
    replyId?: number;
    reason: string;
  };
}

interface AdminAccountRecoverIntent {
  command: 'account-recover';
  payload: {
    targetAccountId: string;
    action: 'reset-password-email' | 'unlock' | 'merge-duplicate' | 'force-reauth';
    reason: string;
  };
}
```

## Forcing function for Phase 7 PAR-07

This document is a **closed-list checklist**. PAR-07 implementation must:

1. Walk every row above and confirm the rebuild has **either** an explicit replacement (typed admin-UI endpoint) **or** an explicit "dropped intentionally" decision committed to the ADR.
2. Never add a code-eval surface to the rebuild. No `eval()`, no `Function(...)` constructors, no clipboard-driven actions, no remote code shipping.
3. Authenticate the admin web UI separately from the game client (different cookies / tokens / hostname). The admin UI is **NOT** part of the Phaser/Pixi game build.
4. **Opcode 12 is permanently removed from the wire.** No "broadcast arbitrary string to clients" channel exists in the rebuild — typed events only.

## See also

- [CLAUDE.md hard rule #3](../../CLAUDE.md) — "NEVER faithfully port 'Ctrl+E run clipboard as superuser'"
- [.planning/codebase/CONCERNS.md](../../.planning/codebase/CONCERNS.md) — broader legacy-archive risk register (plaintext credentials, IP exposure)
- [../extracted-engine/admin-anti-port.md](../extracted-engine/admin-anti-port.md) — client-side mirror of this anti-port reference
- [./chat.md](./chat.md) — benign backtick commands (`maint`, `servermsg`, `clientver`, etc.)
- [./protocol.md](./protocol.md) — opcode 12 row marked REJECTED-AS-PORTED
- Phase 7 PAR-07 plan (TBA) — admin UI design + delivery
