room

package
v1.9.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 6, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package room owns room codes, Host/Viewer credentials, participant membership and persistent room authority. Durable mutations write SQLite before memory; participants and live connections remain process-only.

Locking And Password Work

Store has no mutex. The caller holds the signaling server's global lock for store operations, except password derivation, which must run unlocked.

Password changes use a locked begin, unlocked derive, then locked commit. Pass both material and error from DeriveViewerPasswordMaterial unchanged; the commit revalidates room ownership/capacity before mapping gate outcomes. Viewer login instead returns derivation errors before committing credentials.

A derivation admission predicate runs outside the signaling lock and must acquire that lock when consulting room/session state. ConnectViewerWithPassword also calls its predicate during the locked commit, so that call needs the already-locked form. Both must express the same current-session condition.

HostManagedRoom and ViewerPasswordChallenge return a room reference for identity checks after unlocked work. A removed room is never reused as its replacement; a stale reference cannot authorize mutation of the new room. Do not inspect a retained room's mutable state without the lock.

Options.Random is used under the caller's lock and by derivation goroutines, so it must be concurrency-safe; crypto/rand satisfies this requirement.

Index

Constants

View Source
const Capacity = 9_000

Capacity is ROOM_CAPACITY: every four-digit room code.

Variables

This section is empty.

Functions

This section is empty.

Types

type ClosedRoom

type ClosedRoom struct {
	RoomID     string
	SessionIDs []string
}

ClosedRoom is ClosedRoom.

type CodeEntryUpdate

type CodeEntryUpdate struct {
	CodeEntryPolicy       protocol.CodeEntryPolicy
	ViewerPasswordEnabled bool
}

CodeEntryUpdate is CodeEntryUpdate.

type ConnectParticipantInput

type ConnectParticipantInput struct {
	RoomID      string
	Role        protocol.Role
	Token       string
	ViewerGrant string
	ClientID    string
	SessionID   string
}

ConnectParticipantInput is ConnectParticipantInput; Token is read for a host and ViewerGrant for a viewer, "" meaning the key was absent.

type ConnectViewerWithPasswordInput

type ConnectViewerWithPasswordInput struct {
	RoomID    string
	Password  string
	ClientID  string
	SessionID string
}

ConnectViewerWithPasswordInput is ConnectViewerWithPasswordInput. Password is consumed by the caller's DeriveViewerPassword call, not by the commit.

type ConnectedParticipant

type ConnectedParticipant struct {
	RoomID                        string
	Role                          protocol.Role
	PeerID                        string
	HostOnline                    bool
	ReplacedSessionID             string
	CodeEntryPolicy               protocol.CodeEntryPolicy
	ViewerPasswordEnabled         bool
	ViewerAuthorizationGeneration string
}

ConnectedParticipant is ConnectedParticipant. ReplacedSessionID is "" where the TypeScript omitted replacedSessionId.

type ConnectedPeer

type ConnectedPeer struct {
	PeerID    string
	SessionID string
}

ConnectedPeer is ConnectedPeer.

type CreatedRoom

type CreatedRoom struct {
	RoomID          string
	HostToken       string
	CodeEntryPolicy protocol.CodeEntryPolicy
	ViewerGrant     string
}

CreatedRoom is CreatedRoom. ViewerGrant is "" where the TypeScript had null.

type Database

type Database struct {
	// contains filtered or unexported fields
}

Database is RoomDatabase: the SQLite stable authority. It holds one connection for its lifetime, because `PRAGMA locking_mode = EXCLUSIVE` is a connection property that a recycled pool connection would silently drop.

func NewDatabase

func NewDatabase(path string) (*Database, error)

NewDatabase is the RoomDatabase constructor.

func (*Database) Close

func (d *Database) Close() error

Close closes the connection; it is safe on a database that never opened one.

func (*Database) Initialize

func (d *Database) Initialize() (restored []StoredRoomAuthority, err error)

Initialize opens the file, takes the exclusive lock, creates or verifies the schema and restores all stored room authority.

func (*Database) InsertRoom

func (d *Database) InsertRoom(room StoredRoomAuthority) error

InsertRoom is insertRoom.

func (*Database) ReplaceRoom

func (d *Database) ReplaceRoom(
	oldRoomID string, oldHostTokenDigest []byte, replacement StoredRoomAuthority,
) error

ReplaceRoom is replaceRoom: the new room and the removal of the old one are one authority transition.

func (*Database) SetCodeEntryPolicy

func (d *Database) SetCodeEntryPolicy(
	roomID string, hostTokenDigest []byte, codeEntryPolicy protocol.CodeEntryPolicy,
) error

SetCodeEntryPolicy is setCodeEntryPolicy.

func (*Database) SetViewerGrant

func (d *Database) SetViewerGrant(
	roomID string, hostTokenDigest, viewerGrantDigest []byte,
	viewerAuthorizationGeneration string,
) error

SetViewerGrant is setViewerGrant.

func (*Database) SetViewerPassword

func (d *Database) SetViewerPassword(
	roomID string, hostTokenDigest, viewerPasswordMaterial []byte,
) error

