diff --git a/openspec/changes/add-cups-printserver-container/.openspec.yaml b/openspec/changes/archive/2026-10-18-add-cups-printserver-container/.openspec.yaml similarity index 100% rename from openspec/changes/add-cups-printserver-container/.openspec.yaml rename to openspec/changes/archive/2026-10-18-add-cups-printserver-container/.openspec.yaml diff --git a/openspec/changes/add-cups-printserver-container/design.md b/openspec/changes/archive/2026-10-18-add-cups-printserver-container/design.md similarity index 100% rename from openspec/changes/add-cups-printserver-container/design.md rename to openspec/changes/archive/2026-10-18-add-cups-printserver-container/design.md diff --git a/openspec/changes/add-cups-printserver-container/proposal.md b/openspec/changes/archive/2026-10-18-add-cups-printserver-container/proposal.md similarity index 100% rename from openspec/changes/add-cups-printserver-container/proposal.md rename to openspec/changes/archive/2026-10-18-add-cups-printserver-container/proposal.md diff --git a/openspec/changes/add-cups-printserver-container/specs/printserver/cups/spec.md b/openspec/changes/archive/2026-10-18-add-cups-printserver-container/specs/printserver/cups/spec.md similarity index 100% rename from openspec/changes/add-cups-printserver-container/specs/printserver/cups/spec.md rename to openspec/changes/archive/2026-10-18-add-cups-printserver-container/specs/printserver/cups/spec.md diff --git a/openspec/changes/add-cups-printserver-container/tasks.md b/openspec/changes/archive/2026-10-18-add-cups-printserver-container/tasks.md similarity index 100% rename from openspec/changes/add-cups-printserver-container/tasks.md rename to openspec/changes/archive/2026-10-18-add-cups-printserver-container/tasks.md diff --git a/openspec/specs/printserver/cups/spec.md b/openspec/specs/printserver/cups/spec.md new file mode 100644 index 0000000..4746df1 --- /dev/null +++ b/openspec/specs/printserver/cups/spec.md @@ -0,0 +1,123 @@ +# printserver/cups Specification + +## 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. + +## 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://: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