PrintToVeraeTimeStamp/docs/network-printer.md
Cursor Agent 5ed87da68f
Some checks failed
installers / netprint (push) Has been cancelled
installers / macos-pkg (push) Has been cancelled
installers / publish (push) Has been cancelled
Keep the macOS receipts SMB share across reboots.
Save the installer choice as shareReceiptsSMB, recreate the share when it is missing, and show the live smb:// address on the receipts page.

Co-authored-by: George Lambert <marchon@gmail.com>
2026-10-05 16:34:51 +00:00

20 KiB

Verae network timestamp printer

Optional IPP Everywhere printer. Phones and other computers on the LAN print to it. The default name is Verae Timestamp plus this computer's name, for example Verae Timestamp (Georges-MacBook-Pro). Settings can change it. A saved name that is still the old shared Verae Timestamp is upgraded on the next start. If that name is already registered on the network, mDNS renames it to Name (2) and the new name is saved. This machine hashes the document, registers the SHA-256 with Verae the same way the desktop drivers do, and keeps the receipt. Document bytes are not uploaded.

The printer is off until you install the standalone package or turn on the driver installer option. Both paths install one service and one tray / menu-bar control. They do not share an install folder: the standalone package never overwrites the driver binary. If both are installed, the service runs whichever binary is the same version or newer, and removing one leaves the other in place.

Windows macOS Debian / Ubuntu
Standalone installer VeraeNetPrint-windows-*-setup.exe VeraeNetPrint-macos-arm64-0.3.4.pkg verae-netprint_*_amd64.deb
Driver installer option Unchecked checkbox on the options page Customize → “Network timestamp printer” (unselected) debconf “Enable the Verae network timestamp printer?” (default no), or apt install verae-netprint
Service VeraeNetPrint LaunchDaemon com.verae.netprint verae-netprint.service
Tray / menu bar verae-netprint-tray.exe (notification area) LaunchAgent com.verae.netprint.tray (/Applications/Verae NetPrint.app, Aqua session) verae-print tray (StatusNotifier / AppIndicator)
Standalone command %ProgramFiles%\Verae\NetPrint\bin\verae-print.exe /Library/Application Support/Verae/NetPrint/bin/verae-print /usr/lib/verae-netprint/verae-print
Driver command %ProgramFiles%\Verae\PrintStamp\bin\verae-print.exe /Library/Application Support/Verae/PrintStamp/bin/verae-print /usr/lib/verae-print/verae-print
CLI on PATH the driver binary when the driver is installed /usr/local/bin/verae-print points at the driver when it is installed, otherwise at NetPrint /usr/bin/verae-print points at the driver when it is installed, otherwise at NetPrint
Config %ProgramData%\Verae\PrintStamp\netprint.json /Library/Application Support/Verae/PrintStamp/netprint.json /etc/verae/netprint.json
Receipts %ProgramData%\Verae\NetPrint /Users/Shared/Verae Receipts /var/lib/verae-netprint

The desktop driver's loopback helper stays on 127.0.0.1:8631. The network printer does not use that port.

Try it

  1. Install using either path above. The Windows setup starts the tray for the logged-in user at the end of install (not elevated), and again at the next login. macOS and Linux start the menu bar at the next login.
  2. Open Settings from the icon and paste the same xVerae- signing key the drivers use (or sign in first with verae-print setup on that machine). The service stores the key; the tray does not edit config files.
  3. On another device, add the printer. Its default name includes the computer, for example Verae Timestamp (Georges-MacBook-Pro). It is advertised with DNS-SD (_ipp._tcp, plus the AirPrint _universal and IPP Everywhere _print subtypes) and AirPrint text records. On macOS, Advertise on the network (on by default) calls DNSServiceRegister with regtype _ipp._tcp,_universal,_print and kDNSServiceFlagsNoAutoRename. mDNSResponder publishes the subtype PTRs aimed at <name>._ipp._tcp.local, which is what dns-sd -B _ipp._tcp,_universal and dns-sd -B _ipp._tcp,_print accept. A second registration advertises _verae._tcp. The host argument is empty so mDNSResponder uses this Mac's hostname and addresses. The UDP 5353 socket stays closed while that registration is up, so the printer is not listed twice. If the name is already in use, the callback reports the conflict and both services are registered again as Name (2). That name is saved and becomes the IPP printer name. If DNSServiceRegister fails for another reason, the socket advertises the full set instead. On Windows 10 and later DnsServiceRegister publishes only <name>._ipp._tcp.local, including its SRV, TXT, and address records. It cannot publish a subtype PTR that points at that name, so the service also opens a shared UDP 5353 socket (SO_REUSEADDR, joined to 224.0.0.251 and ff02::fb) that answers only _universal._sub._ipp._tcp and _print._sub._ipp._tcp. Those PTRs point at <name>._ipp._tcp.local. The UDP socket does not answer the base _ipp._tcp PTR and does not send SRV, TXT, or address records, so the two stacks do not advertise two copies of the printer. If DnsServiceRegister fails, the UDP socket advertises the full set instead. dns-sd -B _ipp._tcp and dns-sd -B _ipp._tcp,_universal each list the printer once. On macOS, cupsd keeps 127.0.0.1:631 and [::1]:631. This printer binds 631 only on the Mac's non-loopback addresses, so printing from another device uses port 631 and localhost CUPS is unchanged. mDNS advertises 631. If none of those LAN binds succeed, it listens on 8632 on every interface and advertises 8632. Status JSON ippBind is lan or all, and port is the port that was bound. On Windows and Linux the listener is IPv4 plus [::] with IPV6_V6ONLY, trying 631 and falling back to 8632 when 631 is taken. It does not use 8631. A connect to the host's link-local IPv6 address completes; our own A/AAAA records omit fe80 and 169.254. Get-Printer-Attributes includes media-col-default for US Letter and A4 and document-format-supported lists the same formats as the TXT pdl record: PDF, Apple Raster, PWG Raster, and JPEG.
  4. Print a PDF. Open Receipts from the tray, or browse to http://<lan-address>:<port>/receipts (the AirPrint admin URL). The page lists recent jobs, newest first, with the stamped PDF and the original. Each job also has a verify link on https://explorer.verae.ai/time.

