Sessions
Status: Draft; implementation checked against the development source on 2026-09-08.
What it does
Gives a caller continuity across requests: a session cookie identifies a stored
Session that can hold an Avatar and arbitrary data, reconnected on every
request through the cookie.
When to use it
When callers need to stay logged in, or when you want to carry per-user state
between requests without re-authenticating each time. The session subsystem is active on AsgiServer by default. It keeps a store,
sets the cookie, and rehydrates the session for you.
Setup
Sessions are a mixin capability of AsgiServer (SessionMixin). Its presence wires
the session middleware automatically (unless explicitly disabled). The relevant constructor kwargs are:
session_store— the backing store;Nonedefaults toMemorySessionStore.session_ttl— the session lifetime.
from genro_asgi import AsgiServer, RoutedApplication, MemorySessionStore
from genro_routes import route
class App(RoutedApplication):
mount = ""
@route()
def index(self) -> dict:
return {"ok": True}
server = AsgiServer(applications=[App()], session_store=MemorySessionStore())
server.serve(host="127.0.0.1", port=8000)
The store
One store ships with genro-asgi: MemorySessionStore — sessions live in
the process. Simple, fast, lost on restart (but see the shutdown snapshot
below). A custom backend can be plugged through the session_store= kwarg by
implementing the SessionStore protocol.
The shutdown snapshot
The save_session= kwarg names a pickle file; when set, the server saves
every live session — data included — to that file at shutdown, and loads
it back at the next startup (a session past its TTL is dropped on load; an
absent file starts empty).
The CLI arms this automatically when you name an instance:
$ genro-asgi serve ./config.py --name demo
persists sessions to ~/.genroasgi/sessions/demo.pickle across restarts —
--reload restarts included. A nameless serve stays volatile.
This is a development convenience: your login and your work survive a code restart. It is not a production persistence story — the snapshot is one file written by one process at shutdown.
Session expiry
A session dies by inactivity alone: every request refreshes its
last_access, and a session whose inactivity exceeds the TTL is dropped —
lazily when its cookie comes back, and in a periodic mass reap at
session-creation time. There is no active signal from the client: a closed
tab leaves the session alive until the TTL runs out; explicit death is the
logout’s delete().
The Session object
A Session carries:
.id— the session identifier..data— aBagof arbitrary session data..meta— session metadata..avatar(key="root")— the identity attached underkey, if any; with no argument, the root one (the primary login)..avatars— a read-only view of every keyed identity on the session..dirty— whether the session has unsaved changes..attach_avatar(avatar, key="root")— bind anAvatarto the session underkey; the default root key is what a login does.
Attaching an avatar at login
At the point a caller proves who they are, attach their avatar to the session so subsequent requests carry the identity:
from genro_asgi import Avatar
# inside a handler that has verified the credentials:
session.attach_avatar(Avatar("alice", tags="admin,ops"))
From then on, requests bearing the session cookie resolve to that avatar — no
Authorization header needed. (Remember from the auth
guide that a valid Authorization header still takes
precedence over the session.)
How to verify it
The first response sets the cookie; a second request replaying it reconnects the same session:
$ curl -i http://127.0.0.1:8000/index
HTTP/1.1 200 OK
set-cookie: session_id=...; HttpOnly; ...
$ curl -b "session_id=..." http://127.0.0.1:8000/index
{"ok": true}
Gotchas
MemorySessionStoreloses everything on restart and does not share across processes — the shutdown snapshot (below) covers the development restart, not multi-process deployments.The cookie is always
HttpOnly; you cannot turn that off. You can setsecureandsamesite.Configure the cookie under
middleware={"session": {...}}, not under thesession_store/session_ttlkwargs — those control the store and lifetime, the middleware controls the cookie.SessionMixin,SessionandMemorySessionStoreare all importable fromgenro_asgi.