Ticketsmith is a ticket designer for Boca Systems printers. Put text, barcodes and the printer's own ticket counter on the stock, set a quantity, print. What leaves the app is plain FGL, Boca's Friendly Ghost Language, and you can read every byte before it goes.
Free and open source (MIT) · macOS 12+ · Apple silicon and Intel
FGL for one job — 482 bytes
<RC20,20><LT2><BX360,1060><NR><F6><HW1,1><RC34,44>MIDNIGHT ELECTRIC<NR><F3><HW1,1><RC100,44>SAT OCT 18 2026 8:00 PM<NR><F2><HW1,1><RC140,44>THE ORPHEUM - LOS ANGELES<RC176,44><LT2><HX620><NR><F3><HW1,1><RC200,44>SEC 12 ROW H SEAT 24<NR><F2><HW1,1><RC246,44>GENERAL ADMISSION<NR><F6><HW1,1><RC280,44>$45.00<NR><F1><HW1,1><RC352,44>NO REFUNDS - NO EXCHANGES - ALL SALES FINAL<NR><F1><HW1,1><RC120,748>SCAN AT GATE<NR><RC140,748><X2><BI><oP6>^TKT-10024^<NR><F3><RC240,748><PC><p>
That window is drawn, not screenshotted, and it works. The ticket is the app's starter ticket, element for element; the FGL under it comes from a port of the app's emitter and matches it byte for byte. Drag anything, nudge it with the arrow keys, rename the band — and Save really writes ticket.fgl.
No driver, no PDF, no raster in the middle. Ticketsmith writes the commands you would write by hand — <RC> to place, <F3> to pick a font, <p> to print and cut — and the printer does the rest at full speed.
Positions are printer dots. Text advances one font box per character, using the resident fonts' metrics from Boca's own supplement, so the preview's letters land where the head will put them.
MIT licensed and on GitHub — a native Mac app in Rust, signed and notarized, that keeps itself up to date. Fork it for your box office if it does not do the thing you need.
Coordinates
<RC row,column> counts rows down across the print head and columns along the paper feed. So a 2" × 5.5" ticket is 400 rows by 1,100 columns, and the long side is column — which reads backwards until you hold the ticket the way the printer does.
Ticketsmith draws the ticket in exactly that orientation, the 5.5" feed axis running left to right, so the numbers in the inspector and the picture on the screen never disagree. The status bar shows the dot grid, because that is the unit everything is in.
The same stock at each head density. Both dimensions are free in the app; the glyphs are not — a font's dots are the same at every density, so on a 600 dpi head the same text prints a third the size.
Fonts
Every Boca carries its fonts in firmware, from a 5 × 7 dot fine-print face to Orator at 46 × 91. Each has a glyph size and a larger box it advances by, and Ticketsmith lays text out by the box, the way the printer does. Below, every one at the same scale: the outline is the box, the fill is the glyph. Fonts 14 and 15 are marked “for Miltope users only” in the guide, and are left out.
F1tiny5×7 in 7×8F4OCR-A5×9 in 7×11F5—5×11 in 7×12F9OCR-B13×20 in 13×22F2small8×16 in 10×18F7OCR-A15×29 in 20×31F16Cyrillic18×31 in 20×33F3OCR-B17×31 in 20×33F8Courier20×40 in 20×33F13Courier intl20×40 in 20×42F10Prestige25×41 in 28×41F11Script25×49 in 26×49F6OCR-B large30×52 in 34×56F12Orator46×91 in 47×91Barcodes
Boca firmware encodes every symbol itself — Code 128's code-set switching, the check characters, the lot. What it needs from software is a payload in the right delimiters, data the symbology can actually hold, and a size. So the data is checked per symbology as you type, UPC and EAN check digits are worked out for you, and the preview reserves exactly the space the printed symbol will take.
^TKT-10024^*TKT-10024*:10024000:A10024BJ401234K567893L4J006381K333931L<QR6,0,0,0>{…}The guide works through 40123456789 and gets a check digit of 3. So does the app, and then splits the twelve into the guarded halves FGL wants:
<NR><RC0,10><X2><uP5>J401234K567893L
Orientation is picket fence or ladder, with bar height in 8 dot units, the narrow-bar width, the 3:1 wide ratio where the symbology allows it, and the human-readable line underneath. QR takes a version, a point size and an error-correction level — and FGL numbers those levels M, L, H, Q, which is the kind of thing an app should remember so you do not have to.
A run
Set a quantity and the layout goes out once, with an <RE> repeat asking for the rest — which is how a Boca runs at full speed, instead of waiting on a computer to send fifty copies of the same thing.
Turn on numbering and the job preloads the printer's own seven-digit counter. A Number element on the ticket stamps it, and the printer increments it per ticket, so the run comes out sequential without the layout being sent again.
Deliberately absent is <CB>, the clear-buffer command. The guide is explicit that it costs throughput, and the printer clears itself between tickets anyway.
<TC0001001>Preload the counter at 1001<RC20,20><LT2><BX360,1060>…<PC>The layout, 479 bytes, sent once<RE49>Forty-nine more — the quantity less the one<p>Print and cut. <q> leaves it uncut; <z> ejectsGetting it to the printer
Sending runs on a background executor, so a printer that is switched off waits out its connect timeout without freezing the window — and the status line says what happened in words, because “the printer is unplugged” is a normal Tuesday.
Raw TCP to port 9100
Every Boca ethernet and WiFi interface listens there, and nothing is wrapped around the FGL. Type the printer's address and press print.
A device node
USB when the printer's port is in serial mode (<usbs>): /dev/cu.usbmodem* on macOS. The bytes are written straight at it.
lp -d QUEUE -o raw
USB when the printer is left in its default printer mode (<usbp>) and installed as a CUPS queue. Raw, so no driver gets to reinterpret it.
Write the job to disk
Test the whole pipeline without burning stock — and keep the file, because it is the exact bytes a printer would get.
Updates
The status bar shows the version. When a newer release is out, a link beside it downloads the update and checks it, and nothing is replaced until you click again — a second click because restarting clears the ticket on screen. Then the new app is swapped in for the old in a single step and relaunched.
codesign --verify --deep --strict on the app inside the mounted disk image, the first of the three checks.
A valid Developer ID only proves that somebody signed it, so the leaf certificate has to carry the team the app was built by, and that team is compiled in rather than read from the bundle.
spctl --assess, so an update clears the same bar a first download does when you open it.
The network is /usr/bin/curl and the rest is Apple's own tools, so the updater adds no HTTP or TLS stack to the app. The swap is renamex_np with RENAME_SWAP: there is no moment where the old app is gone and the new one is not there yet. A copy that cannot replace itself — run from the disk image, or from a folder you cannot write to — offers the release page instead.
Underneath
The window is GPUI, the GPU-drawn framework Zed is built with, plus a component library on top of it — because GPUI has no text input, dropdown or checkbox of its own, and this app is mostly a form. Under that, the FGL, the barcodes and the document are plain Rust with no UI dependency at all: 52 of the 98 tests run against them without a window.
The resident fonts and their dot metrics, text, rules, boxes, the counter, print and repeat.
Validation per symbology, FGL emission, GTIN check digits, and bars for the preview.
The document: stock, elements, their geometry, hit testing, and the whole-job emitter.
The four ways out: TCP, a device node, lp, and a file.
Asking GitHub for a newer release, and verifying and installing it.
Projection from printer dots to window pixels, and all the painting.
The window: toolbar, preview interaction, inspector, print panel.
$ git clone https://github.com/wes/ticketsmith.git
$ cd ticketsmith && cargo run
The first build takes a few minutes because it compiles GPUI. After that it is seconds.
Open the disk image and drag Ticketsmith to Applications — it is signed and notarized, so it opens like anything else. No account, no licence key, no trial. Point it at a printer, or save a .fgl first and read what it would have sent.
1 downloadof v0.1.0 since 6 October 2026