Documentation
¶
Overview ¶
Package server provides the server-side runtime for Vango's server-driven architecture.
The server package manages WebSocket connections, component state, event handling, and patch generation. It is the integration layer that brings together the reactive system (pkg/vango), virtual DOM (pkg/vdom), and binary protocol (pkg/protocol).
Architecture ¶
The server runtime consists of several key components:
- Session: Per-connection state container managing component tree, handlers, and reactive ownership
- SessionManager: Manages all active sessions with cleanup and lifecycle hooks
- ComponentInstance: A mounted component with its reactive state and render capability
- Handler: Event handler functions that respond to client events
- Server: HTTP/WebSocket server with handshake and graceful shutdown
Session Lifecycle ¶
Each WebSocket connection creates a Session that manages:
- Component tree and hydration IDs
- Event handler registry (HID -> handler mapping)
- Reactive ownership for signals and effects
- Sequence numbers for reliable delivery
The session runs three goroutines:
- ReadLoop: Receives WebSocket frames, decodes events, queues for processing
- EventLoop: Processes events, runs handlers, generates patches
- WriteLoop: Sends heartbeat pings
Event Processing ¶
When a client sends an event:
- ReadLoop decodes the binary event frame
- Event is queued for the EventLoop
- Handler is found by HID and executed
- Pending effects are run
- Dirty components are re-rendered
- Diff generates patches
- Patches are encoded and sent to client
Example Usage ¶
server := server.MustNew(&server.ServerConfig{
Address: ":8080",
})
server.HandleFunc("/", func(ctx server.Ctx) *vdom.VNode {
count := vango.NewIntSignal(0)
return vdom.Div(
vdom.H1(vdom.Textf("Count: %d", count.Get())),
vdom.Button(
vdom.OnClick(func() { count.Inc() }),
vdom.Text("+"),
),
)
})
server.Run()
Thread Safety ¶
The server package is designed for concurrent access:
- Session.mu protects WebSocket writes
- Events channel serializes event processing
- Signal access uses Phase 1 synchronization
- SessionManager uses RWMutex for session map
Performance Targets ¶
- Memory per session: < 3MB default budget
- Concurrent sessions per GB: 5,000+
- Event processing latency: < 10ms
- WebSocket reconnect: < 500ms
Session Resume and Authentication ¶
When a WebSocket connection drops and reconnects within ResumeWindow, Vango attempts to resume the existing session. For authenticated sessions, Vango enforces auth revalidation by default via AuthResumePolicy.
## Auth Resume Policy
AuthResumePolicyStrict (default): Requires authFunc or OnSessionResume to be configured for authenticated sessions. Without these, authenticated session resume is rejected and the client receives a HandshakeNotAuthorized error, triggering re-authentication. This prevents session IDs from acting as bearer tokens that could be exploited if leaked via XSS, logging, etc.
AuthResumePolicyTrustSessionID: Allows authenticated sessions to resume using only the session ID for in-memory sessions. Use this only if you understand the security implications: a leaked session ID grants authenticated access. Note: Persisted sessions (Redis, NATS) still require auth rehydration because user objects are not serialized.
## Configuring Auth Revalidation
The recommended approach is to configure Server.SetAuthFunc():
server.SetAuthFunc(func(r *http.Request) (any, error) {
return myauth.ValidateRequest(r)
})
This validates the auth cookie/token on every resume and automatically rehydrates the user in the session. If authFunc returns (nil, nil), the resume is rejected for previously authenticated sessions.
Alternatively, configure OnSessionResume for custom rehydration:
cfg.OnSessionResume = func(ctx context.Context, session *Session) error {
user, err := myauth.ValidateFromContext(ctx)
if err != nil {
return err
}
if user != nil {
auth.Set(session, user) // REQUIRED: rehydrate user
}
return nil
}
IMPORTANT: A no-op OnSessionResume that just returns nil does NOT provide security. In strict mode (without authFunc), resume clears runtime auth projection before this hook runs, so previously authenticated sessions are rejected unless the hook actually revalidates and rehydrates auth.
Index ¶
- Constants
- Variables
- func AllowAllOriginsCheck(*http.Request) bool
- func AllowedOriginsCheck(allowedOrigins []string) func(*http.Request) bool
- func BuildCSP(opts CSPOptions) string
- func CSRFCtxToken(ctx Ctx) string
- func CanonicalizePath(path string) (canonPath, query string, changed bool, err error)
- func CurrentMemoryUsage() int64
- func EstimateAnyMemory(value any) int64
- func EstimateMapMemory(length, keySize, valueSize int) int64
- func EstimateSliceMemory(length, elementSize int) int64
- func EstimateStringMemory(s string) int64
- func InspectState(session *Session) vruntime.StateInspection
- func IsAuthError(err error) bool
- func LoggerForEvent(ctx Ctx, base *slog.Logger) *slog.Logger
- func NormalizeAuthCheckConfig(cfg *AuthCheckConfig)
- func RunRouteMiddleware(ctx Ctx, middleware []RouteMiddleware, final func() error) (ranFinal bool, err error)
- func SameOriginCheck(r *http.Request) bool
- func SetCSRFCtxToken(ctx Ctx, token string)
- func SetGlobalPrefetchLimit(limit int)
- func TotalSystemMemory() int64
- func UserFromContext(ctx context.Context) any
- func ValidateExternalRedirectURL(rawURL string, allowedHosts []string) (string, bool)
- func WithUser(ctx context.Context, user any) context.Context
- type AppliedNavigateOptions
- type AuthCheckConfig
- type AuthExpiredAction
- type AuthExpiredConfig
- type AuthExpiredReason
- type AuthFailureMode
- type AuthResumePolicy
- type BudgetExceededMode
- type ByteSize
- type CSPOptions
- type CSRFError
- type Component
- type ComponentInstance
- func (c *ComponentInstance) AddChild(child *ComponentInstance)
- func (c *ComponentInstance) ClearDirty()
- func (c *ComponentInstance) Dispose()
- func (c *ComponentInstance) GetProp(key string) any
- func (c *ComponentInstance) ID() uint64
- func (c *ComponentInstance) IsDirty() bool
- func (c *ComponentInstance) LastTree() *vdom.VNode
- func (c *ComponentInstance) ListKeyPath() []string
- func (c *ComponentInstance) MarkDirty()
- func (c *ComponentInstance) MemoryUsage() int64
- func (c *ComponentInstance) RemoveChild(child *ComponentInstance)
- func (c *ComponentInstance) Render() *vdom.VNode
- func (c *ComponentInstance) Session() *Session
- func (c *ComponentInstance) SetLastTree(tree *vdom.VNode)
- func (c *ComponentInstance) SetProp(key string, value any)
- func (c *ComponentInstance) TrackDependency(dep vango.Dependency)
- type CookieOption
- type CookiePolicy
- type CookiePolicyOptions
- type Ctx
- type Event
- type EventRateLimiter
- type FormData
- type FuncComponent
- type Handler
- type HandlerError
- type HookEvent
- type InstancePath
- type KeyboardEvent
- type LayoutHandler
- type ManagerStats
- type MemoryMonitor
- func (m *MemoryMonitor) ForceCheck()
- func (m *MemoryMonitor) Pause()
- func (m *MemoryMonitor) Resume()
- func (m *MemoryMonitor) SetOnHardLimit(fn func(current, limit int64))
- func (m *MemoryMonitor) SetOnSoftLimit(fn func(current, limit int64))
- func (m *MemoryMonitor) Start()
- func (m *MemoryMonitor) Stop()
- type MemoryMonitorConfig
- type MemoryPressureLevel
- type MemoryStats
- type MetricsCollector
- func (m *MetricsCollector) RecordActionDuration(action string, ms int64)
- func (m *MetricsCollector) RecordActionError(action string)
- func (m *MetricsCollector) RecordBytesReceived(n int)
- func (m *MetricsCollector) RecordBytesSent(n int)
- func (m *MetricsCollector) RecordColdDeployAck(source string)
- func (m *MetricsCollector) RecordEventDropped()
- func (m *MetricsCollector) RecordEventLatency(latencyUs int64)
- func (m *MetricsCollector) RecordEventProcessed()
- func (m *MetricsCollector) RecordEventReceived()
- func (m *MetricsCollector) RecordHandlerPanic()
- func (m *MetricsCollector) RecordPatchBytes(route string, bytes int)
- func (m *MetricsCollector) RecordPatchMismatch(reason string)
- func (m *MetricsCollector) RecordPatchesSent(count int, bytes int)
- func (m *MetricsCollector) RecordPersistBytes(stableID string, bytes int64)
- func (m *MetricsCollector) RecordPersistBytesWritten(primitiveID string, bytes int64)
- func (m *MetricsCollector) RecordPersistWriteRejected(reason string, primitiveID string)
- func (m *MetricsCollector) RecordReadError()
- func (m *MetricsCollector) RecordResourceDuration(resource string, ms int64)
- func (m *MetricsCollector) RecordResourceError(resource string)
- func (m *MetricsCollector) RecordResumeFailure(reason string)
- func (m *MetricsCollector) RecordResumeSuccess()
- func (m *MetricsCollector) RecordSchemaMismatch(reason string)
- func (m *MetricsCollector) RecordSessionActive(delta int)
- func (m *MetricsCollector) RecordSessionDetached(delta int)
- func (m *MetricsCollector) RecordSessionPersistBytes(sessionID string, bytes int64)
- func (m *MetricsCollector) RecordSignalWriteViolation(operation, reason string)
- func (m *MetricsCollector) RecordWriteError()
- func (m *MetricsCollector) Reset()
- func (m *MetricsCollector) Snapshot() *ServerMetrics
- type Middleware
- type MouseEvent
- type NavigateEvent
- type NavigateOption
- type NavigateResult
- type OriginPolicyMode
- type PageHandler
- type PatchHistory
- func (h *PatchHistory) Add(seq uint64, frame []byte)
- func (h *PatchHistory) CanRecover(lastSeq uint64) bool
- func (h *PatchHistory) Clear()
- func (h *PatchHistory) Count() int
- func (h *PatchHistory) GarbageCollect(ackSeq uint64)
- func (h *PatchHistory) GetFrames(afterSeq, toSeq uint64) [][]byte
- func (h *PatchHistory) MaxSeq() uint64
- func (h *PatchHistory) MemoryUsage() int64
- func (h *PatchHistory) MinSeq() uint64
- type PatchHistoryEntry
- type PrefetchCache
- type PrefetchCacheEntry
- type PrefetchConfig
- type PrefetchRateLimiter
- type PrefetchSemaphore
- type PrimitiveInfo
- type ProtocolError
- type ReconnectConfig
- type RenderMode
- type ResizeEvent
- type RouteMatch
- type RouteMiddleware
- type RouteNavigator
- type Router
- type SchemaError
- type ScrollEvent
- type Server
- func (s *Server) CSRFMiddleware() Middleware
- func (s *Server) Config() *ServerConfig
- func (s *Server) CookiePolicy() *CookiePolicy
- func (s *Server) EnsureCSRFCookie(w http.ResponseWriter, r *http.Request) (string, error)
- func (s *Server) GenerateCSRFToken() string
- func (s *Server) HandleWebSocket(w http.ResponseWriter, r *http.Request)
- func (s *Server) Handler() http.Handler
- func (s *Server) Logger() *slog.Logger
- func (s *Server) Metrics() *ServerMetrics
- func (s *Server) PageHandler() http.Handler
- func (s *Server) Router() Router
- func (s *Server) Run() error
- func (s *Server) RunContext(ctx context.Context) error
- func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (s *Server) Sessions() *SessionManager
- func (s *Server) SetAuthFunc(fn func(*http.Request) (any, error))
- func (s *Server) SetCSRFCookie(w http.ResponseWriter, r *http.Request, token string) error
- func (s *Server) SetHandler(h http.Handler)
- func (s *Server) SetLogger(logger *slog.Logger)
- func (s *Server) SetMetricsSink(sink vango.MetricsSink)
- func (s *Server) SetRootComponent(factory func() Component)
- func (s *Server) SetRouter(r Router)
- func (s *Server) Shutdown(ctx context.Context) error
- func (s *Server) UploadHandler(store upload.Store, config *upload.Config) http.Handler
- func (s *Server) Use(mw Middleware)
- func (s *Server) ValidateCSRFRequest(w http.ResponseWriter, r *http.Request) error
- func (s *Server) WebSocketHandler() http.Handler
- func (s *Server) WebSocketURL(r *http.Request) string
- type ServerConfig
- func (c *ServerConfig) Clone() *ServerConfig
- func (c *ServerConfig) EnableToastOnReconnect(message string) *ServerConfig
- func (c *ServerConfig) GetConfigWarnings() []string
- func (c *ServerConfig) IsSecure() bool
- func (c *ServerConfig) ValidateConfig() error
- func (c *ServerConfig) WithAddress(addr string) *ServerConfig
- func (c *ServerConfig) WithAllowedHosts(hosts ...string) *ServerConfig
- func (c *ServerConfig) WithAllowedOrigins(origins ...string) *ServerConfig
- func (c *ServerConfig) WithCSRFSecret(secret []byte) *ServerConfig
- func (c *ServerConfig) WithCanonicalHost(host string) *ServerConfig
- func (c *ServerConfig) WithCookieDomain(domain string) *ServerConfig
- func (c *ServerConfig) WithCookieHTTPOnly(httpOnly bool) *ServerConfig
- func (c *ServerConfig) WithDevMode() *ServerConfig
- func (c *ServerConfig) WithEvictOnIPLimit(evict bool) *ServerConfig
- func (c *ServerConfig) WithEvictionPolicy(policy SessionEvictionPolicy) *ServerConfig
- func (c *ServerConfig) WithMaxConcurrentHandshakes(max int) *ServerConfig
- func (c *ServerConfig) WithMaxConcurrentHandshakesPerIP(max int) *ServerConfig
- func (c *ServerConfig) WithMaxDetachedSessions(max int) *ServerConfig
- func (c *ServerConfig) WithMaxSessions(max int) *ServerConfig
- func (c *ServerConfig) WithMaxSessionsPerIP(max int) *ServerConfig
- func (c *ServerConfig) WithPersistInterval(d time.Duration) *ServerConfig
- func (c *ServerConfig) WithPreUpgradeCheck(check func(r *http.Request) error) *ServerConfig
- func (c *ServerConfig) WithReconnectConfig(rc *ReconnectConfig) *ServerConfig
- func (c *ServerConfig) WithResumeWindow(d time.Duration) *ServerConfig
- func (c *ServerConfig) WithSameSiteMode(mode http.SameSite) *ServerConfig
- func (c *ServerConfig) WithSecureCookies(secure bool) *ServerConfig
- func (c *ServerConfig) WithSessionConfig(sc *SessionConfig) *ServerConfig
- func (c *ServerConfig) WithSessionStore(store session.SessionStore) *ServerConfig
- type ServerMetrics
- type Session
- func (s *Session) BroadcastAuth(channel, typ string, reason AuthExpiredReason)
- func (s *Session) BudgetTracker() *vango.BudgetTracker
- func (s *Session) BytesReceived(n int)
- func (s *Session) Close()
- func (s *Session) Config() *SessionConfig
- func (s *Session) Conn() *websocket.Conn
- func (s *Session) Delete(key string)
- func (s *Session) Deserialize(data []byte) error
- func (s *Session) DeserializeForSessionID(expectedSessionID string, data []byte) error
- func (s *Session) Dispatch(fn func())
- func (s *Session) Done() <-chan struct{}
- func (s *Session) EventLoop()
- func (s *Session) Events() <-chan *Event
- func (s *Session) Get(key string) any
- func (s *Session) GetAllData() map[string]any
- func (s *Session) GetInt(key string) int
- func (s *Session) GetString(key string) string
- func (s *Session) HandleNavigate(path string, replace bool) error
- func (s *Session) Has(key string) bool
- func (s *Session) IsClosed() bool
- func (s *Session) IsDetached() bool
- func (s *Session) Logger() *slog.Logger
- func (s *Session) MemoryUsage() int64
- func (s *Session) MountRoot(component Component)
- func (s *Session) Navigator() *RouteNavigator
- func (s *Session) NeedsRestart() bool
- func (s *Session) Owner() *vango.Owner
- func (s *Session) PersistErrorHandler() func(*vango.PersistBudgetError)
- func (s *Session) PrefetchCache() *PrefetchCache
- func (s *Session) PrimitiveRegistry() *SessionPrimitiveRegistry
- func (s *Session) QueueEvent(event *Event) error
- func (s *Session) ReadLoop()
- func (s *Session) RebuildHandlers() error
- func (s *Session) RegisterIslandHandler(id string, handler vango.IslandMessageHandler) func()
- func (s *Session) RegisterPrimitives(regs []vango.PrimitiveRegistration, component *ComponentInstance)
- func (s *Session) RegisterWasmHandler(id string, handler vango.WasmMessageHandler) func()
- func (s *Session) RestoreData(values map[string]any)
- func (s *Session) Resume(conn *websocket.Conn, lastSeq uint64)
- func (s *Session) RouteID() string
- func (s *Session) SendClose(reason protocol.CloseReason, message string)
- func (s *Session) SendHookRevert(hid string)
- func (s *Session) SendIslandMessage(id string, payload any) error
- func (s *Session) SendPatches(patches []protocol.Patch)
- func (s *Session) SendResyncFull() error
- func (s *Session) SendSessionRefresh(cmd *protocol.SessionRefreshCommand)
- func (s *Session) SendWasmMessage(id string, payload any) error
- func (s *Session) Serialize() ([]byte, error)
- func (s *Session) SessionID() string
- func (s *Session) Set(key string, value any)
- func (s *Session) SetAssetResolver(r assets.Resolver)
- func (s *Session) SetInitialURL(path string, params map[string]string)
- func (s *Session) SetInt(key string, value int)
- func (s *Session) SetJSON(key string, value any)
- func (s *Session) SetPersistErrorHandler(fn func(*vango.PersistBudgetError))
- func (s *Session) SetRouter(r Router)
- func (s *Session) SetString(key string, value string)
- func (s *Session) ShowPersistErrorBanner(err *vango.PersistBudgetError)
- func (s *Session) Start()
- func (s *Session) Stats() SessionStats
- func (s *Session) StormBudget() vango.StormBudgetChecker
- func (s *Session) TriggerSchemaRefresh(reason protocol.SchemaRefreshReason, debug string)
- func (s *Session) UnregisterPrimitives(regs []vango.PrimitiveRegistration, component *ComponentInstance)
- func (s *Session) UpdateLastActive()
- func (s *Session) WriteLoop()
- type SessionConfig
- type SessionError
- type SessionEvictionPolicy
- type SessionLimits
- type SessionManager
- func (sm *SessionManager) CheckIPLimit(ip string) error
- func (sm *SessionManager) CheckMemoryPressure()
- func (sm *SessionManager) Close(id string)
- func (sm *SessionManager) Count() int
- func (sm *SessionManager) Create(conn *websocket.Conn, userID, ip string) (*Session, error)
- func (sm *SessionManager) DispatchAll(fn func(*Session))
- func (sm *SessionManager) EvictLRU(count int) int
- func (sm *SessionManager) ForEach(fn func(*Session) bool)
- func (sm *SessionManager) Get(id string) *Session
- func (sm *SessionManager) HasPersistence() bool
- func (sm *SessionManager) MarkResumed(sessionID string)
- func (sm *SessionManager) OnSessionDisconnect(sess *Session)
- func (sm *SessionManager) OnSessionReconnect(sessionID string) (*Session, error)
- func (sm *SessionManager) PersistenceManager() *session.Manager
- func (sm *SessionManager) ResumeWindow() time.Duration
- func (sm *SessionManager) SetBudgetTracker(tracker *vango.BudgetTracker)
- func (sm *SessionManager) SetCleanupInterval(d time.Duration)
- func (sm *SessionManager) SetMetricsSink(sink vango.MetricsSink)
- func (sm *SessionManager) SetOnSessionClose(fn func(*Session))
- func (sm *SessionManager) SetOnSessionCreate(fn func(*Session))
- func (sm *SessionManager) Shutdown()
- func (sm *SessionManager) ShutdownWithContext(ctx context.Context) error
- func (sm *SessionManager) Stats() ManagerStats
- func (sm *SessionManager) TotalMemoryUsage() int64
- func (sm *SessionManager) UpdateSessionIP(session *Session, newIP string) error
- type SessionManagerOptions
- type SessionPrimitiveRegistry
- func (r *SessionPrimitiveRegistry) All() []*PrimitiveInfo
- func (r *SessionPrimitiveRegistry) AllPersisted() []*PrimitiveInfo
- func (r *SessionPrimitiveRegistry) FindByDebugName(debugName string) []*PrimitiveInfo
- func (r *SessionPrimitiveRegistry) FindByNameOrID(nameOrID string) (*PrimitiveInfo, bool)
- func (r *SessionPrimitiveRegistry) FindByStableID(stableID string) []*PrimitiveInfo
- func (r *SessionPrimitiveRegistry) Register(reg vango.PrimitiveRegistration, instancePath string)
- func (r *SessionPrimitiveRegistry) SignalValue(nameOrID string) (any, bool)
- func (r *SessionPrimitiveRegistry) Unregister(instancePath string)
- type SessionStats
- type StormBudgetConfig
- type TouchEvent
- type TouchPoint
Constants ¶
const CSPHeaderName = "Content-Security-Policy"
CSPHeaderName is the header for Content Security Policy.
const CSPReportOnlyHeaderName = "Content-Security-Policy-Report-Only"
CSPReportOnlyHeaderName is the header for report-only CSP.
const CSRFCookieName = "__vango_csrf"
CSRFCookieName is the name of the CSRF cookie.
const CSRFHeaderName = "X-CSRF-Token"
CSRFHeaderName is the default header used for CSRF tokens on HTTP requests.
const DefaultAuthSessionKey = auth.SessionKey
DefaultAuthSessionKey is the conventional session key for an authenticated user. The vango/pkg/auth helpers use this key, and server.Ctx.User() falls back to it.
Variables ¶
var ( // ErrSessionClosed is returned when an operation is attempted on a closed session. ErrSessionClosed = errors.New("server: session closed") // ErrSessionNotFound is returned when a session ID does not exist. ErrSessionNotFound = errors.New("server: session not found") // ErrHandlerNotFound is returned when no handler is registered for an HID. ErrHandlerNotFound = errors.New("server: handler not found") // ErrEventQueueFull is returned when the event queue is full and an event is dropped. ErrEventQueueFull = errors.New("server: event queue full") // ErrInvalidHandshake is returned when the WebSocket handshake fails. ErrInvalidHandshake = errors.New("server: invalid handshake") // ErrMaxSessionsReached is returned when the maximum number of sessions is reached. ErrMaxSessionsReached = errors.New("server: max sessions reached") // ErrTooManySessionsFromIP is returned when the per-IP session limit is exceeded. ErrTooManySessionsFromIP = errors.New("server: too many sessions from this IP address") // ErrTooManyConcurrentHandshakes is returned when the global pre-auth handshake // concurrency limit is exceeded. ErrTooManyConcurrentHandshakes = errors.New("server: too many concurrent handshakes") // ErrTooManyConcurrentHandshakesFromIP is returned when the per-IP pre-auth // handshake concurrency limit is exceeded. ErrTooManyConcurrentHandshakesFromIP = errors.New("server: too many concurrent handshakes from this IP address") // ErrInvalidCSRF is returned when CSRF token validation fails. ErrInvalidCSRF = errors.New("server: invalid CSRF token") // ErrSecureCookiesRequired is returned when secure cookies are required but the request is not secure. ErrSecureCookiesRequired = errors.New("server: secure cookies require HTTPS or trusted proxy headers") // ErrSessionExpired is returned when a session has expired due to inactivity. ErrSessionExpired = errors.New("server: session expired") // ErrConnectionClosed is returned when the WebSocket connection is closed. ErrConnectionClosed = errors.New("server: connection closed") // ErrWriteTimeout is returned when a write operation times out. ErrWriteTimeout = errors.New("server: write timeout") // ErrReadTimeout is returned when a read operation times out. ErrReadTimeout = errors.New("server: read timeout") // ErrAuthCheckPanicked is returned when an AuthCheck provider panics. // The panic is recovered and surfaced as an error so it cannot terminate // the server process. ErrAuthCheckPanicked = errors.New("server: auth check panicked") // ErrNoConnection is returned when attempting to send on a nil connection. ErrNoConnection = errors.New("server: no connection") )
Sentinel errors for common session and server error conditions.
var ErrForbidden = auth.ErrForbidden
ErrForbidden is returned when authentication is present but insufficient. This is defined in the auth package and re-exported here for convenience.
ErrUnauthorized is returned when authentication is required but not present. This is defined in the auth package and re-exported here for convenience.
Functions ¶
func AllowAllOriginsCheck ¶
AllowAllOriginsCheck allows any origin. Use only in development.
func AllowedOriginsCheck ¶
AllowedOriginsCheck validates that the request Origin matches an allowlist. Origins are normalized by scheme + lowercase host + effective port (default port omitted/explicit forms are equivalent) and must not include path, query, or fragment. Requests without an Origin header are allowed.
func BuildCSP ¶
func BuildCSP(opts CSPOptions) string
BuildCSP constructs a policy string from options.
func CSRFCtxToken ¶
CSRFCtxToken retrieves the CSRF token from the request-scoped context values.
func CanonicalizePath ¶
CanonicalizePath normalizes a URL path for navigation.
func CurrentMemoryUsage ¶
func CurrentMemoryUsage() int64
CurrentMemoryUsage returns the current heap memory usage in bytes.
func EstimateAnyMemory ¶
EstimateAnyMemory approximates the memory usage of a value. It is depth-limited to avoid runaway recursion on complex graphs.
func EstimateMapMemory ¶
EstimateMapMemory estimates memory usage of a map.
func EstimateSliceMemory ¶
EstimateSliceMemory estimates memory usage of a slice.
func EstimateStringMemory ¶
EstimateStringMemory estimates memory usage of a string.
func InspectState ¶
func InspectState(session *Session) vruntime.StateInspection
InspectState returns inspection data for a session.
func IsAuthError ¶
IsAuthError returns true if the error is an authentication or authorization error.
func LoggerForEvent ¶
LoggerForEvent returns a context-rich logger for an event. If ctx.Logger() is present, it is returned as-is; otherwise, it is enriched with available session/user/event details.
func NormalizeAuthCheckConfig ¶
func NormalizeAuthCheckConfig(cfg *AuthCheckConfig)
NormalizeAuthCheckConfig applies defaults to an auth check config in-place.
func RunRouteMiddleware ¶
func RunRouteMiddleware(ctx Ctx, middleware []RouteMiddleware, final func() error) (ranFinal bool, err error)
RunRouteMiddleware executes a route middleware chain and then calls final.
Middleware can short-circuit by returning nil without calling next. In that case ranFinal will be false and err will be nil.
func SameOriginCheck ¶
SameOriginCheck validates that the WebSocket request origin matches the host. This is the secure default for CheckOrigin. SECURITY: Uses strict URL/authority parsing, case-insensitive host comparison, and default-port normalization while keeping scheme checks strict (http != https).
func SetCSRFCtxToken ¶
SetCSRFCtxToken stores the CSRF token in the request-scoped context values. This allows SSR handlers to render CSRF-protected forms.
func SetGlobalPrefetchLimit ¶
func SetGlobalPrefetchLimit(limit int)
SetGlobalPrefetchLimit sets the global prefetch concurrency limit. Should be called at server startup before any prefetch operations.
func TotalSystemMemory ¶
func TotalSystemMemory() int64
TotalSystemMemory returns the total system memory seen by the Go runtime.
func UserFromContext ¶
UserFromContext retrieves the authenticated user stored by WithUser.
func ValidateExternalRedirectURL ¶
ValidateExternalRedirectURL validates an absolute redirect URL against an allowlist. Returns the canonical URL and true when allowed; otherwise returns ("", false).
Types ¶
type AppliedNavigateOptions ¶
type AppliedNavigateOptions struct {
}
AppliedNavigateOptions is the concrete, inspected result of applying NavigateOption closures. This is useful in contexts outside server.ctx (e.g. SSR adapters) where NavigateOption is opaque.
func ApplyNavigateOptions ¶
func ApplyNavigateOptions(opts ...NavigateOption) AppliedNavigateOptions
ApplyNavigateOptions applies NavigateOption closures to the default option set.
func BuildNavigateURL ¶
func BuildNavigateURL(path string, opts ...NavigateOption) (fullPath string, applied AppliedNavigateOptions)
BuildNavigateURL builds the final navigation URL by applying NavigateOption params and validating the resulting path against the navigation security rules.
Returns an empty string if the path is invalid (e.g. absolute URL).
type AuthCheckConfig ¶
type AuthCheckConfig struct {
// Interval between active checks (0 disables periodic checks).
Interval time.Duration
// Timeout caps how long a Check may take.
// Default: 5 seconds.
Timeout time.Duration
// FailureMode controls transient failure handling.
// Default: FailOpenWithGrace.
FailureMode AuthFailureMode
// MaxStale caps how long a session may go without a successful active check
// when FailureMode is fail-open.
// Default: 15 minutes.
MaxStale time.Duration
// Check is called periodically and on-demand.
// This runs off the session loop; do not mutate session state directly.
// Panics are recovered and treated as check failures.
Check func(ctx context.Context, p auth.Principal) error
// OnExpired defines what happens when auth fails.
OnExpired AuthExpiredConfig
}
AuthCheckConfig configures periodic active revalidation.
type AuthExpiredAction ¶
type AuthExpiredAction int
AuthExpiredAction defines what happens when auth expires.
const ( // ForceReload triggers browser reload (re-enters HTTP pipeline). ForceReload AuthExpiredAction = iota NavigateTo // Custom invokes a caller-provided handler. Custom )
type AuthExpiredConfig ¶
type AuthExpiredConfig struct {
Action AuthExpiredAction
Path string
Reason AuthExpiredReason
Handler func(s *Session)
}
AuthExpiredConfig defines behavior when auth expires.
type AuthExpiredReason ¶
type AuthExpiredReason int
AuthExpiredReason provides structured context for auth expiry.
const ( AuthExpiredUnknown AuthExpiredReason = iota AuthExpiredPassiveExpiry AuthExpiredResumeRehydrateFailed AuthExpiredActiveRevalidateFailed AuthExpiredOnDemandRevalidateFailed AuthExpiredNoRevalidationHooks // No authFunc/OnSessionResume configured for authenticated session AuthExpiredResumeIdentityMismatch )
func (AuthExpiredReason) String ¶
func (r AuthExpiredReason) String() string
type AuthFailureMode ¶
type AuthFailureMode int
AuthFailureMode controls what happens when active checks fail.
const ( // FailOpenWithGrace keeps the session alive on transient failures. // Default: availability-first, bounded by MaxStale. FailOpenWithGrace AuthFailureMode = iota // FailClosed expires the session on any check failure. FailClosed )
type AuthResumePolicy ¶
type AuthResumePolicy int
AuthResumePolicy controls how authenticated session resumes are validated.
const ( // AuthResumePolicyStrict (default) requires authFunc or OnSessionResume to be // configured for authenticated sessions to resume. If neither is set and the // session was previously authenticated, resume is rejected. // // This is the secure default: session ID alone is NOT sufficient to resume // an authenticated session. The original auth (cookie, token, etc.) must be // revalidated on every resume. // // IMPORTANT: Simply configuring OnSessionResume is not sufficient for security. // The hook MUST actually revalidate authentication and call auth.Set() to // rehydrate the user. In strict mode, resume clears runtime auth projection // before OnSessionResume when authFunc is not configured, so a no-op hook // is rejected as unauthenticated resume. See documentation for proper implementation. AuthResumePolicyStrict AuthResumePolicy = iota // AuthResumePolicyTrustSessionID treats the session ID as a credential. // Authenticated sessions can resume without revalidation if the session // data is still in server memory. // // IMPORTANT: This policy ONLY applies to in-memory session resume. For sessions // restored from persistence (Redis, NATS, etc.), the user object is not serialized, // so the ghost auth check will still reject resume unless authFunc or OnSessionResume // rehydrates the user. This is by design - persisted sessions should always // revalidate auth on restore. // // SECURITY WARNING: Use this policy ONLY if ALL of the following are true: // - You generate cryptographically strong session IDs (Vango does this) // - Session IDs are NEVER logged, exposed in URLs visible to users, or // accessible to client-side JavaScript beyond the thin client // - You accept the risk that a leaked session ID grants authenticated access // - You have additional protections (short ResumeWindow, IP binding, etc.) // // This policy is appropriate for: // - Internal tools with trusted networks // - Apps where session continuity UX outweighs security concerns // - Apps with very short ResumeWindow (< 30 seconds) // // IMPORTANT: This policy weakens "logout elsewhere" and revocation semantics. // A detached in-memory session may still resume with only the session ID until // ResumeWindow expires or the session is evicted/closed. AuthResumePolicyTrustSessionID )
func (AuthResumePolicy) IsValid ¶
func (p AuthResumePolicy) IsValid() bool
IsValid returns true if the policy is a known valid value.
func (AuthResumePolicy) String ¶
func (p AuthResumePolicy) String() string
String returns the string representation of the auth resume policy.
type BudgetExceededMode ¶
type BudgetExceededMode int
BudgetExceededMode determines behavior when a storm budget is exceeded.
const ( // BudgetThrottle drops excess operations silently (default). // Operations that exceed the budget are not executed. BudgetThrottle BudgetExceededMode = iota // BudgetTripBreaker pauses effect execution until cleared. // Like a circuit breaker, stops all effect processing until reset. BudgetTripBreaker )
type CSPOptions ¶
type CSPOptions struct {
ReportOnly bool
AllowInlineStyles bool
AllowInlineScripts bool
AllowDataImages bool
AllowHTTPSImages bool
IncludeWSS bool
IncludeWS bool
AdditionalConnectSrc []string
AdditionalScriptSrc []string
AdditionalStyleSrc []string
AdditionalImgSrc []string
AdditionalFontSrc []string
AdditionalDirectives []string
}
CSPOptions configures the CSP middleware helper.
func DefaultCSPOptions ¶
func DefaultCSPOptions() CSPOptions
DefaultCSPOptions returns a CSPOptions aligned with the security guide example.
func HardenedCSPOptions ¶
func HardenedCSPOptions() CSPOptions
HardenedCSPOptions returns a stricter CSP profile for production hardening. It disables inline styles while preserving other defaults.
type CSRFError ¶
type CSRFError struct {
Reason string
}
CSRFError represents an HTTP CSRF validation error.
func (CSRFError) StatusCode ¶
type Component ¶
type Component interface {
// Render returns the VNode tree for this component.
Render() *vdom.VNode
}
Component is the interface for renderable components. Components produce VNode trees that represent the UI.
type ComponentInstance ¶
type ComponentInstance struct {
// InstanceID is the unique instance identifier.
InstanceID string
// Component is the component being rendered.
Component Component
// HID is the hydration ID of the root element.
HID string
// Owner manages signal ownership for this component.
Owner *vango.Owner
// Parent is the parent component instance (nil for root).
Parent *ComponentInstance
// Children are child component instances.
Children []*ComponentInstance
// Props are the current props passed to the component.
Props map[string]any
// contains filtered or unexported fields
}
ComponentInstance represents a mounted component with its state. It holds the component's reactive ownership, tracking, and rendering context.
func (*ComponentInstance) AddChild ¶
func (c *ComponentInstance) AddChild(child *ComponentInstance)
AddChild adds a child component instance.
func (*ComponentInstance) ClearDirty ¶
func (c *ComponentInstance) ClearDirty()
ClearDirty clears the dirty flag.
func (*ComponentInstance) Dispose ¶
func (c *ComponentInstance) Dispose()
Dispose disposes the component instance and all its children.
func (*ComponentInstance) GetProp ¶
func (c *ComponentInstance) GetProp(key string) any
GetProp gets a prop value.
func (*ComponentInstance) ID ¶
func (c *ComponentInstance) ID() uint64
ID implements vango.Listener and returns a globally unique identifier.
func (*ComponentInstance) IsDirty ¶
func (c *ComponentInstance) IsDirty() bool
IsDirty returns whether the component needs re-rendering.
func (*ComponentInstance) LastTree ¶
func (c *ComponentInstance) LastTree() *vdom.VNode
LastTree returns the last rendered VNode tree.
func (*ComponentInstance) ListKeyPath ¶
func (c *ComponentInstance) ListKeyPath() []string
ListKeyPath returns a copy of the list key path for this instance.
func (*ComponentInstance) MarkDirty ¶
func (c *ComponentInstance) MarkDirty()
MarkDirty marks the component as needing re-render.
func (*ComponentInstance) MemoryUsage ¶
func (c *ComponentInstance) MemoryUsage() int64
MemoryUsage returns an estimate of memory used by this instance.
func (*ComponentInstance) RemoveChild ¶
func (c *ComponentInstance) RemoveChild(child *ComponentInstance)
RemoveChild removes a child component instance.
func (*ComponentInstance) Render ¶
func (c *ComponentInstance) Render() *vdom.VNode
Render renders the component and returns the VNode tree. It sets up the tracking context so signals are properly tracked, and sets the runtime context so UseCtx() works during render.
func (*ComponentInstance) Session ¶
func (c *ComponentInstance) Session() *Session
Session returns the owning session.
func (*ComponentInstance) SetLastTree ¶
func (c *ComponentInstance) SetLastTree(tree *vdom.VNode)
SetLastTree sets the last rendered tree (used after diffing).
func (*ComponentInstance) SetProp ¶
func (c *ComponentInstance) SetProp(key string, value any)
SetProp sets a prop value.
func (*ComponentInstance) TrackDependency ¶
func (c *ComponentInstance) TrackDependency(dep vango.Dependency)
type CookieOption ¶
type CookieOption func(*CookiePolicyOptions)
CookieOption configures cookie policy application.
func WithCookieHTTPOnly ¶
func WithCookieHTTPOnly(enabled bool) CookieOption
WithCookieHTTPOnly overrides the default HttpOnly behavior for a cookie.
type CookiePolicy ¶
type CookiePolicy struct {
// contains filtered or unexported fields
}
CookiePolicy enforces cookie defaults and security requirements.
func (*CookiePolicy) Apply ¶
func (p *CookiePolicy) Apply(r *http.Request, cookie *http.Cookie, opts ...CookieOption) (*http.Cookie, error)
Apply applies cookie defaults and enforces SecureCookies requirements. Returns ErrSecureCookiesRequired if secure cookies are enabled but the request is not secure.
func (*CookiePolicy) ApplyCookiePolicy ¶
func (p *CookiePolicy) ApplyCookiePolicy(r *http.Request, cookie *http.Cookie) (*http.Cookie, error)
ApplyCookiePolicy applies defaults without overrides. This is a compatibility hook for external packages that accept a generic cookie policy interface.
func (*CookiePolicy) IsRequestSecure ¶
func (p *CookiePolicy) IsRequestSecure(r *http.Request) bool
IsRequestSecure reports whether the request is considered secure (HTTPS), honoring trusted proxy headers when configured.
type CookiePolicyOptions ¶
type CookiePolicyOptions struct {
// contains filtered or unexported fields
}
CookiePolicyOptions configures optional overrides for cookie policy.
type Ctx ¶
type Ctx interface {
// Request returns the underlying HTTP request.
Request() *http.Request
// Path returns the URL path.
Path() string
// Method returns the HTTP method.
Method() string
// Query returns the URL query parameters as url.Values.
Query() url.Values
// QueryParam returns a single query parameter value by key.
// Returns an empty string if the key is not present.
QueryParam(key string) string
// Param returns a route parameter by key.
Param(key string) string
// Header returns a request header value.
Header(key string) string
// Cookie returns a cookie by name.
Cookie(name string) (*http.Cookie, error)
// Status sets the HTTP response status code.
Status(code int)
// Redirect redirects to the given URL with the given status code.
// Redirects are relative-only and reject absolute URLs.
Redirect(url string, code int)
// RedirectExternal redirects to an absolute URL with the given status code.
// External redirects require an explicit allowlist.
RedirectExternal(url string, code int)
// SetHeader sets a response header.
SetHeader(key, value string)
// SetCookie sets a response cookie.
SetCookie(cookie *http.Cookie)
// SetCookieStrict sets a response cookie with enforced security defaults.
// Returns ErrSecureCookiesRequired if secure cookies are enabled and the request is not secure.
SetCookieStrict(cookie *http.Cookie, opts ...CookieOption) error
// Session returns the WebSocket session (nil for SSR-only requests).
Session() vango.Session
// AuthSession returns the WebSocket session as an auth.Session interface.
// This allows auth helpers to persist to the session without importing server.
AuthSession() auth.Session
// User returns the authenticated user (set by auth middleware).
User() any
// SetUser sets the authenticated user.
SetUser(user any)
// Principal returns the authenticated principal, if any.
Principal() (auth.Principal, bool)
// MustPrincipal returns the principal or panics if not set.
MustPrincipal() auth.Principal
// RevalidateAuth forces an immediate active check if configured.
// Returns any error from the check, including ErrAuthCheckPanicked when
// the configured check panics.
// If authenticated session projection exists but principal is missing,
// this fails closed with auth.ErrSessionExpired.
RevalidateAuth() error
// BroadcastAuthLogout notifies other tabs that auth was cleared and they should reload.
BroadcastAuthLogout()
// Logger returns the request-scoped logger.
Logger() *slog.Logger
// Done returns a channel that's closed when the request is canceled.
Done() <-chan struct{}
// SetValue stores a request-scoped value.
// These values are only available for the duration of the current event/request.
SetValue(key, value any)
// Value retrieves a request-scoped value.
Value(key any) any
// Emit dispatches a custom event to the client.
// The event will be dispatched as a CustomEvent with the given name and detail.
// Use this for notifications, toast messages, analytics, etc.
Emit(name string, data any)
// Dispatch queues a function to run on the session's event loop.
// This is safe to call from any goroutine and is the correct way to
// update signals from asynchronous operations (database calls, timers, etc.).
//
// The function will be executed synchronously on the event loop, ensuring
// signal writes are properly serialized. After the function completes,
// pending effects will run and dirty components will re-render.
//
// Example:
//
// go func() {
// user, err := db.Users.FindByID(ctx.StdContext(), id)
// ctx.Dispatch(func() {
// if err != nil {
// errorSignal.Set(err)
// } else {
// userSignal.Set(user)
// }
// })
// }()
//
// IMPORTANT: This method MUST be safe to call from any goroutine.
Dispatch(fn func())
// Unlike Redirect(), this updates the browser URL and re-renders the page
// without a full HTTP redirect. The navigation is queued and processed
// after the current handler completes.
//
// Options can be provided to customize behavior:
// - WithReplace() - replace current history entry instead of pushing
// - WithNavigateParams(map[string]any) - add query parameters to the URL
// - WithoutScroll() - disable scrolling to top after navigation
//
// Example:
//
// func handleSave(ctx vango.Ctx) {
// project := saveProject()
// ctx.Navigate("/projects/" + project.ID)
// }
//
// For HTTP-only requests (SSR without WebSocket), this falls back to
// an HTTP redirect.
Navigate(path string, opts ...NavigateOption)
// StdContext returns the standard library context with trace propagation.
// Use this when calling external services or database drivers.
//
// Example:
// row := db.QueryRowContext(ctx.StdContext(), "SELECT * FROM users WHERE id = $1", userID)
// req, _ := http.NewRequestWithContext(ctx.StdContext(), "GET", url, nil)
//
// The context includes any trace spans injected by middleware (e.g., OpenTelemetry).
StdContext() context.Context
// WithStdContext updates the standard context used by StdContext().
// This is used by middleware to inject trace spans for downstream calls.
//
// This is typically called by observability middleware:
// spanCtx, span := tracer.Start(ctx.StdContext(), "operation")
// defer span.End()
// ctx = ctx.WithStdContext(spanCtx)
// return next()
WithStdContext(stdCtx context.Context) Ctx
// Event returns the current WebSocket event being processed.
// Returns nil for HTTP-only requests (SSR).
//
// Example:
// if event := ctx.Event(); event != nil {
// log.Printf("Processing %s event on %s", event.Type, event.HID)
// }
Event() *Event
// PatchCount returns the number of patches sent during this request.
// This is updated after each render cycle.
PatchCount() int
// AddPatchCount increments the patch count for this request.
// Called internally by the render system.
AddPatchCount(count int)
// StormBudget returns the storm budget checker for this session.
// Used by primitives (Action, Resource, GoLatest) to check rate limits.
// Returns nil if storm budgets are not configured.
//
// See SPEC_ADDENDUM.md §A.4 for storm budget configuration.
StormBudget() vango.StormBudgetChecker
// Mode returns the current render mode as int.
// Returns 0 (ModeNormal) for regular requests, 1 (ModePrefetch) during prefetch.
//
// Primitives like Effect, Interval, and signal.Set() check this to
// enforce read-only behavior during prefetch:
// - ModePrefetch (1): Signal writes are dropped, effects are no-ops
// - ModeNormal (0): All operations proceed normally
//
// This method implements vango.PrefetchModeChecker interface.
//
// Example (for primitive implementations):
// if ctx := UseCtx(); ctx != nil && ctx.Mode() == 1 {
// if DevMode { panic("signal write forbidden in prefetch") }
// return // drop in prod
// }
Mode() int
// Asset resolves a source asset path to its fingerprinted path.
// Returns the original path if no manifest is configured or the asset is not found.
//
// This enables cache-busting via content-hashed filenames while keeping
// templates simple:
//
// Example:
//
// <script src={ctx.Asset("vango.js")}></script>
// // In dev: "/public/vango.js"
// // In prod: "/public/vango.a1b2c3d4.min.js"
Asset(source string) string
}
Ctx provides access to request data within components. It is passed to route handlers and can be used to access the request, session, and response control methods.
func NewTestContext ¶
NewTestContext creates a context for testing with the given session. This allows testing components that require a valid context.
func NewTestContextWithDebug ¶
NewTestContextWithDebug creates a test context with an explicit debug flag.
type Event ¶
type Event struct {
// Seq is the sequence number of the event.
Seq uint64
// Type is the type of event (click, input, submit, etc.).
Type protocol.EventType
// HID is the hydration ID of the target element.
HID string
// Payload contains type-specific event data.
Payload any
// Session is the session that received the event.
Session *Session
// Time is when the event was received by the server.
Time time.Time
// contains filtered or unexported fields
}
Event represents a decoded event from the client with runtime context.
func (*Event) TypeString ¶
TypeString returns the string representation of the event type. Used for logging and tracing.
type EventRateLimiter ¶
type EventRateLimiter struct {
// contains filtered or unexported fields
}
EventRateLimiter implements token bucket rate limiting for events.
func (*EventRateLimiter) Allow ¶
func (r *EventRateLimiter) Allow() bool
Allow returns true if the event is allowed.
type FormData ¶
type FormData struct {
// contains filtered or unexported fields
}
FormData represents submitted form data.
type FuncComponent ¶
FuncComponent wraps a render function as a Component.
func (FuncComponent) Render ¶
func (f FuncComponent) Render() *vdom.VNode
Render calls the wrapped function.
type Handler ¶
type Handler func(event *Event)
Handler is the internal event handler function type. It receives a decoded event and processes it.
type HandlerError ¶
HandlerError wraps a panic that occurred in an event handler.
func NewHandlerError ¶
func NewHandlerError(sessionID, hid, eventType string, panicVal any, stack []byte) *HandlerError
NewHandlerError creates a new HandlerError.
type HookEvent ¶
HookEvent represents a client hook event.
type InstancePath ¶
InstancePath uniquely identifies a primitive instance.
func ComputeInstancePath ¶
func ComputeInstancePath(session *Session, component *ComponentInstance, stableID string) InstancePath
ComputeInstancePath builds the full path for a primitive.
func (InstancePath) String ¶
func (p InstancePath) String() string
type KeyboardEvent ¶
KeyboardEvent represents a keyboard event with key and modifiers.
type LayoutHandler ¶
LayoutHandler wraps child content in a layout.
type ManagerStats ¶
type ManagerStats struct {
Active int
TotalCreated uint64
TotalClosed uint64
Peak int
TotalMemory int64
}
ManagerStats contains aggregated session manager statistics.
type MemoryMonitor ¶
type MemoryMonitor struct {
// contains filtered or unexported fields
}
MemoryMonitor monitors system memory and triggers actions when thresholds are exceeded.
func NewMemoryMonitor ¶
func NewMemoryMonitor(config *MemoryMonitorConfig) *MemoryMonitor
NewMemoryMonitor creates a new memory monitor.
func (*MemoryMonitor) ForceCheck ¶
func (m *MemoryMonitor) ForceCheck()
ForceCheck performs an immediate memory check.
func (*MemoryMonitor) Pause ¶
func (m *MemoryMonitor) Pause()
Pause temporarily pauses memory checking.
func (*MemoryMonitor) SetOnHardLimit ¶
func (m *MemoryMonitor) SetOnHardLimit(fn func(current, limit int64))
SetOnHardLimit sets the callback for hard limit breach.
func (*MemoryMonitor) SetOnSoftLimit ¶
func (m *MemoryMonitor) SetOnSoftLimit(fn func(current, limit int64))
SetOnSoftLimit sets the callback for soft limit breach.
type MemoryMonitorConfig ¶
type MemoryMonitorConfig struct {
SoftLimit int64 // Bytes at which to start evicting (default: 80% of system memory)
HardLimit int64 // Bytes at which to aggressively evict (default: 90% of system memory)
CheckInterval time.Duration // How often to check memory (default: 10s)
GCCooldown time.Duration // Minimum time between forced GCs (default: 30s)
}
MemoryMonitorConfig configures the memory monitor.
func DefaultMemoryMonitorConfig ¶
func DefaultMemoryMonitorConfig() *MemoryMonitorConfig
DefaultMemoryMonitorConfig returns sensible defaults.
type MemoryPressureLevel ¶
type MemoryPressureLevel int
MemoryPressureLevel indicates the current memory pressure.
const ( MemoryPressureNone MemoryPressureLevel = iota // Below soft limit MemoryPressureLow // Between soft and hard limit MemoryPressureHigh // Above hard limit MemoryPressureCritical // Near OOM )
func GetMemoryPressureLevel ¶
func GetMemoryPressureLevel(softLimit, hardLimit int64) MemoryPressureLevel
GetMemoryPressureLevel returns the current memory pressure level.
func (MemoryPressureLevel) String ¶
func (l MemoryPressureLevel) String() string
String returns the string representation of the pressure level.
type MemoryStats ¶
type MemoryStats struct {
HeapAlloc int64 // Bytes allocated on heap
HeapSys int64 // Bytes obtained from OS for heap
HeapIdle int64 // Bytes in idle spans
HeapInuse int64 // Bytes in non-idle spans
HeapReleased int64 // Bytes released to OS
StackInuse int64 // Bytes used by stack
NumGC uint32 // Number of completed GC cycles
LastGC time.Time // Time of last GC
GCPauseTotal time.Duration // Total GC pause time
}
MemoryStats returns detailed memory statistics.
func GetMemoryStats ¶
func GetMemoryStats() *MemoryStats
GetMemoryStats returns current memory statistics.
type MetricsCollector ¶
type MetricsCollector struct {
// contains filtered or unexported fields
}
MetricsCollector collects and aggregates metrics over time.
func NewMetricsCollector ¶
func NewMetricsCollector() *MetricsCollector
NewMetricsCollector creates a new MetricsCollector.
func (*MetricsCollector) RecordActionDuration ¶
func (m *MetricsCollector) RecordActionDuration(action string, ms int64)
RecordActionDuration records action duration.
func (*MetricsCollector) RecordActionError ¶
func (m *MetricsCollector) RecordActionError(action string)
RecordActionError records an action error.
func (*MetricsCollector) RecordBytesReceived ¶
func (m *MetricsCollector) RecordBytesReceived(n int)
RecordBytesReceived records bytes received.
func (*MetricsCollector) RecordBytesSent ¶
func (m *MetricsCollector) RecordBytesSent(n int)
RecordBytesSent records bytes sent.
func (*MetricsCollector) RecordColdDeployAck ¶
func (m *MetricsCollector) RecordColdDeployAck(source string)
RecordColdDeployAck records a cold deploy acknowledgment.
func (*MetricsCollector) RecordEventDropped ¶
func (m *MetricsCollector) RecordEventDropped()
RecordEventDropped records an event dropped.
func (*MetricsCollector) RecordEventLatency ¶
func (m *MetricsCollector) RecordEventLatency(latencyUs int64)
RecordEventLatency records event processing latency in microseconds.
func (*MetricsCollector) RecordEventProcessed ¶
func (m *MetricsCollector) RecordEventProcessed()
RecordEventProcessed records an event processed.
func (*MetricsCollector) RecordEventReceived ¶
func (m *MetricsCollector) RecordEventReceived()
RecordEventReceived records an event received.
func (*MetricsCollector) RecordHandlerPanic ¶
func (m *MetricsCollector) RecordHandlerPanic()
RecordHandlerPanic records a handler panic.
func (*MetricsCollector) RecordPatchBytes ¶
func (m *MetricsCollector) RecordPatchBytes(route string, bytes int)
RecordPatchBytes records patch bytes by route.
func (*MetricsCollector) RecordPatchMismatch ¶
func (m *MetricsCollector) RecordPatchMismatch(reason string)
RecordPatchMismatch records a patch mismatch by reason.
func (*MetricsCollector) RecordPatchesSent ¶
func (m *MetricsCollector) RecordPatchesSent(count int, bytes int)
RecordPatchesSent records patches sent.
func (*MetricsCollector) RecordPersistBytes ¶
func (m *MetricsCollector) RecordPersistBytes(stableID string, bytes int64)
RecordPersistBytes records bytes written by stable ID.
func (*MetricsCollector) RecordPersistBytesWritten ¶
func (m *MetricsCollector) RecordPersistBytesWritten(primitiveID string, bytes int64)
RecordPersistBytesWritten records bytes successfully written.
func (*MetricsCollector) RecordPersistWriteRejected ¶
func (m *MetricsCollector) RecordPersistWriteRejected(reason string, primitiveID string)
RecordPersistWriteRejected records a rejected persist write.
func (*MetricsCollector) RecordReadError ¶
func (m *MetricsCollector) RecordReadError()
RecordReadError records a read error.
func (*MetricsCollector) RecordResourceDuration ¶
func (m *MetricsCollector) RecordResourceDuration(resource string, ms int64)
RecordResourceDuration records resource duration.
func (*MetricsCollector) RecordResourceError ¶
func (m *MetricsCollector) RecordResourceError(resource string)
RecordResourceError records a resource error.
func (*MetricsCollector) RecordResumeFailure ¶
func (m *MetricsCollector) RecordResumeFailure(reason string)
RecordResumeFailure records a failed resume with a reason.
func (*MetricsCollector) RecordResumeSuccess ¶
func (m *MetricsCollector) RecordResumeSuccess()
RecordResumeSuccess records a successful resume.
func (*MetricsCollector) RecordSchemaMismatch ¶
func (m *MetricsCollector) RecordSchemaMismatch(reason string)
RecordSchemaMismatch records a schema mismatch event.
func (*MetricsCollector) RecordSessionActive ¶
func (m *MetricsCollector) RecordSessionActive(delta int)
RecordSessionActive records an active session delta.
func (*MetricsCollector) RecordSessionDetached ¶
func (m *MetricsCollector) RecordSessionDetached(delta int)
RecordSessionDetached records a detached session delta.
func (*MetricsCollector) RecordSessionPersistBytes ¶
func (m *MetricsCollector) RecordSessionPersistBytes(sessionID string, bytes int64)
RecordSessionPersistBytes records total persisted bytes per session.
func (*MetricsCollector) RecordSignalWriteViolation ¶
func (m *MetricsCollector) RecordSignalWriteViolation(operation, reason string)
RecordSignalWriteViolation records an invalid signal write attempt.
func (*MetricsCollector) RecordWriteError ¶
func (m *MetricsCollector) RecordWriteError()
RecordWriteError records a write error.
func (*MetricsCollector) Snapshot ¶
func (m *MetricsCollector) Snapshot() *ServerMetrics
Snapshot returns current metrics.
type Middleware ¶
Middleware is a function that wraps an HTTP handler.
func CSPHeaderMiddleware ¶
func CSPHeaderMiddleware(policy string, reportOnly bool) Middleware
CSPHeaderMiddleware sets a CSP header with the provided policy string.
func CSPMiddleware ¶
func CSPMiddleware(opts CSPOptions) Middleware
CSPMiddleware sets a Content-Security-Policy header using a default policy with optional tweaks from CSPOptions.
type MouseEvent ¶
type MouseEvent struct {
ClientX int
ClientY int
Button int
CtrlKey bool
ShiftKey bool
AltKey bool
MetaKey bool
}
MouseEvent represents a mouse event with position and modifiers.
type NavigateOption ¶
type NavigateOption func(*navigateOptions)
NavigateOption is a functional option for Navigate.
func WithNavigateParams ¶
func WithNavigateParams(params map[string]any) NavigateOption
WithNavigateParams adds query parameters to the navigation URL.
func WithReplace ¶
func WithReplace() NavigateOption
WithReplace replaces the current history entry instead of pushing.
func WithoutScroll ¶
func WithoutScroll() NavigateOption
WithoutScroll disables scrolling to top after navigation.
type NavigateResult ¶
type NavigateResult struct {
Path string
Matched bool
Patches []vdom.Patch
NavPatch protocol.Patch
Error error
}
NavigateResult contains the result of a navigation operation.
type OriginPolicyMode ¶
type OriginPolicyMode int
OriginPolicyMode describes how WebSocket origins are validated.
const ( OriginPolicyUnset OriginPolicyMode = iota OriginPolicySameOrigin OriginPolicyAllowedOrigins OriginPolicyCustom OriginPolicyAllowAll )
type PageHandler ¶
PageHandler handles a page request, returning a component to render.
type PatchHistory ¶
type PatchHistory struct {
// contains filtered or unexported fields
}
PatchHistory is a thread-safe ring buffer for storing sent patch frames. It supports:
- Fast insertion at head
- Lookup by sequence range for resync
- Garbage collection based on acknowledged sequence
The ring buffer overwrites oldest entries when full, maintaining a sliding window of recent patches that can be replayed if a client misses some.
func NewPatchHistory ¶
func NewPatchHistory(capacity int) *PatchHistory
NewPatchHistory creates a new patch history ring buffer with the given capacity.
func (*PatchHistory) Add ¶
func (h *PatchHistory) Add(seq uint64, frame []byte)
Add stores a patch frame in the buffer. This should be called ONLY after a successful write to the WebSocket. The frame bytes are copied to prevent issues with buffer reuse.
func (*PatchHistory) CanRecover ¶
func (h *PatchHistory) CanRecover(lastSeq uint64) bool
CanRecover checks if the buffer can provide frames to recover from lastSeq. Returns true if all sequences from lastSeq+1 to maxSeq are available.
func (*PatchHistory) Clear ¶
func (h *PatchHistory) Clear()
Clear removes all entries from the buffer. This is useful during session resume when starting fresh.
func (*PatchHistory) Count ¶
func (h *PatchHistory) Count() int
Count returns the number of entries in the buffer.
func (*PatchHistory) GarbageCollect ¶
func (h *PatchHistory) GarbageCollect(ackSeq uint64)
GarbageCollect updates minSeq based on the acknowledged sequence. Entries with sequence <= ackSeq are no longer needed for resync and will be overwritten naturally by the ring buffer.
func (*PatchHistory) GetFrames ¶
func (h *PatchHistory) GetFrames(afterSeq, toSeq uint64) [][]byte
GetFrames returns frames for sequences (afterSeq, toSeq]. Returns nil if any sequence in the requested range is not available. The returned frames are in sequence order, ready to be replayed.
func (*PatchHistory) MaxSeq ¶
func (h *PatchHistory) MaxSeq() uint64
MaxSeq returns the maximum sequence in buffer.
func (*PatchHistory) MemoryUsage ¶
func (h *PatchHistory) MemoryUsage() int64
MemoryUsage estimates the memory used by the patch history, including frame bytes.
func (*PatchHistory) MinSeq ¶
func (h *PatchHistory) MinSeq() uint64
MinSeq returns the minimum recoverable sequence.
type PatchHistoryEntry ¶
type PatchHistoryEntry struct {
Seq uint64 // Patch sequence number
Frame []byte // Pre-encoded FramePatches for fast replay
SentAt time.Time // When the frame was sent
}
PatchHistoryEntry stores a sent patch frame for potential replay.
type PrefetchCache ¶
type PrefetchCache struct {
// contains filtered or unexported fields
}
PrefetchCache is an LRU cache for prefetched route renders. Each session has its own cache instance.
Per Section 8.2:
- Keyed by canonical path
- TTL: 30 seconds (configurable)
- Max entries: 10 (LRU eviction)
func NewPrefetchCache ¶
func NewPrefetchCache(config *PrefetchConfig) *PrefetchCache
NewPrefetchCache creates a new prefetch cache.
func (*PrefetchCache) Delete ¶
func (c *PrefetchCache) Delete(path string)
Delete removes a cached entry.
func (*PrefetchCache) Get ¶
func (c *PrefetchCache) Get(path string) *PrefetchCacheEntry
Get retrieves a cached prefetch result. Returns nil if not found or expired.
func (*PrefetchCache) Len ¶
func (c *PrefetchCache) Len() int
Len returns the number of cached entries.
func (*PrefetchCache) MemoryUsage ¶
func (c *PrefetchCache) MemoryUsage() int64
MemoryUsage estimates the memory used by the prefetch cache.
type PrefetchCacheEntry ¶
type PrefetchCacheEntry struct {
// Tree is the rendered VNode tree
Tree *vdom.VNode
// CreatedAt is when this entry was created
CreatedAt time.Time
// ExpiresAt is when this entry expires
ExpiresAt time.Time
}
PrefetchCacheEntry holds a cached prefetch result.
func (*PrefetchCacheEntry) IsExpired ¶
func (e *PrefetchCacheEntry) IsExpired() bool
IsExpired returns true if the entry has expired.
type PrefetchConfig ¶
type PrefetchConfig struct {
// TTL is how long a prefetched result is valid.
// Default: 30 seconds
TTL time.Duration
// MaxEntries is the maximum number of cached entries per session.
// Uses LRU eviction when exceeded.
// Default: 10
MaxEntries int
// Timeout is the maximum time to wait for a prefetch to complete.
// If exceeded, the prefetch is aborted and result is not cached.
// Default: 100ms
Timeout time.Duration
// RateLimit is the maximum prefetch requests per second per session.
// Excess requests are silently dropped.
// Default: 5
RateLimit float64
// SessionConcurrency is the max simultaneous prefetch evaluations per session.
// Default: 2
SessionConcurrency int
// GlobalConcurrency is the max simultaneous prefetch evaluations globally.
// Default: 50
GlobalConcurrency int
}
PrefetchConfig holds configuration for the prefetch system. These defaults are per Section 8.2-8.3 of the Routing Spec.
func DefaultPrefetchConfig ¶
func DefaultPrefetchConfig() *PrefetchConfig
DefaultPrefetchConfig returns the default prefetch configuration. Per Section 8.2 and 8.3.3 of the Routing Spec.
type PrefetchRateLimiter ¶
type PrefetchRateLimiter struct {
// contains filtered or unexported fields
}
PrefetchRateLimiter implements token bucket rate limiting for prefetch requests. Per Section 8.5:
- Max 5 prefetch requests per second per session
- Excess requests are silently dropped
func NewPrefetchRateLimiter ¶
func NewPrefetchRateLimiter(ratePerSecond float64) *PrefetchRateLimiter
NewPrefetchRateLimiter creates a new rate limiter.
func (*PrefetchRateLimiter) Allow ¶
func (r *PrefetchRateLimiter) Allow() bool
Allow returns true if a prefetch request is allowed. Returns false if rate limit is exceeded (request should be dropped).
type PrefetchSemaphore ¶
type PrefetchSemaphore struct {
// contains filtered or unexported fields
}
PrefetchSemaphore limits concurrent prefetch operations. Used both per-session and globally.
func GlobalPrefetchSemaphore ¶
func GlobalPrefetchSemaphore() *PrefetchSemaphore
GlobalPrefetchSemaphore returns the global prefetch semaphore. Used by sessions to check global concurrency limits.
func NewPrefetchSemaphore ¶
func NewPrefetchSemaphore(limit int) *PrefetchSemaphore
NewPrefetchSemaphore creates a new semaphore with the given limit.
func (*PrefetchSemaphore) Acquire ¶
func (s *PrefetchSemaphore) Acquire() bool
Acquire tries to acquire a slot. Returns true if successful. If the semaphore is full, returns false immediately (non-blocking).
type PrimitiveInfo ¶
type PrimitiveInfo struct {
StableID string
DebugName string
AnchorKey string
Kind string
Class string
Persisted bool
InstancePath string
Primitive any
}
PrimitiveInfo contains inspection data for a registered primitive.
type ProtocolError ¶
ProtocolError represents an error in the binary protocol.
func NewProtocolError ¶
func NewProtocolError(sessionID, op, message string) *ProtocolError
NewProtocolError creates a new ProtocolError.
func (*ProtocolError) Error ¶
func (e *ProtocolError) Error() string
Error returns the error message.
type ReconnectConfig ¶
type ReconnectConfig struct {
// ToastOnReconnect shows a toast notification when connection is restored.
// Default: false.
ToastOnReconnect bool
// ToastMessage is the message shown in the reconnection toast.
// Default: "Connection restored".
ToastMessage string
// MaxRetries is the maximum number of reconnection attempts before giving up.
// Default: 10.
MaxRetries int
// BaseDelay is the initial delay between reconnection attempts (milliseconds).
// Uses exponential backoff: delay = min(baseDelay * 2^attempt, maxDelay).
// Default: 1000 (1 second).
BaseDelay int
// MaxDelay is the maximum delay between reconnection attempts (milliseconds).
// Default: 30000 (30 seconds).
MaxDelay int
}
ReconnectConfig configures client-side reconnection behavior. These values are sent to the client during handshake and control how the thin client handles connection interruptions.
func DefaultReconnectConfig ¶
func DefaultReconnectConfig() *ReconnectConfig
DefaultReconnectConfig returns a ReconnectConfig with sensible defaults.
type RenderMode ¶
type RenderMode int
RenderMode indicates the rendering context for a request. This is used by the prefetch system to enforce read-only behavior.
const ( // ModeNormal is the default rendering mode for regular requests. // All operations are allowed. ModeNormal RenderMode = iota // ModePrefetch is used when prefetching a route for caching. // In this mode, side effects are forbidden: // - Signal.Set() panics in dev / drops in prod // - Effect/Interval/Timeout panic in dev / no-op in prod // - SetUser() panics // - Navigate() is ignored // // Per Routing Spec Section 8.3.1, prefetch uses "bounded I/O": // synchronous work is allowed, but no async work may outlive the prefetch. ModePrefetch )
func (RenderMode) String ¶
func (m RenderMode) String() string
String returns a human-readable name for the render mode.
type ResizeEvent ¶
ResizeEvent represents a resize event with dimensions.
type RouteMatch ¶
type RouteMatch interface {
// GetParams returns the extracted route parameters.
GetParams() map[string]string
// GetPageHandler returns the page handler, if any.
GetPageHandler() PageHandler
// GetLayoutHandlers returns the layout handlers in order (root to leaf).
GetLayoutHandlers() []LayoutHandler
// GetMiddleware returns the middleware chain.
GetMiddleware() []RouteMiddleware
}
RouteMatch contains the result of matching a path against the router. This interface is implemented by router.MatchResult.
type RouteMiddleware ¶
RouteMiddleware processes requests before they reach the handler. This is different from HTTP middleware - it operates on the routing level.
type RouteNavigator ¶
type RouteNavigator struct {
// contains filtered or unexported fields
}
RouteNavigator handles route-based navigation for sessions. It is responsible for matching routes, invoking page handlers, and managing the page component lifecycle during navigation.
func NewRouteNavigator ¶
func NewRouteNavigator(session *Session, r Router) *RouteNavigator
NewRouteNavigator creates a new route navigator for a session.
func (*RouteNavigator) CurrentParams ¶
func (rn *RouteNavigator) CurrentParams() map[string]string
CurrentParams returns the current route parameters.
func (*RouteNavigator) CurrentPath ¶
func (rn *RouteNavigator) CurrentPath() string
CurrentPath returns the current route path.
func (*RouteNavigator) Navigate ¶
func (rn *RouteNavigator) Navigate(path string, replace bool) *NavigateResult
Navigate handles navigation to a new path. This is called when:
- ctx.Navigate() is called and pending navigation is processed
- EventNavigate is received from the client
The navigation process:
- Canonicalize the path
- Determine if NAV_PUSH or NAV_REPLACE should be used
- Match the route
- Create the new page component
- Mount and render the new page
- Diff against the old tree
- Return patches
Per Section 4.4 (Programmatic Navigation), this is ONE transaction - NAV_* patch and DOM patches are returned together.
type Router ¶
type Router interface {
// Match finds the handler for a path.
// Returns the match result and whether a match was found.
Match(method, path string) (RouteMatch, bool)
// NotFound returns the 404 handler, if configured.
NotFound() PageHandler
}
Router defines the interface for route matching. This interface is implemented by router.Router.
type SchemaError ¶
type SchemaError struct {
Reason string
Message string
Reasons []string
IsCold bool
SchemaHash string
SchemaVersion uint16
}
SchemaError represents a schema-related resume failure.
func (*SchemaError) Error ¶
func (e *SchemaError) Error() string
type ScrollEvent ¶
ScrollEvent represents a scroll event with position.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is the main HTTP/WebSocket server for Vango.
func MustNew ¶
func MustNew(config *ServerConfig) *Server
MustNew creates a new Server and panics if the configuration is invalid.
func New ¶
func New(config *ServerConfig) (*Server, error)
New creates a new Server with the given configuration. Returns an error if the configuration is invalid for non-DevMode environments.
func (*Server) CSRFMiddleware ¶
func (s *Server) CSRFMiddleware() Middleware
CSRFMiddleware enforces CSRF for state-changing HTTP requests.
func (*Server) Config ¶
func (s *Server) Config() *ServerConfig
Config returns the server configuration.
func (*Server) CookiePolicy ¶
func (s *Server) CookiePolicy() *CookiePolicy
CookiePolicy returns the server cookie policy helper.
func (*Server) EnsureCSRFCookie ¶
EnsureCSRFCookie ensures a valid CSRF cookie exists for the request. Returns the token value when present or generated.
func (*Server) GenerateCSRFToken ¶
GenerateCSRFToken generates a new cryptographically secure CSRF token. If CSRFSecret is set, the token is HMAC-signed for additional security. This should be: 1. Set as a cookie with path=/, SameSite from config, Secure when required 2. Embedded in the initial HTML page for the client to send in handshake
func (*Server) HandleWebSocket ¶
func (s *Server) HandleWebSocket(w http.ResponseWriter, r *http.Request)
HandleWebSocket handles WebSocket upgrade and connection.
func (*Server) Handler ¶
Handler returns an http.Handler for mounting in external routers. This is the primary integration point for ecosystem compatibility with Chi, Gorilla, Echo, stdlib mux, etc.
The handler dispatches based on path:
- /_vango/ws, /_vango/live → WebSocket upgrade
- /_vango/* → Internal routes (future: CSRF, assets)
- /* → Page routes via SSR/handler
Example:
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Use(authMiddleware)
r.Handle("/*", app.Handler())
http.ListenAndServe(":3000", r)
func (*Server) Metrics ¶
func (s *Server) Metrics() *ServerMetrics
Metrics collects and returns server metrics.
func (*Server) PageHandler ¶
PageHandler returns an http.Handler for page routes only. Use when you want to handle /_vango/* routes separately.
func (*Server) RunContext ¶
RunContext starts the server and blocks until:
- the HTTP server returns an error (including unexpected exit), or
- ctx is canceled (graceful shutdown).
When ctx is canceled, RunContext attempts a graceful shutdown and returns nil unless shutdown fails.
func (*Server) ServeHTTP ¶
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP implements http.Handler.
func (*Server) Sessions ¶
func (s *Server) Sessions() *SessionManager
Sessions returns the session manager.
func (*Server) SetAuthFunc ¶
SetAuthFunc sets the authentication function for validating requests.
The function is called during:
- SSR rendering to populate user context
- WebSocket session resume to revalidate authentication
For session resume, the return values are interpreted as:
- (user, nil): Auth valid, user is set on session
- (nil, nil): Auth invalid/absent, resume rejected if session was previously authenticated
- (nil, err): Auth failed, resume rejected if session was previously authenticated
IMPORTANT: Returning (nil, nil) for a previously authenticated session will reject the resume. This is the expected behavior for sessions where auth is no longer valid (e.g., cookie expired, user logged out elsewhere).
func (*Server) SetCSRFCookie ¶
SetCSRFCookie sets the CSRF cookie on the response. Call this when rendering the initial page. Uses request TLS or trusted proxy headers to decide the Secure flag. Returns ErrSecureCookiesRequired if secure cookies are enabled and the request is not secure.
func (*Server) SetHandler ¶
SetHandler sets the HTTP handler for non-WebSocket requests.
func (*Server) SetMetricsSink ¶
func (s *Server) SetMetricsSink(sink vango.MetricsSink)
SetMetricsSink updates the metrics sink used by the server and new sessions. Call this during setup before accepting connections.
func (*Server) SetRootComponent ¶
SetRootComponent sets the root component factory.
func (*Server) SetRouter ¶
SetRouter sets the router for route-based navigation. When set, this router is passed to all new sessions to enable:
- Client-side navigation via EventNavigate (link clicks, popstate)
- Programmatic navigation via ctx.Navigate()
The router must implement the Router interface (defined in navigation.go). Use router.NewRouterAdapter to wrap a router.Router for use with Server.
Example:
r := router.NewRouter()
r.AddPage("/", index.IndexPage)
r.AddPage("/about", about.AboutPage)
r.AddPage("/projects/:id", projects.ShowPage)
r.SetNotFound(notfound.NotFoundPage)
app.SetRouter(router.NewRouterAdapter(r))
func (*Server) UploadHandler ¶
UploadHandler returns an upload handler wrapped with CSRF middleware. Use this when you need fine-grained control over routing, or when mounting upload handlers on a custom mux.
The handler is automatically wrapped with CSRF middleware when CSRF is enabled. Clients must include the CSRF token in the X-CSRF-Token header.
Example:
mux.Handle("POST /api/upload", srv.UploadHandler(store, nil))
With custom config:
mux.Handle("POST /api/upload", srv.UploadHandler(store, &upload.Config{
MaxFileSize: 5 * 1024 * 1024,
AllowedTypes: []string{"image/png", "image/jpeg"},
}))
For most applications, prefer using app.HandleUpload() instead, which handles routing automatically.
func (*Server) ValidateCSRFRequest ¶
ValidateCSRFRequest validates CSRF for an HTTP request. It enforces CSRF for state-changing methods (POST, PUT, PATCH, DELETE).
func (*Server) WebSocketHandler ¶
WebSocketHandler returns an http.Handler for WebSocket upgrade only. Use when you want fine-grained control over routing.
func (*Server) WebSocketURL ¶
WebSocketURL returns the WebSocket URL for this server and request. It respects TLS and trusted proxy headers when determining scheme.
SECURITY: This function validates the Host header against AllowedHosts to prevent Host header poisoning attacks. If the host is not in the allowlist, it returns an empty string (fail secure).
If CanonicalHost is configured and the request host is valid, the canonical host is used instead of the request host.
type ServerConfig ¶
type ServerConfig struct {
// Address is the address to listen on (e.g., ":8080" or "localhost:3000").
// Default: ":8080".
Address string
// ReadBufferSize is the WebSocket read buffer size.
// Default: 4096.
ReadBufferSize int
// WriteBufferSize is the WebSocket write buffer size.
// Default: 4096.
WriteBufferSize int
// CheckOrigin is called to validate the request origin.
// Default: same-origin validation.
// For allowlist policies, configure via WithAllowedOrigins or AllowedOriginsCheck.
CheckOrigin func(r *http.Request) bool
// OriginPolicy describes how WebSocket origin checks are configured.
// Used for validation and guardrails in production.
OriginPolicy OriginPolicyMode
// AllowedOrigins lists domains allowed for WebSocket connections.
// Entries must be valid origins (scheme + host + optional port) with no path,
// query, or fragment. Origin matching is normalized by scheme + lowercase host
// + effective port (default ports may be omitted or explicit).
// Used for validation when OriginPolicy is OriginPolicyAllowedOrigins.
AllowedOrigins []string
// AllowSameOrigin enables same-origin validation.
// Used for validation when OriginPolicy is OriginPolicySameOrigin.
AllowSameOrigin bool
// AllowCustomOriginPolicy explicitly permits custom origin checks in production.
AllowCustomOriginPolicy bool
// SessionConfig is the configuration for individual sessions.
// Default: DefaultSessionConfig().
SessionConfig *SessionConfig
// SessionLimits overrides the default session limits when non-nil.
// Values in this struct are authoritative, including zero values
// (for example, MaxSessions=0 means unlimited).
SessionLimits *SessionLimits
// ShutdownTimeout is the maximum time to wait for graceful shutdown.
// Default: 30 seconds.
ShutdownTimeout time.Duration
// ReadHeaderTimeout is the maximum time to read request headers.
// Default: 5 seconds.
ReadHeaderTimeout time.Duration
// ReadTimeout is the maximum time to read the entire request.
// Default: 30 seconds.
ReadTimeout time.Duration
// WriteTimeout is the maximum time to write the response.
// Default: 30 seconds.
WriteTimeout time.Duration
// IdleTimeout is the maximum time to wait for the next request
// when keep-alives are enabled.
// Default: 60 seconds.
IdleTimeout time.Duration
// MaxSessions is a legacy convenience override for the global session cap.
// Used only when SessionLimits is nil.
// Values > 0 override DefaultSessionLimits().MaxSessions.
// Values <= 0 leave the default effective cap unchanged.
// Default: 0 (no legacy override).
MaxSessions int
// MaxMemoryPerSession is the approximate memory limit per session.
// Sessions exceeding this may be evicted under memory pressure.
// Default: 3MB.
MaxMemoryPerSession int64
// MaxConcurrentHandshakes is the maximum number of concurrent WebSocket
// connections waiting to complete handshake/auth/session admission.
// Helps bound pre-auth resource usage (FDs/goroutines/memory).
// 0 means no limit.
// Default: 1024.
MaxConcurrentHandshakes int
// MaxConcurrentHandshakesPerIP is the maximum number of concurrent
// pre-auth handshakes from a single client IP.
// Helps prevent per-IP pre-auth socket exhaustion.
// 0 means no limit.
// Default: 64.
MaxConcurrentHandshakesPerIP int
// CSRFSecret is the secret key for CSRF token generation.
// If nil and CSRF is enabled in non-DevMode, a random secret is generated
// at startup (tokens won't survive server restarts).
// To disable CSRF protection entirely, set CSRFEnabled = false explicitly.
// For production, prefer a stable 32+ byte secret.
CSRFSecret []byte
// CSRFEnabled enables CSRF protection for state-changing HTTP endpoints.
// Default: true.
CSRFEnabled bool
// PreUpgradeCheck runs before upgrading HTTP to WebSocket.
//
// Use this as a lightweight pre-upgrade gate (for example:
// token/header/query/subprotocol checks) to reject unauthenticated
// or malformed upgrade attempts before a WebSocket socket is allocated.
//
// Return nil to allow the upgrade. Return an error to reject with HTTP 403.
// If the returned error implements `StatusCode() int`, that status code is used
// when it is in the 4xx/5xx range.
// Default: nil (disabled).
PreUpgradeCheck func(r *http.Request) error
// AllowedRedirectHosts lists hostnames (and optional ports) allowed for external redirects.
// When empty, external redirects are rejected.
// Example: []string{"accounts.example.com", "auth.example.com:8443"}
AllowedRedirectHosts []string
// AllowedHosts lists hostnames (and optional ports) that are valid for this server.
// Used to validate the Host header in WebSocketURL() to prevent Host header poisoning.
// If empty, WebSocketURL() returns an empty string (fail secure).
// Example: []string{"myapp.com", "www.myapp.com", "myapp.com:8080"}
AllowedHosts []string
// CanonicalHost is the preferred hostname to use when generating URLs.
// If set and the request Host matches any AllowedHosts entry, this value
// is used instead. Useful for normalizing "www" vs non-"www" hosts.
// If empty, the validated request host is used as-is.
CanonicalHost string
// CleanupInterval is the interval for the session cleanup loop.
// Default: 30 seconds.
CleanupInterval time.Duration
// OnSessionStart is called during WebSocket upgrade, BEFORE the handshake completes.
// Use this to copy data from the HTTP context (e.g., authenticated user) to the Vango session.
// This runs SYNCHRONOUSLY before the WebSocket upgrade completes, while r.Context() is still alive.
// After this callback returns, the HTTP context is dead and cannot be accessed.
//
// Example:
// OnSessionStart: func(httpCtx context.Context, session *Session) {
// if user := myauth.UserFromContext(httpCtx); user != nil {
// auth.Set(session, user) // Use auth.Set to set presence flag
// }
// }
OnSessionStart func(httpCtx context.Context, session *Session)
// OnSessionResume is called during WebSocket session resume, BEFORE the handshake completes.
// Use this to rehydrate session data from the HTTP context (e.g., re-validate and restore user).
//
// Unlike OnSessionStart, this is called when resuming an existing session after disconnect.
// The session's signal state is preserved, but auth data should be revalidated from cookies/headers.
//
// Return nil to allow the resume, or an error to reject it.
// If an error is returned and the session was previously authenticated (auth.WasAuthenticated),
// the resume is rejected with HandshakeNotAuthorized. If the session was not authenticated,
// an error is logged but the resume continues as a guest session.
//
// IMPORTANT: Simply returning nil is NOT sufficient for security. The hook MUST
// actually revalidate authentication and call auth.Set() to rehydrate the user.
// In strict mode without authFunc, resume clears runtime auth projection before
// this hook runs, so a no-op OnSessionResume is rejected for previously
// authenticated sessions.
//
// Example:
// OnSessionResume: func(httpCtx context.Context, session *Session) error {
// user, err := myauth.ValidateFromContext(httpCtx)
// if err != nil {
// return err // Reject resume if previously authenticated
// }
// if user != nil {
// auth.Set(session, user) // REQUIRED: rehydrate user
// }
// return nil
// }
OnSessionResume func(httpCtx context.Context, session *Session) error
// AuthResumePolicy controls how authenticated session resumes are validated.
//
// Default: AuthResumePolicyStrict (requires authFunc or OnSessionResume for
// authenticated sessions to resume).
//
// SECURITY: The strict default ensures that leaked session IDs cannot be used
// to hijack authenticated sessions. If a session ID leaks via XSS, logging,
// or other vectors, the attacker cannot resume the session without also
// presenting valid authentication credentials.
//
// Set to AuthResumePolicyTrustSessionID only if you understand the security
// implications and have compensating controls in place.
AuthResumePolicy AuthResumePolicy
// AllowTrustSessionIDInProduction acknowledges the security tradeoff of
// AuthResumePolicyTrustSessionID in non-DevMode.
//
// When false (default), non-Dev configs that set TrustSessionID are rejected.
// TrustSessionID also requires ResumeWindow <= 30s in non-DevMode.
AllowTrustSessionIDInProduction bool
// TrustedProxies lists trusted reverse proxy IPs for X-Forwarded-* headers.
// If set, the server will trust forwarded headers (including X-Forwarded-Proto)
// from these IPs when determining request security.
// Default: nil (don't trust proxy headers).
TrustedProxies []string
// DebugMode enables extra validation and logging for development.
// When true:
// - Session.Set() panics on unserializable types (func, chan)
// - auth.Get() logs warnings on type mismatches
// Default: false.
DebugMode bool
// DevMode disables security checks for local development.
// SECURITY: NEVER use in production - this disables:
// - Origin checking (allows all origins)
// - CSRF validation
// - Secure cookie requirements
// Default: false (secure by default)
DevMode bool
// SecureCookies enforces Secure flag on cookies set by the server.
// Should be true when using HTTPS.
// Default: true
SecureCookies bool
// CookieHTTPOnly sets the HttpOnly flag on cookies set by the server.
// Prevents JavaScript access to cookies that should not be read by JS.
// Default: true (except for cookies that explicitly opt out like CSRF).
CookieHTTPOnly bool
// SameSiteMode sets the SameSite attribute for cookies.
// Lax is safe for most use cases and allows OAuth flows.
// Default: http.SameSiteLaxMode
SameSiteMode http.SameSite
// CookieDomain sets the Domain attribute for cookies set by the server.
// Empty string uses the current domain (most secure).
// Default: "" (current domain)
CookieDomain string
// SessionStore is the persistence backend for sessions.
// If nil, sessions are only stored in memory (lost on restart).
// Use session.NewMemoryStore(), session.NewRedisStore(), or session.NewSQLStore().
// Default: nil (in-memory only).
SessionStore session.SessionStore
// GlobalSignalPubSub is the broadcast backend for global signals.
// When nil, global signals update via eventual consistency only.
GlobalSignalPubSub corevango.GlobalBroadcastBackend
// GlobalSignalRefreshInterval controls how often global signals refresh
// from persistence when no broadcast backend is configured.
// Default: 30 seconds (when GlobalSignalPubSub is nil).
GlobalSignalRefreshInterval time.Duration
// ResumeWindow is how long a detached session remains resumable after disconnect.
// After this window, the session is permanently expired.
// 0 disables resumption entirely.
// Default: 5 minutes.
ResumeWindow time.Duration
// MaxDetachedSessions is the maximum number of disconnected sessions to keep in memory.
// When exceeded, the least recently used sessions are evicted (and persisted if store is configured).
// Default: 10000.
MaxDetachedSessions int
// MaxSessionsPerIP is the maximum number of concurrent sessions from a single IP address.
// Helps prevent DoS attacks from IP exhaustion.
// 0 means no limit.
// Default: 100.
MaxSessionsPerIP int
// EvictionPolicy determines how detached sessions are evicted when limits are exceeded.
// Default: EvictionLRU.
EvictionPolicy SessionEvictionPolicy
// EvictOnIPLimit controls whether hitting MaxSessionsPerIP evicts the oldest
// detached session for that IP instead of rejecting the new session.
// Default: true when MaxSessionsPerIP > 0.
EvictOnIPLimit bool
// PersistInterval is how often to persist dirty sessions to the store.
// Set to 0 to only persist on disconnect.
// Default: 30 seconds.
PersistInterval time.Duration
// ReconnectConfig configures client-side reconnection behavior.
// Default: ReconnectConfig with sensible defaults.
ReconnectConfig *ReconnectConfig
// AssetResolver is the resolver for fingerprinted asset paths.
// When set, ctx.Asset() will use this resolver to map source paths
// to their fingerprinted versions (e.g., "vango.js" → "vango.a1b2c3d4.min.js").
//
// If nil, ctx.Asset() returns paths unchanged.
//
// Example:
//
// manifest, _ := vango.LoadAssetManifest("dist/manifest.json")
// config.AssetResolver = vango.NewAssetResolver(manifest, "/public/")
//
// For development (no fingerprinting):
//
// config.AssetResolver = vango.NewPassthroughResolver("/public/")
//
// Default: nil (passthrough behavior).
AssetResolver assets.Resolver
// contains filtered or unexported fields
}
ServerConfig holds configuration for the HTTP/WebSocket server.
func DefaultServerConfig ¶
func DefaultServerConfig() *ServerConfig
DefaultServerConfig returns a ServerConfig with sensible defaults. SECURITY: All security features are ENABLED by default. SECURITY: CheckOrigin enforces same-origin by default to prevent CSWSH. SECURITY: CSRFSecret is nil by default; server.New generates an ephemeral secret in non-DevMode and logs a warning. SECURITY: SecureCookies is true by default for HTTPS environments.
func (*ServerConfig) Clone ¶
func (c *ServerConfig) Clone() *ServerConfig
Clone returns a copy of the ServerConfig.
func (*ServerConfig) EnableToastOnReconnect ¶
func (c *ServerConfig) EnableToastOnReconnect(message string) *ServerConfig
EnableToastOnReconnect enables toast notifications on reconnect. Shorthand for modifying ReconnectConfig.
func (*ServerConfig) GetConfigWarnings ¶
func (c *ServerConfig) GetConfigWarnings() []string
GetConfigWarnings returns a list of configuration warnings. Useful for displaying warnings in a custom way.
func (*ServerConfig) IsSecure ¶
func (c *ServerConfig) IsSecure() bool
IsSecure returns true if the configuration has all security features enabled. Useful for startup checks.
func (*ServerConfig) ValidateConfig ¶
func (c *ServerConfig) ValidateConfig() error
ValidateConfig validates the server configuration. Called automatically by Server.New() and Server.Run(). Returns any fatal configuration errors.
func (*ServerConfig) WithAddress ¶
func (c *ServerConfig) WithAddress(addr string) *ServerConfig
WithAddress sets the server address and returns the config for chaining.
func (*ServerConfig) WithAllowedHosts ¶
func (c *ServerConfig) WithAllowedHosts(hosts ...string) *ServerConfig
WithAllowedHosts sets the allowed hostnames for WebSocketURL validation. This prevents Host header poisoning attacks.
func (*ServerConfig) WithAllowedOrigins ¶
func (c *ServerConfig) WithAllowedOrigins(origins ...string) *ServerConfig
WithAllowedOrigins sets the allowed WebSocket origins and configures the origin policy. This enforces an allowlist-based CheckOrigin in production.
func (*ServerConfig) WithCSRFSecret ¶
func (c *ServerConfig) WithCSRFSecret(secret []byte) *ServerConfig
WithCSRFSecret sets the CSRF secret and returns the config for chaining.
func (*ServerConfig) WithCanonicalHost ¶
func (c *ServerConfig) WithCanonicalHost(host string) *ServerConfig
WithCanonicalHost sets the canonical hostname for URL generation.
func (*ServerConfig) WithCookieDomain ¶
func (c *ServerConfig) WithCookieDomain(domain string) *ServerConfig
WithCookieDomain sets the domain for session cookies.
func (*ServerConfig) WithCookieHTTPOnly ¶
func (c *ServerConfig) WithCookieHTTPOnly(httpOnly bool) *ServerConfig
WithCookieHTTPOnly sets whether cookies should be HttpOnly by default.
func (*ServerConfig) WithDevMode ¶
func (c *ServerConfig) WithDevMode() *ServerConfig
WithDevMode enables development mode which disables security checks. SECURITY WARNING: NEVER use in production. This disables:
- Origin checking (allows all origins)
- CSRF validation (disabled)
- Secure cookie requirements (disabled)
Only use for local development:
config := server.DefaultServerConfig().WithDevMode()
func (*ServerConfig) WithEvictOnIPLimit ¶
func (c *ServerConfig) WithEvictOnIPLimit(evict bool) *ServerConfig
WithEvictOnIPLimit sets whether to evict the oldest detached session on IP limit.
func (*ServerConfig) WithEvictionPolicy ¶
func (c *ServerConfig) WithEvictionPolicy(policy SessionEvictionPolicy) *ServerConfig
WithEvictionPolicy sets the detached session eviction policy.
func (*ServerConfig) WithMaxConcurrentHandshakes ¶
func (c *ServerConfig) WithMaxConcurrentHandshakes(max int) *ServerConfig
WithMaxConcurrentHandshakes sets the global pre-auth concurrent handshake cap.
func (*ServerConfig) WithMaxConcurrentHandshakesPerIP ¶
func (c *ServerConfig) WithMaxConcurrentHandshakesPerIP(max int) *ServerConfig
WithMaxConcurrentHandshakesPerIP sets the per-IP pre-auth concurrent handshake cap.
func (*ServerConfig) WithMaxDetachedSessions ¶
func (c *ServerConfig) WithMaxDetachedSessions(max int) *ServerConfig
WithMaxDetachedSessions sets the maximum detached sessions and returns the config for chaining.
func (*ServerConfig) WithMaxSessions ¶
func (c *ServerConfig) WithMaxSessions(max int) *ServerConfig
WithMaxSessions sets the maximum sessions and returns the config for chaining.
func (*ServerConfig) WithMaxSessionsPerIP ¶
func (c *ServerConfig) WithMaxSessionsPerIP(max int) *ServerConfig
WithMaxSessionsPerIP sets the per-IP session limit and returns the config for chaining.
func (*ServerConfig) WithPersistInterval ¶
func (c *ServerConfig) WithPersistInterval(d time.Duration) *ServerConfig
WithPersistInterval sets the persist interval and returns the config for chaining.
func (*ServerConfig) WithPreUpgradeCheck ¶
func (c *ServerConfig) WithPreUpgradeCheck(check func(r *http.Request) error) *ServerConfig
WithPreUpgradeCheck sets the pre-upgrade check callback.
func (*ServerConfig) WithReconnectConfig ¶
func (c *ServerConfig) WithReconnectConfig(rc *ReconnectConfig) *ServerConfig
WithReconnectConfig sets the reconnect configuration and returns the config for chaining.
func (*ServerConfig) WithResumeWindow ¶
func (c *ServerConfig) WithResumeWindow(d time.Duration) *ServerConfig
WithResumeWindow sets the resume window duration and returns the config for chaining.
func (*ServerConfig) WithSameSiteMode ¶
func (c *ServerConfig) WithSameSiteMode(mode http.SameSite) *ServerConfig
WithSameSiteMode sets the SameSite attribute for cookies.
func (*ServerConfig) WithSecureCookies ¶
func (c *ServerConfig) WithSecureCookies(secure bool) *ServerConfig
WithSecureCookies sets whether cookies should have the Secure flag.
func (*ServerConfig) WithSessionConfig ¶
func (c *ServerConfig) WithSessionConfig(sc *SessionConfig) *ServerConfig
WithSessionConfig sets the session configuration and returns the config for chaining.
func (*ServerConfig) WithSessionStore ¶
func (c *ServerConfig) WithSessionStore(store session.SessionStore) *ServerConfig
WithSessionStore sets the session persistence backend and returns the config for chaining.
type ServerMetrics ¶
type ServerMetrics struct {
// Sessions
ActiveSessions int64
TotalSessions int64
SessionCreates int64
SessionCloses int64
PeakSessions int64
// Events
EventsReceived int64
EventsProcessed int64
EventsDropped int64
// Patches
PatchesSent int64
PatchBytes int64
// Network
BytesSent int64
BytesReceived int64
// Errors
HandlerPanics int64
WriteErrors int64
ReadErrors int64
// Signal write enforcement
SignalWriteViolationTotal int64
// Latency (microseconds)
EventLatencyP50 int64
EventLatencyP99 int64
// Memory
TotalMemory int64
// Persistence
PersistWriteRejectedTotal int64
PersistBytesTotal int64
SchemaMismatchTotal int64
// Timestamp
CollectedAt time.Time
}
ServerMetrics aggregates metrics across the server.
type Session ¶
type Session struct {
// Identity
ID string
UserID string
CreatedAt time.Time
LastActive time.Time // Guarded by metaMu after initialization.
// Phase 12: Session persistence fields
IP string // Client IP address for per-IP limits
CurrentRoute string // Current page route for restoration. Guarded by metaMu after initialization.
DetachedAt time.Time // Time when the session detached (per-IP eviction ordering). Guarded by metaMu after initialization.
// contains filtered or unexported fields
}
Session represents a single WebSocket connection and its state. Each session has its own component tree, reactive ownership, and handler registry.
func NewMockSession ¶
func NewMockSession() *Session
NewMockSession creates a session without a WebSocket connection for testing. The session has all fields initialized except conn.
func (*Session) BroadcastAuth ¶
func (s *Session) BroadcastAuth(channel, typ string, reason AuthExpiredReason)
BroadcastAuth sends an auth broadcast control message to the client. This should be called on the session loop.
func (*Session) BudgetTracker ¶
func (s *Session) BudgetTracker() *vango.BudgetTracker
BudgetTracker returns the state budget tracker for this session.
func (*Session) BytesReceived ¶
BytesReceived adds to the bytes received counter.
func (*Session) Config ¶
func (s *Session) Config() *SessionConfig
Config returns the session configuration.
func (*Session) Conn ¶
Conn returns the underlying WebSocket connection. Use with caution - prefer session methods when possible.
func (*Session) Deserialize ¶
Deserialize restores session state from bytes. Phase 7: Validates schema compatibility before restoring.
func (*Session) DeserializeForSessionID ¶
DeserializeForSessionID restores session state and enforces that the persisted session payload identity matches the requested persistence key.
func (*Session) Dispatch ¶
func (s *Session) Dispatch(fn func())
Dispatch queues a function to run on the session's event loop. This is safe to call from any goroutine and is the correct way to update signals from asynchronous operations (database calls, timers, etc.).
The function will be executed synchronously on the event loop, ensuring signal writes are properly serialized. After the function completes, pending effects will run and dirty components will re-render.
Example:
go func() {
user, err := db.Users.FindByID(ctx.StdContext(), id)
ctx.Dispatch(func() {
if err != nil {
errorSignal.Set(err)
} else {
userSignal.Set(user)
}
})
}()
func (*Session) Done ¶
func (s *Session) Done() <-chan struct{}
Done returns a channel that's closed when the session is done.
func (*Session) EventLoop ¶
func (s *Session) EventLoop()
EventLoop processes queued events, dispatch callbacks, and render signals. It runs handlers, schedules effects, and triggers re-renders.
func (*Session) Get ¶
Get retrieves a value from session data. Returns nil if key doesn't exist. This is thread-safe and can be called from any goroutine.
func (*Session) GetAllData ¶
GetAllData returns a copy of all session data. Persistence helpers enforce which keys are durable. Returns nil if no data has been set.
func (*Session) GetInt ¶
GetInt is a convenience method that returns value as int. Returns 0 if key doesn't exist or value is not numeric. Handles int, int64, and float64 conversions.
func (*Session) GetString ¶
GetString is a convenience method that returns value as string. Returns empty string if key doesn't exist or value is not a string.
func (*Session) HandleNavigate ¶
HandleNavigate processes a navigation request and returns patches. This is called from handleEvent when an EventNavigate is received, or from flush() when ctx.Navigate() was called during event handling.
Per Section 4.4 (Programmatic Navigation), navigation is ONE transaction: NAV_* patch + DOM patches are sent together in a single frame.
Parameters:
- path: The target path (may include query string)
- replace: If true, use NAV_REPLACE instead of NAV_PUSH
Returns:
- error if navigation failed (no route matched and no NotFound handler)
func (*Session) IsDetached ¶
IsDetached reports whether the session currently has no active WebSocket connection but is still kept in memory for ResumeWindow.
func (*Session) MemoryUsage ¶
MemoryUsage estimates the memory used by this session.
func (*Session) Navigator ¶
func (s *Session) Navigator() *RouteNavigator
Navigator returns the route navigator for this session. Returns nil if no router has been set.
func (*Session) NeedsRestart ¶
NeedsRestart returns true if session goroutines need to be restarted. This should be checked after Resume() to decide whether to call Start(). If the session's done channel was closed, goroutines have exited and need to be restarted for the session to function.
func (*Session) PersistErrorHandler ¶
func (s *Session) PersistErrorHandler() func(*vango.PersistBudgetError)
PersistErrorHandler returns the current persist error handler.
func (*Session) PrefetchCache ¶
func (s *Session) PrefetchCache() *PrefetchCache
PrefetchCache returns the prefetch cache for this session. Returns nil if prefetch is not initialized.
func (*Session) PrimitiveRegistry ¶
func (s *Session) PrimitiveRegistry() *SessionPrimitiveRegistry
PrimitiveRegistry returns the session's primitive registry.
func (*Session) QueueEvent ¶
QueueEvent queues an event for processing.
func (*Session) ReadLoop ¶
func (s *Session) ReadLoop()
ReadLoop continuously reads messages from the WebSocket connection. It decodes frames, processes control messages, and queues events. This method blocks until the connection is closed or an error occurs.
func (*Session) RebuildHandlers ¶
RebuildHandlers clears and rebuilds the handler map with fresh HIDs. This is called on session resume to match SSR-rendered DOM.
Unlike a full remount, this preserves:
- Owner (signals stay alive with their values)
- Component instances (state preserved)
It regenerates:
- HID assignments (reset to h1, h2... matching SSR)
- Handler map (rebuilt from fresh render)
- Component ownership map
func (*Session) RegisterIslandHandler ¶
func (s *Session) RegisterIslandHandler(id string, handler vango.IslandMessageHandler) func()
RegisterIslandHandler registers a handler for island messages within this session. Returns a cleanup function that unregisters the handler.
func (*Session) RegisterPrimitives ¶
func (s *Session) RegisterPrimitives(regs []vango.PrimitiveRegistration, component *ComponentInstance)
RegisterPrimitives adds primitives from a component's setup.
func (*Session) RegisterWasmHandler ¶
func (s *Session) RegisterWasmHandler(id string, handler vango.WasmMessageHandler) func()
RegisterWasmHandler registers a handler for WASM messages within this session. Returns a cleanup function that unregisters the handler.
func (*Session) RestoreData ¶
RestoreData restores session data from serialized values. This is used during session restoration after server restart or reconnection. Values are merged into existing data (doesn't clear existing keys).
func (*Session) Resume ¶
Resume resumes a session after reconnect. It swaps the WebSocket connection, resets sequence numbers, and reinitializes channels if the session was previously closed. Call NeedsRestart() after Resume() to check if Start() should be called to restart goroutines.
func (*Session) SendClose ¶
func (s *Session) SendClose(reason protocol.CloseReason, message string)
SendClose sends a close control message to the client.
func (*Session) SendHookRevert ¶
SendHookRevert requests the client to revert a hook's optimistic UI change. The client will invoke the revert callback registered for the given HID.
func (*Session) SendIslandMessage ¶
SendIslandMessage sends a message to the client-side island.
func (*Session) SendPatches ¶
SendPatches is a public wrapper for sendPatches.
func (*Session) SendResyncFull ¶
SendResyncFull sends the full HTML tree to the client. This is used as a fallback when HIDs may not align between SSR and remount. The client will replace its entire body content with this HTML.
func (*Session) SendSessionRefresh ¶
func (s *Session) SendSessionRefresh(cmd *protocol.SessionRefreshCommand)
SendSessionRefresh sends a session refresh control message to the client.
func (*Session) SendWasmMessage ¶
SendWasmMessage sends a message to the client-side WASM component.
func (*Session) Serialize ¶
Serialize converts the session state to bytes for persistence. Phase 7: Uses schema-gated, HMAC-signed blobs with stable IDs.
func (*Session) Set ¶
Set stores a value in session data. Value must be safe to access concurrently (immutable or properly synchronized).
Session.Set stores runtime-only KV by default. Durable session state should use vango.SessionKey[T], setup.SharedSignal, or setup.GlobalSignal.
In debug mode (SessionConfig.DebugMode = true), this panics on obviously unserializable types like func and chan.
func (*Session) SetAssetResolver ¶
SetAssetResolver sets the asset resolver for this session. This is called by the server when a session is created or resumed.
func (*Session) SetInitialURL ¶
SetInitialURL sets the initial URL parameters from the client handshake. This is called when processing the initial handshake message from the client. URLParam hydration reads this state snapshot during setup.
func (*Session) SetJSON ¶
SetJSON stores runtime-only KV with JSON-oriented intent. This is equivalent to Set(); it does not make the value durable.
func (*Session) SetPersistErrorHandler ¶
func (s *Session) SetPersistErrorHandler(fn func(*vango.PersistBudgetError))
SetPersistErrorHandler registers a persist error handler for this session.
func (*Session) SetRouter ¶
SetRouter sets the router for this session and creates a navigator. This enables route-based navigation via ctx.Navigate() and EventNavigate. The router must implement the Router interface (defined in navigation.go).
func (*Session) ShowPersistErrorBanner ¶
func (s *Session) ShowPersistErrorBanner(err *vango.PersistBudgetError)
ShowPersistErrorBanner triggers the default persist banner for this session.
func (*Session) Start ¶
func (s *Session) Start()
Start starts all session loops. This should be called after the handshake is complete.
func (*Session) StormBudget ¶
func (s *Session) StormBudget() vango.StormBudgetChecker
StormBudget returns the storm budget checker for this session. Returns nil if storm budgets are not configured.
func (*Session) TriggerSchemaRefresh ¶
func (s *Session) TriggerSchemaRefresh(reason protocol.SchemaRefreshReason, debug string)
TriggerSchemaRefresh invalidates the session and forces a client reload. This is used when the running binary detects persisted-state corruption or incompatibility after the session is already mounted (e.g., SharedSignal restore failure).
It is safe to call multiple times; only the first invocation sends the control message.
func (*Session) UnregisterPrimitives ¶
func (s *Session) UnregisterPrimitives(regs []vango.PrimitiveRegistration, component *ComponentInstance)
UnregisterPrimitives removes primitives for a component instance.
func (*Session) UpdateLastActive ¶
func (s *Session) UpdateLastActive()
UpdateLastActive updates the last activity timestamp.
type SessionConfig ¶
type SessionConfig struct {
// ReadTimeout is the maximum time to wait for a message from the client.
// Default: 60 seconds.
ReadTimeout time.Duration
// WriteTimeout is the maximum time to wait when sending a message.
// Default: 10 seconds.
WriteTimeout time.Duration
// IdleTimeout is the time after which an inactive session is closed.
// Default: 5 minutes.
IdleTimeout time.Duration
// HandshakeTimeout is the maximum time for the initial handshake.
// Default: 5 seconds.
HandshakeTimeout time.Duration
// HeartbeatInterval is the time between heartbeat pings.
// Default: 30 seconds.
HeartbeatInterval time.Duration
// MaxMessageSize is the maximum size of an incoming WebSocket message.
// Default: 64KB.
MaxMessageSize int64
// MaxHandshakePathBytes limits the size of the `?path=` query parameter
// used during the WebSocket handshake (decoded bytes, includes query string).
// 0 means no limit (not recommended).
// Default: 8KB.
MaxHandshakePathBytes int
// MaxPatchHistory is the number of recent patches to keep for resync.
// Default: 100.
MaxPatchHistory int
// MaxEventQueue is the maximum number of queued client events per session.
// When full, new events are dropped and QueueEvent returns ErrEventQueueFull.
// Default: 256.
MaxEventQueue int
// MaxDispatchQueue is the maximum number of queued dispatch callbacks per session.
//
// Dispatch callbacks are framework-critical (they apply Resource/Action results and
// other off-loop work back onto the session loop). They MUST NOT be silently dropped.
// If the dispatch queue is full and a callback cannot be enqueued, the session will
// self-heal (fatal error + reload).
//
// If zero, it defaults to MaxEventQueue for backwards compatibility.
// Default: 2048.
MaxDispatchQueue int
// MaxDetachedSessions limits the number of detached sessions retained in memory.
// 0 means unlimited, which is not recommended for production.
// Default: 10000.
MaxDetachedSessions int
// EvictionPolicy controls which detached session is evicted when MaxDetachedSessions is reached.
// Default: EvictionLRU.
EvictionPolicy SessionEvictionPolicy
// EnableCompression enables WebSocket compression.
// Default: true.
EnableCompression bool
// EnableOptimistic enables optimistic updates on the client.
// Default: true.
EnableOptimistic bool
// DebugMode enables extra validation and logging for development.
// When true:
// - Session.Set() panics on unserializable types (func, chan)
// - auth.Get() logs warnings on type mismatches
// Default: false.
DebugMode bool
// URLPolicy controls scheme allowlists for URL-bearing attributes.
// If zero-valued, defaults are used.
URLPolicy vdom.URLPolicy
// StormBudget configures rate limits for effect primitives to prevent
// amplification bugs (e.g., effect triggers resource refetch triggers effect).
// See SPEC_ADDENDUM.md §A.4.
StormBudget *StormBudgetConfig
// AuthCheck configures authentication freshness checks for the session.
// When nil, no passive or active auth checks are performed.
AuthCheck *AuthCheckConfig
// StateBudget configures persisted state size limits.
StateBudget *corevango.StateBudgetConfig
// Observability controls optional logging for mutations.
Observability corevango.ObservabilityConfig
// Metrics is the sink for runtime metric emissions.
// Nil disables metric emission (safe default).
Metrics corevango.MetricsSink
// EventMiddleware runs for every WebSocket event before handler execution.
// Use this for tracing, logging, or rate limiting (e.g., middleware.OpenTelemetry()).
EventMiddleware []RouteMiddleware
// PersistenceSecret signs persisted blobs.
// This includes session persistence and global-signal blobs when
// global persistence/pubsub paths are enabled.
// Must be 32+ bytes of cryptographically random data.
PersistenceSecret []byte
// PersistenceSecretPrevious allows secret rotation.
// If set, blobs signed with this secret are accepted on resume.
PersistenceSecretPrevious []byte
// MaxBlobAgeMs sets the maximum age in milliseconds for persisted session resume blobs.
// Blobs older than this are rejected during resume, triggering a fresh session.
// This applies to session resume only (not global signals).
// This helps prevent replay attacks with stale session data.
// 0 means no limit (default).
MaxBlobAgeMs int64
// HookEventValidator validates client hook event payloads.
// Return an error to reject the event.
// In production, startup warnings are emitted when this is unset.
HookEventValidator corevango.HookEventValidator
// DOMEventRateLimit applies per-session rate limiting to standard DOM events
// (click/input/scroll/etc., including navigation).
// RatePerSecond <= 0 disables rate limiting.
DOMEventRateLimit *corevango.EventRateLimitConfig
// HookEventRateLimit applies per-session rate limiting to hook events.
// RatePerSecond <= 0 disables rate limiting.
HookEventRateLimit *corevango.EventRateLimitConfig
// HookEventMaxPayloadBytes limits JSON size of hook event payloads.
// 0 means no limit.
HookEventMaxPayloadBytes int
// HookEventMaxNameBytes limits hook event name length in bytes.
// 0 means no limit.
HookEventMaxNameBytes int
// IslandMessageValidator validates island message payloads.
// Return an error to reject the message.
// In production, startup warnings are emitted when this is unset.
IslandMessageValidator corevango.IslandMessageValidator
// IslandEventRateLimit applies per-session rate limiting to island messages.
// RatePerSecond <= 0 disables rate limiting.
IslandEventRateLimit *corevango.EventRateLimitConfig
// IslandEventMaxPayloadBytes limits JSON size of island message payloads.
// 0 means no limit.
IslandEventMaxPayloadBytes int
// IslandEventMaxWrapperBytes limits JSON size of island wrapper payloads.
// Wrapper payloads are the custom event JSON object containing `id` + `payload`.
// 0 means no limit.
IslandEventMaxWrapperBytes int
// IslandEventMaxJSONDepth limits maximum JSON nesting depth for island
// wrapper and payload JSON structures.
// 0 means no limit.
IslandEventMaxJSONDepth int
// IslandEventMaxIDBytes limits island identifier length in bytes.
// 0 means no limit.
IslandEventMaxIDBytes int
// WasmMessageValidator validates WASM message payloads.
// Return an error to reject the message.
// In production, startup warnings are emitted when this is unset.
WasmMessageValidator corevango.WasmMessageValidator
// WasmEventRateLimit applies per-session rate limiting to WASM messages.
// RatePerSecond <= 0 disables rate limiting.
WasmEventRateLimit *corevango.EventRateLimitConfig
// WasmEventMaxPayloadBytes limits JSON size of WASM message payloads.
// 0 means no limit.
WasmEventMaxPayloadBytes int
// WasmEventMaxWrapperBytes limits JSON size of WASM wrapper payloads.
// Wrapper payloads are the custom event JSON object containing `id` + `payload`.
// 0 means no limit.
WasmEventMaxWrapperBytes int
// WasmEventMaxJSONDepth limits maximum JSON nesting depth for WASM
// wrapper and payload JSON structures.
// 0 means no limit.
WasmEventMaxJSONDepth int
// WasmEventMaxIDBytes limits WASM identifier length in bytes.
// 0 means no limit.
WasmEventMaxIDBytes int
// AllowRawHTMLInEventPayloads permits raw HTML or executable strings in
// hook/island/WASM payloads. Default: false.
// NOTE: Vango's unsafe-payload check is a heuristic substring filter,
// not a sanitizer. Always validate and/or sanitize untrusted payloads.
AllowRawHTMLInEventPayloads bool
// ResyncRateLimitConfig applies per-session rate limiting to resync requests.
// Resync requests can trigger expensive full HTML renders (SendResyncFull) when
// the patch history is exhausted, making them a DoS amplification vector.
//
// A malicious client can repeatedly send resync requests to:
// - Consume CPU (rendering full component tree to HTML)
// - Consume bandwidth (sending large HTML payloads)
// - Amplify per-session cost across many concurrent sessions
//
// Default: 2/sec with burst 5 (conservative, resync should be rare).
// RatePerSecond <= 0 disables rate limiting (NOT RECOMMENDED in production).
ResyncRateLimitConfig *corevango.EventRateLimitConfig
}
SessionConfig holds configuration for individual sessions.
func DefaultSessionConfig ¶
func DefaultSessionConfig() *SessionConfig
DefaultSessionConfig returns a SessionConfig with sensible defaults.
func (*SessionConfig) Clone ¶
func (c *SessionConfig) Clone() *SessionConfig
Clone returns a copy of the SessionConfig.
type SessionError ¶
type SessionError struct {
SessionID string
Op string // Operation that failed
Err error // Underlying error
}
SessionError wraps an error with session context for debugging.
func NewSessionError ¶
func NewSessionError(sessionID, op string, err error) *SessionError
NewSessionError creates a new SessionError.
func (*SessionError) Error ¶
func (e *SessionError) Error() string
Error returns the error message with session context.
func (*SessionError) Unwrap ¶
func (e *SessionError) Unwrap() error
Unwrap returns the underlying error for errors.Is/As.
type SessionEvictionPolicy ¶
type SessionEvictionPolicy int
SessionEvictionPolicy determines which detached sessions are evicted first.
const ( // EvictionLRU evicts the least recently accessed detached sessions first. EvictionLRU SessionEvictionPolicy = iota // EvictionOldest evicts the oldest sessions first (by creation time). EvictionOldest // EvictionRandom evicts sessions randomly (faster but less fair). EvictionRandom )
type SessionLimits ¶
type SessionLimits struct {
// MaxSessions is the maximum number of concurrent sessions.
MaxSessions int
// MaxMemoryPerSession is the approximate memory limit per session.
MaxMemoryPerSession int64
// MaxTotalMemory is the total memory budget for all sessions.
// If exceeded, least recently used sessions are evicted.
MaxTotalMemory int64
}
SessionLimits defines limits for session management.
func DefaultSessionLimits ¶
func DefaultSessionLimits() *SessionLimits
DefaultSessionLimits returns default session limits.
type SessionManager ¶
type SessionManager struct {
// contains filtered or unexported fields
}
SessionManager manages all active sessions. It handles session creation, lookup, cleanup, and lifecycle callbacks.
func NewSessionManager ¶
func NewSessionManager(config *SessionConfig, limits *SessionLimits, logger *slog.Logger) *SessionManager
NewSessionManager creates a new SessionManager with the given configuration.
func NewSessionManagerWithOptions ¶
func NewSessionManagerWithOptions(config *SessionConfig, limits *SessionLimits, logger *slog.Logger, opts *SessionManagerOptions) *SessionManager
NewSessionManagerWithOptions creates a SessionManager with Phase 12 persistence options.
func (*SessionManager) CheckIPLimit ¶
func (sm *SessionManager) CheckIPLimit(ip string) error
CheckIPLimit checks if the IP has exceeded the session limit. Returns ErrTooManySessionsFromIP if the limit is exceeded. This should be called before creating a new session.
func (*SessionManager) CheckMemoryPressure ¶
func (sm *SessionManager) CheckMemoryPressure()
CheckMemoryPressure checks if memory usage exceeds limits and evicts if needed.
func (*SessionManager) Close ¶
func (sm *SessionManager) Close(id string)
Close closes a session by ID and removes it from the manager.
func (*SessionManager) Count ¶
func (sm *SessionManager) Count() int
Count returns the number of active sessions.
func (*SessionManager) DispatchAll ¶
func (sm *SessionManager) DispatchAll(fn func(*Session))
DispatchAll dispatches a function to every active session.
func (*SessionManager) EvictLRU ¶
func (sm *SessionManager) EvictLRU(count int) int
EvictLRU evicts the least recently used sessions. This is called when memory pressure is high.
func (*SessionManager) ForEach ¶
func (sm *SessionManager) ForEach(fn func(*Session) bool)
ForEach iterates over all sessions. The callback should not perform long-running operations as it holds the read lock.
func (*SessionManager) Get ¶
func (sm *SessionManager) Get(id string) *Session
Get retrieves a session by ID.
func (*SessionManager) HasPersistence ¶
func (sm *SessionManager) HasPersistence() bool
HasPersistence returns true if session persistence is configured.
func (*SessionManager) MarkResumed ¶
func (sm *SessionManager) MarkResumed(sessionID string)
MarkResumed removes a session from detached tracking after a successful resume.
func (*SessionManager) OnSessionDisconnect ¶
func (sm *SessionManager) OnSessionDisconnect(sess *Session)
OnSessionDisconnect is called when a WebSocket connection closes. It persists the session state for potential reconnection.
func (*SessionManager) OnSessionReconnect ¶
func (sm *SessionManager) OnSessionReconnect(sessionID string) (*Session, error)
OnSessionReconnect attempts to restore a session after reconnection. Returns ErrSessionNotFound if the session cannot be resumed.
func (*SessionManager) PersistenceManager ¶
func (sm *SessionManager) PersistenceManager() *session.Manager
PersistenceManager returns the underlying persistence manager for advanced use. Returns nil if persistence is not configured.
func (*SessionManager) ResumeWindow ¶
func (sm *SessionManager) ResumeWindow() time.Duration
ResumeWindow returns the configured resume window duration. This is how long detached sessions remain resumable.
func (*SessionManager) SetBudgetTracker ¶
func (sm *SessionManager) SetBudgetTracker(tracker *vango.BudgetTracker)
SetBudgetTracker configures the budget tracker for new sessions.
func (*SessionManager) SetCleanupInterval ¶
func (sm *SessionManager) SetCleanupInterval(d time.Duration)
SetCleanupInterval sets the cleanup interval.
func (*SessionManager) SetMetricsSink ¶
func (sm *SessionManager) SetMetricsSink(sink vango.MetricsSink)
SetMetricsSink updates the metrics sink used for session lifecycle metrics. Call this before creating sessions to ensure they inherit the sink.
func (*SessionManager) SetOnSessionClose ¶
func (sm *SessionManager) SetOnSessionClose(fn func(*Session))
SetOnSessionClose sets the callback for session close.
func (*SessionManager) SetOnSessionCreate ¶
func (sm *SessionManager) SetOnSessionCreate(fn func(*Session))
SetOnSessionCreate sets the callback for session creation.
func (*SessionManager) Shutdown ¶
func (sm *SessionManager) Shutdown()
Shutdown gracefully shuts down all sessions.
func (*SessionManager) ShutdownWithContext ¶
func (sm *SessionManager) ShutdownWithContext(ctx context.Context) error
ShutdownWithContext gracefully shuts down all sessions with context for timeout.
func (*SessionManager) Stats ¶
func (sm *SessionManager) Stats() ManagerStats
Stats returns aggregated session statistics.
func (*SessionManager) TotalMemoryUsage ¶
func (sm *SessionManager) TotalMemoryUsage() int64
TotalMemoryUsage returns the total memory usage of all sessions.
func (*SessionManager) UpdateSessionIP ¶
func (sm *SessionManager) UpdateSessionIP(session *Session, newIP string) error
UpdateSessionIP updates the session's IP address and enforces per-IP limits. Returns ErrTooManySessionsFromIP if the new IP bucket is full.
type SessionManagerOptions ¶
type SessionManagerOptions struct {
// SessionStore is the persistence backend for sessions.
SessionStore session.SessionStore
// ResumeWindow is how long detached sessions remain resumable.
// nil means use the default (5 minutes).
// 0 disables resumption entirely.
ResumeWindow *time.Duration
// MaxDetachedSessions is the maximum detached sessions before LRU eviction.
MaxDetachedSessions int
// MaxSessionsPerIP is the maximum sessions per IP address.
MaxSessionsPerIP int
// EvictOnIPLimit controls whether to evict the oldest detached session
// for a full IP bucket instead of rejecting new sessions.
EvictOnIPLimit bool
// EvictionPolicy determines how detached sessions are evicted when limits are exceeded.
EvictionPolicy SessionEvictionPolicy
// PersistInterval is how often to persist dirty sessions.
PersistInterval time.Duration
// GlobalSignalPubSub is the broadcast backend for global signals.
GlobalSignalPubSub vango.GlobalBroadcastBackend
// GlobalSignalRefreshInterval controls the refresh cadence when no pubsub is configured.
GlobalSignalRefreshInterval time.Duration
// RedirectAllowlist is the normalized allowlist for external redirects.
// When nil or empty, external redirects are rejected.
RedirectAllowlist map[string]struct{}
}
SessionManagerOptions contains optional Phase 12 configuration.
type SessionPrimitiveRegistry ¶
type SessionPrimitiveRegistry struct {
// contains filtered or unexported fields
}
SessionPrimitiveRegistry tracks all primitives in a session for inspection.
func NewSessionPrimitiveRegistry ¶
func NewSessionPrimitiveRegistry() *SessionPrimitiveRegistry
NewSessionPrimitiveRegistry creates a new registry.
func (*SessionPrimitiveRegistry) All ¶
func (r *SessionPrimitiveRegistry) All() []*PrimitiveInfo
All returns all registered primitives.
func (*SessionPrimitiveRegistry) AllPersisted ¶
func (r *SessionPrimitiveRegistry) AllPersisted() []*PrimitiveInfo
AllPersisted returns only persisted primitives.
func (*SessionPrimitiveRegistry) FindByDebugName ¶
func (r *SessionPrimitiveRegistry) FindByDebugName(debugName string) []*PrimitiveInfo
FindByDebugName returns all primitives with the given debug name.
func (*SessionPrimitiveRegistry) FindByNameOrID ¶
func (r *SessionPrimitiveRegistry) FindByNameOrID(nameOrID string) (*PrimitiveInfo, bool)
FindByNameOrID resolves a primitive by debugName, stableID, or anchorKey.
func (*SessionPrimitiveRegistry) FindByStableID ¶
func (r *SessionPrimitiveRegistry) FindByStableID(stableID string) []*PrimitiveInfo
FindByStableID returns all primitives with the given stable ID.
func (*SessionPrimitiveRegistry) Register ¶
func (r *SessionPrimitiveRegistry) Register(reg vango.PrimitiveRegistration, instancePath string)
Register adds a primitive to the registry.
func (*SessionPrimitiveRegistry) SignalValue ¶
func (r *SessionPrimitiveRegistry) SignalValue(nameOrID string) (any, bool)
SignalValue returns the current value of a signal by nameOrID.
func (*SessionPrimitiveRegistry) Unregister ¶
func (r *SessionPrimitiveRegistry) Unregister(instancePath string)
Unregister removes a primitive instance by instance path.
type SessionStats ¶
type SessionStats struct {
ID string
UserID string
CreatedAt time.Time
LastActive time.Time
EventCount uint64
PatchCount uint64
BytesSent uint64
BytesRecv uint64
HandlerCount int
ComponentCount int
}
SessionStats contains session statistics.
type StormBudgetConfig ¶
type StormBudgetConfig struct {
// MaxResourceStartsPerSecond limits how many Resource fetches can start per second.
// 0 means no limit.
// Default: 50.
MaxResourceStartsPerSecond int
// MaxActionStartsPerSecond limits how many Action runs can start per second.
// 0 means no limit.
// Default: 100.
MaxActionStartsPerSecond int
// MaxGoLatestStartsPerSecond limits how many GoLatest work items can start per second.
// 0 means no limit.
// Default: 50.
MaxGoLatestStartsPerSecond int
// MaxEffectRunsPerTick limits effect runs within a single event/dispatch tick.
// Helps catch infinite loops where effects trigger effects.
// 0 means no limit.
// Default: 1000.
MaxEffectRunsPerTick int
// WindowDuration is the sliding window for per-second limits.
// Default: 1 second.
WindowDuration time.Duration
// OnExceeded determines what happens when a budget is exceeded.
// Default: BudgetThrottle (drop excess operations).
OnExceeded BudgetExceededMode
}
StormBudgetConfig configures rate limits for effect primitives. These limits help prevent amplification bugs where effects cascade into more effects, potentially causing performance issues or infinite loops.
func DefaultStormBudgetConfig ¶
func DefaultStormBudgetConfig() *StormBudgetConfig
DefaultStormBudgetConfig returns a StormBudgetConfig with sensible defaults. These defaults are conservative but should handle most applications.
type TouchEvent ¶
type TouchEvent struct {
Touches []TouchPoint
}
TouchEvent represents a touch event with touch points.
type TouchPoint ¶
TouchPoint represents a single touch point.
Source Files
¶
- client_ip.go
- component.go
- config.go
- context.go
- cookie_security.go
- csp.go
- csrf_http.go
- custom_event_security.go
- doc.go
- errors.go
- event_keys.go
- event_security.go
- global_signal_store.go
- handler.go
- handshake_admission.go
- handshake_path.go
- log_safety.go
- logging_context.go
- manager.go
- memory.go
- metrics.go
- navigate_helpers.go
- navigation.go
- origin_context.go
- patch_history.go
- patch_validation.go
- prefetch.go
- redirect.go
- route_middleware.go
- route_root.go
- schema_refresh.go
- server.go
- session.go
- session_event_security.go
- session_primitives.go
- session_signal_store.go
- test_bypass.go
- thin_client.go
- url_params.go
- user_context.go
- websocket.go