session
Version, currently 133 versions
- 1.1latestOct 27, 2026
- 1.0.32Oct 22, 2026
- 1.0.31Mar 8, 2026
- 1.0.30not indexedMar 8, 2026
- 1.0.29not indexedMar 8, 2026
- 1.0.28not indexedMar 8, 2026
- 1.0.27not indexedMar 8, 2026
- 1.0.26not indexedMar 8, 2026
- 1.0.25not indexedMar 8, 2026
- 1.0.24not indexedMar 8, 2026
- 1.0.23not indexedMar 8, 2026
- 1.0.22not indexedMar 8, 2026
- 1.0.21not indexedMar 8, 2026
- 1.0.20not indexedMar 8, 2026
- 1.0.19not indexedMar 8, 2026
- 1.0.18not indexedMar 8, 2026
- 1.0.17not indexedMar 8, 2026
- 1.0.16not indexedMar 8, 2026
- 1.0.15not indexedMar 8, 2026
- 1.0.14not indexedMar 8, 2026
- 1.0.13not indexedMar 8, 2026
- 1.0.12not indexedMar 8, 2026
- 1.0.11not indexedMar 8, 2026
- 1.0.10not indexedMar 8, 2026
- 1.0.9not indexedMar 8, 2026
- 1.0.8not indexedMar 8, 2026
- 1.0.7not indexedMar 8, 2026
- 1.0.6not indexedMar 8, 2026
- 1.0.5not indexedMar 8, 2026
- 1.0.4not indexedMar 8, 2026
- 1.0.3not indexedMar 8, 2026
- 1.0.2not indexedMar 8, 2026
- 1not indexedMar 8, 2026
github.com/azutoolkit/session
Type-safe, production-ready session management for Crystal.
Nothing has been indexed for 1 yet. The tag is recorded, its shard.yml has not been read, so the manifest and dependency list below are empty because they are unknown rather than because they are absent.
Installation
# Add this to your shard.yml
dependencies:
session:
github: azutoolkit/session
version: ~> 1Then run:
shards installshard.yml
No shard.yml has been indexed for 1. You can read it on the repository.
Dependencies
Unknown: the shard.yml for this version has not been read yet.
README
This README is the one indexed from the repository at its latest ref, not from the tag for this version.
Session
Type-safe, production-ready session management for Crystal.
Why Session?
| Type-Safe | Define sessions as Crystal classes with compile-time guarantees |
| Multiple Backends | Cookie, Memory, Redis, or Clustered Redis—pick what fits |
| Battle-Tested Security | AES-256 encryption, HMAC-SHA256, PBKDF2, client binding |
| Production Resilience | Circuit breakers, retry logic, graceful degradation |
| Azu Ready | require "session/azu" adds typed context.session and context.flash |
Requires Crystal 1.22.1 or newer (crystal: ">= 1.22.1" in shard.yml; it was a 1.4.1 minimum that no job ever built, see UPGRADE.md; older compilers are unsupported).
Quick Start
Install (add to shard.yml):
dependencies:
session:
github: azutoolkit/session
Use (< 30 seconds to your first session):
require "session"
# Define your session data
class UserSession < Session::Base
property? authenticated : Bool = false
property username : String? = nil
end
# Declare the session type and its store once (this is what types `config.store`)
Session.use_store(Session::MemoryStore(UserSession))
# Configure once
Session.configure do |config|
# The secret is read from SESSION_SECRET (random per process in development without it)
config.store = Session::MemoryStore(UserSession).new
end
# Create and use sessions
store = Session.config.store.not_nil!
session = store.create
session.username = "alice"
That's it. You're ready to build.
Key Features
Storage Backends
| Backend | Best For | Persistence | Multi-Node |
|---|---|---|---|
| CookieStore | Stateless apps, serverless | Client-side | Yes |
| MemoryStore | Development, single-server | None | No |
| RedisStore | Production deployments | Redis | Yes |
| ClusteredRedisStore | High-scale, multi-node | Redis + local cache | Yes |
# One store at a time: `Session.use_store(StoreType)` declares which
# Cookie (stateless, client-side)
config.store = Session::CookieStore(UserSession).new
# Memory (development)
config.store = Session::MemoryStore(UserSession).new
# Redis (production)
config.store = Session::RedisStore(UserSession).new(client: Redis.new)
# Clustered Redis (high-scale production)
config.cluster.enabled = true
config.store = Session::ClusteredRedisStore(UserSession).new(client: Redis.new)
Security
- Encryption — AES-256-CBC with random IV per operation
- Signing — HMAC-SHA256 to detect tampering
- Key Derivation — Optional PBKDF2 with configurable iterations
- Client Binding — Lock sessions to IP and/or User-Agent
- Size Protection — Automatic cookie size validation (4KB limit)
Session.configure do |config|
# The secret is read from SESSION_SECRET (random per process in development without it)
config.use_kdf = true # Enable PBKDF2
config.kdf_iterations = 100_000 # OWASP recommended
config.bind_to_ip = true # Prevent session hijacking
config.bind_to_user_agent = true
end
Resilience
- Circuit Breaker — Fail fast when backends are down
- Retry Logic — Exponential backoff with jitter
- Compression — Gzip for large session payloads
Session.configure do |config|
config.circuit_breaker_enabled = true
config.circuit_breaker_config = Session::CircuitBreakerConfig.new(
failure_threshold: 5,
reset_timeout: 30.seconds
)
config.enable_retry = true
config.retry_config = Session::RetryConfig.new(
max_attempts: 3,
base_delay: 100.milliseconds,
backoff_multiplier: 2.0
)
end
Clustering
Multi-node session management with Redis Pub/Sub invalidation and local caching.
flowchart LR
subgraph Node_A["Node A"]
A_Cache["Local Cache"]
end
subgraph Node_B["Node B"]
B_Cache["Local Cache"]
end
Redis[("Redis")]
PubSub{{"Pub/Sub"}}
A_Cache <--> Redis
B_Cache <--> Redis
A_Cache -.->|invalidate| PubSub -.->|evict| B_Cache
Session.configure do |config|
config.cluster.enabled = true
config.cluster.node_id = ENV["POD_NAME"]? || UUID.random.to_s
config.cluster.local_cache_ttl = 30.seconds
config.cluster.local_cache_max_size = 10_000
config.store = Session::ClusteredRedisStore(UserSession).new(
client: Redis.new(url: ENV["REDIS_URL"])
)
end
API Essentials
Store Operations
store = Session.config.store.not_nil!
store.create # New session
store.delete # Destroy session
store.regenerate_id # New ID, keep data (post-login security)
store.valid? # Check validity
store.current_session # Access your typed session data
store.flash # One-request flash messages
Session Object
session = store.current_session
session.session_id # Unique ID
session.username # Your session properties directly
session.valid? # Not expired?
session.expired? # Past expiration?
session.time_until_expiry # Remaining lifetime
session.touch # Extend expiration
Flash Messages
# Set (available next request)
store.flash["notice"] = "Saved successfully"
store.flash["error"] = "Something went wrong"
# Read (on the next request only; the request after that no longer sees it)
store.flash.now["notice"] # => "Saved successfully"
Query & Bulk Operations
store = Session::MemoryStore(UserSession).new
compromised_id = 42_i64
# Iterate sessions
store.each_session { |s| puts s.username }
# Find by criteria
admins = store.find_by { |s| s.roles.includes?("admin") }
# Bulk delete (e.g., revoke compromised user)
store.bulk_delete { |s| s.user_id == compromised_id }
HTTP Integration
require "http/server"
class HelloHandler
include HTTP::Handler
def call(context : HTTP::Server::Context)
context.response.print "Hello, #{Session.session.current_session.username || "guest"}"
end
end
Session.configure do |config|
# The secret is read from SESSION_SECRET (random per process in development without it)
config.store = Session::MemoryStore(UserSession).new
end
server = HTTP::Server.new([
Session::SessionHandler.new(Session.session),
HelloHandler.new,
] of HTTP::Handler)
server.listen(8080)
Session::SessionHandler loads the session from the request cookies, validates client binding, recovers from corrupt or expired sessions with a fresh one, and sends the cookies with the response, even for large or streamed bodies. Session.session is the store you configured, typed (Session.use_store).
Azu Integration
require "session/azu" is opt-in. It adds the typed context.session and context.flash to HTTP::Server::Context, which is what an Azu endpoint has at hand.
require "session/azu"
class UserSession < Session::Base
property user_id : Int64? = nil
def authenticated? : Bool
!user_id.nil?
end
end
Session::Web.use_store(Session::CookieStore(UserSession))
Session.configure do |config|
# The secret is read from SESSION_SECRET (random per process in development without it)
config.store = Session::CookieStore(UserSession).new
end
def sign_in(context : HTTP::Server::Context, user_id : Int64) : Nil
Session.session.authenticate { |session| session.user_id = user_id }
context.flash["notice"] = "Welcome back"
end
def welcome(context : HTTP::Server::Context) : String?
context.flash.now["notice"]?
end
Cookies are always HttpOnly, Secure and SameSite=Strict; config.cookie_secure and config.cookie_same_site change that. See the Azu guide.
Configuration Reference
Session.configure do |config|
# Core
# The secret is read from SESSION_SECRET (random per process in development without it)
config.timeout = 1.hour # Session lifetime
config.session_key = "_session" # Cookie name
config.sliding_expiration = true # Extend on access
# Security
config.use_kdf = true # PBKDF2 key derivation
config.kdf_iterations = 100_000
config.bind_to_ip = true
config.bind_to_user_agent = true
config.encrypt_redis_data = true
# Performance
config.compress_data = true
config.compression_threshold = 256
# Resilience
config.enable_retry = true
config.circuit_breaker_enabled = true
# Clustering
config.cluster.enabled = true
config.cluster.local_cache_ttl = 30.seconds
config.cluster.local_cache_max_size = 10_000
# Callbacks
config.on_started = ->(id : String, session : Session::Base) { Log.info { "Session #{id} created" } }
config.on_deleted = ->(id : String, session : Session::Base) { Log.info { "Session #{id} destroyed" } }
# Metrics
config.metrics_backend = Session::Metrics::LogBackend.new
end
Documentation
Full documentation available at GitBook (or see the docs/ directory).
- Getting Started
- Configuration Guide
- Storage Backends
- Clustering Guide
- Security Best Practices
- AZU Framework Integration
- HTTP::Server Integration
- Upgrade Guide
Contributing
- Fork it
- Create your branch (
git checkout -b feature/awesome) - Write tests
- Make sure
crystal specpasses - Commit and push
- Open a PR
License
MIT — see LICENSE
Built with Crystal. Maintained by @eliasjpr.
Documentation
Built from the current release. The first visit to a release nobody has asked for starts its build.
Links
This release
- Version
1- Tagged
- Mar 8, 2026
- Commit
963641b3acf8- Indexed
- not yet
Dependents
Repository
github.com/azutoolkit/session
Metadata
- Created
- Aug 16, 2026
- Updated
- Nov 9, 2026
- Synced
- Nov 9, 2026
- Versions
- 33