141 lines
4.7 KiB
Markdown
141 lines
4.7 KiB
Markdown
# Capability Spec: network-session
|
|
|
|
## Capability ID
|
|
|
|
`network-session`
|
|
|
|
## Status
|
|
|
|
PROPOSED
|
|
|
|
## Description
|
|
|
|
Logical session overlay on top of the tiered mesh network. Sessions provide player visibility scopes, nested hierarchies (free-roam → mission → phase), per-session host nomination, capacity enforcement, and seamless join/leave transitions.
|
|
|
|
## Interfaces
|
|
|
|
### `Session`
|
|
|
|
```typescript
|
|
interface Session {
|
|
readonly id: SessionId;
|
|
readonly parentId: SessionId | null;
|
|
readonly hostId: string;
|
|
readonly peers: ReadonlyArray<string>;
|
|
readonly capacity: number;
|
|
readonly rules: SessionRules;
|
|
readonly state: SessionState;
|
|
readonly depth: number;
|
|
}
|
|
```
|
|
|
|
### `SessionManager`
|
|
|
|
```typescript
|
|
interface SessionManager {
|
|
createSession(parentId: SessionId, config: SessionConfig): Session;
|
|
destroySession(sessionId: SessionId): void;
|
|
join(sessionId: SessionId, peerId: string): Promise<void>;
|
|
leave(sessionId: SessionId, peerId: string): Promise<void>;
|
|
move(peerId: string, fromSessionId: SessionId, toSessionId: SessionId): Promise<void>;
|
|
getSession(peerId: string): Session | undefined;
|
|
getSessionPath(sessionId: SessionId): ReadonlyArray<SessionId>;
|
|
}
|
|
```
|
|
|
|
### `SessionRouter`
|
|
|
|
```typescript
|
|
interface SessionRouter {
|
|
send(sessionId: SessionId, envelope: MessageEnvelope): void;
|
|
broadcast(sessionId: SessionId, envelope: MessageEnvelope): void;
|
|
sendToParent(sessionId: SessionId, envelope: MessageEnvelope): void;
|
|
}
|
|
```
|
|
|
|
### `SessionHostElection`
|
|
|
|
```typescript
|
|
interface SessionHostElection {
|
|
nominate(sessionId: SessionId): Promise<string>;
|
|
getHost(sessionId: SessionId): string | undefined;
|
|
}
|
|
```
|
|
|
|
## Behaviors
|
|
|
|
### NET-SESS-01 — Session tree
|
|
|
|
Sessions form a tree with exactly one root session per mesh. Each session has exactly one parent (except root). Cycles are impossible.
|
|
|
|
### NET-SESS-02 — Leaf membership
|
|
|
|
A peer belongs to exactly one leaf session at any time. The peer implicitly belongs to all ancestor sessions for parent-state-bridge purposes.
|
|
|
|
### NET-SESS-03 — Session-scoped routing
|
|
|
|
Messages sent within a session scope are delivered only to peers in that session or its descendants. Peers outside the scope do not receive the message.
|
|
|
|
### NET-SESS-04 — Upward routing
|
|
|
|
Messages with `scope: 'parent'` are delivered only to the parent session's host. This is used for child-to-parent state reporting (e.g., "mission complete").
|
|
|
|
### NET-SESS-05 — Broadcast within branch
|
|
|
|
Messages with `scope: 'broadcast'` are delivered to all peers in the target session and all descendant sessions.
|
|
|
|
### NET-SESS-06 — Per-session host election
|
|
|
|
Each session nominates its own host independently. The election uses the same scoring algorithm as mesh governance but scoped to session peers. A peer can host multiple sessions simultaneously.
|
|
|
|
### NET-SESS-07 — Host independence from mesh
|
|
|
|
The session host can be different from the mesh Tier 0 host. If the mesh host and session host are the same peer, that peer hosts both layers.
|
|
|
|
### NET-SESS-08 — Join with state snapshot
|
|
|
|
When a peer joins a session, the session host provides a state snapshot. The peer transitions through `requesting → syncing → active` before receiving live messages.
|
|
|
|
### NET-SESS-09 — Leave with cleanup
|
|
|
|
When a peer leaves a session, the session host cleans up peer state and notifies remaining peers. The leaving peer is moved to the parent session.
|
|
|
|
### NET-SESS-10 — Move with forwarding tunnel
|
|
|
|
When a peer moves from session A to session B, a temporary forwarding tunnel routes in-flight messages from A's host to B's host until the peer is fully synced.
|
|
|
|
### NET-SESS-11 — Capacity enforcement
|
|
|
|
`join()` is rejected when the session is at capacity. The rejection includes `session:capacity-exceeded` on the bus.
|
|
|
|
### NET-SESS-12 — Parent state bridge
|
|
|
|
Child sessions optionally receive read-only state from parent sessions (e.g., world time, weather). Child peers cannot send messages to parent.
|
|
|
|
## Error codes
|
|
|
|
- `NET_SESSION_NOT_FOUND` — Session does not exist
|
|
- `NET_SESSION_PEER_NOT_FOUND` — Peer not in session
|
|
- `NET_SESSION_CAPACITY_EXCEEDED` — Session at capacity
|
|
- `NET_SESSION_CYCLE_DETECTED` — Attempted to create session cycle
|
|
- `NET_SESSION_HOST_UNREACHABLE` — Session host not responding
|
|
- `NET_SESSION_TRANSITION_TIMEOUT` — Join/move/sync timed out
|
|
- `NET_SESSION_INVALID_SCOPE` — Unknown message scope
|
|
|
|
## Dependencies
|
|
|
|
- `@suaveplan/network`
|
|
- `@suaveplan/error`
|
|
- `@suaveplan/types`
|
|
- `@suaveplan/events`
|
|
- `@suaveplan/fsm`
|
|
- `@suaveplan/timing`
|
|
- `@opentelemetry/api`
|
|
- `zod`
|
|
|
|
## Notes
|
|
|
|
- Session depth should be bounded (suggested max: 4) to prevent routing overhead.
|
|
- Session state is in-memory only; persistence is deferred to `network-plugin-session-persistence`.
|
|
- The root session typically maps 1:1 with the mesh group.
|