The icon shows running or stopped, the printer name, a LAN address and port, how many jobs arrived today, whether discovery is advertising, and the receipts URL. It can start, stop, and restart the service, turn launch-at-login on or off for the service and for the icon, turn Email stamped PDFs on or off, open recent receipts, and open settings (printer name, forward-to printer, signing key, mail, and the receipts PIN). On Windows it can also allow Public networks. When the active network is Public and that option is off, the menu says other devices cannot reach the printer.

Email

Settings and the tray can mail each completed job. The message subject is Verae timestamp: <job title>. The body contains the receipt ID, the SHA-256, and the .verae.json receipt summary. The certificate PDF and the original document are attached. The default recipient is marchon@gmail.com, and the default server is smtp.gmail.com port 587 with STARTTLS. A Gmail app password works: the username is the Gmail address and the password is the 16-character app password. Mail stays off until the toggle is on and a password is saved. A failure is written to the log and to emailError in the status JSON, then retried in the background. It does not fail the print.

The SMTP password and the optional receipts PIN are not stored in netprint.json. On Windows they are sealed with DPAPI for the service account. On macOS and Linux they are in netprint-secrets.bin next to the config, mode 0600. Neither value is written to the log.

Receipts on the phone

http://<host>:<port>/receipts is served on the same port as IPP, so the existing Private and Domain firewall rules cover it. Public networks stay blocked unless that option is on. An optional PIN (or ?access= token) can be required on the LAN. The loopback control page does not ask for the PIN. The menu bar Open Receipts… item opens the live receiptsUrl from status (for example http://192.168.1.108:631/receipts or http://<hostname>.local:631/receipts), not a fixed address. Open Settings… opens the control page. The menu also shows how to print from an iPhone: Share → Print → Select Printer → the advertised printer name. The status JSON fields are receiptsUrl and receiptsDir. On macOS receiptsDir is /Users/Shared/Verae Receipts. That folder holds the job files and is readable by the console user. The macOS installer Customize pane has Share receipts folder over the network (SMB), off unless you check it. When checked, postinstall shares that folder as Verae Receipts (sharing -a with -s 001 -g 000: SMB only, guest off) and enables com.apple.smbd so File Sharing stays on across reboots. The choice is saved as shareReceiptsSMB in netprint.json. Settings and the menu bar can turn it on or off later. If the choice is on and the share is missing, the daemon recreates it at startup, and the menu-bar app asks it to again at login. Status JSON includes shareReceiptsSMB and smbUrl (for example smb://Georges-MacBook-Pro.local/Verae%20Receipts). The receipts page shows that address. The first connection from iPhone Files needs a username and password from this Mac. Guest access is off. The Windows installers have the same checkbox, off unless you check it or pass /SMB=1, and create a read-only share named Verae Receipts. The program stays in /Library/Application Support/Verae/NetPrint/bin and is not in that folder. An older install that stored jobs next to the program moves those job files on the next start. The page links to the iPhone shortcut and shows a QR code for http://<host>:<port>/ios-shortcut.

Stamp and return