SetViewerPassword is setViewerPassword; nil material clears the password.

type DisconnectedParticipant

type DisconnectedParticipant struct {
	RoomID string
	Role   protocol.Role
	PeerID string
}

DisconnectedParticipant is DisconnectedParticipant.

type Error

type Error struct {
	Code ErrorCode
}

Error is RoomStoreError. Its message is the code, as `super(code)` made it.

func (*Error) Error

func (e *Error) Error() string

Error returns the code, which is the message `super(code)` produced.

type ErrorCode

type ErrorCode string

ErrorCode is RoomStoreErrorCode.

const (
	CodeInvalidToken         ErrorCode = "INVALID_TOKEN"
	CodeRoomAccessDenied     ErrorCode = "ROOM_ACCESS_DENIED"
	CodeRoomBusy             ErrorCode = "ROOM_BUSY"
	CodeRoomNotFound         ErrorCode = "ROOM_NOT_FOUND"
	CodeRoomFull             ErrorCode = "ROOM_FULL"
	CodeHostAlreadyConnected ErrorCode = "HOST_ALREADY_CONNECTED"
	CodeRoomLimit            ErrorCode = "ROOM_LIMIT"
)

The RoomStoreErrorCode set.

type Options

type Options struct {
	MaxRooms          int
	MaxViewersPerRoom int
	Database          *Database
	Random            func(size int) []byte
	Now               func() int64
}

Options is RoomStoreOptions.

type ReplacedRoom

type ReplacedRoom struct {
	Created CreatedRoom
	Closed  ClosedRoom
}

ReplacedRoom is ReplacedRoom.

type RevokedViewer

type RevokedViewer struct {
	PeerID    string
	SessionID string
}

RevokedViewer is RevokedViewer; SessionID is "" where the TypeScript omitted sessionId.

type Room

type Room struct {
	// contains filtered or unexported fields
}

Room is one room's authority plus its participants. It has no exported members: callers hold the pointer only to compare identity across the unlocked password derivation, as the TypeScript compared room objects.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is RoomStore. See the package comment for the locking contract.

func New

func New(options Options) (*Store, error)

New is the RoomStore constructor; the TypeScript threw where this returns an error.

func (*Store) AbandonAllRooms

func (s *Store) AbandonAllRooms() ([]ClosedRoom, error)

AbandonAllRooms is abandonAllRooms, in room insertion order.

func (*Store) AbandonRoom

func (s *Store) AbandonRoom(roomID string) (*ClosedRoom, error)

AbandonRoom is abandonRoom; it returns nil where the TypeScript returned undefined.

func (*Store) BeginCreateRoom

func (s *Store) BeginCreateRoom() error

BeginCreateRoom checks room capacity before password derivation.

func (*Store) Close

func (s *Store) Close() error

Close closes the database.

func (*Store) ConnectParticipant

func (s *Store) ConnectParticipant(input ConnectParticipantInput) (ConnectedParticipant, error)

ConnectParticipant is connectParticipant.

func (*Store) ConnectViewerWithPassword

func (s *Store) ConnectViewerWithPassword(
	input ConnectViewerWithPasswordInput,
	derived, expectedMaterial []byte,
	room *Room,
	mayConnect func() bool,
) (ConnectedParticipant, error)

ConnectViewerWithPassword is the commit half of connectViewerWithPassword. derived is the DeriveViewerPassword result; expectedMaterial and room come from ViewerPasswordChallenge; mayConnect is evaluated with the lock held.

func (*Store) CreateRoom

func (s *Store) CreateRoom(
	policy protocol.CodeEntryPolicy,
	material []byte,
	derived error,
	preferredRoomID string,
) (CreatedRoom, error)

CreateRoom is the commit half of createRoom. material and derived are the results of DeriveViewerPasswordMaterial (both zero when no password was given).

func (*Store) DeriveViewerPassword

func (s *Store) DeriveViewerPassword(
	password string, salt []byte, mayConnect func() bool,
) ([]byte, error)

DeriveViewerPassword is the unlocked middle of connectViewerWithPassword: it derives the verifier for the salt ViewerPasswordChallenge returned. Unlike DeriveViewerPasswordMaterial it maps both gate outcomes itself, because the TypeScript threw them immediately after the await, before re-reading the room.

func (*Store) DeriveViewerPasswordMaterial

func (s *Store) DeriveViewerPasswordMaterial(
	password string, mayStart func() bool,
) ([]byte, error)

DeriveViewerPasswordMaterial is createViewerPasswordMaterial: it validates the password, draws a fresh salt and returns salt||verifier. Call it WITHOUT the caller's lock held and hand both results to CreateRoom, ReplaceRoom or SetViewerPassword unchanged; those reproduce the TypeScript order in which a busy or cancelled outcome is turned into an error. mayStart may be nil, which is the TypeScript default of `() => true`.

func (*Store) DisconnectParticipant

func (s *Store) DisconnectParticipant(
	roomID, peerID, sessionID string,
) (*DisconnectedParticipant, error)

DisconnectParticipant is disconnectParticipant; it returns nil where the TypeScript returned undefined.

