Skip to content

Projects

A Project is the isolation scope for sessions and pools — and the durable, developer-declared home for their configuration. It is what keeps one deployment's rooms from colliding with another's: all session and pool names resolve within a Project, never globally. Two clients in Project studio-42 and Project gallery-north that both join a session named "lobby" land in different rooms.

A Project is structural (an isolation scope) and, when provisioned through the control plane, configured — it owns matchmaking policies, credential bindings, and a lockdown flag. Isolation itself needs no credentials; enforcing who may enter a Project is the separate concern of authentication.

Connection-scoped

A connection is bound to exactly one Project, established once during the handshake. The client declares it up front and never repeats it — the Project is not a per-frame field, so a client cannot reach another Project by altering a message after connecting.

ts
import { StarfishClient } from "@starfish/client";

// Declare the Project at construction…
const client = new StarfishClient({
  server: "ws://localhost:4000",
  project: "studio-42",
});
await client.connect();

// …or when you connect.
await client.connect("studio-42");

// The effective Project the server bound you to:
console.log(client.project); // "studio-42"
python
from starfish import StarfishClient, StarfishClientOptions

# Declare the Project at construction…
client = StarfishClient(StarfishClientOptions(
    server="ws://localhost:4000",
    project="studio-42",
))
await client.connect()

# The effective Project the server bound you to:
print(client.project)  # "studio-42"

Omitting project (or passing an empty string) binds the connection to the reserved Project "default". The server resolves the effective Project — the value you declared, or one assigned by the authenticator — and echoes it back in the welcome as client.project.

Implicit vs. declared

A referenced Project resolves into one of two modes. The server decides by looking up the Project id in its store: found → declared, not found → implicit.

Implicit (undeclared)Declared (provisioned)
How it existsSimply referenced by a client — never provisionedCreated through the control plane
Session / pool creationAd-hoc create: true with client-chosen names and configAd-hoc still allowed by default; denied under lockdown
MatchmakingClient supplies pool config on entryClients join declared policies by name
ConfigurationNoneMatchmaking policies, credential keys, lockdown
Use forLocal development, trusted single-tenantProduction, multi-tenant, server-owned matchmaking

Implicit is the zero-config path. A Project id that was never provisioned behaves permissively — clients create sessions and pools with their own names and config, exactly as they always have. The reserved "default" Project is always implicit and can never be provisioned. A deployment that provisions nothing runs entirely in implicit mode, behaving as a single global keyspace.

Declared is the opt-in production layer. A Project created via the control plane carries durable configuration: matchmaking policies that clients join by name, credential keys that bind a credential to the Project, and a lockdown flag. By default a declared Project still permits ad-hoc create: true (the addition is non-breaking). When lockdown: true, only declared policies may be used, and ad-hoc creation is rejected with project.lockdown.

Zero-config still works

Adding the Project model changed nothing for local development. Point a client at a server without declaring a Project — or provisioning one — and you get the same single-keyspace behavior as before. Provisioning is a progressive, opt-in step you take when you need server-owned matchmaking or tenant isolation.

Relationship to sessions, pools, and auth

  • Sessions and pools resolve inside the Project. Matchmaking pairs members only within the same Project, and every session it mints belongs to that Project. See Core Concepts.
  • Matchmaking policies move the pool config a client used to supply on entry server-side, so clients join by policy name instead. See the Control Plane reference.
  • Authentication is a separate concern that composes with Projects. A credential can be bound to a Project through its keys; that binding is authoritative and overrides the client's declared project. A client whose credential maps to one Project but declares a different one is rejected with project.forbidden. This is how a deployment mints one Project per API key. See Projects and authentication.

Next steps