POST http://<host>:<port>/api/stamp accepts a PDF or JPEG, either as the raw body (set X-Filename or Content-Disposition for the title) or as multipart with a file field. It runs the same pipeline as Print-Job, including the same stampWithClient call the desktop driver uses (job.Run against the Verae client, with the document title as the file name). The job is stored, it shows on the receipts page, and it is emailed when email is on. The response is the stamped certificate PDF. Headers X-Verae-Receipt-Id, X-Verae-SHA256, and X-Verae-Receipt-JSON identify the receipt. ?format=zip returns original.pdf, certificate.pdf, and receipt.json. A receipt id beginning with mock-cert is refused, so a test double is not returned outside test builds. The same Private and Domain rule and the same PIN apply. Send the PIN as ?access=, the X-Verae-Access header, or Authorization: Bearer. A failure does not return a PDF.

The server also advertises _verae._tcp (PTR, SRV, and TXT path=/api/stamp) beside the IPP printer. That record is a different service name, so it does not add a second _ipp._tcp advertisement. While Windows DNS-SD owns the printer, the shared UDP socket still answers only the AirPrint subtype PTRs for _ipp._tcp and does not add IPP SRV, TXT, or address records. The _verae._tcp SRV and TXT go out on that socket because DNS-SD does not register that name. Address records stay with the printer advertisement.

The Share sheet steps are in docs/ios-shortcut/README.md. A signed .shortcut file is not in this tree: signing needs the Shortcuts app on a Mac or iPhone. The design for a native share extension in the Verae Timestamping iOS app (com.flightlookup.verae-timestamping) is docs/ios-shortcut/share-extension.md.

Forward-to is optional: a local queue name, or an ipp:// URI. A failed forward does not fail the timestamp.

Firewall

The Windows installers add inbound rules named Verae NetPrint IPP (TCP 631), Verae NetPrint IPP Fallback (TCP 8632), and Verae NetPrint mDNS (UDP 5353) on the private and domain profiles. Each rule sets remoteip=any, which includes IPv4, global IPv6, and link-local fe80::/10. A checkbox, off by default, also allows Public networks. A silent install can pass /PUBLIC=1. The tray has the same switch. The rules are removed when the last NetPrint product is uninstalled. Port 8631 is not opened; that is the driver helper on loopback.

An optional checkbox (silent /QUEUE=1) adds a local Windows printer named Verae Timestamp aimed at http://127.0.0.1:<bound-port>/ipp/print. It is removed when the last NetPrint product is uninstalled. The driver printer Verae TimeStamp PDF is a different queue.

On Debian and Ubuntu, postinst opens the same three ports with ufw when ufw is installed and already active. The rules are removed when the last package is removed.

On macOS, the Application Firewall is per application (System Settings → Network → Firewall). Install runs /usr/libexec/ApplicationFirewall/socketfilterfw --add and --unblockapp on the service binary when that tool is present. If other devices still cannot connect, allow verae-print in that pane. The first time the service advertises or accepts a LAN connection, macOS asks once for Local Network permission for verae-print. Allow it. The menu-bar app may ask as well; allow that too.

macOS package

The Apple Silicon installer is VeraeNetPrint-macos-arm64-0.3.4.pkg. GitHub Actions builds it on a macos-14 runner with pkgbuild and productbuild, then runs pkgutil --expand, pkgutil --check-signature, and installer -pkginfo. It installs:

  • /Library/Application Support/Verae/NetPrint/bin/verae-print (the arm64 daemon binary)
  • /Applications/Verae NetPrint.app (the menu-bar app: Contents/Info.plist with bundle id ai.verae.netprint, LSUIElement, and NSLocalNetworkUsageDescription; Contents/MacOS/verae-print, Contents/MacOS/verae-menubar, and Contents/MacOS/verae-airdrop)
  • LaunchDaemon com.verae.netprint (root, the print server, the binary under Application Support)
  • LaunchAgent com.verae.netprint.tray in /Library/LaunchAgents, limited to the Aqua session, running /Applications/Verae NetPrint.app/Contents/MacOS/verae-print tray as the logged-in user

Receipts and job files go to /Users/Shared/Verae Receipts, not next to those binaries. The package from CI is not signed and not notarized. macOS blocks a normal double-click until the package is signed with a Developer ID Installer certificate and notarized. To install this unsigned package, right-click it in Finder and choose Open, then Open again. Or, in Terminal:

sudo installer -pkg VeraeNetPrint-macos-arm64-0.3.4.pkg -target /

postinstall checks that verae-print, verae-airdrop, and verae-menubar are in the app, then runs verae-print netprint install --owner standalone, which bootstraps the daemon into system and the menu-bar agent into the console user's gui domain. The agent is not the root daemon. AirDrop runs there.

