NAME
Punk::Session - signed cookie sessions
SYNOPSIS
package MyApp;
use Punk;
session
secret => secret('session_key'), # from the secrets system
expires => '7d',
samesite => 'Lax';
post '/login' => sub {
my ($c) = @_;
$c->session->{user_id} = $user->id; # signed into the cookie
$c->redirect('/');
};
get '/me' => sub {
my ($c) = @_;
my $id = $c->session->{user_id} or return $c->redirect('/login');
$c->json({ id => $id });
};
post '/logout' => sub { my ($c) = @_; $c->session_expire; $c->redirect('/') };
DESCRIPTION
A session is a hashref carried in a cookie: the handler reads and writes $c->session, and at the end of the request Punk serializes it (JSON), signs it with HMAC-SHA256 and the configured secret, and sets the cookie - only when it actually changed. The client can read the contents but cannot forge them, so do not put secrets in a session; a tampered cookie is rejected and the session comes up empty.
"Cannot forge them" is exactly as true as the secret is, which is why there is no default for it and no way to start without one. See "THE KEYWORD".
There is no server-side store in this cut: the whole session lives in the cookie. A session that serializes over ~4KB croaks, pointing at where a server-side store would go.
THE KEYWORD
session secret => secret('key'), expires => '7d', secure => 1;
Enables sessions for the application. Options:
secret- the signing key. Required, and required to be non-empty:sessioncroaks at boot without one rather than starting, because the alternative is signing with a zero-length HMAC key, which anybody who knows the cookie format can reproduce offline. Source it from the secrets system so it never sits in the code - and becausesecretitself fails closed, an unset environment variable or a missing config path becomes a startup error rather than a forgeable cookie.cookie- the cookie name (defaultpunk.sid).expires- a lifetime like'7d'/'12h'/'30m'/ seconds; omitted means a session cookie (gone when the browser closes).path(default/),domain,secure,httponly(default on),samesite(defaultLax) - the cookie attributes.
It also reads from config/punk.yml under a session: block.
CONTEXT METHODS
session
The session hashref. Read and write it; it is written back to the cookie at the end of the request if it changed.
session_expire
Log out: empty the session and delete the cookie.
flash
flash_keep
One-request messages riding the session - see "FLASH".
FLASH
post '/save' => sub {
my ($c) = @_;
$c->flash(notice => 'Saved.'); # for the NEXT request
$c->redirect('/list');
};
get '/list' => sub {
my ($c) = @_;
$c->render('list', { flash => $c->flash });
};
A flash message lives exactly one request: set it, redirect, and the redirected-to page reads it once. It rides the session under one reserved key (punk.flash), so it shares the session's machinery - the signing, the change-detected write-back, and the ~4KB cap - and needs the session keyword configured.
$c->flash(key => $value, ...) sets messages for the next request; $c->flash('key') reads one of this request's inbound messages; a bare $c->flash returns the whole inbound hashref (possibly empty) - which is also how a template gets it, passed explicitly like any other render data. $c->flash_keep re-arms the inbound messages for one more request - the redirect-through-a-redirect case.
The rotation is the session's own change detection: the first flash call of a request moves the inbound hash out of the session, so the consuming response rewrites the cookie without it, while a request that never touches flash leaves it riding untouched. Setting and reading in the same request do not meet: reads see the previous request's messages, writes feed the next one's.
SEE ALSO
Punk, "cookie" in Punk::Context, "secret" in Punk.
AUTHOR
LNATION <email@lnation.org>
LICENSE AND COPYRIGHT
This software is Copyright (c) 2026 by LNATION <email@lnation.org>.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)