Matchmaking as a Policy
Problem
You want strangers to be paired into a session, but you don't want clients deciding the matchmaking rules. Group size, mode, and filters should be owned by the server, so a client simply asks to be matched — it can't inflate the group size, change the mode, or bypass a filter.
Solution
Provision a declared Project with a named matchmaking policy, then have clients join by policy name. The policy is a pre-declared pool: it captures the mode/groupSize/filter a client used to supply on entry, moved server-side. Clients call joinProject(project, { policy }), which connects, enters the policy, waits for the server to mint a session, and joins it — all in one call.
1. Provision the Project (once, out-of-band)
Start a server with a management key, then create the Project through the control plane. This example declares one auto policy named duos (group size 2), marked as the Project's default.
curl -X POST http://localhost:8080/admin/projects \
-H "Authorization: Bearer $STARFISH_MGMT_KEY" \
-H "Content-Type: application/json" \
-d '{
"id": "arcade",
"displayName": "Arcade",
"policies": [
{ "name": "duos", "mode": "auto", "groupSize": 2, "default": true }
]
}'See the Project Provisioning API for the full schema and CRUD endpoints.
2. Join by policy
import { StarfishClient } from "@starfish/client";
const client = new StarfishClient({
server: "ws://localhost:4000",
project: "arcade",
});
// Connects, enters the "duos" policy, waits for a match, and joins the
// server-minted session — all in one call.
const { session } = await client.joinProject("arcade", { policy: "duos" });
console.log("Landed in session:", session);from starfish import StarfishClient, StarfishClientOptions
client = StarfishClient(StarfishClientOptions(
server="ws://localhost:4000",
project="arcade",
))
# join_project returns the session/join response frame.
join = await client.join_project("arcade", policy="duos")
print("Landed in session:", join.header.session)Explanation
- No client-side pool config. The client names the policy (
duos) and nothing else.mode,groupSize, andfiltercome from the Project — the client cannot override them. That is the whole point of declaring a policy. joinProjectis the "join a Project, not a Session" ergonomic. It connects (declaring the Project), enters the named policy, awaits the server'smatchedevent, and joins the minted session. It is meant forautopolicies, which yield server routing.- Omit
policyto use the Project's default.joinProject("arcade")enters the policy markeddefault: true. If the Project declares no default, the server returnspool.policy_required. Naming a policy that doesn't exist returnspool.policy_not_found. - No lobby. Matchmaking precedes session membership — you don't join a session before entering a policy. The first matched member to join opens the session; the rest join it.
Variations
Attach metadata for filtering
A policy's filter matches on member metadata. Pass member metadata when you join (meta in TypeScript, attributes in Python); the server applies the policy's filter to it.
await client.joinProject("arcade", {
policy: "ranked",
meta: { region: "eu", skill: "intermediate" },
});await client.join_project("arcade", policy="ranked", attributes={"region": "eu", "skill": "intermediate"})The policy that owns the filter — for example { "region": "@self" }, meaning "only match members whose region equals mine" — is declared server-side:
{ "name": "ranked", "mode": "auto", "groupSize": 2, "filter": { "region": "@self" } }Interactive policies
Policies can also wrap the interactive modes (claim, mutual, propose, delegated). Entering by policy name is allowed — the config is server-declared — but the interactive claim/propose/assign flow then proceeds as usual. For those, enter by policy with pool.enter({ policy }) and drive the flow yourself rather than using joinProject (which awaits an automatic match). See Pool Matchmaking.
Local development without provisioning
If you haven't provisioned a Project, you're in an implicit Project — there are no policies, so clients supply pool config directly. See Auto Pairing for that client-driven form.
See also
- Projects — the isolation + configuration model
- Project Provisioning API — the control-plane reference
- Pool Matchmaking API — the full pool/policy API