BUILD WITH
arcnet.
Use arcnet. to admit players, create rooms, discover matches, exchange messages and signal peer connections. Keep your game logic in your own backend or dedicated server.
Your first connection
ArcNet separates developer accounts from game players. You manage games and service credentials in the Developer Console. Provide a public Game ID and a Player ID to the SDK. ArcNet handles transport authorization internally. If you have accounts or a trusted backend, pass your existing account ID as the Player ID.
- Create your developer account. Register with your email and password, then sign in.
- Create a workspace and game. Open the console, create an organization, then create a game with Relay mode for the simplest first test.
- Configure browser origins. Add the exact origin hosting your game, for example
http://localhost:3000. Origins are configured per game, not per developer login. - Connect your game client. Add your Game ID to the ArcNet SDK and provide your game’s Player ID when connecting. If none is provided, ArcNet assigns a temporary ID.
- Join a room. Create a room or use matchmaking; connect players using their own distinct Player IDs.
- Ship your game. The SDK handles network authorization automatically. Do not put a private service credential into a browser or Unity client.
Before you begin: ArcNet manages the networking infrastructure. Create a game service in your console, add your allowed browser origins, and use unique Player IDs from your game. Private credentials are needed only for trusted backend integrations and should never be embedded in shipped clients.
Player IDs and server credentials
The Game ID identifies the game. The Player ID identifies a specific player, and a previously supplied Player ID is preserved as their networking identity. Without an ID, the SDK generates a temporary one. Client-supplied IDs are not proof of account ownership; use your own authenticated backend for trusted account identities. Private sk_arc_ server credentials must never be embedded in browser, WebGL or compiled Unity clients.
Advanced: trusted backend player authorization
Most games do not call this endpoint directly. If you need to verify player accounts, your trusted backend can authorize a Player ID and call this endpoint. The SDK uses the resulting short-lived transport authorization internally. That token never becomes the Player ID.
curl -X POST "$ARCNET_PORTAL_URL/api/v1/tickets" \
-H "Authorization: Bearer $ARCNET_GAME_SECRET" \
-H "Content-Type: application/json" \
-d '{"gameId":"game_YOUR_ID","playerId":"player-123","displayName":"Player123"}'Example trusted backend
Store ARCNET_PORTAL_URL, ARCNET_GAME_ID and ARCNET_GAME_SECRET in server-only environment variables. The production Portal is https://arcnet.inuv8studios.com. The following Node.js example assumes you already authenticated your game's player.
// Run on your trusted game backend. Never ship the game secret to a client.
const portal = process.env.ARCNET_PORTAL_URL;
const gameId = process.env.ARCNET_GAME_ID;
const gameSecret = process.env.ARCNET_GAME_SECRET;
export async function issueArcNetTicket(authenticatedPlayer) {
if (!authenticatedPlayer?.id) throw new Error('Player login required');
const res = await fetch(`${portal}/api/v1/tickets`, {
method: 'POST',
headers: {
Authorization: `Bearer ${gameSecret}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
gameId,
playerId: authenticatedPlayer.id,
displayName: authenticatedPlayer.name,
}),
});
if (!res.ok) throw new Error(`Ticket request failed: ${res.status}`);
return res.json();
}Connect from your game
The current browser SDK lives at packages/sdk-web in the ArcNet monorepo. It is usable within that repository's npm workspaces; it has not been released to the public npm registry. External developers can use the documented HTTP and WebSocket protocol or package the SDK for their project.
Provide your Game ID and existing Player ID. ArcNet automatically handles the short-lived transport credentials and gateway discovery. For staging, configure portalUrl with your Preview URL. Production uses https://arcnet.inuv8studios.com automatically. Only override portalUrl for staging or a custom deployment.
import { ArcNetClient } from '@arcnet/sdk-web';
// Game ID comes from your ArcNet Console. No private key or ticket helper.
// Use the same playerId assigned by your game's user/account system.
const client = await ArcNetClient.connect({
gameId: 'game_YOUR_PUBLIC_ID',
playerId: 'player-123',
// portalUrl: 'https://YOUR-STAGING-PREVIEW.vercel.app', // staging only
});
console.log('Player ID:', client.playerId);
const { roomId } = await client.matchmake({ topology: 'relay', capacity: 4 });
await client.join(roomId);
client.chat('Hello from arcnet.');For a second player, call ArcNetClient.connect with the same Game ID and that player's own Player ID, then use join(roomId). A Player ID identifies a session participant. Keep it consistent between sessions if your game has accounts.
Unity: connect with your existing Player ID
// ArcNetSettings asset: set Game ID once in the Unity Inspector.
// Keep private game secrets on a trusted server, never in this asset.
service.SetPlayerId("player-123");
yield return service.Connect(null, connected => {
if (!connected) Debug.LogError("Could not connect to ArcNet");
});
// After connection, create or join a room using ArcNetService.If your game has account authentication, integrate its trusted backend to prevent players from claiming another account's Player ID. Game IDs are public; server credentials remain private.
Rooms and matchmaking
createRoom() always creates a new room. matchmake() looks for a compatible room and can create one if none is found. You can select Relay, P2P, Turn-based or Dedicated topology only when that mode is allowed by your game settings.
// Use a new, unexpired ticket for each player.
const result = await client.matchmake({ topology: 'relay', capacity: 4, region: 'auto' });
await client.join(result.roomId);
// Relay messages are untrusted data, not validated game outcomes.
client.relay({ action: 'wave' });
// Disconnect when leaving a room.
client.close();Rooms currently allow 2 to 32 players and expire after 24 hours. Matchmaking is a basic room directory, not a skill-ranking or tournament system. A room's WebSocket connection uses a separate, single-use ticket with a roughly 30-second lifetime.
Verify two-player connectivity
After deploying the Portal and Edge, you can run the included examples/relay-two-player-smoke.mjs from the ArcNet repository with Node.js 22+. It requests distinct tickets for two players, creates a Relay room, connects both clients and verifies message delivery.
# Set these privately in your shell or trusted local environment.
export ARCNET_PORTAL_URL="https://arcnet.inuv8studios.com"
export ARCNET_EDGE_URL="https://arcnet-network-edge-production.arcnet-network-edge.workers.dev"
export ARCNET_GAME_ID="game_YOUR_ID"
# Prompt without echoing the secret, then export it.
read -rs ARCNET_GAME_SECRET; echo; export ARCNET_GAME_SECRET
node examples/relay-two-player-smoke.mjs
unset ARCNET_GAME_SECRETHTTP API
Portal endpoints run on the ArcNet service. Networking endpoints use the ArcNet gateway address returned by the player ticket API.
Developer portal API
| Method | Path | Purpose |
|---|---|---|
| POST | /api/v1/tickets | Issue a signed player ticket from a trusted backend using the private game secret |
| POST | /api/v1/introspect | Validate a player ticket from a trusted game server |
| GET | /api/v1/games/:id | Read an active game's public metadata with permitted authorization |
| GET, POST | /api/console/orgs | List or create developer workspaces (session required) |
| GET, POST | /api/console/games | List or create game services (session required) |
| GET, PATCH | /api/console/games/:id | Read or update a game configuration |
| GET, POST, DELETE | /api/console/games/:id/secrets | Manage private game credentials |
| GET | /api/console/games/:id/health | View your game's dedicated-server report (session and membership required) |
| POST | /api/v1/games/:id/heartbeat | Report your own server status (private server credential required) |
Networking Edge API
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/rooms | Create a room with a game:connect ticket |
| POST | /v1/matchmake | Find or create a matching room |
| GET | /v1/rooms/:id/info | Read room status for the authorized game |
| POST | /v1/rooms/:id/ticket | Issue a room WebSocket ticket |
| GET + Upgrade | /v1/rooms/:id/connect | Open room WebSocket with room ticket in the query string |
| POST | /v1/ice | Generate ICE/TURN configuration when TURN is enabled |
POST /v1/rooms
Authorization: Bearer <90-second game:connect ticket>
Content-Type: application/json
{"topology":"relay","capacity":4,"region":"auto"}
// Returns: {"ok":true,"roomId":"room_...","joinUrl":"https://.../ticket"}
POST /v1/rooms/room_ID/ticket
Authorization: Bearer <same unexpired game:connect ticket>
// Returns a short-lived WSS connection endpoint.WebSocket messages
Once connected, send JSON packets with v: 1 and a supported type. ArcNet attaches the true sender ID to relayed events; you cannot claim another player's identity by setting from.
// Client to room, after opening the WSS endpoint issued by /ticket:
{"v":1,"type":"PING"}
{"v":1,"type":"CHAT","text":"Hello"}
{"v":1,"type":"RELAY","data":{"action":"wave"}}
{"v":1,"type":"SIGNAL","to":"player-2","signalType":"offer","data":{}}
// Server events: WELCOME, ROOM_STATE, PONG, CHAT, RELAY, SIGNAL, PEER_LEFTLimits in the current Edge implementation include 8 KB incoming frames, 300 characters per chat message, 4 KB serialized relay payloads and approximately 60 messages per second per connection. Relay and peer packets remain untrusted; your authoritative game server must validate gameplay outcomes.
Choose your networking model
Your server's health status
Dedicated-server health is scoped to your own game and workspace. First save a valid WSS endpoint in the Game Service page. From your trusted dedicated server, send a signed heartbeat every 30 seconds using one of that game's active server credentials.
# Run on your trusted dedicated server every 30 seconds.
# ARCNET_GAME_SECRET is a SERVER credential, not a player ticket.
curl -X POST "$ARCNET_PORTAL_URL/api/v1/games/$ARCNET_GAME_ID/heartbeat" \
-H "Authorization: Bearer $ARCNET_GAME_SECRET" \
-H "Content-Type: application/json" \
-d '{"status":"online"}'Report online when accepting players, downtime when degraded or undergoing maintenance, and offline during controlled shutdown. A fresh report is valid for 90 seconds; missing reports display Downtime for up to 5 minutes, then Offline. A server that has never reported shows Awaiting heartbeat.
ArcNet does not probe arbitrary customer URLs. These statuses are server-reported and may be stale or inaccurate if a customer's service is misconfigured or dishonest. Only authorized members of your workspace can view them.
Troubleshooting
| Symptom | Check |
|---|---|
| Sign-in fails | Verify the developer account, BETTER_AUTH_URL, and the database's Better Auth tables |
| Dashboard fails to load | Verify all ArcNet database migrations are applied to the database set by DATABASE_URL |
| INVALID_CREDENTIAL | Verify Game ID, private secret, and the portal credential pepper; generate a new secret if compromised |
| UNAUTHORIZED | Check the player ticket type and lifetime, and request a fresh ticket if necessary |
| ORIGIN_DENIED | Add the exact browser origin to the game's allowed origins in the console |
| TURN_NOT_CONFIGURED | Configure TURN credentials before testing WebRTC relay fallback |
| Room WebSocket rejected | Request a fresh room ticket and connect promptly; room tickets cannot be reused |
Keep credentials private: Never place sk_arc_ secrets, database URLs, or signing keys in browser bundles. For Unity WebGL embedded in a React shell, one browser networking connection can deliver updates to the Unity presentation layer.