Revision history for Punk

0.12    2026-08-15
        - Adds Punk::Auth. The `auth` keyword freezes the config at 
	  to_app (model, a fields map so existing schemas need no renames, 
          session_key, roles hook, rank ladder); croaks without a session, on any
          unknown option, and on a roles target typo - at boot.
        - Context surface: $c->login / logout / auth_id /
          current_user (memoized per request, model loaded through an
          await seam so the blocking DBI backend and the
          future-returning DBIx::Loop backend both work).
        - auth_guard for `under`: the bare is-anyone-signed-in form
          runs entirely in C. Denial negotiates - a browser is
          redirected to login_path with a relative-only ?to= return-to,
          an API client gets the house 401 - with on_denied '403',
          '404' or a coderef. The roles hook may return one role, a
          list or an arrayref; a required role on the rank ladder means
          "this or better", one outside it matches exactly (a staff
          role next to tenant roles). Guards record what they learned
          in $c->stash->{auth}.
        - Bare `model;` registers everything under MyApp::Model::*

0.11    2026-08-15
        - Security response headers (Punk::Headers): a `headers` keyword
          freezing a header policy at to_app, applied in C on the way
          out of the dispatcher.
        - Content negotiation ($c->respond_to): calls the handler for
          the most acceptable offered format (json/html/text/xml or a
          full media type) and returns its response. 

0.10    2026-08-15
        - Development error pages (Punk::DevError): in development a die
          renders an HTML debug page
        - The environment now defaults to production; development is an
          opt-in: punk dev sets PUNK_ENV=development for its server, or
          set it yourself in the Env.
        - Punk::Validate: request validation, all in C on the
          JSON::Schema::Fast C ABI. $c->validate($schema) collects into
          a Result - errors in the Open::API shape plus name, valid()
          hands back typed filtered params - and a bare $c->validate
          reads the Result a route-level check stashed. Routes take an
          options hashref: get '/x' => $target, { validate => \%schema }
          (or { schema, source, on_invalid }), compiled once at to_app
          and run as a C guard after any auth guards; failures answer
          the OpenAPI-mount-shaped 400 or the on_invalid target.
        - New on_not_found keyword: the on_error contract for a 404 so
          you can return a custom response
        - The getentropy probe now links instead of only compiling.
          FreeBSD before 12 has no getentropy and its old compilers take
          the implicit declaration as a warning, so 0.09's compile-only
          probe false-positived and the .so failed to load with
          "Undefined symbol getentropy".

0.09    2026-08-15
        - Adds Punk::Test, Punk::Test::WS and Punk::Test::WS::Conn
        - Flash messages, in C (punk_flash.h): $c->flash and
          $c->flash_keep over the session's reserved punk.flash key. 

0.08    2026-08-14
        - t/42-immortal-refcount.t failed the distribution on perl 5.18,
          where perl itself moves the refcount it measures. Before 5.20
          av_extend filled the slots it allocated with &PL_sv_undef and
          av_undef released every slot up to the fill.

0.07    2026-08-13
        - The router stored &PL_sv_yes bare in three set-membership hashes
        - New PUNK_SET_TRUE (include/punk/punk_compat.h) is the sanctioned
          way to put a true value in a set slot - it takes the reference
          first, so the container has one to spend, with no allocation.

0.06    2026-08-13
        - Rate limiting and IP blocking over Hyperman's shared arena
          (requires Hyperman 0.18, ABI v3; fails open without it).
        - New `rate_limit` keyword: installs a before_dispatch that answers
          429 (with Retry-After and the X-RateLimit-* headers) when a
          caller is over the limit. Key by client IP (default), a request
          header (by => 'header:X-Api-Key'), or a coderef; scope to a path
          prefix with `for`. Chainable for layered limits. The counters
          live in the shared arena, so a limit is exact across the worker
          pool rather than per worker.
        - New context methods: $c->block_ip([$ip [, $ttl]]) and
          $c->unblock_ip([$ip]) add or lift an IP on the edge denylist
          ($ip defaults to the request's REMOTE_ADDR), and $c->rate_hit
          for a raw counter check.

0.05    2026-08-12
        - Fixed a segfault on perl before 5.20. Six places handed one of
          perl's immortal SVs to something that would release it.
        - t/42-immortal-refcount.t asserts the invariant rather than the
          symptom: an immortal's refcount may not move across these
          calls. That fails on any perl, including the ones where the bug
          is invisible.
	- Update min version of deps

0.04    2026-08-08
        - Fixed the configure-time dependency declaration, which is why 
          0.03 could not be installed from CPAN at all.
        - $app->install_kw($name => $code, $owner?): a plugin can install
          a declaration keyword of its own into the application class 
          instead of assigning to a glob there.
         - params now takes names: `$c->params(@names)` and `$req->params(@names)`
           return just those, as a list of values in the order asked for, or - 
           in scalar context - a hashref of only the names that were passed.

0.03    2026-08-07
        - Punk::Model::DBIx::Loop, a non-blocking model backend. The same
          six-method contract, the same SQL and the same result shapes as
          Punk::Model::DBI, but every method returns a Punk::Future and the
          statement runs on DBIx::Loop over the worker's own event loop.
        - It runs on DBIx::Loop's C ABI (dbil_abi.h, via ExtUtils::Depends)
        - Fixed Punk::Model's delegation returning the caller's own
          arguments alongside the backend's result.
        - Punk::Model::DBI is ~40% faster per statement: quote_identifier
          and the fixed-shape get/delete SQL are memoised on the pooled
          connection instead of rebuilt every call. A get is 2.05us against
          3.61us. The table is part of the statement cache key - the cache
          is shared by every model on a dsn.
        - Add the `markdown` mount/keyword: point it at a nested directory of .md
          files and get a documentation site. Navigation reflecting the
          tree, a per-page table of contents from the headings, syntax
          highlighting, ranked search, and the images sitting alongside
          the markdown served as static files.
        - The site is built at boot. The tree is walked, every page is
          rendered through Markdown::Simple and wrapped by
          Template::Stencil, the search index is filled, and the finished
          bytes are frozen. A request is then a hash lookup and a triplet,
          with no markdown parse, no template render and no Perl frame.
          `reload => 1` re-renders a page whose source has changed, for
          the edit loop under `punk dev`.
        - Refactor the stat / conditional-request / sendfile half of
          punk_static.h out as ps_serve_file, so the markdown mount serves
          the assets in its tree through the same code rather than a
          second implementation that would drift from it.

0.02    2026-08-06
        - $c->ua: an outbound HTTP agent on the context
        - The agent is one per worker, not one per request: it owns a
          keep-alive pool and its DNS state.
        - More than one agent: `ua partner => { ... }` names a second, the
          same shape `database` uses for a second database, and
          $c->ua('partner') asks for it. 
        - Needs Fetch 0.11 for the per-request jar (Fetch->clone).

0.01    2026-08-06
        First version
