Skip to main content
The Playground server (apps/playground) serves the built page and runs the relay, the proxies and the lab sellers. It is stateless and holds no secret: every setting below is public, and most of it is published to the page at GET /api/config. It serves stellar:testnet only.

From source

From a source checkout with Node.js 24.11 or later:
pnpm build builds the workspace packages and the page (web/dist). By default the server uses the SDF testnet RPC, Horizon and Friendbot, Rail402 at https://testnet.rail402.dev as facilitator and Bazaar, the demo seller at https://demo.rail402.dev, and Circle’s testnet USDC. PLAYGROUND_PAY_TO is the public G… testnet account the lab sellers are paid to. It needs a trustline for the asset; create it with the CLI (rail402 keys new, then rail402 account setup) and keep its key to yourself: the Playground never takes one. Without an account, set LAB_ENABLED=false: the lab sellers (the sandbox route, the greedy route and the offers) then answer playground_feature_disabled, and the page shows what needs them (“Sell and get listed”, “Seller charges over the cap” and the network, asset and amount checks) as turned off. GET /health reports the version and commit; GET /ready passes once the page is built and the facilitator answers /supported.

Docker

The image holds the server and the built page, runs as the node user and listens on port 8080. Build it from the repository root:
Behind a TLS proxy, set PUBLIC_URL to the public https origin and TRUSTED_PROXY_HOPS to the number of proxies that set X-Forwarded-For, so the per-client limits see the client’s address. Run one replica: the limits are kept in memory.

Local mode

PLAYGROUND_MODE=local lets the relay and the seller check reach sellers on loopback over plain http on their own port, for a Rail402 and sellers running on the same machine. It is allowed only with a loopback PUBLIC_URL, and the server then listens on loopback only: HOST defaults to 127.0.0.1, and any other address than 127.0.0.1, ::1 or localhost is refused, since anyone who could reach the server could use the relay to call the services on your machine. Point FACILITATOR_URL, BAZAAR_URL, DEMO_SELLER_URL, RPC_URL and the asset settings at your own services; the conformance run does exactly that on the public testnet. The “Sell and get listed” scenario lists the sandbox route under PUBLIC_URL. A Bazaar lists only routes it can call back, so the listing needs a public https PUBLIC_URL, or local mode with your own Rail402 started with DISCOVERY_ALLOW_LOOPBACK=true. Against the hosted Bazaar from a loopback PUBLIC_URL, the payment settles but the Bazaar refuses the listing (bazaar_resource_unsafe), and the run says so.

Configuration

The server reads its environment once at startup and refuses to start (exit code 78, playground_config_invalid) on any invalid value, naming the variable, and when any value is a Stellar secret seed. The limits in How it works are set with RELAY_UNPAID_PER_MINUTE, RELAY_UNPAID_BURST, RELAY_UNPAID_PER_NETWORK_PER_MINUTE, RELAY_PAID_PER_MINUTE, RELAY_PAID_GLOBAL_PER_MINUTE, RELAY_TRUSTED_PAID_GLOBAL_PER_MINUTE, RELAY_IN_FLIGHT_PER_IP, RELAY_IN_FLIGHT_PER_HOST, RELAY_IN_FLIGHT_GLOBAL, RELAY_STREAMS_PER_IP, RELAY_STREAM_MAX_MS, RELAY_HOST_PER_MINUTE, RELAY_TRUSTED_HOST_PER_MINUTE, RELAY_BYTES_PER_IP_PER_DAY, RELAY_BYTES_PER_NETWORK_PER_DAY, RELAY_BYTES_GLOBAL_PER_DAY, SELLER_CHECK_PER_MINUTE, SELLER_CHECK_GLOBAL_PER_MINUTE, PROXY_PER_MINUTE, PROXY_GLOBAL_PER_MINUTE, LAB_UNPAID_PER_MINUTE, LAB_PAID_PER_MINUTE and LAB_PAID_GLOBAL_PER_MINUTE.