func (*Store) EmptyRoomIDs added in v1.9.1

func (s *Store) EmptyRoomIDs(beforeMs int64) []string

EmptyRoomIDs returns empty rooms in creation order, including never-joined rooms. A connected Host or Viewer protects an idle chat room just like a share. Callers retire candidates synchronously under the same signaling lock.

func (*Store) ExpireEmptyRooms added in v1.9.1

func (s *Store) ExpireEmptyRooms(beforeMs int64) ([]ClosedRoom, error)

ExpireEmptyRooms commits storage retirement before returning effect cleanup.

func (*Store) GetConnectedHost

func (s *Store) GetConnectedHost(roomID string) (ConnectedPeer, bool)

GetConnectedHost is getConnectedHost.

func (*Store) GetConnectedViewer

func (s *Store) GetConnectedViewer(roomID, peerID string) (ConnectedPeer, bool)

GetConnectedViewer is getConnectedViewer.

func (*Store) GetConnectedViewers

func (s *Store) GetConnectedViewers(roomID string) []ConnectedPeer

GetConnectedViewers is getConnectedViewers, in viewer insertion order.

func (*Store) GetViewerPeerIDs

func (s *Store) GetViewerPeerIDs(roomID string) []string

GetViewerPeerIDs is getViewerPeerIds, in viewer insertion order.

func (*Store) HostManagedRoom

func (s *Store) HostManagedRoom(roomID, hostToken string) (*Room, error)

HostManagedRoom is getHostManagedRoom: the room the exact Host token owns. It is the pre-KDF half of replaceRoom and setViewerPassword.

func (*Store) HostStillOwnsRoom

func (s *Store) HostStillOwnsRoom(roomID, hostToken string, current *Room) bool

HostStillOwnsRoom is the mayStart closure replaceRoom and setViewerPassword hand to the KDF gate: the room must still be the same object and the token must still own it. Call it with the lock held.

func (*Store) Initialize

func (s *Store) Initialize() error

Initialize recovers the stable authority. It is a no-op without a database.

func (*Store) MaxViewersPerRoom

func (s *Store) MaxViewersPerRoom() int

MaxViewersPerRoom is the readonly RoomStore field of the same name.

func (*Store) RemoveDisconnectedViewer

func (s *Store) RemoveDisconnectedViewer(roomID, peerID string) bool

RemoveDisconnectedViewer is removeDisconnectedViewer.

func (*Store) ReplaceRoom

func (s *Store) ReplaceRoom(
	roomID, hostToken string,
	policy protocol.CodeEntryPolicy,
	material []byte,
	derived error,
	current *Room,
) (ReplacedRoom, error)

ReplaceRoom is the commit half of replaceRoom. current is the room HostManagedRoom returned before the derivation.

func (*Store) SetCodeEntryPolicy

func (s *Store) SetCodeEntryPolicy(
	roomID string,
	policy protocol.CodeEntryPolicy,
	hostToken string,
) (CodeEntryUpdate, error)

SetCodeEntryPolicy is setCodeEntryPolicy.

func (*Store) SetViewerGrant

func (s *Store) SetViewerGrant(
	roomID, action, hostToken string,
) (ViewerGrantUpdate, error)

SetViewerGrant is setViewerGrant; action is "rotate" or "revoke".

func (*Store) SetViewerPassword

func (s *Store) SetViewerPassword(
	roomID, hostToken string,
	material []byte,
	derived error,
	current *Room,
) (bool, error)

SetViewerPassword is the commit half of setViewerPassword; it reports whether a password is now set. current is the room HostManagedRoom returned before the derivation.

func (*Store) Size

func (s *Store) Size() int

Size is the `size` getter.

func (*Store) ViewerGrantMayEnter

func (s *Store) ViewerGrantMayEnter(roomID, grant string) (bool, error)

ViewerGrantMayEnter is viewerGrantMayEnter.

func (*Store) ViewerPasswordChallenge

func (s *Store) ViewerPasswordChallenge(
	roomID string,
) (salt, expectedMaterial []byte, room *Room, err error)

ViewerPasswordChallenge is the locked first half of connectViewerWithPassword: it returns the salt to derive against, a copy of the material to compare with and the room reference to re-check afterwards. A missing room and a room without a private password both answer with the same 48 zero bytes so the derivation cost does not reveal which. The only error is the `ensureInitialized()` the TypeScript ran before the password check.

type StoredRoomAuthority

type StoredRoomAuthority struct {
	RoomID                        string
	HostTokenDigest               []byte
	ViewerGrantDigest             []byte
	ViewerAuthorizationGeneration string
	CodeEntryPolicy               protocol.CodeEntryPolicy
	ViewerPasswordMaterial        []byte
}

StoredRoomAuthority excludes transient sessions and presence.

type ViewerGrantUpdate

type ViewerGrantUpdate struct {
	ViewerGrant                           string
	ViewerAuthorizationGeneration         string
	PreviousViewerAuthorizationGeneration string
	RevokedViewers                        []RevokedViewer
}

ViewerGrantUpdate is ViewerGrantUpdate. ViewerGrant is "" for a revoke.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL