Docs
Browser Print agent — protocol reference
The Linux agent exposes Zebra printers through two complementary surfaces: an HTTP/HTTPS API wire-compatible with Zebra's official Mac/Windows agent, and a session-D-Bus interface for native UIs. This page is the contract.
| Surface | Audience | Transport |
|---|---|---|
| HTTP/HTTPS | Web pages | localhost:9100 / localhost:9101 |
| D-Bus | Native UIs (libadwaita) | session bus, com.unifiedretail.ZebraBrowserPrint |
1. HTTP / HTTPS
Two listeners run side-by-side. Mixed-content rules in modern browsers force pages served over https: to call
https: — the official Mac/Windows agent splits the same way.
- HTTP:
127.0.0.1:9100(override viaZBPL_HTTP_ADDR) - HTTPS:
127.0.0.1:9101(override viaZBPL_HTTPS_ADDR) — TLS 1.2+, self-signed cert generated on first run, regenerated on expiry, stored in$XDG_CONFIG_HOME/pppro-browser-linux/tls.crt
First-time users get a browser warning on
https://localhost:9101 — the recommended onboarding flow is to visit the HTTPS URL
once, accept the certificate, then the production app's fetch() calls work for the
rest of the session.
Routes
All routes return JSON unless noted. Errors come back as
{"error": "...message..."} with the appropriate 4xx/5xx status.
GET|POST /available
Returns every printer the agent currently sees — USB devices via the kernel's usblp interface, plus any TCP-9100 network printers configured in network-printers.json.
{
"printer": [
{
"deviceType": "printer",
"uid": "D4J251202398",
"name": "ZTC ZD220-203dpi ZPL",
"connection": "usb",
"version": 0,
"provider": "com.zebra.printer",
"manufacturer": "Zebra Technologies"
},
{
"deviceType": "printer",
"uid": "net:192.168.1.42:9100",
"name": "Front Desk ZD420",
"connection": "network",
"version": 0,
"provider": "com.zebra.printer",
"manufacturer": "Zebra Technologies"
}
],
"deviceList": [ /* same content, legacy field */ ]
} uid is the stable identifier /write
and /read reference. For USB it's the printer's serial number when present, falling
back to the device path. For network it's net:host:port.
connection is "usb" or "network". Browser Print's
Mac/Windows agent only ever returns
"usb"; the "network" value is a Linux extension that browser-side code
can ignore (treat it like
"usb") or use for UI affordances.
GET|POST /default
Returns the same shape as a single entry in
/available.printer[0], or an empty {}
if no printers are connected. USB takes priority over network when both exist.
POST /write
Body:
{
"device": { "uid": "D4J251202398" },
"data": "^XA\n^FO50,50^A0N,40,40^FDHello^FS\n^XZ\n"
} Status codes:
200 OK— bytes were handed off to the printer400 Bad Request— malformed JSON403 Forbidden— origin not in the allowlist (see § 3)404 Not Found— no printer with that UID500 Internal Server Error— kernel/socket write failed
data is opaque bytes; the agent makes no semantic checks. Any ZPL or SGD payload is
fine.
POST /read
Body: same device.uid as /write. No
data field. Returns text/plain with whatever the printer has emitted within
250 ms (the protocol's official "give up" timeout). Empty body is normal.
2. D-Bus
Native UIs talk to the agent over the session bus. The interface is intentionally narrow — only the operations a UI actually needs.
- Bus name:
com.unifiedretail.ZebraBrowserPrint - Object path:
/com/unifiedretail/ZebraBrowserPrint - Interface:
com.unifiedretail.ZebraBrowserPrint1
Introspection works (gdbus introspect --session ...) so any typed binding
generator (zbus' zbus_codegen, Vala's gdbus-codegen, etc.) can
produce a strongly- typed client from the running agent.
Methods
| Name | Signature | Description |
|---|---|---|
GetVersion | () → (s) | Agent version, e.g. "0.3.0". |
ListOrigins | () → (a(sst)) | Approved origins. Each entry: (origin, source, approved_at_unix). source is "prompt", "cli", or "env". |
Approve | (s) → () | Approve origin (with source "cli"). |
Revoke | (s) → () | Remove origin. |
ListPrinters | () → (a(ssss)) | Connected printers — USB + network. Each entry: (uid, name, manufacturer, serial). |
GetAutostartState | () → (s) | "enabled", "disabled", or "unsupported". |
SetAutostart | (b) → () | Enable/disable the systemd user unit. |
Errors return as standard D-Bus error names under
com.unifiedretail.ZebraBrowserPrint.Error.*:
ApproveFailed, RevokeFailed,
DiscoverFailed, AutostartFailed.
Signals
-
OriginPendingApproval(s, s)— fires the moment the HTTP CORS gate sees an unknown origin. Args:(origin, token). UI subscribers show a native approval dialog; the HTML approval flow at/__approve?token=…stays available as a fallback.
3. Per-origin allowlist
The agent's CORS gate refuses any cross-origin POST/GET that doesn't match an allowlisted origin. This is the security boundary between "any random web page can print on the user's computer" and "only the apps the user has explicitly approved can print".
- Storage: JSON at
$XDG_CONFIG_HOME/pppro-browser-linux/allowed_origins.json, mode 0600, atomic writes. - Approval flow: 403 +
approveUrlin the JSON body for HTML fallback; D-BusOriginPendingApprovalsignal for native UIs. Tokens are 32-byte CSPRNG, 5-minute TTL, single-use. - Pre-seeding (kiosk):
--allow URLon the CLI persists to disk;ZBPL_ALLOWED_ORIGINSenv-var seeds in-memory only.
4. Printer addressing
- USB: auto-discovered via
/dev/usb/lp*filtered on USB vendor0x0a5f(Zebra Technologies). UID = serial when present, else device path. Permissions handled by the shipped udev rule (TAG+="uaccess"). - Network (TCP 9100): not auto-discovered. Register with
pppro-browser-linux --add-printer "Name=host:port"; port defaults to 9100. Persisted to$XDG_CONFIG_HOME/pppro-browser-linux/network-printers.json. UID =net:host:port.
5. Versioning
The interface name com.unifiedretail.ZebraBrowserPrint1
carries an explicit 1 suffix so future incompatible D-Bus changes can ship as Interface2 without breaking clients. The HTTP routes follow Browser Print's conventions and don't carry a
version.
Last updated: 8 May 2026 — agent version 0.3.0.