{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreidgta7po3ywqxgo7i3zynatihm4ssq7m3evzyt5h5iqqfzt2vacxy",
"uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mpadwqhkma32"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreiguzu2dko53d3p3g4zrbkwx6gz3b2z3onsfxytfosgosr7phbqn3e"
},
"mimeType": "image/webp",
"size": 41572
},
"path": "/jjn1056/pagi-distribution-split-1kdo",
"publishedAt": "2026-06-27T01:26:47.000Z",
"site": "https://dev.to",
"tags": [
"perl",
"webdev",
"pagi",
"PAGI-Server",
"PAGI-Tools",
"IO::Async",
"tutorial",
"PSGI migration guide",
"guide for framework authors",
"migration guide"
],
"textContent": "The news: PAGI is now **three CPAN distributions** instead of one.\n\n * **PAGI-Server** — the reference server\n * **PAGI-Tools** — the application toolkit\n * **PAGI** — the specification\n\n\n\n## Install what you actually run\n\nIn practice, almost everyone starts the same way: `cpanm PAGI::Server` to get a\nserver that runs PAGI apps, and probably `PAGI::Tools` for the request/response\nhelpers, router, and middleware you'll want while building one. That's the\ncommon case, and the split is built around it — **you install the pieces you\nuse** instead of swallowing one monolith that bundled the server, the toolkit,\nand the spec together.\n\nUnderneath both sits the **specification** : a small, deliberately stable\ncontract — the shape of `$scope`, `$receive`, `$send`, and the event types —\nthat the server and the toolkit both implement. Keeping it in its own\ndistribution is the real reason to split: the fast-moving parts (the server and\nthe toolkit, where the churn actually lives) can iterate freely without\ndestabilizing the protocol you write your apps against.\n\nTo be candid: today there is exactly **one** reference server, so \"just depend\non the bare spec\" isn't something most people will do yet. That separation is\nforward-looking — it's what makes an _alternative_ server, or a framework built\nstraight on the protocol, possible without forking everything else. The split\nlays that groundwork; it isn't pretending it's already the common path.\n\nAnd nothing breaks in the meantime: installing `PAGI` still pulls in the server\nand toolkit as dependencies during the transition, so `cpanm PAGI` gives you the\nwhole stack exactly as before. That convenience dependency is temporary — depend\non `PAGI::Server` and/or `PAGI::Tools` directly when you're ready.\n\n## What's notable in each\n\n**PAGI-Server — the reference\nserver.** An IO::Async-based\nimplementation handling HTTP/1.1, HTTP/2, WebSocket, SSE, TLS, and multi-worker\npre-forking, with the `pagi-server` CLI and a swappable-server runner. It's\nvalidated against a compliance suite, and any server implementing the\ndocumented contract is a drop-in alternative — which is exactly the door the\nsplit holds open.\n\n**PAGI-Tools — the toolkit.** The\nergonomics you reach for when actually building apps: a router and a\nclass-based endpoint framework, a middleware suite, ready-made apps (static\nfiles, proxy, a PSGI bridge), and `Request`/`Response`/`Context` helpers. A few\nhighlights: `PAGI::Response` is now a **value** you build and then send (a clean\nsplit between assembling a response and committing it to the wire), a new\nordered, case-insensitive **`PAGI::Headers`** container, and a `to_app`\ncoercion that lets every composition point accept coderefs, objects, or class\nnames interchangeably. An in-process `PAGI::Test::Client` lets you test apps\nwithout a live server.\n\n**PAGI — the spec.** Now pure documentation\n(the module plus the `PAGI::Spec::*` POD) and the canonical place to start: the\ntutorial, a cookbook, a\nPSGI migration guide, and a\nguide for framework authors. Recent\nprotocol work made connection state observable — `pagi.connection` gained\n**`response_started`** / `response_complete` as observer-independent facts —\npinned down scope shallow-clone semantics (how per-request state is shared vs.\nisolated through middleware), and now mandates server-side **header\nbyte-safety** (a server must reject CR/LF/NUL in header names and values rather\nthan forwarding or silently rewriting them).\n\n## Status, and a note\n\nThe **specification is stable** — breaking changes won't be made except for\ncritical security issues, so raw PAGI apps you write today keep working. The\n**server and toolkit are beta** : solid, but not yet battle-tested in production,\nso run the server behind nginx/Apache/Caddy for now.\n\nPAGI is a labor of love for the future of async web programming in Perl. The\nproject is dedicated to the memory of Matt S. Trout, who encouraged the\nauthor's first CPAN contribution two decades ago — without which none of this\nwould exist.\n\nTo kick the tires:\n\n\n\n cpanm PAGI::Server PAGI::Tools\n pagi-server --app ./app.pl --port 5000\n\n\nFeedback, bug reports, and contributions are all welcome. Start with the\ntutorial; if you're coming from PSGI,\nthe migration guide maps the mental model\nacross.",
"title": "PAGI Distribution Split"
}