PROTOCOL2 Print Deploy client: from the Printer JWT to a ready-to-print queue Scope ----- This document continues PROTOCOL1. It starts with the Printer JWT already in hand and stops immediately before the first operation that actually creates a print job. It covers everything needed to set up printing: * resolve the per-user printer queue URL (GET /printer-url) * download the driver package (driver .zip) * download driver catalog extras (catalog.json files[]) * download the capabilities file (driver .cap) * obtain the static driver and connection metadata (/deploy/printers) * inspect the PPD and .cap contents * query the static IPP printer capabilities (Get-Printer-Attributes) * validate a job template without creating a job (Validate-Job) It does NOT cover the actual job: Create-Job (0x0005), Send-Document (0x0006), Print-Job (0x0002), job polling, or release at the printer. Provenance: capture convo6_reallyfull.dump, client 1.10.3838, flows 13-20, 2026-11-10 07:50:46-07:50:58 UTC. Server: Print Deploy https://printdeploy.asg.nl:9174, Mobility Print https://172.25.1.14:9164. Secrets (tokens, per-user URL hash) are placeholders in this document. 1. Overview ----------- After PROTOCOL1 the client holds a Printer JWT (141-char HS256 JWT, claims {"exp","username"}, 365-day lifetime). That JWT is enough to authorize the printer-queue requests; the Microsoft and PaperCut session tokens are not used again in this phase. The setup sequence observed in the capture: flow 13 GET {mp}/printer-url?printerName=...&scheme=http&serverName=... Authorization: Bearer -> per-user queue URL (text) flow 14 GET {pd}/deploy/drivers/.../Follow-Me.zip (no auth) flow 15 GET {pd}/deploy/drivers/.../container/...zip (no auth) flow 16 GET {pd}/deploy/capabilities/.../Follow-Me.cap (no auth) flow 17 POST {mp}/ipp/{host}_{port}/{hex}/printers/{name} Authorization: Bearer operation Get-Printer-Attributes (0x000b) -> static capabilities, printer state flow 18 POST same endpoint, Validate-Job (0x0004) -> attributes accepted, no job created flow 19 POST same endpoint, Get-Printer-Attributes again flow 20 POST same endpoint, Create-Job (0x0005) <-- first real job, STOP "Ready to print" means: you have the queue URL/endpoint, the driver files, the static capabilities, and a validated job template. Creating the job is the next protocol phase. 2. Artifact inventory --------------------- Everything the client needs for the queue, and where it comes from: NAME SOURCE AUTH SIZE --------------------------------------------------------------------------- Printer JWT {mp}/token (PROTOCOL1) Basic 141 chars Queue URL {mp}/printer-url Bearer 129 bytes Driver zip {pd} none 3266 bytes Follow-Me.ppd entry in driver zip (uncompressed) - 16053 bytes catalog.json entry in driver zip - 175 bytes Catalog extra {pd}/ none 1794625 bytes Capabilities .cap {pd} none 404 bytes Static IPP attrs {mp}/ipp/... Get-Printer-Attributes Bearer 10597 bytes PPD / .cap metadata see sections 5-7 Connection metadata {pd}/deploy/printers response session (PROTOCOL1) All driver downloads are unauthenticated GETs from the Print Deploy server on port 9174. The queue URL and IPP calls are on the Mobility Print server, port 9164 (the queue URL itself points at 9163, see section 3). 3. Queue URL resolution: GET /printer-url ------------------------------------------ This is the first call after the Printer JWT is minted. Request (flow 13): GET /printer-url?printerName=Follow-Me&scheme=http&serverName=172.25.1.14 Host: 172.25.1.14:9164 User-Agent: Print Deploy Client/1.10.3838/Debian Authorization: Bearer Accept-Encoding: gzip Query parameters: printerName queue name from /deploy/printers (Follow-Me) scheme scheme requested for the returned URL (http) serverName Mobility Print host, no scheme/port (172.25.1.14) Response: 200, text/plain; charset=utf-8, 129 bytes, Server: Mobility Print. http://172.25.1.14:9163/printers/Follow-Me/users//<64-hex> Anatomy of the returned URL: scheme http (from the scheme= query parameter) host 172.25.1.14 (from serverName=) port 9163 (chosen by the server; not in the query) path /printers/Follow-Me user /users/ (e.g. name%40domain in the capture) secret /<64 lowercase hex chars> (per-user queue hash) The 64-hex suffix is a per-user capability, not a static printer ID; treat the whole URL as sensitive. The capture never connects to port 9163: all IPP traffic in this phase goes to port 9164 (sections 9-10). The returned URL is the per-user queue URL the client receives; the capture does not show the subsequent CUPS handoff. The other queue URI forms are listed below. Queue URI forms seen in the same setup: A. per-user URL from /printer-url (this step) http://172.25.1.14:9163/printers/Follow-Me/users// B. DeviceURI packaged in the .cap capabilities file ipps://172.25.1.14:9164/printers/Follow-Me C. direct IPP endpoint used by the client for validation/job requests https://172.25.1.14:9164/ipp/172.25.1.14_9164/466f6c6c6f772d4d65/printers/Follow-Me C is derived from the mobility host, port, and printer name: path = /ipp/{host}_{port}/{hex(UTF-8 printer name)}/printers/{printer name} hex("Follow-Me") = 466f6c6c6f772d4d65 A, B and C identify the same Follow-Me queue at different layers; A is the per-user CUPS URL, B is the shared CUPS device URI shipped in the capability file, and C is what the Print Deploy client used on the wire. 4. Driver package: GET {pd} --------------------------------------------- Request (flow 14): GET /deploy/drivers/mac/26.6-arm64/Follow-Me.zip Host: printdeploy.asg.nl:9174 User-Agent: Print Deploy Client/1.10.3838/Debian Accept-Encoding: gzip No Authorization header. Response: 200 content-type: application/x-zip-compressed content-length: 3266 accept-ranges: bytes last-modified: Tue, 08 Sep 2026 08:02:26 GMT ZIP contents (exactly two entries): Follow-Me.ppd 16053 bytes uncompressed (the CUPS PPD) catalog.json 175 bytes The zip is a generic AirPrint PPD package. See section 8 for the PPD. The path is platform-specific (mac/26.6-arm64 in the capture; the client sends os.name/os.version/os.arch in the machine fingerprint and the server returns the matching driver tree). The protocol is the same for other platforms. Advertised sha256 (in /deploy/printers): d1f63ebc3d3f96d9a295ca8735e6650b16956149eefe56751bc78c19e04e84ef Captured body sha256: 41cacc2b5c52610aabb592bfc42e08124515299121e61e13e87d7f48718179c1 -> mismatch in the capture; see section 12. 5. Catalog extras: GET {pd}/ ---------------------------------------------------------------- catalog.json (175 bytes): { "files": [ { "name": "container/System/Library/ColorSync.tar.zip", "sha256": "a8fb40d2b3743e17cb8e244e776b9acbf040114de1d12401ff69019294b49f0a" } ] } Resolution is relative to the directory of the driver zip: driver zip: /deploy/drivers/mac/26.6-arm64/Follow-Me.zip dirname: /deploy/drivers/mac/26.6-arm64 extra: /deploy/drivers/mac/26.6-arm64/container/System/Library/ColorSync.tar.zip Request (flow 15), no auth. Response: 200 content-type: application/x-zip-compressed content-length: 1794625 accept-ranges: bytes last-modified: Tue, 08 Sep 2026 08:02:26 GMT Advertised sha256: a8fb40d2b3743e17cb8e244e776b9acbf040114de1d12401ff69019294b49f0a Captured body sha256: 659fecd60b35f7eeb998ea6779a27228d6f49f617661d906c577f8fedd663b65 -> mismatch in the capture; advisory only. This is a macOS ColorSync profile container referenced by the macOS driver package. On other platforms the catalog may list different files or none. 6. Capabilities file: GET {pd} --------------------------------------------------------- Request (flow 16): GET /deploy/capabilities/mac/26.6-arm64/Follow-Me.cap Host: printdeploy.asg.nl:9174 User-Agent: Print Deploy Client/1.10.3838/Debian Accept-Encoding: gzip No auth. Response: 200 content-type: image/CAP content-length: 404 accept-ranges: bytes last-modified: Tue, 08 Sep 2026 08:02:26 GMT Body (404 bytes, full content): PrinterId 9 UUID urn:uuid:c090f159-4fbb-34fd-7c59-c627cd40a7e6 AuthInfoRequired username,password Info Follow-Me Location SRV-ASG-PAPERCT MakeModel PaperCut-AirPrint DeviceURI ipps://172.25.1.14:9164/printers/Follow-Me State Idle StateTime 1788854515 ConfigTime 1788854420 Type 28764 Accepting Yes Shared No JobSheets none none QuotaPeriod 0 PageLimit 0 KLimit 0 OpPolicy default ErrorPolicy stop-printer Advertised sha256: 4d61c80c839aa97df4780993540a40904ce3bd018e5aacd606985bad35368f6d Captured body sha256: 4d61c80c839aa97df4780993540a40904ce3bd018e5aacd606985bad35368f6d -> matches. This file is a CUPS-style capability record (macOS uses image/CAP). Important static fields for setup: DeviceURI (shared queue), MakeModel, AuthInfoRequired username,password, location, accepting/state, and policy. It is the 9164/ipps shared URI, not the per-user 9163 URL from section 3. 7. Static driver and connection metadata (/deploy/printers) ------------------------------------------------------------ The printer object returned in PROTOCOL1 (POST /deploy/printers) supplies the static driver/connection facts used by this phase. For the captured printer: printer name Follow-Me description Follow-Me zoneName ASG optional false defaultprinter false advancedPrintingFeatures false driver.os darwin driver.name Follow-Me driver.file Follow-Me.ppd driver.zip.name /deploy/drivers/mac/26.6-arm64/Follow-Me.zip driver.zip.sha256 d1f63ebc3d3f96d9a295ca8735e6650b16956149eefe56751bc78c19e04e84ef driver.capabilities /deploy/capabilities/mac/26.6-arm64/Follow-Me.cap driver.capabilities.sha 4d61c80c839aa97df4780993540a40904ce3bd018e5aacd606985bad35368f6d driver.defaults.name "" (none advertised) driver.extras null connection.type mobilityprint connection.name https://172.25.1.14:9164 connection.host "" connection.queue "" printer defaults color "", plex "", resolution 0 `connection.name` is the Mobility Print base used by sections 3 and 9. `driver.file` is the PPD file name inside the zip (Follow-Me.ppd). 8. PPD contents (the driver) ---------------------------- The PPD is a generic AirPrint PPD produced by ipp2ppd, not a vendor-specific driver. Key metadata: *PPD-Adobe: "4.3" *% PPD created by ipp2ppd (v4:Jul 11 2026) *FormatVersion: "4.3" *LanguageLevel: "3" *cupsVersion: 2.0 *APAirPrint: True *APAirPrintVersion: 1.1 *APURFVersion: 1.1 *PCFileName: "APPAPERC.PPD" *ModelName: "PaperCut" *Product: (PaperCut) *ShortNickName: "PaperCut" *Manufacturer: "PaperCut" *NickName: "PaperCut-AirPrint" Filters declared by the PPD: *cupsFilter2: "application/vnd.cups-pdf application/pdf 0 maxsize(0) -" *cupsFilter: "application/vnd.cups-pdf 0 -" *cupsFilter2: "image/urf image/urf 10 -" *cupsFilter: "image/urf 10 -" Meaning: the queue accepts PDF (`application/pdf`, CUPS PDF filter) and Apple URF (`image/urf`) and does not require a vendor PPD option set. The PPD also notes missing pages-per-minute/firmware data (suppressed throughput), so the driver is capability-driven rather than model-driven. No DeviceURI is embedded in the PPD; the queue URI comes from section 3 or 6. 9. Static IPP capabilities: Get-Printer-Attributes --------------------------------------------------- Endpoint used by the client (flow 17/19): POST https://172.25.1.14:9164/ipp/172.25.1.14_9164/466f6c6c6f772d4d65/printers/Follow-Me Host: 172.25.1.14:9164 User-Agent: Print Deploy Client/1.10.3838/Debian Authorization: Bearer Content-Type: application/ipp X-Forwarded-For: 127.0.0.1 Date: Accept-Encoding: gzip IPP header: version 2.0, operation 0x000b (Get-Printer-Attributes), request-id 1; the repeated query later uses request-id 3. The body is an operation-attributes group with: attributes-charset utf-8 attributes-natural-language en-us printer-uri http://127.0.0.1:9177/ipp/172.25.1.14_9164/ 466f6c6c6f772d4d65/printers/Follow-Me requesting-user-name anonymous requested-attributes 23 names, including compression-supported, copies-supported, cups-version, document-format-supported, job-password-encryption-supported, marker-*, media-col-supported, operations-supported, print-color-mode-supported, printer-state, printer-state-message, printer-state-reasons Note the printer-uri attribute points at the client's local CUPS/proxy (127.0.0.1:9177), while the HTTP request goes to the mobility server on 9164. The server accepts this; the request path identifies the queue. Response: 200, application/ipp, 10597 bytes, IPP status 0x0000. Static attributes returned (24 names; the large size is dominated by a media-col collection): printer-state 3 (idle at capture) printer-state-reasons none-report printer-state-message Accepting printer-is-accepting-jobs true queued-job-count 0 (dynamic) printer-up-time 1000 (dynamic, seconds) printer-uri-supported http://127.0.0.1:9177/ipp/172.25.1.14_9164/ 466f6c6c6f772d4d65/printers/Follow-Me (returned twice, reflecting the client proxy) charset-configured utf-8 charset-supported utf-8 natural-language-configured en-us generated-natural-language-supported en-us ipp-versions-supported 1.0, 1.1, 2.0, 2.1, 2.2 document-format-default application/pdf document-format-supported application/pdf, image/urf compression-supported none pdl-override-supported attempted operations-supported 2, 4, 5, 6, 8, 9, 10, 11 copies-supported 1x999 print-color-mode-supported color, monochrome uri-security-supported none, tls uri-authentication-supported requesting-user-name, basic media-col-supported large collection (765 member values in the capture; media sizes and margins) Operations (0x0002...): 2 Print-Job 4 Validate-Job 5 Create-Job 6 Send-Document 8 Cancel-Job 9 Get-Job-Attributes 10 Get-Jobs 11 Get-Printer-Attributes Static facts for job setup: accept PDF or URF, no compression, a large media-col size/margin collection, 1-999 copies, color or monochrome, IPP 1.0-2.2, TLS and basic/requesting-user-name authentication. Attributes not returned in this capture include printer-name, media-supported, sides-supported, and resolution-supported; the server exposes only its supported subset. 10. Validate-Job: the last step before a real job ------------------------------------------------- Validate-Job checks a complete job template without creating a job. This is the boundary of PROTOCOL2: the next operation (Create-Job) starts the job. Request (flow 18), same endpoint/headers as section 9: version 2.0, operation 0x0004 (Validate-Job), request-id 2 attributes-charset utf-8 attributes-natural-language en-us printer-uri http://127.0.0.1:9177/ipp/172.25.1.14_9164/ 466f6c6c6f772d4d65/printers/Follow-Me requesting-user-name anonymous job-name Test Page document-format application/pdf job-originating-host-name localhost job-uuid urn:uuid:99ca6116-d724-3b05-5a8b-b054f9f65c99 Body size 360 bytes (attributes only; no document data). Response: 200, application/ipp, 75 bytes, IPP status 0x0000 (successful-ok). No job-id is created. The response contains only operation attributes (charset/language). This shows the job template the captured client intended to submit: PDF, job-name "Test Page", anonymous requesting user, job UUID. Any real client must use the same operation/attribute structure and a valid Printer JWT. Boundary: the next captured request is Create-Job (0x0005, flow 20, 325 bytes, largely the same attributes but without document-format), followed by Send-Document (0x0006, flow 21) carrying the document. Those are the actual job and are outside this document. 11. Ready-to-print checklist ---------------------------- Minimum static setup obtained by this phase: [ ] Printer JWT (valid; check exp) [ ] Mobility Print base URL (from printer.connection.name) [ ] Per-user queue URL: GET /printer-url (Bearer JWT) [ ] Direct IPP endpoint: /ipp/{host}_{port}/{hex(printer)}/printers/{printer} [ ] Driver zip downloaded; PPD extracted (Follow-Me.ppd) [ ] catalog extras downloaded (ColorSync container on macOS) [ ] Capabilities .cap downloaded (DeviceURI, AuthInfoRequired, state) [ ] Static IPP attributes: formats, operations, media, color, copies [ ] Job template validated with Validate-Job (0x0004) To configure a local CUPS queue: install the PPD, set the device URI to the per-user URL from /printer-url (or the .cap DeviceURI depending on platform), and satisfy AuthInfoRequired username,password. Then a job can be created with Print-Job or Create-Job + Send-Document using the Printer JWT. 12. Hash behavior ----------------- The server advertises sha256 values, but the captured client did not appear to enforce them: artifact advertised captured body result --------------------------------------------------------------------------- Follow-Me.zip d1f63ebc... 41cacc2b... mismatch ColorSync.tar.zip a8fb40d2... 659fecd6... mismatch Follow-Me.cap 4d61c80c... 4d61c80c... match The captured client continued after the mismatches. Treat the hashes as advisory; strict verification is an implementation choice. (The reproduction tool `printdeploy_client.py` defaults to advisory and takes `--verify-sha256` for strict checking.) 13. Quirks and observations --------------------------- * Driver and capability downloads are unauthenticated GETs from the Print Deploy server (9174); no JWT or session token is sent. * `/printer-url` and all IPP calls require only the Printer JWT. * The direct IPP requests are sent to port 9164 under `/ipp/_/`, while the queue URL returned by `/printer-url` lives on port 9163. The capture never exercises the 9163 URL. * The `.cap` DeviceURI is the shared `ipps://...:9164/printers/Follow-Me`; it is different from both the per-user URL and the `/ipp/...` endpoint. * The client's IPP `printer-uri` attribute contains its local proxy address (127.0.0.1:9177), not the mobility server address; the server accepts it. * `X-Forwarded-For: 127.0.0.1` and a `Date` header are present on the IPP requests in the capture. * All three driver artifacts share `last-modified: 2026-09-08 08:02:26 GMT`; the PPD was produced by ipp2ppd on 2026-07-11, so the driver bundle is generic and recent. * The capabilities file says `Shared No` and `Accepting Yes` and `State Idle`; the IPP attributes likewise reported printer-state 3 (idle) and accepting. * The IPP response is 10597 bytes mostly because of the `media-col-supported` collection (765 member values), not because of many scalar attributes. * The server returns a minimal attribute set; do not expect printer-name, media-supported, sides-supported, or resolution-supported from this instance. * The Printer JWT is the only credential needed for the on-wire queue setup after PROTOCOL1; it is not bound to a printer, so use it only against the issuing Mobility Print instance. 14. Capture flow map (this document) ------------------------------------ flow 13 GET /printer-url?... 200 129 bytes flow 14 GET driver zip 200 3266 bytes flow 15 GET catalog extra (ColorSync.tar.zip) 200 1794625 bytes flow 16 GET capabilities .cap 200 404 bytes flow 17 IPP Get-Printer-Attributes (0x000b) req 1 200 10597 bytes flow 18 IPP Validate-Job (0x0004) req 2 200 75 bytes flow 19 IPP Get-Printer-Attributes (0x000b) req 3 200 10597 bytes flow 20 IPP Create-Job (0x0005) req 4 --- boundary, excluded flow 21 IPP Send-Document (0x0006, PDF data) --- excluded The mobility calls connect to 172.25.1.14:9164; the driver calls connect to 172.25.1.14:9174 with Host printdeploy.asg.nl. 15. Related tooling ------------------- PROTOCOL1.md credentials, OAuth, session, Printer JWT issuance extract_tokens.py dump tokens from the capture printdeploy_client.py interactive implementation through the driver downloads and (optionally) IPP job replay acquire_queue_info.py minimal acquisition of the Printer JWT and queue URL, written as YAML for a CUPS loopback relay print_pdf_jwt.py downstream: submit a PDF with only the Printer JWT Appendix A - constants and derived identifiers ---------------------------------------------- Print Deploy server https://printdeploy.asg.nl:9174 Mobility Print server https://172.25.1.14:9164 Per-user queue host:port http://172.25.1.14:9163 (returned URL) Printer name Follow-Me ASCII hex of printer 466f6c6c6f772d4d65 Driver zip /deploy/drivers/mac/26.6-arm64/Follow-Me.zip Capabilities /deploy/capabilities/mac/26.6-arm64/Follow-Me.cap Catalog extra /deploy/drivers/mac/26.6-arm64/container/ System/Library/ColorSync.tar.zip IPP path /ipp/172.25.1.14_9164/466f6c6c6f772d4d65/ printers/Follow-Me IPP version 2.0 Document formats application/pdf, image/urf Supported operations 0x0002, 0x0004, 0x0005, 0x0006, 0x0008, 0x0009, 0x000A, 0x000B IPP security/auth none/tls; requesting-user-name/basic Driver build date 2026-09-08 (all artifacts) PPD generator ipp2ppd v4, 2026-07-11 Printer UUID urn:uuid:c090f159-4fbb-34fd-7c59-c627cd40a7e6