Worker-safe session lifecycle over NextPDF Connect
At a glance
Section titled “At a glance”Use a request-scoped session lifecycle in a long-running PHP worker (RoadRunner,
Swoole, Laravel Octane). Each request creates its own document session and
destroys it after output_pdf. Font, page, and handle state stay inside the
worker’s request boundary. The tools are create_pdf, set_font, add_text,
and output_pdf — all Core.
Install
Section titled “Install”composer require nextpdf/serverBind a transport. This pattern works the same with any transport.
Conceptual overview
Section titled “Conceptual overview”A document_id is an opaque handle scoped to one request. Follow one rule:
create per request, destroy per request, never cache or share. With the
default destroy: true, output_pdf renders the document and frees the
session in one atomic step. The next request on the same worker gets a fresh,
independent session. The previous session’s font and content are gone. Each
tool result is a normal transport response (PSR-18 §p2). The server code is
isolated by the autoload boundary (PSR-4 §3). The session store is the only
cross-request state, and it is keyed by id.
API surface
Section titled “API surface”| Tool | Role | Risk tier |
|---|---|---|
create_pdf | Open a per-request session | Safe |
set_font | Set the active font (per session) | Caution |
add_text | Write content | Caution |
output_pdf | Render and destroy the session | Approval Required / Review (base64) |
The tool catalog is the source of truth. The tools you can use depend on the installed tier.
Code sample — Quick start
Section titled “Code sample — Quick start”Request 1: create_pdf → set_font → add_text → output_pdf
(destroy: true). Request 2 on the same worker starts a new create_pdf
session. The old id is now invalid. Set the font again, because it does not
carry over.
Code sample — Production
Section titled “Code sample — Production”Wrap the lifecycle so cleanup always runs:
- Acquire the session when the request starts.
- Build content.
- In a
finally-equivalent block, calloutput_pdf, which destroys the session. If you useddestroy: falsefor an inspect-after-output flow, destroy the session explicitly instead.
Never store a document_id in a worker-global, static, or shared container.
Never pass it between coroutines, fibers, or request handlers.
Edge cases & gotchas
Section titled “Edge cases & gotchas”- Reused id after destroy. Returns an unknown-document error. Create a new session per request.
- Forgotten
output_pdf. Memory grows silently until the session TTL expires or the process restarts. Always finalize. - Session-limit under load. The store has a concurrent cap. Prompt destruction helps you stay under it.
destroy: falsewithout cleanup. Memory grows gradually. Use it only for an explicit inspect-after-output flow, then destroy the session.- Shared id across concurrent requests. A race that corrupts the output. Each request owns its own session.
Performance
Section titled “Performance”Per-request output is a few KB. The benefit is bounded worker memory across
the worker’s lifetime. The profile is structural.
Security notes
Section titled “Security notes”Session isolation is also a confidentiality property. One request’s content never reaches another request, because handles are not shared and the session is destroyed at output.
Conformance
Section titled “Conformance”| Statement | Spec | Clause | reference_id |
|---|---|---|---|
| Each tool result is a normal transport response. | PSR-18 | §p2 | |
| Code is isolated by the autoload class→file mapping. | PSR-4 | §3 |
Commercial context
Section titled “Commercial context”Not applicable — all tools are Core.
Transport availability
Section titled “Transport availability”| Transport | Available | Notes |
|---|---|---|
| MCP (stdio) | Yes | One stdio process per worker is typical. |
| REST | Yes | The HTTP request boundary maps to the session boundary. |
| gRPC | Yes | One session per RPC sequence. |
HITL risk tier
Section titled “HITL risk tier”create_pdf is Safe. set_font and add_text are Caution. output_pdf is
Approval Required, downgraded to Review in base64 mode. In a worker, base64
output is the common path and adds no gate
(HITL risk tiers).
Confirmation gate JSON envelope
Section titled “Confirmation gate JSON envelope”Base64 worker output:
{ "allowed": true }