verae-print tray loads AppKit and stays in NSApplication run on the main thread. If that run returns without Quit, or the status item cannot be created, it logs the reason to ~/Library/Logs/Verae/netprint-tray.log and execs verae-menubar in the same app (a small AppKit status item with status, Start, Stop, Open Receipts…, Open Settings…, Share receipts folder over SMB, and AirDrop again). A failure exits 1. It does not exit 0 when the menu bar cannot stay up. verae-print tray --smoke prints smoke ok or no window server and does not enter the run loop. Customize on the installer shows Share receipts folder over the network (SMB), unselected unless you check it. That choice is saved as shareReceiptsSMB. The first connection from iPhone Files needs a username and password from this Mac.

AirDrop on the Mac

Settings and the menu bar have AirDrop stamped PDF after each job. It is off until you turn it on. When a job completes, the daemon records it and tells the menu-bar app on GET http://127.0.0.1:8630/api/v1/events (an event stream on the loopback control API). The agent downloads the certificate and the receipt JSON through that API and opens the AirDrop share sheet with both files, using NSSharingService(named: .sendViaAirDrop). A notification shows the receipt ID and an AirDrop again action. The same action is on the menu. The root daemon never opens the sheet.

platform/macos/airdrop/main.swift is that share sheet as verae-airdrop. The macOS package build compiles it with swiftc and installs it inside Verae NetPrint.app, next to the tray executable. The menu-bar app launches that helper. macOS asks for notification permission the first time the agent posts AirDrop again. UNUserNotificationCenter is used only when the process is that app bundle (bundleIdentifier set and the bundle path ends in .app). A bare binary skips the call instead of crashing, because currentNotificationCenter throws when mainBundle is a directory.

The control API listens only on 127.0.0.1:8630. The receipts page is on the IPP port, not on the control port, and it stays on Private and Domain networks with the rest of the printer.

A silent Windows install (/S) does not wait on a dialog. Upgrades stop VeraeNetPrint, wait until it is stopped, and close verae-netprint-tray.exe before replacing those files. If a file is still locked, setup logs the error to %ProgramData%\Verae\logs\netprint-installer.log and exits with a failure instead of leaving the old binary in place. The service is started again after the new files are in place. verae-print netprint install starts the tray once, through Explorer (via a one-shot scheduled task when an interactive user is available). A second copy exits immediately on the Local\VeraeNetPrintTray mutex. The tray is not a Task Scheduler job and not a child of setup, so Start-Process -Wait returns and the icon stays up. Tray startup, mutex, and message-loop errors are appended to netprint.log. An unknown netprint subcommand does not start a second server. The service appends startup, mDNS, and job lines to %ProgramData%\Verae\logs\netprint.log and flushes each line, including when the process has no console. Job numbers continue from %ProgramData%\Verae\NetPrint\next-job and from existing job-N folders, so a restart does not write a new document into an old folder. http://127.0.0.1:8630/api/v1/status includes mdnsAdvertising, mdnsMode, mdnsDetail, mdnsError, receiptsUrl, receiptsDir, ippBind, emailEnabled, emailTo, emailError, smtpHost, smtpUser, smtpPasswordSet, receiptsPinSet, shareReceiptsSMB, and smbUrl. It does not include the SMTP password or the receipts PIN.

macOS upgrades run launchctl bootout on com.verae.netprint before the package copies the binary. Debian upgrades run systemctl stop verae-netprint from preinst when the unit is running that package's binary.

Troubleshooting

  • The icon says stopped. Start it from the menu. On Linux, systemctl status verae-netprint. On Windows, sc query VeraeNetPrint. On macOS, sudo launchctl print system/com.verae.netprint.
  • Other devices do not see it. Open http://127.0.0.1:8630/api/v1/status and read mdnsMode / mdnsError, and %ProgramData%\Verae\logs\netprint.log on Windows. mdnsMode dns-sd means Windows DNS-SD accepted the printer. Confirm the host has a LAN address and the network is Private, or turn on Allow Public networks. On a Mac, CUPS keeps localhost port 631. This printer uses 631 on the LAN addresses (ippBind lan) or 8632 when that bind does not succeed (ippBind all, port 8632). It does not fall back to 8631. Receipt files are in receiptsDir.
  • Windows setup seemed to hang with /S. Every installer dialog has a silent default. Read %ProgramData%\Verae\logs\netprint-installer.log.
  • Jobs fail with an authentication error. Open Settings and save a current xVerae- key. The service reads the machine token (/etc/verae/xverae-token, the macOS Application Support file, or %ProgramData%\Verae\PrintStamp\xverae-token) and the usual per-user token.
  • Raster jobs (AirPrint image/urf, Linux image/pwg-raster) and image/jpeg are converted to PDF on this machine before they are hashed. PDF is accepted as-is.

Commands:

verae-print netprint                         # foreground, for debugging
verae-print netprint install --owner driver --allow-public=false
verae-print netprint install --owner standalone --allow-public=true --local-queue
verae-print netprint launch-tray             # Windows: tray as the logged-in user
verae-print netprint uninstall --owner driver
verae-print tray