Archive the add-cups-printserver-container change
Moves the completed change to openspec/changes/archive/2026-10-18-add-cups-printserver-container/ and syncs the printserver/cups capability into openspec/specs/.
This commit is contained in:
1 parent
30c685e085
commit
adaa11e416
6 files changed
+123
No files matched your search
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-10-17
|
||||
@@ -0,0 +1,105 @@
|
||||
# Design
|
||||
|
||||
## Context
|
||||
|
||||
See proposal.md. We need a Podman container that runs CUPS for the Brother
|
||||
HL-4150CDN network printer and shares it over IPP/AirPrint.
|
||||
|
||||
Facts established while exploring the device at `192.168.100.7`:
|
||||
|
||||
- The printer is a Brother HL-4150CDN (`Ver.1.22`) with PCL6 and **BR-Script3
|
||||
(PostScript 3)** emulation — `@PJL INFO CONFIG` lists `POSTSCRIPT`.
|
||||
- Open services: `:9100` (raw/AppSocket), `:515` (LPR/LPD), `:631` (IPP/IPPS)
|
||||
and `:80` (Brother web UI). It has one paper cassette plus the MP tray.
|
||||
- A raw PostScript job sent to `:9100` is interpreted by the printer, and a full
|
||||
CUPS job with `Duplex=DuplexNoTumble BRPrintQuality=Color` printed correctly
|
||||
(duplex, colour).
|
||||
|
||||
An earlier iteration used Brother's Linux packages. That vendor CUPS chain is
|
||||
broken in `debian:bookworm-slim`: `filterhl4150cdn` needs `file` (and `a2ps` as
|
||||
a fallback), which are absent, so the filter exits 1 and emits zero bytes. That
|
||||
chain also rasterises locally (Ghostscript → `ppmraw` → `brhl4150cdnfilter`) and
|
||||
stores per-queue options in a single global file (`brhl4150cdnrc`), which is
|
||||
heavy and racy. The packages have since been removed from the repository.
|
||||
|
||||
Crucially, the Brother **vendor** PPD (`brhl4150cdn.ppd`) cannot be used for
|
||||
direct PostScript because its option values are blank (`" "`); all
|
||||
option handling lives in the vendor filter. Brother's **BR-Script3 PPD**, in
|
||||
contrast, encodes every option as PostScript `setpagedevice`.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Build a working CUPS image for Podman with no 32-bit dependencies.
|
||||
- Drive the printer with its native PostScript (BR-Script3) interpreter.
|
||||
- Preserve the hardware options the user cares about: paper tray, duplex and
|
||||
colour/mono (plus the other options the PPD exposes).
|
||||
- Share the queue over IPP and advertise it via AirPrint.
|
||||
- Keep the printer address/name runtime-configurable.
|
||||
|
||||
**Non-Goals:**
|
||||
- Managing multiple printers.
|
||||
- Sharing via SMB/Samba (Windows uses a client-side Brother driver).
|
||||
- Persisting CUPS spool/state beyond the container lifecycle.
|
||||
- TLS for the CUPS web interface.
|
||||
|
||||
## Decisions
|
||||
|
||||
### Base image and packages
|
||||
Use `debian:bookworm-slim` with `cups`, `cups-client`, `cups-filters`,
|
||||
`avahi-daemon`, `avahi-utils`, `libnss-mdns`, `dbus`, `ghostscript` and
|
||||
`poppler-utils`. No `dpkg --add-architecture i386` and no Brother `.deb`
|
||||
packages: the job is converted to PostScript in CUPS and interpreted by the
|
||||
printer, so no vendor binary is needed.
|
||||
|
||||
_Alternative considered:_ keep Brother's Linux CUPS wrapper + LPR filter. It
|
||||
provides the same options but requires 32-bit libraries, Ghostscript
|
||||
rasterisation and the missing `file`/`a2ps` tools, and it writes options to a
|
||||
single global config file. Rejected.
|
||||
|
||||
### Print path: direct PostScript via the BR-Script3 PPD
|
||||
Install Brother's official BR-Script3 PPD as
|
||||
`/usr/share/cups/model/BR4150_2.PPD` (committed at
|
||||
`assets/Brother HL-4150CDN/ppd/BR4150_2.PPD`, extracted from the vendor Windows
|
||||
driver) and create the queue with it. CUPS's `pstops` filter reads the PPD and
|
||||
injects the selected option code (e.g. `/BRFeeder`, `/Duplex`,
|
||||
`/BRProcessColor`, `/MediaType`) into the job; the PPD's `*JCLBegin`,
|
||||
`*JCLToPSInterpreter` and `*JCLEnd` entries wrap it in PJL. The result is
|
||||
forwarded unchanged to the printer.
|
||||
|
||||
A stock PostScript PPD (like CUPS's own `generic.ppd`) has no `*cupsFilter`
|
||||
either, and CUPS accepts such PPDs — the printer's native format is
|
||||
`application/vnd.cups-postscript`.
|
||||
|
||||
### Device URI
|
||||
Use raw AppSocket as the default: `socket://<ip>:9100`. `lpd://<ip>/binary_p1`
|
||||
and `ipp://…` remain available through `PRINTER_PROTOCOL`/`PRINTER_URI`.
|
||||
|
||||
### Client support
|
||||
- macOS / iOS / iPadOS: AirPrint → CUPS. The queue advertises `image/urf`,
|
||||
`image/pwg-raster`, `application/pdf` and `application/postscript`, with
|
||||
`urf-supported = V1.4,CP1,W8,PQ4,SRGB24,RS600,DM1,FN3`, `sides-supported`
|
||||
(one-sided/two-sided long/short edge) and `color-supported = true`.
|
||||
- Linux: IPP / IPP Everywhere, or plain IPP with PDF/PostScript.
|
||||
- Windows: client-side Brother BR-Script3 Windows driver, pointed at the
|
||||
printer directly or at the shared queue.
|
||||
|
||||
### Runtime configuration
|
||||
The printer endpoint and advertised name come from environment variables
|
||||
(`PRINTER_IP`, `PRINTER_PROTOCOL`, `PRINTER_URI`, `PRINTER_NAME`,
|
||||
`PRINTER_DESCRIPTION`, `PPD_FILE`) rather than being baked into the image. The
|
||||
entrypoint derives the URI and runs `lpadmin` on startup, then shares the queue.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Risk] The AirPrint path (URF → PDF → PostScript) relies on cups-filters'
|
||||
`rastertopdf`; it was not exercised with a real URF job during exploration.
|
||||
macOS/iOS printing should be verified end to end.
|
||||
- [Risk] Colour handling now relies on the printer's PostScript RIP rather than
|
||||
Brother's host-side colour tables; output may differ slightly from the vendor
|
||||
raster driver.
|
||||
- [Risk] The committed PPD is a Windows PPD and contains no CUPS-specific keys.
|
||||
CUPS accepts it as a PostScript PPD (verified for this CUPS version), but this
|
||||
should be re-checked on CUPS upgrades.
|
||||
- [Risk] Avahi needs host networking (or equivalent) to advertise mDNS.
|
||||
- [Risk] CUPS state is not persisted; the queue is recreated on every start.
|
||||
@@ -0,0 +1,57 @@
|
||||
# Proposal
|
||||
|
||||
## Why
|
||||
|
||||
Run a Podman container that shares the Brother HL-4150CDN network printer over
|
||||
IPP/AirPrint, so clients on macOS, iOS/iPadOS, Linux and Windows can print.
|
||||
|
||||
The HL-4150CDN already emulates BR-Script3 (PostScript 3) and accepts PostScript
|
||||
on its AppSocket/LPD ports, so the container can forward PostScript directly to
|
||||
the printer instead of shipping Brother's 32-bit Linux CUPS wrapper plus
|
||||
Ghostscript rasterisation. This removes the i386 dependency, the multiarch
|
||||
runtime and the fragile vendor filter chain.
|
||||
|
||||
The previous approach (Brother Linux `.deb` packages) turned out not to work in
|
||||
`debian:bookworm-slim`: the vendor filter `filterhl4150cdn` needs `file` (with
|
||||
`a2ps` as a fallback), which are not installed, so it exits with an error and
|
||||
produces no output. The official BR-Script3 PPD avoids that entire code path.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Create a Containerfile for Podman based on Debian stable running CUPS, Avahi
|
||||
and D-Bus (no i386 multiarch).
|
||||
- Install Brother's official **BR-Script3 PPD** (committed under
|
||||
`assets/Brother HL-4150CDN/ppd/`) so CUPS emits device options — paper source
|
||||
(tray), duplex and colour/mono, plus media type, print quality, toner save and
|
||||
secure print — as PostScript `setpagedevice`.
|
||||
- Point the queue at the printer with a raw AppSocket URI (`socket://<ip>:9100`)
|
||||
by default.
|
||||
- Convert incoming client formats (URF/Apple Raster, PWG Raster, PDF, PostScript)
|
||||
to PostScript inside CUPS and pass them through to the printer's PostScript
|
||||
interpreter.
|
||||
- Share the queue over IPP and advertise it via mDNS/DNS-SD (Avahi/AirPrint).
|
||||
- Keep runtime configuration (printer address, protocol, queue name) in
|
||||
environment variables.
|
||||
- Document the supported client matrix.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `printserver/cups`: Containerised CUPS print server which shares the Brother
|
||||
HL-4150CDN over IPP/AirPrint and prints by sending PostScript (BR-Script3)
|
||||
directly to the printer.
|
||||
|
||||
### Modified Capabilities
|
||||
None
|
||||
|
||||
## Impact
|
||||
|
||||
- Containerfile plus supporting `cupsd.conf` and `entrypoint.sh` in the
|
||||
repository root.
|
||||
- Uses `assets/Brother HL-4150CDN/ppd/BR4150_2.PPD`.
|
||||
- Runtime ports: CUPS/IPP (631/tcp), mDNS/DNS-SD (5353/udp).
|
||||
- No 32-bit runtime libraries; the image no longer depends on the host CPU
|
||||
architecture.
|
||||
- Windows clients install Brother's own BR-Script3 Windows driver client-side.
|
||||
- The unused Brother Linux `.deb` packages previously under
|
||||
`assets/Brother HL-4150CDN/` have been removed.
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
# Spec Delta
|
||||
|
||||
## Purpose
|
||||
|
||||
Provide a containerized CUPS print server that shares the Brother HL-4150CDN
|
||||
network printer as an AirPrint/IPP printer and prints by sending PostScript
|
||||
directly to the printer, so clients can print without a server-side vendor
|
||||
driver.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Container image builds with required dependencies
|
||||
The container SHALL include CUPS, cups-filters, Avahi/mDNS, D-Bus and the
|
||||
conversion tools needed to turn client documents into PostScript, and SHALL NOT
|
||||
require any 32-bit runtime libraries.
|
||||
|
||||
#### Scenario: Image contains CUPS
|
||||
- **WHEN** the image is built
|
||||
- **THEN** CUPS is installed and available at /usr/sbin/cupsd
|
||||
|
||||
#### Scenario: No 32-bit runtime required
|
||||
- **WHEN** the image is built
|
||||
- **THEN** the image contains no i386 multiarch libraries and no Brother vendor binaries
|
||||
|
||||
### Requirement: Brother BR-Script3 PPD is installed
|
||||
The container SHALL install Brother's official HL-4150CDN BR-Script3 PPD from
|
||||
the committed asset so CUPS can emit device options as PostScript.
|
||||
|
||||
#### Scenario: PPD present
|
||||
- **WHEN** the image is built
|
||||
- **THEN** the BR-Script3 PPD is present in the CUPS model directory
|
||||
|
||||
#### Scenario: CUPS recognises the model
|
||||
- **WHEN** the queue is created with the PPD
|
||||
- **THEN** CUPS reports the printer make and model as "Brother HL-4150CDN BR-Script3"
|
||||
|
||||
### Requirement: Hardware options are sent to the printer
|
||||
The system SHALL translate the PPD's device options into PostScript
|
||||
`setpagedevice` instructions so the printer's own hardware options are used.
|
||||
|
||||
#### Scenario: Paper source
|
||||
- **WHEN** a job selects an input slot (auto select, a tray or the MP tray)
|
||||
- **THEN** the emitted PostScript selects the corresponding paper source on the printer
|
||||
|
||||
#### Scenario: Duplex
|
||||
- **WHEN** a job selects long-edge or short-edge two-sided printing
|
||||
- **THEN** the emitted PostScript sets `/Duplex` and `/Tumble` accordingly
|
||||
|
||||
#### Scenario: Colour or mono
|
||||
- **WHEN** a job selects colour or mono
|
||||
- **THEN** the emitted PostScript sets the corresponding colour/mono device parameters
|
||||
|
||||
### Requirement: Jobs are printed via direct PostScript
|
||||
The container SHALL forward the converted job as PostScript to the printer
|
||||
without a server-side vendor filter or host-side rasterisation.
|
||||
|
||||
#### Scenario: Default device URI
|
||||
- **WHEN** the container is started with a printer IP and no explicit URI
|
||||
- **THEN** the queue is created with a raw AppSocket URI (`socket://<ip>:9100`)
|
||||
|
||||
#### Scenario: A job is printed
|
||||
- **WHEN** a print job is submitted to the queue
|
||||
- **THEN** the job is delivered to the printer as PostScript and printed by the printer's PostScript interpreter
|
||||
|
||||
### Requirement: Printer is shared and discoverable
|
||||
The system SHALL share the queue over IPP and advertise it via AirPrint
|
||||
(mDNS/DNS-SD).
|
||||
|
||||
#### Scenario: IPP printer is shared
|
||||
- **WHEN** CUPS runs in the container
|
||||
- **THEN** the printer is shared via IPP for network clients
|
||||
|
||||
#### Scenario: AirPrint discovery
|
||||
- **WHEN** Avahi runs alongside CUPS
|
||||
- **THEN** the printer is advertised via AirPrint with URF, sides and colour capabilities so macOS and iOS/iPadOS clients can discover it without a driver
|
||||
|
||||
### Requirement: Supported client platforms
|
||||
The shared printer SHALL be usable from macOS, iOS/iPadOS, Linux and Windows.
|
||||
|
||||
#### Scenario: macOS and iOS/iPadOS
|
||||
- **WHEN** a macOS or iOS/iPadOS client prints via AirPrint
|
||||
- **THEN** the job is converted to PostScript and printed
|
||||
|
||||
#### Scenario: Linux
|
||||
- **WHEN** a Linux client prints over IPP
|
||||
- **THEN** the job is converted to PostScript and printed
|
||||
|
||||
#### Scenario: Windows
|
||||
- **WHEN** a Windows client prints
|
||||
- **THEN** it uses a client-side BR-Script3 driver against the printer or the shared queue
|
||||
|
||||
### Requirement: Runtime configuration is exposed
|
||||
The container SHALL expose CUPS on port 631 and mDNS on UDP port 5353.
|
||||
|
||||
#### Scenario: CUPS port exposed
|
||||
- **WHEN** the container runs
|
||||
- **THEN** port 631 is available for CUPS administration and IPP
|
||||
|
||||
#### Scenario: mDNS port exposed
|
||||
- **WHEN** the container runs
|
||||
- **THEN** UDP port 5353 is available for AirPrint discovery
|
||||
|
||||
### Requirement: Printer endpoint and advertised name are configurable at runtime
|
||||
The container SHALL accept the network printer address and the advertised printer name through environment variables so the same image can target a different printer or name without rebuilding.
|
||||
|
||||
#### Scenario: Printer IP is provided
|
||||
- **WHEN** the container is started with `PRINTER_IP` set
|
||||
- **THEN** a device URI is derived from that address and the Brother HL-4150CDN queue is created pointing at it
|
||||
|
||||
#### Scenario: Printer protocol is selected
|
||||
- **WHEN** the container is started with `PRINTER_IP` and `PRINTER_PROTOCOL`
|
||||
- **THEN** the device URI uses that protocol (socket, lpd or ipp), defaulting to socket
|
||||
|
||||
#### Scenario: Full URI override
|
||||
- **WHEN** `PRINTER_URI` is set
|
||||
- **THEN** it is used as the device URI and `PRINTER_IP` is ignored
|
||||
|
||||
#### Scenario: Custom advertised name
|
||||
- **WHEN** the container is started with `PRINTER_NAME`
|
||||
- **THEN** the CUPS queue uses that name and it is the name advertised via AirPrint/DNS-SD
|
||||
|
||||
#### Scenario: Custom CUPS description
|
||||
- **WHEN** the container is started with `PRINTER_DESCRIPTION`
|
||||
- **THEN** the queue's CUPS Description (Info) is set to that value
|
||||
@@ -0,0 +1,33 @@
|
||||
# Tasks
|
||||
|
||||
## 1. Repository initialization
|
||||
- [x] 1.1 Initialize jujutsu repository with `jj git init` in repo root and verify `.jj` directory exists
|
||||
|
||||
## 2. BR-Script3 PostScript PPD asset
|
||||
- [x] 2.1 Extract Brother's official HL-4150CDN BR-Script3 PPD (`eng/BR4150_2.PPD`) from the vendor Windows driver archive and commit it under `assets/Brother HL-4150CDN/ppd/BR4150_2.PPD`
|
||||
- [x] 2.2 Document the provenance and the option→`setpagedevice` mapping in `assets/Brother HL-4150CDN/ppd/README.md`
|
||||
|
||||
## 3. Container build
|
||||
- [x] 3.1 Create Containerfile based on Debian stable with CUPS, cups-filters, Avahi, D-Bus, Ghostscript and Poppler (no i386 multiarch)
|
||||
- [x] 3.2 Install the BR-Script3 PPD into the CUPS model directory in the Containerfile
|
||||
- [x] 3.3 Remove the Brother vendor `.deb` installation, i386 multiarch and the 32-bit loader setup from the Containerfile
|
||||
- [x] 3.4 Keep cupsd.conf (listen on all interfaces, share printers, DNS-SD) and the required runtime directories
|
||||
- [x] 3.5 Set up the startup script to run dbus, avahi-daemon and cupsd, and create the queue
|
||||
- [x] 3.6 Expose ports 631/tcp and 5353/udp in the Containerfile
|
||||
|
||||
## 4. Printer configuration
|
||||
- [x] 4.1 Create the queue with the BR-Script3 PPD and a raw AppSocket URI by default (`socket://<ip>:9100`)
|
||||
- [x] 4.2 Keep environment-based configuration (`PRINTER_IP`, `PRINTER_PROTOCOL`, `PRINTER_URI`, `PRINTER_NAME`, `PRINTER_DESCRIPTION`, `PPD_FILE`)
|
||||
- [x] 4.3 Share the queue and set it as default; verify AirPrint capabilities (URF, sides, colour)
|
||||
|
||||
## 5. Documentation and testing
|
||||
- [x] 5.1 Update README for the PostScript/BR-Script3 approach and the client matrix (macOS, iOS/iPadOS, Linux, Windows)
|
||||
- [x] 5.2 Build the image with podman and verify it builds
|
||||
- [x] 5.3 Run the container and verify CUPS is reachable and the queue is shared/discoverable
|
||||
- [x] 5.4 Submit a job (colour + duplex) and verify it prints correctly
|
||||
- [x] 5.5 Verify `PRINTER_IP`/`PRINTER_PROTOCOL`/`PRINTER_NAME` derive the expected queue URI and advertised name
|
||||
|
||||
## 6. Cleanup
|
||||
- [x] 6.1 Add `.gitignore` for macOS metadata and stop tracking existing `.DS_Store` files
|
||||
- [x] 6.2 Remove the unused Brother Linux vendor packages (`lpr-driver/`, `cupswrapper/`, `installer/`) from the repository
|
||||
- [x] 6.3 Prune the obsolete `printserver:amd64` image, the test containers and scratch files
|
||||
Reference in new issue
Block a user