Documentation
¶
Index ¶
- Constants
- Variables
- func BuildSystemPrompt(...) string
- func DecodeRealtimePayload(eventType EventType, value any) (any, error)
- func MustRealtimeObject(value any) map[string]any
- func RealtimeArray[T any](values []T) []any
- func RealtimeFloat64[T realtimeNumber](value T) *float64
- func RealtimeInt64[T realtimeNumber](value T) *int64
- func RealtimePtr[T any](value T) *T
- func SanitizeSVG(input string) (string, error)
- func ValidateListenSecurity(addr string, cfg ListenSecurityConfig) error
- type AdventureUpdatedPayload
- type AutomationDispatchHealth
- type AutomationHealth
- type AutomationJob
- type AutomationSetting
- type BreakerPermit
- type BreakerRegistry
- type Bus
- type CalendarUpdatedPayload
- type CampaignClosedPayload
- type CampaignConfigUpdatedPayload
- type CampaignCreatedPayload
- type CampaignDeletedPayload
- type CampaignReopenedPayload
- type CampaignUpdatedPayload
- type CardDrawnPayload
- type CharacterCreatedPayload
- type CharacterUpdatedPayload
- type CombatEndedPayload
- type CombatStartedPayload
- type CombatantUpdatedPayload
- type ContextUpdatedPayload
- type DeltaSSEPayload
- type DiceRolledPayload
- type Dispatcher
- type DispatcherOptions
- type DoneSSEPayload
- type ErrorSSEPayload
- type Event
- type EventType
- type ExpectedActionPayload
- type FactionUpdatedPayload
- type Hub
- type ItemUpdatedPayload
- type JobMode
- type ListenSecurityConfig
- type MapCreatedPayload
- type MapFXPayload
- type MapPinAddedPayload
- type MessageCreatedPayload
- type NPCStatUpdatedPayload
- type NPCUpdatedPayload
- type ObjectiveUpdatedPayload
- type OracleResultPayload
- type OracleRolledPayload
- type RelationshipUpdatedPayload
- type ResyncRequiredPayload
- type SecretRevealedPayload
- type SecretsUpdatedPayload
- type Server
- func (s *Server) Bus() *Bus
- func (s *Server) Close() error
- func (s *Server) ListenAndServe(addr string) error
- func (s *Server) ListenAndServeTLS(addr, certFile, keyFile string) error
- func (s *Server) RegisterStatic(fsys http.FileSystem)
- func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (s *Server) SetAllowedOrigins(origins []string)
- func (s *Server) Shutdown(ctx context.Context) error
- func (s *Server) Start(addr, certFile, keyFile string) error
- type ServerOptions
- type SessionDeletedPayload
- type SessionEndedPayload
- type SessionInfo
- type SessionStartedPayload
- type SessionUpdatedPayload
- type TensionUpdatedPayload
- type TokenMovedPayload
- type TokenPlacedPayload
- type TokenRemovedPayload
- type TurnAdvancedPayload
- type TypingPayload
- type WorldNoteCreatedPayload
- type WorldNoteRevealedPayload
- type WorldNoteUpdatedPayload
- type XPAddedPayload
- type XPSpendSuggestionsPayload
- type ZoneRevealedPayload
Constants ¶
const ( BreakerClosed = "closed" BreakerOpen = "open" BreakerHalfOpen = "half-open" )
const ( SSEEventDelta = "delta" SSEEventDone = "done" SSEEventError = "error" )
const RealtimeContractVersion = 1
Variables ¶
var ( ErrAutomationQueueFull = errors.New("automation queue full") ErrAutomationDispatcherClosed = errors.New("automation dispatcher closed") )
var ErrUnsafeSVG = errors.New("unsafe SVG")
Functions ¶
func BuildSystemPrompt ¶
func BuildSystemPrompt(base, ruleset, campaignGuidance, contentBoundaries, narrativeLocale, reminder string) string
BuildSystemPrompt is the single immutable composition boundary for GM prompts. Campaign-controlled values are encoded as JSON strings inside fixed sections.
func DecodeRealtimePayload ¶
func MustRealtimeObject ¶
func RealtimeArray ¶
func RealtimeFloat64 ¶
func RealtimeFloat64[T realtimeNumber](value T) *float64
func RealtimeInt64 ¶
func RealtimeInt64[T realtimeNumber](value T) *int64
func RealtimePtr ¶
func RealtimePtr[T any](value T) *T
RealtimePtr preserves presence for optional primitive payload fields, including explicit zero and false values.
func SanitizeSVG ¶
SanitizeSVG validates and normalizes generated SVG using a strict XML allowlist.
func ValidateListenSecurity ¶
func ValidateListenSecurity(addr string, cfg ListenSecurityConfig) error
ValidateListenSecurity rejects incomplete non-loopback configurations.
Types ¶
type AdventureUpdatedPayload ¶
type AutomationHealth ¶
type AutomationHealth struct {
Key string `json:"key"`
Status string `json:"status"`
FailureCount int `json:"failure_count"`
CoolingDown bool `json:"cooling_down"`
OpenedAt *time.Time `json:"opened_at,omitempty"`
LastSuccess *time.Time `json:"last_success,omitempty"`
LastError string `json:"last_error,omitempty"`
}
AutomationHealth is an immutable value snapshot of one automation breaker.
type AutomationJob ¶
type AutomationSetting ¶
type AutomationSetting struct {
Key string `json:"key"`
Label string `json:"label"`
Default bool `json:"default"`
}
AutomationSetting holds a single automation toggle definition.
func AllAutomationSettings ¶
func AllAutomationSettings() []AutomationSetting
AllAutomationSettings returns all automation config keys with their defaults.
type BreakerPermit ¶
type BreakerPermit struct {
// contains filtered or unexported fields
}
BreakerPermit identifies one admitted operation in one breaker epoch. A permit resolves at most once, and only against the operation that acquired it.
func (*BreakerPermit) Abort ¶
func (p *BreakerPermit) Abort()
Abort resolves this permit without changing failure health. Aborting a half-open probe immediately releases the probe slot for another caller.
func (*BreakerPermit) Complete ¶
func (p *BreakerPermit) Complete(err error)
Complete maps nil to success, caller/root cancellation to abort, and all other errors (including deadline expiry) to breaker failure.
func (*BreakerPermit) Failure ¶
func (p *BreakerPermit) Failure(err error)
Failure resolves this permit as a genuine operation failure.
func (*BreakerPermit) Success ¶
func (p *BreakerPermit) Success()
Success resolves this permit successfully.
type BreakerRegistry ¶
type BreakerRegistry struct {
// contains filtered or unexported fields
}
BreakerRegistry owns independent closed/open/half-open breakers by provider and automation key.
func NewBreakerRegistry ¶
func NewBreakerRegistry(now func() time.Time, threshold int, cooldown time.Duration) *BreakerRegistry
NewBreakerRegistry constructs a registry. The clock is injectable so state transitions remain deterministic in tests.
func (*BreakerRegistry) Acquire ¶
func (r *BreakerRegistry) Acquire(key string) (*BreakerPermit, bool)
Acquire admits an operation and returns its correlated permit. After cooldown, only one caller can acquire the half-open probe permit.
func (*BreakerRegistry) Snapshot ¶
func (r *BreakerRegistry) Snapshot() []AutomationHealth
Snapshot returns independently allocated, key-sorted values suitable for serialization. It also exposes cooldown-complete breakers as half-open/ready.
type Bus ¶
type Bus struct {
// contains filtered or unexported fields
}
Bus is a fan-out pub/sub for Events. Publishers call Publish; the WebSocket hub calls Subscribe to receive a channel of all events.
type CalendarUpdatedPayload ¶
type CalendarUpdatedPayload struct {
CampaignID *int64 `json:"campaign_id,omitempty"`
}
type CampaignClosedPayload ¶
type CampaignClosedPayload struct {
CampaignID *int64 `json:"campaign_id,omitempty"`
}
type CampaignConfigUpdatedPayload ¶
type CampaignConfigUpdatedPayload struct {
CampaignID *int64 `json:"campaign_id,omitempty"`
}
type CampaignCreatedPayload ¶
type CampaignDeletedPayload ¶
type CampaignDeletedPayload struct {
CampaignID *int64 `json:"campaign_id,omitempty"`
}
type CampaignReopenedPayload ¶
type CampaignReopenedPayload struct {
CampaignID *int64 `json:"campaign_id,omitempty"`
}
type CampaignUpdatedPayload ¶
type CardDrawnPayload ¶
type CardDrawnPayload struct {
CampaignID *int64 `json:"campaign_id,omitempty"`
Card map[string]any `json:"card,omitempty"`
DeckID *int64 `json:"deck_id,omitempty"`
DeckName *string `json:"deck_name,omitempty"`
DrawIndex *int64 `json:"draw_index,omitempty"`
SessionID *int64 `json:"session_id,omitempty"`
Total *int64 `json:"total,omitempty"`
}
type CharacterCreatedPayload ¶
type CharacterUpdatedPayload ¶
type CharacterUpdatedPayload struct {
CharacterID *int64 `json:"character_id,omitempty"`
Clan *string `json:"clan,omitempty"`
CurrencyBalance *int64 `json:"currency_balance,omitempty"`
CurrencyDelta *int64 `json:"currency_delta,omitempty"`
CurrencyLabel *string `json:"currency_label,omitempty"`
DataJson *string `json:"data_json,omitempty"`
Embrace *bool `json:"embrace,omitempty"`
Field *string `json:"field,omitempty"`
ID *int64 `json:"id,omitempty"`
NewValue *int64 `json:"new_value,omitempty"`
PortraitPath *string `json:"portrait_path,omitempty"`
PredatorType *string `json:"predator_type,omitempty"`
SessionID *int64 `json:"session_id,omitempty"`
XPCost *int64 `json:"xp_cost,omitempty"`
}
type CombatEndedPayload ¶
type CombatStartedPayload ¶
type CombatantUpdatedPayload ¶
type ContextUpdatedPayload ¶
type DeltaSSEPayload ¶
type DeltaSSEPayload struct {
Delta string `json:"delta"`
}
type DiceRolledPayload ¶
type DiceRolledPayload struct {
BestialFail *bool `json:"bestial_fail,omitempty"`
Breakdown []any `json:"breakdown,omitempty"`
CharacterName *string `json:"character_name,omitempty"`
Expression *string `json:"expression,omitempty"`
Hidden *bool `json:"hidden,omitempty"`
HungerDice []any `json:"hunger_dice,omitempty"`
MessyCritical *bool `json:"messy_critical,omitempty"`
NormalDice []any `json:"normal_dice,omitempty"`
Result *int64 `json:"result,omitempty"`
Rolls []any `json:"rolls,omitempty"`
SessionID *int64 `json:"session_id,omitempty"`
Successes *int64 `json:"successes,omitempty"`
}
type Dispatcher ¶
type Dispatcher struct {
// contains filtered or unexported fields
}
Dispatcher bounds automation execution while preserving accepted event work and serializing mutations that belong to the same session.
func NewDispatcher ¶
func NewDispatcher(parent context.Context, options DispatcherOptions) *Dispatcher
func (*Dispatcher) ForceCancel ¶
func (d *Dispatcher) ForceCancel()
ForceCancel rejects new jobs and cancels all dispatcher-owned work. It is used by abrupt server Close and by root-context cancellation.
func (*Dispatcher) Shutdown ¶
func (d *Dispatcher) Shutdown(ctx context.Context) error
Shutdown rejects new work, drains accepted jobs while ctx remains live, and force-cancels dispatcher-owned work if the deadline expires.
func (*Dispatcher) Snapshot ¶
func (d *Dispatcher) Snapshot() []AutomationDispatchHealth
Snapshot returns independently allocated, kind-sorted dispatcher health.
func (*Dispatcher) Submit ¶
func (d *Dispatcher) Submit(ctx context.Context, job AutomationJob) error
Submit transfers ownership of a job to the dispatcher. Event submission waits for capacity; snapshots fail immediately when a pending slot cannot be acquired, except that an equivalent pending snapshot may be replaced.
type DispatcherOptions ¶
type DoneSSEPayload ¶
type DoneSSEPayload struct {
}
type ErrorSSEPayload ¶
type Event ¶
type Event struct {
Type EventType `json:"type"`
Sequence uint64 `json:"sequence"`
Payload any `json:"payload"`
}
Event is published by MCP tool handlers and broadcast to WebSocket clients.
type EventType ¶
type EventType string
const ( EventAdventureUpdated EventType = "adventure_updated" EventCalendarUpdated EventType = "calendar_updated" EventCampaignClosed EventType = "campaign_closed" EventCampaignConfigUpdated EventType = "campaign_config_updated" EventCampaignCreated EventType = "campaign_created" EventCampaignDeleted EventType = "campaign_deleted" EventCampaignReopened EventType = "campaign_reopened" EventCampaignUpdated EventType = "campaign_updated" EventCardDrawn EventType = "card_drawn" EventCharacterCreated EventType = "character_created" EventCharacterUpdated EventType = "character_updated" EventCombatEnded EventType = "combat_ended" EventCombatStarted EventType = "combat_started" EventCombatantUpdated EventType = "combatant_updated" EventContextUpdated EventType = "context_updated" EventDiceRolled EventType = "dice_rolled" EventExpectedAction EventType = "expected_action" EventFactionUpdated EventType = "faction_updated" EventItemUpdated EventType = "item_updated" EventMapCreated EventType = "map_created" EventMapFX EventType = "map_fx" EventMapPinAdded EventType = "map_pin_added" EventMessageCreated EventType = "message_created" EventNPCStatUpdated EventType = "npc_stat_updated" EventNPCUpdated EventType = "npc_updated" EventObjectiveUpdated EventType = "objective_updated" EventOracleResult EventType = "oracle_result" EventOracleRolled EventType = "oracle_rolled" EventRelationshipUpdated EventType = "relationship_updated" EventResyncRequired EventType = "resync_required" EventSecretRevealed EventType = "secret_revealed" EventSecretsUpdated EventType = "secrets_updated" EventSessionDeleted EventType = "session_deleted" EventSessionEnded EventType = "session_ended" EventSessionStarted EventType = "session_started" EventSessionUpdated EventType = "session_updated" EventTensionUpdated EventType = "tension_updated" EventTokenMoved EventType = "token_moved" EventTokenPlaced EventType = "token_placed" EventTokenRemoved EventType = "token_removed" EventTurnAdvanced EventType = "turn_advanced" EventTyping EventType = "typing" EventWorldNoteCreated EventType = "world_note_created" EventWorldNoteRevealed EventType = "world_note_revealed" EventWorldNoteUpdated EventType = "world_note_updated" EventXPAdded EventType = "xp_added" EventXPSpendSuggestions EventType = "xp_spend_suggestions" EventZoneRevealed EventType = "zone_revealed" )
type ExpectedActionPayload ¶
type FactionUpdatedPayload ¶
type Hub ¶
type Hub struct {
// contains filtered or unexported fields
}
Hub manages WebSocket connections and broadcasts events to all clients. Each client gets a dedicated send channel and write goroutine so that broadcast (called from Hub.Run) never shares a *websocket.Conn with the per-connection read goroutine in ServeWS.
func (*Hub) ClientCount ¶
ClientCount returns the number of currently connected WebSocket clients.
func (*Hub) Run ¶
Run subscribes to the event bus and broadcasts all events to connected clients. Call in a goroutine.
func (*Hub) ServeWS ¶
func (h *Hub) ServeWS(w http.ResponseWriter, r *http.Request)
ServeWS upgrades an HTTP connection to WebSocket and registers it with the hub.
func (*Hub) ServeWSAuthorized ¶
func (h *Hub) ServeWSAuthorized(w http.ResponseWriter, r *http.Request, configure func(*hubClient) bool)
ServeWSAuthorized upgrades and registers a connection whose authorization is configured before registration. A nil configurator preserves unsecured and bearer-authenticated connection behavior.
func (*Hub) SetAllowedOrigins ¶
SetAllowedOrigins replaces the explicit WebSocket origin allowlist.
type ItemUpdatedPayload ¶
type ListenSecurityConfig ¶
type ListenSecurityConfig struct {
AuthSecret string
TLSCertFile string
TLSKeyFile string
AllowedOrigins []string
}
ListenSecurityConfig contains the security settings required to serve on a non-loopback address.
type MapCreatedPayload ¶
type MapFXPayload ¶
type MapPinAddedPayload ¶
type MessageCreatedPayload ¶
type NPCStatUpdatedPayload ¶
type NPCUpdatedPayload ¶
type ObjectiveUpdatedPayload ¶
type OracleResultPayload ¶
type OracleRolledPayload ¶
type ResyncRequiredPayload ¶
type SecretRevealedPayload ¶
type SecretsUpdatedPayload ¶
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server holds dependencies and registers routes.
func NewServer ¶
NewServer creates the HTTP server. dataDir is the base path for uploaded files (e.g. ~/.ttrpg). aiClient may be nil if AI features are disabled.
func NewServerWithOptions ¶
func NewServerWithOptions(database *db.DB, dataDir string, aiClient ai.Completer, options ServerOptions) *Server
NewServerWithOptions creates an HTTP server with explicit security options.
func (*Server) Close ¶
Close force-closes active HTTP connections after beginning lifecycle cancellation. Callers should first attempt Shutdown with a bounded context.
func (*Server) ListenAndServe ¶
ListenAndServe is retained for callers that do not use TLS.
func (*Server) ListenAndServeTLS ¶
ListenAndServeTLS is retained for callers that use TLS.
func (*Server) RegisterStatic ¶
func (s *Server) RegisterStatic(fsys http.FileSystem)
RegisterStatic serves the embedded React SPA for all routes not matched by /api/ or /ws. index.html is served with Cache-Control: no-cache so browsers always re-validate it after a binary update (Vite hashes JS/CSS names; a stale index.html causes blank screens).
func (*Server) SetAllowedOrigins ¶
SetAllowedOrigins configures explicit WebSocket origins in addition to the request's own origin.
type ServerOptions ¶
type ServerOptions struct {
Security ListenSecurityConfig
// AutomationBreakerCooldown overrides the one-minute production default.
// Zero preserves the default and is intended for process-scoped reliability tests.
AutomationBreakerCooldown time.Duration
// RootContext owns request and startup-background lifetimes. A nil context
// defaults to context.Background.
RootContext context.Context
// contains filtered or unexported fields
}
ServerOptions configures optional security behavior for the HTTP server.
type SessionDeletedPayload ¶
type SessionDeletedPayload struct {
SessionID *int64 `json:"session_id,omitempty"`
}
type SessionEndedPayload ¶
type SessionEndedPayload struct {
SessionID *int64 `json:"session_id,omitempty"`
}
type SessionInfo ¶
type SessionInfo struct {
Authenticated bool `json:"authenticated"`
CSRFToken string `json:"csrf_token,omitempty"`
}
SessionInfo describes the browser's current authentication state.
type SessionStartedPayload ¶
type SessionUpdatedPayload ¶
type SessionUpdatedPayload struct {
AdventureID *int64 `json:"adventure_id,omitempty"`
CampaignID *int64 `json:"campaign_id,omitempty"`
MasqueradeIntegrity *int64 `json:"masquerade_integrity,omitempty"`
Notes *string `json:"notes,omitempty"`
SceneTags *string `json:"scene_tags,omitempty"`
SessionID *int64 `json:"session_id,omitempty"`
Summary *string `json:"summary,omitempty"`
}
type TensionUpdatedPayload ¶
type TokenMovedPayload ¶
type TokenPlacedPayload ¶
type TokenRemovedPayload ¶
type TurnAdvancedPayload ¶
type TypingPayload ¶
type WorldNoteCreatedPayload ¶
type WorldNoteUpdatedPayload ¶
type XPAddedPayload ¶
type XPSpendSuggestionsPayload ¶
type XPSpendSuggestionsPayload struct {
CharacterID *int64 `json:"character_id,omitempty"`
CharacterName *string `json:"character_name,omitempty"`
CurrentXP *int64 `json:"current_xp,omitempty"`
SessionID *int64 `json:"session_id,omitempty"`
Suggestions []any `json:"suggestions,omitempty"`
XPLabel *string `json:"xp_label,omitempty"`
}
Source Files
¶
- auth.go
- automation.go
- automation_config.go
- automation_dispatcher.go
- automation_jobs.go
- automation_prompts.go
- breaker.go
- events.go
- middleware.go
- prompt.go
- realtime_gen.go
- routes.go
- routes_advance.go
- routes_adventures.go
- routes_assets.go
- routes_calendar.go
- routes_campaign_config.go
- routes_character.go
- routes_decks.go
- routes_factions.go
- routes_fx.go
- routes_macros.go
- routes_manage.go
- routes_messages.go
- routes_npc_stats.go
- routes_phase_a.go
- routes_phase_c.go
- routes_phase_d.go
- routes_rulebook.go
- routes_secrets.go
- routes_tokens.go
- routes_world.go
- routes_zones.go
- security_config.go
- server.go
- svg_sanitize.go
- vtm.go
- ws.go