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
- type ClosedRoom
- type CodeEntryUpdate
- type ConnectParticipantInput
- type ConnectViewerWithPasswordInput
- type ConnectedParticipant
- type ConnectedPeer
- type CreatedRoom
- type Database
- func (d *Database) Close() error
- func (d *Database) Initialize() (restored []StoredRoomAuthority, err error)
- func (d *Database) InsertRoom(room StoredRoomAuthority) error
- func (d *Database) ReplaceRoom(oldRoomID string, oldHostTokenDigest []byte, replacement StoredRoomAuthority) error
- func (d *Database) SetCodeEntryPolicy(roomID string, hostTokenDigest []byte, ...) error
- func (d *Database) SetViewerGrant(roomID string, hostTokenDigest, viewerGrantDigest []byte, ...) error
- func (d *Database) SetViewerPassword(roomID string, hostTokenDigest, viewerPasswordMaterial []byte) error
- type DisconnectedParticipant
- type Error
- type ErrorCode
- type Options
- type ReplacedRoom
- type RevokedViewer
- type Room
- type Store
- func (s *Store) AbandonAllRooms() ([]ClosedRoom, error)
- func (s *Store) AbandonRoom(roomID string) (*ClosedRoom, error)
- func (s *Store) BeginCreateRoom() error
- func (s *Store) Close() error
- func (s *Store) ConnectParticipant(input ConnectParticipantInput) (ConnectedParticipant, error)
- func (s *Store) ConnectViewerWithPassword(input ConnectViewerWithPasswordInput, derived, expectedMaterial []byte, ...) (ConnectedParticipant, error)
- func (s *Store) CreateRoom(policy protocol.CodeEntryPolicy, material []byte, derived error, ...) (CreatedRoom, error)
- func (s *Store) DeriveViewerPassword(password string, salt []byte, mayConnect func() bool) ([]byte, error)
- func (s *Store) DeriveViewerPasswordMaterial(password string, mayStart func() bool) ([]byte, error)
- func (s *Store) DisconnectParticipant(roomID, peerID, sessionID string) (*DisconnectedParticipant, error)
- func (s *Store) EmptyRoomIDs(beforeMs int64) []string
- func (s *Store) ExpireEmptyRooms(beforeMs int64) ([]ClosedRoom, error)
- func (s *Store) GetConnectedHost(roomID string) (ConnectedPeer, bool)
- func (s *Store) GetConnectedViewer(roomID, peerID string) (ConnectedPeer, bool)
- func (s *Store) GetConnectedViewers(roomID string) []ConnectedPeer
- func (s *Store) GetViewerPeerIDs(roomID string) []string
- func (s *Store) HostManagedRoom(roomID, hostToken string) (*Room, error)
- func (s *Store) HostStillOwnsRoom(roomID, hostToken string, current *Room) bool
- func (s *Store) Initialize() error
- func (s *Store) MaxViewersPerRoom() int
- func (s *Store) RemoveDisconnectedViewer(roomID, peerID string) bool
- func (s *Store) ReplaceRoom(roomID, hostToken string, policy protocol.CodeEntryPolicy, material []byte, ...) (ReplacedRoom, error)
- func (s *Store) SetCodeEntryPolicy(roomID string, policy protocol.CodeEntryPolicy, hostToken string) (CodeEntryUpdate, error)
- func (s *Store) SetViewerGrant(roomID, action, hostToken string) (ViewerGrantUpdate, error)
- func (s *Store) SetViewerPassword(roomID, hostToken string, material []byte, derived error, current *Room) (bool, error)
- func (s *Store) Size() int
- func (s *Store) ViewerGrantMayEnter(roomID, grant string) (bool, error)
- func (s *Store) ViewerPasswordChallenge(roomID string) (salt, expectedMaterial []byte, room *Room, err error)
- type StoredRoomAuthority
- type ViewerGrantUpdate
Constants ¶
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 ¶
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 ¶
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 ¶
NewDatabase is the RoomDatabase constructor.
func (*Database) Close ¶
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.
type DisconnectedParticipant ¶
DisconnectedParticipant is DisconnectedParticipant.
type Error ¶
type Error struct {
Code ErrorCode
}
Error is RoomStoreError. Its message is the code, as `super(code)` made it.
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 ¶
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 (*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 ¶
BeginCreateRoom checks room capacity before password derivation.
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
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 ¶
GetViewerPeerIDs is getViewerPeerIds, in viewer insertion order.
func (*Store) HostManagedRoom ¶
HostManagedRoom is getHostManagedRoom: the room the exact Host token owns. It is the pre-KDF half of replaceRoom and setViewerPassword.
func (*Store) HostStillOwnsRoom ¶
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 ¶
Initialize recovers the stable authority. It is a no-op without a database.
func (*Store) MaxViewersPerRoom ¶
MaxViewersPerRoom is the readonly RoomStore field of the same name.
func (*Store) RemoveDisconnectedViewer ¶
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) ViewerGrantMayEnter ¶
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.