Documentation
¶
Overview ¶
Package session provides a session storage interface with controls.
Session storage extends key-value storage with session-specific operations:
- User-based session lookups and deletion
- Automatic TTL management
- Size limits and validation
- Touch/heartbeat operations
Available Backends ¶
- memory: In-memory storage (for development/testing)
- kvs: Adapts any kvs.ListableStore (Redis, SQLite, etc.)
Index ¶
- Constants
- Variables
- func ErrorCode(err error) string
- func ErrorDetails(err error) map[string]any
- func GenerateSessionID() (string, error)
- func SessionRef(id string) string
- func ValidSessionID(id string) bool
- type Config
- type ControlOption
- type ControlledStore
- func (s *ControlledStore) Close() error
- func (s *ControlledStore) Create(ctx context.Context, session *Session) error
- func (s *ControlledStore) Delete(ctx context.Context, id string) error
- func (s *ControlledStore) DeleteByUserID(ctx context.Context, userID string) (int, error)
- func (s *ControlledStore) Get(ctx context.Context, id string) (*Session, error)
- func (s *ControlledStore) ListByUserID(ctx context.Context, userID string) ([]*Session, error)
- func (s *ControlledStore) Touch(ctx context.Context, id string) error
- func (s *ControlledStore) Unwrap() Store
- func (s *ControlledStore) Update(ctx context.Context, session *Session) error
- type Session
- type SessionError
- type SessionLister
- type Store
- type ViolationEvent
- type ViolationHandler
- type ViolationType
Constants ¶
const ( // ErrCodeInvalidSession indicates the session is malformed or missing required fields. ErrCodeInvalidSession = "SESSION_INVALID" // ErrCodeSizeLimitExceeded indicates the session data exceeds the configured limit. ErrCodeSizeLimitExceeded = "SESSION_SIZE_EXCEEDED" // ErrCodeNotSerializable indicates session data cannot be serialized to JSON. ErrCodeNotSerializable = "SESSION_NOT_SERIALIZABLE" // ErrCodeNotFound indicates the session does not exist. ErrCodeNotFound = "SESSION_NOT_FOUND" // ErrCodeExpired indicates the session has expired. ErrCodeExpired = "SESSION_EXPIRED" // ErrCodeStoreClosed indicates the store has been closed. ErrCodeStoreClosed = "STORE_CLOSED" // ErrCodeSessionLimitExceeded indicates the user has too many concurrent sessions. ErrCodeSessionLimitExceeded = "SESSION_LIMIT_EXCEEDED" // ErrCodeSiteMismatch indicates the session belongs to a different site. ErrCodeSiteMismatch = "SESSION_SITE_MISMATCH" )
Error codes for session operations. These can be used by HTTP handlers to return appropriate status codes.
const MaxSessionIDLength = 256
MaxSessionIDLength is the longest session ID ValidSessionID accepts.
const SessionIDLength = 32
SessionIDLength is the length of generated session IDs in bytes.
Variables ¶
var ( ErrNotFound = errors.New("session not found") ErrExpired = errors.New("session expired") ErrInvalidSession = errors.New("invalid session") ErrSizeLimitExceeded = errors.New("session size limit exceeded") ErrSessionLimitExceeded = errors.New("session limit exceeded") ErrSiteMismatch = errors.New("session belongs to different site") ErrClosed = errors.New("store is closed") )
Common errors.
Functions ¶
func ErrorCode ¶
ErrorCode extracts the error code from an error, if it's a SessionError. Returns empty string for non-SessionError errors.
func ErrorDetails ¶
ErrorDetails extracts details from an error, if it's a SessionError.
func GenerateSessionID ¶
GenerateSessionID generates a cryptographically secure session ID.
func SessionRef ¶ added in v0.6.0
SessionRef returns a short, non-reversible reference to a session ID for logs, errors and violation events: the first 8 hex characters of the SHA-256 of the ID. Session IDs are bearer credentials and must not be logged in full; the reference lets log lines about the same session be correlated without exposing it. SessionRef returns "" for an empty ID.
func ValidSessionID ¶ added in v0.6.0
ValidSessionID reports whether id is a well-formed session ID: 1 to MaxSessionIDLength characters from the URL-safe set A-Z, a-z, 0-9, '-', '_' and '.', which covers GenerateSessionID. Session IDs come from client cookies, so stores must refuse others: a backend that keeps sessions and its own index records in one key space (such as the kvs backend's "user:" index keys) would otherwise let a crafted ID address an index.
Types ¶
type Config ¶
type Config struct {
// SiteID identifies which site/service this store handles.
// Used for multi-site isolation (e.g., "academyos", "agentos", "dashforge").
// When set, all sessions are tagged with this SiteID and Get operations
// validate the session belongs to this site.
// Required for ControlledStore when running multiple sites.
SiteID string
// MaxSessionSize is the maximum serialized session size in bytes.
// Set to 0 for no limit.
MaxSessionSize int
// MaxSessionsPerUser limits concurrent sessions per user.
// When exceeded, oldest sessions are deleted to make room.
// Set to 0 for no limit.
MaxSessionsPerUser int
// DefaultTTL is the default session TTL if not specified.
DefaultTTL time.Duration
// CleanupInterval is how often to run automatic cleanup.
// Set to 0 to disable automatic cleanup.
CleanupInterval time.Duration
// KeyPrefix is the prefix for session keys (for KVS backends).
KeyPrefix string
}
Config contains configuration for session stores.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns sensible default configuration.
type ControlOption ¶
type ControlOption func(*ControlledStore)
ControlOption configures a ControlledStore.
func WithLogger ¶
func WithLogger(logger *slog.Logger) ControlOption
WithLogger sets the logger for the controlled store.
func WithViolationHandler ¶
func WithViolationHandler(handler ViolationHandler) ControlOption
WithViolationHandler sets a callback for policy violations. Use this to emit metrics (e.g., OTel counters) or alerts.
type ControlledStore ¶
type ControlledStore struct {
// contains filtered or unexported fields
}
ControlledStore wraps a Store with size limits, validation, and observability.
func WithControls ¶
func WithControls(store Store, cfg Config, opts ...ControlOption) *ControlledStore
WithControls wraps a Store with size limits and validation controls.
func (*ControlledStore) Create ¶
func (s *ControlledStore) Create(ctx context.Context, session *Session) error
Create stores a new session with validation.
func (*ControlledStore) Delete ¶
func (s *ControlledStore) Delete(ctx context.Context, id string) error
Delete removes a session by ID. If SiteID is configured, a session that belongs to another site is left in place and, like a missing session, Delete returns nil.
func (*ControlledStore) DeleteByUserID ¶
DeleteByUserID removes all sessions for a user. If SiteID is configured, only the user's sessions for this site are removed; sessions of other sites sharing the backend are left in place. That requires the underlying store to implement SessionLister; if it does not, DeleteByUserID returns an error and deletes nothing.
func (*ControlledStore) Get ¶
Get retrieves a session by ID. If SiteID is configured, validates the session belongs to this site.
func (*ControlledStore) ListByUserID ¶
ListByUserID returns all sessions for a user if the underlying store supports it. If SiteID is configured, only returns sessions for this site. This implements SessionLister for transparent pass-through.
func (*ControlledStore) Touch ¶
func (s *ControlledStore) Touch(ctx context.Context, id string) error
Touch updates the LastAccessedAt timestamp. If SiteID is configured, a session that belongs to another site returns ErrNotFound.
func (*ControlledStore) Unwrap ¶
func (s *ControlledStore) Unwrap() Store
Unwrap returns the underlying store.
func (*ControlledStore) Update ¶
func (s *ControlledStore) Update(ctx context.Context, session *Session) error
Update updates an existing session with validation. If SiteID is configured, the stored session must belong to this site (otherwise ErrNotFound is returned), and the updated session is stamped with the configured SiteID so the site binding survives the write.
type Session ¶
type Session struct {
// ID is the unique session identifier.
ID string `json:"id"`
// UserID is the authenticated user's ID.
UserID uuid.UUID `json:"user_id"`
// SiteID identifies which site/service this session belongs to.
// Used for multi-site isolation (e.g., "academyos", "agentos", "dashforge").
// Set automatically by ControlledStore based on Config.SiteID.
SiteID string `json:"site_id,omitempty"`
// OrganizationID is the current organization context (optional).
OrganizationID *uuid.UUID `json:"organization_id,omitempty"`
// Data contains arbitrary session data.
// This can include tokens, preferences, etc.
Data map[string]any `json:"data,omitempty"`
// CreatedAt is when the session was created.
CreatedAt time.Time `json:"created_at"`
// UpdatedAt is when the session was last updated.
UpdatedAt time.Time `json:"updated_at"`
// LastAccessedAt is when the session was last accessed.
LastAccessedAt time.Time `json:"last_accessed_at"`
// ExpiresAt is when the session expires.
ExpiresAt time.Time `json:"expires_at"`
// IPAddress is the IP address that created the session.
IPAddress string `json:"ip_address,omitempty"`
// UserAgent is the user agent that created the session.
UserAgent string `json:"user_agent,omitempty"`
}
Session represents a server-side session.
func NewSession ¶
NewSession creates a new session with the given parameters.
type SessionError ¶
type SessionError struct {
// Code is a machine-readable error code.
Code string
// Message is a human-readable error message.
Message string
// Details contains additional error context.
Details map[string]any
// Cause is the underlying error, if any.
Cause error
}
SessionError is a structured error with a code for programmatic handling.
func NewSessionError ¶
func NewSessionError(code, message string, details map[string]any, cause error) *SessionError
NewSessionError creates a new SessionError.
func (*SessionError) Error ¶
func (e *SessionError) Error() string
func (*SessionError) Is ¶
func (e *SessionError) Is(target error) bool
Is implements errors.Is for SessionError.
func (*SessionError) Unwrap ¶
func (e *SessionError) Unwrap() error
type SessionLister ¶
type SessionLister interface {
// ListByUserID returns all sessions for a user, sorted by CreatedAt ascending (oldest first).
ListByUserID(ctx context.Context, userID string) ([]*Session, error)
}
SessionLister is an optional interface for stores that support listing sessions. Stores implementing this interface can enforce MaxSessionsPerUser.
type Store ¶
type Store interface {
// Create stores a new session.
Create(ctx context.Context, session *Session) error
// Get retrieves a session by ID.
// Returns ErrNotFound if the session doesn't exist.
// Returns ErrExpired if the session has expired.
Get(ctx context.Context, id string) (*Session, error)
// Update updates an existing session.
// Returns ErrNotFound if the session doesn't exist.
Update(ctx context.Context, session *Session) error
// Delete removes a session by ID.
Delete(ctx context.Context, id string) error
// DeleteByUserID removes all sessions for a user.
// Returns the number of sessions deleted.
DeleteByUserID(ctx context.Context, userID string) (int, error)
// Touch updates the LastAccessedAt timestamp.
// This is used to track session activity without modifying other fields.
Touch(ctx context.Context, id string) error
// Close closes the store and releases any resources.
Close() error
}
Store defines the interface for session storage. Implementations must be safe for concurrent use.
type ViolationEvent ¶
type ViolationEvent struct {
Type ViolationType
// SessionID is no longer set, because session IDs are bearer
// credentials and must not reach logs or metrics.
//
// Deprecated: Use SessionRef.
SessionID string
// SessionRef is SessionRef(id) of the session involved, a
// non-reversible reference safe to log.
SessionRef string
UserID uuid.UUID
SiteID string // Site identifier for multi-site isolation
Size int // Current size in bytes
Limit int // Configured limit
Message string
}
ViolationEvent contains information about a policy violation.
type ViolationHandler ¶
type ViolationHandler func(ctx context.Context, event ViolationEvent)
ViolationHandler is called when a session policy is violated. Use this to emit metrics, alerts, or audit logs.
type ViolationType ¶
type ViolationType string
ViolationType identifies the type of policy violation.
const ( ViolationSizeExceeded ViolationType = "size_exceeded" ViolationNotSerializable ViolationType = "not_serializable" ViolationInvalidSession ViolationType = "invalid_session" ViolationSessionLimitExceeded ViolationType = "session_limit_exceeded" ViolationSiteMismatch ViolationType = "site_mismatch" )
Directories
¶
| Path | Synopsis |
|---|---|
|
backend
|
|
|
kvs
Package kvs provides a session storage backend using kvs.ListableStore.
|
Package kvs provides a session storage backend using kvs.ListableStore. |
|
memory
Package memory provides an in-memory session storage backend.
|
Package memory provides an in-memory session storage backend. |