veratown-prototype

Railway Deployment

This repository runs as a long-lived Node.js worker on Railway. The existing veratown Railway project and production service can be reused; connect the service to the Rarsus/veratown-prototype GitHub repository and deploy its main branch.

An optional, independent map analysis service can be deployed in the same project. It has its own build/start commands and does not launch the bot process.

For end-to-end service setup, see HOWTO: Configure Railway Supervisor And Helper Bots. Supervisor-managed ABCL and its required region helper are covered by HOWTO: Enable Supervisor-Managed ABCL.

Build and start

Use Railway’s Railpack builder; a Dockerfile is not needed. The Node.js engine in package.json requires Node 24.

The build compiles TypeScript into dist/ and generates the API reference. The start command runs dist/index.js. Railway should redeploy when changes are pushed to the connected branch.

This process maintains an outbound Bondage Club Socket.IO connection. It serves no HTTP traffic by default. When optional helper position sharing is enabled, the supervisor accepts a small authenticated HTTP stream only on its Railway private network port; helpers make outbound requests to that private endpoint. Do not configure a public domain or HTTP healthcheck.

Retired standalone map analysis service

The map-web service (build:map-web / start:map-web) has been removed. The walkthrough, map analysis and image export are part of the configuration dashboard (Map tools), authenticated by the dashboard’s own login and available to viewers. Delete the old Railway service and its WEB_AUTH_* variables once the dashboard is deployed; no replacement service is needed.

Configuration management dashboard

The separate configuration dashboard has its own Railway service, database identity, and local passkey-only authentication by default; it does not require Cloudflare Access. Follow the configuration management service guide for account bootstrap, passkey setup, and test-database-first setup. Do not add BC credentials or helper-sharing secrets to that service. It remains read-only unless ADMIN_WEB_WRITES_ENABLED=true explicitly enables administrator writes. Qualify on the test database with a dedicated, least-privilege identity and transaction support before considering production. Local mode stores passwordless accounts, passkey public credentials, and single-use invitation metadata in dedicated prototype collections. Environment operator password recovery is optional and explicitly enabled with AUTHENTICATION_RECOVERY_METHOD=environment-password; see the Admin Web guide for bootstrap and recovery details. Keep the prototype database encrypted at rest. Writable deployments require exact WEB_AUTH_ORIGIN protection in either authentication mode. Deployment does not itself enable configuration writes.

The dashboard supports Serverless idle sleep: it closes its MongoDB client after 15 minutes without database work and reconnects on demand. Admin login sessions are stored in MongoDB, so reconnects and cold wakes do not require signing in again. Enable Serverless only on the independent web service and redeploy for the setting to take effect. The bot and helper workers must remain always-on.

Environment variables

Set runtime values in the Railway service’s production environment. Use the matching entries from your local .env file; never commit .env or paste credential values into this document.

Variable Required Notes
RUNTIME_TARGET Yes Set to production; pins the runtime database to veratown_prototype.
BOT_INSTANCE_ID No Stable ID of the database-managed profile; its role and non-secret settings override runtime variables.
RUNTIME_ROLE No Environment-only mode: supervisor by default; use helper or abcl for dedicated services. Ignored with a profile.
BC_SERVER_URL Conditional Required without a profile; otherwise supplied by the profile.
BC_USERNAME Yes Dedicated prototype bot account.
BC_PASSWORD Yes Password for the dedicated prototype account.
BC_ROOM Conditional Required without a profile; otherwise supplied by the profile.
MONGODB_URI Yes MongoDB connection string.
MONGODB_DATABASE No Must be veratown_prototype; this is the runtime’s isolated database.
BC_ORIGIN No Defaults to https://www.bondageprojects.elementfx.com; overridden by a selected profile.
HELPER_AGENT_ID Conditional Required for an environment-configured helper; a selected profile supplies its helper identity.
MOTD_TEXT No Fallback greeting when no valid scheduled MOTD rule matches; defaults to the prototype welcome message.
APPEARANCE_QUALIFICATION_ENABLED No Defaults to false; the profile can also set it. Enable only for an explicitly approved qualification.
DOOR_DEBUG No Set to true only when door debug logging is needed.
POSITION_OBSERVATION_ENABLED No Defaults to false; environment or profile must enable it on both supervisor and helper.
POSITION_OBSERVATION_LISTEN_HOST Sharing Supervisor only; profile or environment value; bind to 0.0.0.0.
POSITION_OBSERVATION_PORT Sharing Supervisor only; profile or environment value must match the Railway private service port.
POSITION_OBSERVATION_HELPER_TOKENS Sharing Supervisor-only JSON object mapping enabled helper IDs to distinct random bearer tokens.
POSITION_OBSERVATION_ENDPOINT Sharing Helper only; profile or environment value for the supervisor’s private endpoint.
POSITION_OBSERVATION_HELPER_TOKEN Sharing Helper-only token matching its supervisor mapping.
POSITION_OBSERVATION_HANDSHAKE_TIMEOUT_MS No Helper handshake budget; profile or environment value, default 3,000 ms (500–10,000 ms).

SOURCE_MONGODB_DATABASE is only used by the manual configuration-copy script. Do not configure it as a runtime database variable. The runtime refuses any MONGODB_DATABASE other than veratown_prototype.

