veratown-prototype

HOWTO: Configure Railway Supervisor And Helper Bots

This guide deploys the supervisor and each helper as separate Railway services from this repository. Every service runs one bot process, one dedicated BC account, and one runtime database. Start with position sharing disabled; it is an optional feature for helper startup teleports, not a prerequisite for helper task dispatch.

1. Prepare accounts and runtime data

  1. Create a dedicated BC account for the supervisor and a different account for every helper. Do not use an existing production bot account.
  2. Choose the runtime before configuring services:
    • Production: RUNTIME_TARGET=production and database veratown_prototype.
    • Qualification: RUNTIME_TARGET=test and database veratown_prototype_test, with dedicated non-production BC accounts.
  3. Ensure the MongoDB user can access the selected runtime database and Railway can connect to the MongoDB provider. Never set the runtime database to the legacy source database.
  4. In the selected runtime database, configure the helper settings, one helper agent record per helper, and reviewed message templates as described in the Helper Bots guide. The agent’s agentId, memberNumber, roomKey, runtimeTarget, and enabled value must match its Railway service and account. Do not store BC credentials or observation tokens in MongoDB.

2. Create Railway services

  1. In the existing veratown Railway project, create a supervisor service connected to Rarsus/veratown-prototype, deploying the intended branch (normally main).
  2. Configure Railpack with repository root as the root directory, build command npm run build, and start command npm start. The application requires Node.js 24; no Dockerfile is needed.
  3. For each helper, create another service from the same repository and branch, using the same build and start commands. Give each service a clear name, such as Supervisor, Helper-A, and Helper-B.
  4. Keep the services in the same Railway project and environment when they need private communication. Do not generate a public domain or configure a public HTTP healthcheck; these are long-running workers.

3. Configure the supervisor

In the supervisor service’s Variables, set the common runtime variables:

The examples below use environment-only configuration. For database-managed configuration, first create a supervisor profile in Admin Web for the selected runtime target, assign its stable ID, and set only BOT_INSTANCE_ID for profile selection. The profile supplies the role, non-secret BC endpoint/room, and supported capabilities; keep Mongo bootstrap settings, the dedicated BC username/password, and observation tokens in Railway variables/secrets. The profile wizard and restart behavior are described in the Admin Web guide.

Variable Production value
RUNTIME_TARGET production
MONGODB_DATABASE veratown_prototype (optional, but if set must match the runtime)
MONGODB_URI The MongoDB connection string stored as a Railway secret
BC_SERVER_URL The Bondage Club Socket.IO server URL
BC_USERNAME / BC_PASSWORD Credentials for the dedicated supervisor account; store as secrets
BC_ROOM The room the supervisor should join or create

In environment-only mode, set RUNTIME_ROLE=supervisor and leave HELPER_AGENT_ID unset. With a profile, the profile’s role selects the supervisor composition and its non-secret settings override environment values. Both modes start the room features and helper-task supervisor. Review the other optional values in the Railway deployment guide; leave appearance qualification disabled unless separately approved.

Deploy the service and verify successful startup, BC login, and room synchronization in Railway logs before configuring helpers.

4. Configure each helper service

Set the same common variable names on each helper service, but use that helper’s dedicated BC credentials. Keep BC_ROOM and RUNTIME_TARGET consistent with the supervisor. Set these additional values:

For profile mode, create a helper profile in the same target database with a stable deployment ID and the helper registry identity. Set BOT_INSTANCE_ID to that ID; the profile supplies HELPER_AGENT_ID and the non-secret capability settings. The helper record must match the service’s runtime and room. Do not put its BC credentials or observation bearer token in the profile.

Variable Example
RUNTIME_ROLE helper
HELPER_AGENT_ID helper-a — exactly the matching agentId in MongoDB
RUNTIME_TARGET production only for the production fleet
MONGODB_DATABASE veratown_prototype for production, or veratown_prototype_test for test

Do not configure HELPER_AGENT_ID on the supervisor. The helper starts only its helper-worker plug-in; identity, room, member number, runtime, and enabled-state checks must match the helper-agent record before work can run. Deploy and verify each helper’s BC room sync before assigning tasks.

Use the room-admin helper commands documented in the Helper Bots guide to assign work. A task uses a reviewed template ID and configured location; never put arbitrary message text or BC credentials in task documents.

5. Optional: enable private position sharing

Only do this after the supervisor and helper are working independently and test-runtime qualification has passed. Sharing defaults off and is used only when a helper has an optional startLocationKey that requires supervisor-side position observation.

  1. In Railway Networking for the supervisor service, enable a private service port (for example, 8080). Do not create a public domain. In its Variables set:

     POSITION_OBSERVATION_LISTEN_HOST=0.0.0.0
     POSITION_OBSERVATION_PORT=8080
     POSITION_OBSERVATION_ENABLED=false
    

    The configured port must match the service’s private networking port.

  2. Generate a unique URL-safe token for each helper using a trusted local machine, for example:

     node -e 'console.log(require("node:crypto").randomBytes(32).toString("base64url"))'
    

    Do not paste tokens into chat, source control, MongoDB, or logs. Store each token only in Railway Variables.

  3. On the supervisor, set POSITION_OBSERVATION_HELPER_TOKENS to a JSON object mapping each enabled helper ID to its own token:

     { "helper-a": "<helper-a-token>", "helper-b": "<helper-b-token>" }
    
  4. On each helper, set its matching POSITION_OBSERVATION_HELPER_TOKEN and set POSITION_OBSERVATION_ENDPOINT to the supervisor’s private URL:

     http://$:$/v1/map-position-observations
    

    Replace Supervisor with the exact Railway service reference/name. If Railway variable interpolation is unavailable, enter the resolved private domain and port directly. The endpoint must use http, the private railway.internal host, the configured port, and the exact path shown.

  5. First test with RUNTIME_TARGET=test, the test database, and dedicated test accounts. Confirm listening on the supervisor and connected on the helper in logs, and verify a startup teleport succeeds only after a matching fresh BC position observation. Exercise stream loss and stop; neither may cause an automatic movement retry or unlock task polling.
  6. Enable POSITION_OBSERVATION_ENABLED=true on the supervisor and only the one qualified helper. Restart/redeploy both services. Keep other helpers disabled until separately qualified. Before production use, repeat the qualification with the intended production account and database.

Railway’s private WireGuard network encrypts service traffic; the app still requires the per-helper bearer tokens and validates the supervisor’s HMAC handshake. Heartbeats only detect stream liveness; they never count as position evidence. See the position-sharing runbook for scope, failure behavior, and rollback.

Push-assisted task pickup

With sharing enabled on both updated services, helpers assign first persists the task and then notifies the assigned helper over this same stream. No new Railway variables or ports are needed. The helper still uses MongoDB atomic claims, leases, and the normal action executor; the stream cannot submit raw commands. Capability negotiation allows either service to be updated first; mixed-version pairs retain polling.

Qualify in the test runtime by assigning a reviewed public template while the helper is at its required observed location. Verify task pickup before the configured polling interval, and verify the supervisor’s matching BC observation is still required for succeeded. Interrupt the private stream and assign another task: periodic polling must still pick it up without duplicate dispatch. After stream reconnection, queued tasks also trigger an immediate queue check. Check that a helper with an unobserved startup teleport remains fenced despite new assignments. A log saying the wake-up is unavailable means the durable task is still queued, not failed; it is not a reason to assign it again.

6. Troubleshooting and rollback