> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reactor.inc/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> To build and serve your own model, start at /deploy/development/quickstart and /deploy/development/overview. Deploying is the default path: reactor init scaffolds a workspace, reactor auth login authenticates, and reactor model deploy registers the model, publishes the release with the weights/ folder, and activates it on Reactor's GPUs, in one command from that workspace. Docker must be running, because the publish step builds the image locally. Bump model.version in reactor.yaml before redeploying a change, because a release that already has an image is reactivated as it is. Deployment access is granted per account, so contact team@reactor.inc if a deploy is refused. Every key in reactor.yaml is documented at /deploy/platform/reactor-yaml. Model code imports reactor_runtime; Python client code imports reactor_sdk. The runtime overview explains the model interface. Running the model on your own machine with reactor run is optional and needs a GPU you attach with --gpus; /deploy/development/local-testing covers that loop and pairs a complete brightness model with a Python client test in a separate brightness-test workspace.
> Reactor hosts multiple models, each with its own connect slug (modelName) and command/event schema. The catalog of every model — slug, typed SDK package, and links to its schema — is at /model-api-reference/overview. Some models expose one slug per experience (e.g. HappyOyster); always take the slug from the model's own pages, never guess it.
> Fastest path to a working app: `npx create-reactor-app my-app --model=<slug>` scaffolds a complete app with secure auth wired up. Typed TypeScript SDKs are published as @reactor-models/<model>; Python uses the base reactor-sdk package.
> Auth: exchange an API key (rk_...) for a JWT via POST https://api.reactor.inc/tokens from your server. Never put the API key in client-side code.
> Append .md to any docs URL for clean Markdown. Search these docs via the MCP server at https://docs.reactor.inc/mcp.

# Sessions & Clients

> Sessions, connections, the hooks that fire for each, and serving more than one client at once.

A model serving in real time stays up for minutes at a time, and clients join, drop, and reconnect
while it runs, several at once. This page is about what survives a reconnect, what belongs to one
client, and how the runtime tells you when each happens.

A **session** is one continuous run of the model, from the moment the runtime opens it to the
moment it tears it down. A **connection** is one client inside that session. Clients come and go,
but the session keeps running underneath them.

<Frame caption="One session, two clients. Client A leaves and comes back inside the same session. The model steps whenever at least one client is connected.">
  <img src="https://mintcdn.com/reactortechnologiesinc/dCQX8iFwCidKaSoT/diagrams/session-connections.svg?fit=max&auto=format&n=dCQX8iFwCidKaSoT&q=85&s=960cd4ab2626dc00251f7061597a2939" alt="A timeline. A session bar spans the full width, with session_started at its left end and session_ended at its right. Below it, client A is connected, disconnects, and reconnects later; client B connects in between and overlaps with A. A band along the bottom marks where generate() runs, covering the whole time any client is connected." width="680" height="280" data-path="diagrams/session-connections.svg" />
</Frame>

The state a client can set belongs to the session, not to a connection, so a client that drops
and reconnects finds the values it left. The model also runs once for everyone: all connected
clients share the same state, so a `set_paused` from one of them pauses generation for all of
them. What each client receives is up to you. A model with one output track shows everyone the
same video. A model can also declare several tracks, one per view, and each client subscribes to
the tracks it wants. Messages work the same way: send one to everyone, or to a single client
through its handle, covered [below](#the-client-handle).

## The four hooks

You get one hook for each edge in the diagram. Decorate a method with it and the runtime calls the
method at that moment:

```python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
from reactor_runtime import (
    ClientInfo, ReactorApp, connected, disconnected, session_ended, session_started,
)


class MyModel(ReactorApp):
    @session_started
    async def on_session_start(self) -> None: ...

    @connected
    async def on_connect(self, client: ClientInfo) -> None: ...

    @disconnected
    async def on_disconnect(self, client: ClientInfo) -> None: ...

    @session_ended
    async def on_session_end(self) -> None: ...
```

`@session_started` runs before any client has connected, and `self.state` already exists by then,
built from the defaults, so this hook can write to it. `@session_ended` runs when the runtime tears
the session down, while the state is still readable. The two connection hooks run once per client,
each time one joins or leaves.

A hook can be `async def` or a plain `def`, and a model declares at most one of each. Hooks run
between steps, never in the middle of one.

<Note>
  When a session ends, the runtime tears down every connection at once and does not call
  `@disconnected` for each of them. Anything that must happen no matter how the session ends
  belongs in `@session_ended`.
</Note>

## Which hook to use

Put work where its lifetime matches.

Anything that outlives a single client goes in the session hooks. The typical job for
`@session_ended` is to reset whatever your model holds outside the state, such as caches or a
world the model has been generating, so the next session starts clean. The runtime resets the
state for you but never touches your model:

```python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
@session_ended
def on_session_ended(self) -> None:
    self.pipe.reset()
    self.output.flush()
```

Anything that belongs to one client goes in the connection hooks: their entry in a roster, their
cursor, a greeting message.

Anything expensive, such as loading a checkpoint, goes in `load()`. It runs once when the
container starts, long before either kind of hook.

## The client handle

To know which client a hook or an `@event` handler is running for, add a `client: ClientInfo`
parameter to it. The runtime fills it in with the client that triggered the call. It is not part
of the command's payload and does not appear in the schema.

```python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
@connected
async def on_connect(self, client: ClientInfo) -> None:
    await client.send(Status(prompt=self.state.prompt, chunk=self.chunk_index))
```

`ClientInfo` carries the connection's id, when it joined, and a `send()` that reaches that client
alone. The handle stays valid for as long as the client is connected, including inside its own
`@disconnected` hook.

<Card title="Sessions & clients reference" icon="book" href="/deploy/runtime-reference/symbols#sessions-and-clients">
  The four decorators and every field on the client handle.
</Card>

## Serving several clients

`self.send()` broadcasts a message to everyone in the session. `client.send()` reaches one client.
To message a specific client later, outside the hook that gave you its handle, keep the handle:

```python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
@connected
async def on_connect(self, client: ClientInfo) -> None:
    self._clients[client.id] = client

@disconnected
async def on_disconnect(self, client: ClientInfo) -> None:
    self._clients.pop(client.id, None)
```

`self.connected` is an `asyncio.Event` that is set while at least one client is connected and clear
when the last one has left. It tracks whether any client is connected, not which one.

## Next

<CardGroup cols={2}>
  <Card title="The Step Loop" icon="play" href="/deploy/development/reactor-app/step-loop">
    How the runtime drives the three calls, when to refuse a step, and how playout is paced.
  </Card>

  <Card title="Events & Messages" icon="bolt" href="/deploy/development/reactor-app/events-and-messages">
    Commands the client can call and messages the model sends back.
  </Card>
</CardGroup>
