Files
servuo-plugins/overlay/Config/Bridge.cfg
Claude 5816c29c67 feat(protocol2): website account provisioning & unlinking (Part A)
Adds the account-provisioning plane from docs/PROTOCOL_2.md Part A: the
website can create game accounts and unlink them, gated by a shard-wide
signup mode. The existing [link flow is unchanged.

Overlay:
- BridgeConfig: SignupMode (website|game|hybrid, default hybrid; unrecognized
  falls back to game), AccountCreateEnabled (mode-following default),
  RequireIpForCreate, name/password caps, and a boot warning when the core
  Accounts.AutoCreateAccounts setting contradicts the mode.
- BridgeAccounts (new): account.create (mode gate, actor required, char-safety
  mirrored from AccountHandler, collision check, per-IP cap via CanCreate/
  LogAccess with fail-closed missing/loopback IP, create + WebsiteUserId link,
  account.audit; password never logged or echoed) and account.unlink (Owner
  floor via BridgeAdmin.Protected, clears the tag).
- BridgeAccountLink: in-game [unlink command, emits account.unlinked.
- BridgeAdmin: Protected / ResolveTargetAccount promoted to public for reuse.

Sidecar:
- POST /accounts/create, DELETE /link/:account, respond_account status mapping
  (409 collision / 429 ip cap / 403 disabled|protected / 404 not-linked / 400).
- store.record_unlink drops the mirrored link row.
- PROTOCOL_VERSION -> 2 (outbound events additive; new endpoints need v2).

Docs: INTEGRATION.md protocol bump, account.* events, endpoints, 409/429;
PROTOCOL_2.md Part A marked built.

Verified: sidecar cargo check clean; overlay compiles in the full ServUO
Scripts tree (0 errors, 0 warnings). Live end-to-end run still pending.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 07:42:06 -05:00

89 lines
4.3 KiB
INI

# uo-link bridge settings.
#
# Key scope is the filename: Bridge.cfg + StatSweepSeconds => "Bridge.StatSweepSeconds".
# Read in Configure(), which runs before World.Load.
# Loopback only. The socket being local is the trust boundary for inbound commands;
# if the sidecar ever moves off-host, add a shared secret first.
Host=127.0.0.1
Port=7788
# Outbound queue cap. On overflow the plugin drops oldest and counts the drops,
# because a stalled sidecar must never OOM the shard.
QueueCap=10000
# Sweep intervals, seconds. Measured on a 150-character shard: a vitals sweep costs
# 0.0015 ms/char, so 1000 online players is ~1.5 ms per sweep. See docs/PLAN.md §1.
StatSweepSeconds=30
DecaySweepSeconds=60
EconomySweepSeconds=300
# Champion-spawn board poll. ChampionSpawn has no EventSink, so every spawn is diffed on
# this interval to emit champ.update on any status/level/kills/boss change. The world holds
# only a handful of spawns, so the pass is trivial; 5-10s is well within site tolerance.
ChampSweepSeconds=10
# Help-page queue poll. The in-game page queue has no EventSink, so it is diffed on this
# interval to emit page.new / page.closed / page.updated. A few seconds is fine for a
# support queue; the full open queue is also available on demand via pages.snapshot.
PageSweepSeconds=5
# Shown to a player when they run [link. The website page where they enter the code.
LinkUrl=https://yoursite/link
# Town-crier news pushed from the website. Caps are defense in depth on top of the
# loopback trust boundary: a buggy or compromised sidecar still cannot flood the criers.
TownCrierMaxLines=6
TownCrierMaxLineLength=200
TownCrierMaxActive=20
TownCrierMaxDurationSec=86400
# Admin write plane (staff moderation from the website). OFF by default: the whole
# feature is opt-in per shard. When enabled, inbound admin.* commands (kick/ban/unban/
# broadcast) are honored. Authorization is enforced on the website; the shard trusts the
# loopback socket and applies a hard floor below.
AdminWriteEnabled=false
# The one shard-side safety floor. An admin.* command refuses any target whose AccessLevel
# is at or above this, so even a compromised sidecar can never touch the Owner. Values are
# AccessLevel names (Player, VIP, Counselor, Decorator, Spawner, GameMaster, Seer,
# Administrator, Developer, CoOwner, Owner). Default CoOwner => only Owner/CoOwners shielded.
AdminAccessFloor=CoOwner
# Defense-in-depth caps on admin.* payloads (mirroring the town-crier caps).
AdminBroadcastMaxLength=300
AdminReasonMaxLength=400
# Clamp on a timed ban's duration, seconds. A ban with no/zero duration is indefinite.
AdminBanMaxDurationSec=31536000
# Account provisioning (docs/PROTOCOL_2.md Part A). Which side may mint game accounts:
# website — the website is the authority; pair with Accounts.AutoCreateAccounts=false
# (else an in-game login of any new name still mints an account).
# game — the game server is the authority; website account.create is refused.
# hybrid — either side may create (the default).
# The bridge governs only the account.create verb; the in-game first-login auto-create is
# the core Accounts.AutoCreateAccounts setting, which you pair with the mode above. On boot
# the bridge warns if the two contradict. An unrecognized value here falls back to 'game'
# (the safest — no website creation).
SignupMode=hybrid
# Master switch for the account.create verb. Absent, it follows the mode (on unless
# SignupMode=game). Set explicitly to force it on or off regardless of mode.
AccountCreateEnabled=true
# Fail closed if account.create omits a usable browser IP. The per-IP cap
# (Accounts.AccountsPerIp) only means something if a missing/loopback IP is refused rather
# than waved through. Turn off only for a deployment that deliberately does not cap website
# signups by IP (MaxAccountsPerIP still applies in-game either way).
RequireIpForCreate=true
# Length caps on a website-supplied username / password, checked before the account is made.
AccountNameMaxLength=16
AccountPasswordMaxLength=30
# The test scaffolding in tools/scaffolding/ reads its own flags from this file
# (SeedOnStart, CensusOnStart, ProbeOnStart). They are absent here on purpose:
# Config.Get returns the default of false when a key is missing, so a deployed
# server never runs the scaffolding even if its .cs files are present.