With BOT_INSTANCE_ID, startup uses only RUNTIME_TARGET, MONGODB_URI, optional fixed-mapping MONGODB_DATABASE, and the instance ID to connect and load the profile. The profile role overrides RUNTIME_ROLE and selects the existing static composition; non-secret profile values override their environment equivalents. Create the profile in Admin Web before deploying that ID. Profile writes do not restart Railway services, and all current profile-controlled changes require an operator-managed restart. The profile’s runtime-target field is validation metadata only; RUNTIME_TARGET selects the fixed database mapping and cannot be overridden by a profile. Keep BC_USERNAME, BC_PASSWORD, Mongo settings, and position-observation tokens in Railway variables/secrets; they are never profile data. The Admin Web profile workflow and revision/audit behavior are documented in the bot instance profile guide.

The prototype can share a MongoDB cluster with another bot, but its runtime must use the isolated database above. Ensure the MongoDB user can access that database and the provider permits connections from Railway.

Private helper position sharing

Railway’s private WireGuard network provides encrypted service-to-service traffic. This implementation uses Node’s built-in HTTP client/server on that private network and does not require TLS certificates. Application-level authentication remains required: give each helper a distinct random token, configure the exact same value for its helper ID in the supervisor’s POSITION_OBSERVATION_HELPER_TOKENS JSON object and the helper’s POSITION_OBSERVATION_HELPER_TOKEN, and store those values only in Railway variables. The supervisor also checks that the helper is currently enabled and registered for the exact room/runtime, verifies its fleet membership, and sends only that helper’s member position. An HMAC proof authenticates the supervisor stream response. Tokens and packet contents are never logged.

  1. Keep sharing disabled while configuring the services. On the supervisor, enable the Railway private service port (do not generate a public domain), set POSITION_OBSERVATION_LISTEN_HOST=0.0.0.0, and set POSITION_OBSERVATION_PORT to the same port shown in Railway Networking.
  2. Set POSITION_OBSERVATION_HELPER_TOKENS to a JSON object whose keys are enabled helper agentId values and whose values are distinct random URL-safe tokens of at least 32 characters.
  3. On each helper service, set POSITION_OBSERVATION_ENDPOINT to the supervisor’s Railway private domain with that port and the documented /v1/map-position-observations path. Set its own matching POSITION_OBSERVATION_HELPER_TOKEN.
  4. Set POSITION_OBSERVATION_ENABLED=true on the supervisor and the intended single helper only after verifying the room, runtime target, registry record, service port, private DNS name, and token mapping. Leave all other helpers disabled until separately qualified.
  5. First qualify in the test runtime with dedicated accounts. Confirm the listening/connected transport status, helper startup teleport outcome, one dispatch/one journal record, and no task polling after any timeout, disconnect, or stop. Then qualify production with one helper and monitor sanitized status/latency and timeout outcomes before expanding.

Sharing defaults off and can be rolled back by disabling POSITION_OBSERVATION_ENABLED on both services and restarting them. The helper then uses its existing local BC observations; no cached/persisted positions are used. On stream loss, room/runtime mismatch, invalid frame, or stale/future observation, remote entries are invalidated. Timeouts remain fenced, and no movement is retried. The transport has bounded client count, frame size, cache, reconnect backoff, and per-helper scope; do not add a public route, database polling, whispers, or another BC connection as a fallback.

The sender emits a sequenced liveness heartbeat every second. The receiver invalidates its remote cache and reconnects after 3.5 seconds without stream data. Heartbeats are control frames only; they do not create or refresh BC position evidence.

Operations

  1. Connect the Railway service to Rarsus/veratown-prototype, branch main, while keeping it in the existing veratown project and production environment.
  2. Configure the environment variables above using the intended dedicated BC account and prototype MongoDB settings.
  3. Deploy and check Railway build/deploy logs for a successful startup and BC connection.
  4. Push future changes to the connected branch to trigger automatic deployments.

Do not enable a public domain or HTTP healthcheck for this worker. Monitor availability through Railway deployment status and logs, and through the prototypeRuntime heartbeat in MongoDB.

Startup and crash diagnostics

Records emitted through the shared runtime logger are one JSON object per line. Schema version 1 records include timestamp, severity, eventName, deployment, service, and replica; correlated work adds a correlation object and event-specific data lives under fields. Errors use name, message, and stack; non-Error rejection values are omitted. Sensitive field names and configured secrets are redacted by the shared logger.

Startup phase records use eventName: "startup.phase" with phase and status (started, completed, or failed) under fields. Phases cover config, database, repositories, defaults, room_configuration, composition, plugins, heartbeat, bc, and ready. Concurrent repository initialization and room loading include a component name so a failed index or document load can be traced to its owner. ready means startup finished after BC readiness, not merely socket dispatch.

Action transitions emit separate action.lifecycle records joined by correlation.operationId; dispatch and confirmation remain distinct. Position observation rejections use position_observation.rejected and include the bounded observation metadata, computed age, freshness limit, and rejection reason when those values are available.

Fatal records go to stderr with event names startup_failed, unhandled_rejection, or uncaught_exception. They include the same process envelope, classification, startup phase when available, error message, stack trace, and nested causes/aggregate failures. Plugin startup errors identify the failing plugin; rollback errors identify the plugin whose cleanup failed without replacing the original failure.

Check the final fatal record and preceding phase records:

Configuration validation logs variable names and issue codes, not supplied values. Other error messages and stacks redact configured credentials/account values, configured URLs/URIs, and inline connection URLs; arbitrary properties and non-Error rejection payloads are omitted. Do not add credentials, player text, or private payloads to error messages. Treat logs as operator-only data.

Fatal records are written synchronously before exiting with status 1, including when open handles would otherwise leave the container alive. Startup failures retain the existing cleanup paths. Unhandled rejections and uncaught exceptions terminate immediately rather than continuing potentially inconsistent state; they do not attempt asynchronous cleanup. SIGINT/SIGTERM still use normal graceful shutdown. Configure Railway’s restart policy as appropriate.