release noteswhat's new in labelpipe
Every version, newest first. To update, run the install command again.
1.0.0 2026-11-06
- CI: Dependabot PRs no longer fail the image builds once
SIXLABORS_LICENSE_KEY is also a Dependabot
secret (the error now says so), never run on the self-hosted runner, and no longer propose Node major versions
for the sign-up service. The website CI job builds on Node 24, the LTS the sign-up service ships with.
- Upgrade gate with real data (QA, WP-J): before a beta can become stable, the upgrade test now seeds people,
printers, a routing rule, an API client and printed jobs on the previous stable, and after the upgrade checks that
every one is unchanged (including the stored PDFs and template layouts), that passwords still work, and that old
jobs reprint and new ones print. Developer docs, Production deploy.
- Our own release registry: betas and stable releases now come from
registry.labelpipe.app, without a login:
docker compose -f oci://registry.labelpipe.app/labelpipe-compose:latest up -d. GHCR stays private for the team’s
edge builds. Every release is signed with cosign keyless, and a beta becomes stable only if its signatures verify
(replaces the R-ATT waiver). Setup and operations: developer docs, Release registry.
- CI: fewer GitHub Actions minutes.
:edge is built for amd64 only, and a newer push to main cancels the
running build; beta tags, and so every release, stay amd64 + arm64. The image build on PRs runs only when the
PR touches the image (docker/, Composer and npm files, database/, config/, VERSION, the workflow);
acceptance screenshots skip docs-only changes. The repository variable LABELPIPE_RUNNER moves the heavy test jobs
to a self-hosted runner (developer docs, CI runners and Actions minutes).
- Website:
site/ builds www.labelpipe.app with Astro (static HTML, served by Caddy): home, features, Free vs Pro,
download and install, docs (the user manual), release notes (this file), and draft legal pages (imprint, privacy,
terms, Free licence). The Free limits and the version come from config/editions.php and VERSION. CI builds and
checks it (job website); site/ never goes into the app image. See the developer docs, The public website.
- Tell me when Pro launches: a double opt-in sign-up on the site, run by a small service on our own server
(
site/notify/), with no third party.
- The website’s licence page (
/eula) now shows the same text as the setup wizard, built from
resources/legal/eula-free.md, with its version.
- Licence agreement: labelpipe is proprietary and free of charge in the Free edition (
composer.json says
proprietary). The setup wizard has a new step 2, the licence agreement, with a required I accept. labelpipe records the
version, the administrator and the time. When an update brings a new version, administrators are asked to accept it
at their next sign-in (/admin/licence); other users keep working. The text still has placeholders for the company
data and waits for a lawyer’s review.
- Security: the production image sends
X-Frame-Options: SAMEORIGIN and
Content-Security-Policy: frame-ancestors 'self', so no other site can frame labelpipe and trick a signed-in admin
into clicks. labelpipe’s own PDF previews (designer, import) still show. The Caddy example in the deploy guide adds the CSP line.
- Security: in
compose.prod.yaml the session idle timeout is fixed at 60 minutes (SESSION_LIFETIME from .env
is ignored), so the cookie lifetime matches the sign-in policy. Before, an install without a .env value used 120.
- Readability: muted text, small links, status lines and Delete in the label designer, and empty-value
placeholders in tables (such as draft only), now meet 4.5:1 contrast in light and dark mode (QA M1-7 F1, F2), and so do the hints under condition inputs
such as Empty — always true (F1b).
- Fix (MariaDB): parallel print jobs no longer deadlock while reserving the daily quota. Under concurrent API calls
some requests answered 500 instead of 201 or 429. A MariaDB race test job now runs in CI.
- Search help: press Ctrl + / (⌘ + / on a Mac) or click Search help in the top bar to search the user manual
as you type. Results show the chapter, the section and the matching text; Enter opens the section. The manual page’s
filter box now lists matching sections instead of hiding chapters. Administrators also get developer-docs results.
0.8.1 2026-11-06
- Security: someone who signs in only with Google and has no second step yet can add an authenticator app or
e-mail codes only within 5 minutes of signing in (Security page). Otherwise the button is greyed out and asks
them to sign in again. Forced set-up right after sign-in is unchanged.
0.8.0 2026-11-06
- labelpipe free: up to 500 labels and documents a day, 1 store, 3 active users, 5 enabled printers, 1 integration client
and 30 days of print job history. Every feature still works. See the manual, Editions and usage.
- A job that would cross the daily limit is refused whole. The API answers
429 quota_exceeded with
Retry-After, X-Labelpipe-Quota-Limit and X-Labelpipe-Quota-Remaining. Designer and ZPL editor previews
don’t count; every label POST /api/v1/zpl/render returns does. ZPL counts
what the printer prints (^PQ quantities, several labels per record; a label may have only one ^PQ); a ZPL print job that turns out bigger than
what is left today fails with quota_exceeded before anything is sent. Reprints and retries count again, and a job that failed while being drawn gives its labels back.
- At a limit, the New button for stores, users, printers or integration clients is greyed out and says why.
Existing ones are kept. Only active users and enabled printers count: deactivate a leaver or disable a printer to
free the place. Reactivating, enabling, or approving a Google sign-up counts like adding one.
labelpipe:create-admin --deactivate=<e-mail> frees a place when nobody can sign in.
- Finished print jobs older than 30 days are deleted every night (
labelpipe:prune-jobs), with their files.
- Setup → Usage (administrators) and a dashboard widget: today’s labels against the limit, when it resets, and the
counted limits. API: new
GET /api/v1/usage; GET /api/v1/ping gains usage.
- Development: the demo data has one demo store (
demo-main).
0.7.0 2026-11-06
- First-run setup in the browser: on a new server the web container’s log prints a one-time link
(
docker compose logs web | grep "first-run setup"). It opens set up labelpipe: paste or follow the setup key,
create the first administrator and turn on the authenticator app; you are then signed in. The link changes on
every restart, expires after 24 hours and stops working once an administrator exists. labelpipe:create-admin
still works; LABELPIPE_WEB_SETUP=false turns the web setup off.
- The web container’s access log no longer records query strings (setup key, Google sign-in codes).
0.6.0 2026-11-06
- Two-step sign-in: administrators and template managers must set up an authenticator app the next time they
sign in (QR code, then 10 one-time recovery codes). Operators may use an app or e-mail codes; require for everyone
is in Access → Security settings (default
LABELPIPE_MFA_REQUIRE_ALL). No “remember me”; sessions end after 12 hours.
- Sign in with Google (when
GOOGLE_CLIENT_ID is set): for people who already have an account with the same
verified e-mail, always followed by the labelpipe second step. Optional allowed domains and auto-created accounts
that wait for an administrator’s approval.
- Access → Users: Allow password sign-in, two-step and Google columns, Awaiting approval.
- Access → Sign-in log: sign-ins, refusals, wrong codes, lockouts and Google links, kept 90 days.
- Security page (user menu): change your password, link or unlink Google, manage your second step.
- Confirm it’s you: sensitive changes (passwords, Google links, API tokens and secrets, roles, security and ZPL
preview settings) ask for a fresh code, good for 5 minutes.
- Reset two-step for someone who lost their phone (another administrator, with a reason; they are signed out and
e-mailed). Users who still have to set up their second step are marked red in Access → Users.
- Access → Security settings: Require two-step for everyone.
- Sign-in screens: the code step shows no Sign in with Google button; setting up the second step after signing in
does not ask for the password again and needs I saved these codes somewhere safe ticked; the set-up page has
Sign out; recovery codes are typed as plain text.
0.5.1 2026-11-06
- Fix: Duplicate in Setup → Label templates failed with “Error while loading page” for every template
(ZPL and PDF). It now creates the copy and opens it in the designer.
0.5.0 2026-11-06
- Starter gallery: administrators can add nine ready-made templates from Setup → Label templates →
Install starter gallery: shelf labels (70 × 38 mm and an A4 sheet of 21), a price tag, a product sticker, 4 × 6
shipping labels in PDF and ZPL, and A4 picking slip, delivery note and invoice forms. You choose who sees them;
installing again only adds what is missing (the dialog names them). Also
labelpipe:install-gallery. New installs
still start empty.
0.4.0 2026-11-05
- ZPL preview without the internet: a bundled preview service (
zpl-preview) draws ZPL labels on your own
server. Previews from it are marked approximate: Zebra’s fonts are replaced by open fonts.
- Setup → ZPL preview: administrators choose Local, Labelary or Off. Labelary’s public service is used
only after an administrator confirms that label content is sent to labelary.com (name and time are recorded).
LABELARY_ENABLED is gone; see LABELPIPE_ZPL_PREVIEW_URL and LABELPIPE_ZPL_PREVIEW_DEFAULT in .env.example.
- Every preview says which service drew it. Previews are limited to 50 labels, 40 million dots and 60 a minute per
person or API client; errors no longer quote the preview service’s answer.
- API:
/api/v1/zpl/render returns driver in JSON and X-Preview-Driver on png/pdf; 503 preview_disabled
when previews are off, 429 preview_busy over the limits.
- The ZPL editor no longer suggests commands that templates may not use (
^IM, ^CC, ^CT, ^CD …).
- Server installs:
docker/compose.prod.yaml and compose.trial.yaml run the zpl-preview service
(ghcr.io/plamenbv/labelpipe-zpl-preview) on an internal network next to the app; nothing to configure.
- Security: line breaks inside a ZPL command or graphic no longer slip past the preview limits; at most 2
previews are drawn at once; the preview services’ redirects are not followed.
- Development:
./dev up on a fresh clone runs the first-time setup; the dev API token is limited to printing and
expires after 30 days. Building zpl-preview needs SIXLABORS_LICENSE_KEY in your shell (see Local development).
0.3.1 2026-11-06
- Install with one command:
docker compose -f oci://ghcr.io/plamenbv/labelpipe-compose:latest up -d.
- The compose file is published to GHCR with the image’s tags, and every image is pinned by digest.
- Alternatively, download
docker/compose.prod.yaml and run docker compose -f compose.prod.yaml up -d.
- Nothing has to be edited first. Optional settings go in a
.env next to it (see docker/.env.prod.example).
- Choose the version with the artifact tag:
:X.Y.Z, :X.Y, :latest or :edge. With the OCI form,
LABELPIPE_VERSION has no effect.
- Production image
ghcr.io/plamenbv/labelpipe for free and pro, amd64 and arm64:
- one image runs web, worker and scheduler;
- it runs as a non-root user with a read-only root filesystem;
- it is scanned by Trivy and published with an SBOM and a provenance attestation.
- Secrets are generated on first start:
- The
init service creates the app key and the database password in the new secrets volume.
- The database container gets its copy through the new
db-secret volume.
- The MariaDB root password is random, and root can only log in from inside the database container.
- An
APP_KEY or DB_PASSWORD already in .env is kept and stored.
- Back up the
secrets volume together with dbdata and storage. Without the app key, encrypted data and
sessions cannot be read.
- The web port listens on 127.0.0.1 only.
LABELPIPE_TRUSTED_PROXIES defaults to the compose network’s gateway, so
a reverse proxy on the host works out of the box.
- Labelary previews are off by default (ADR-004).
- The image ships only the documentation the app shows (Help → Developer docs, API docs). Internal design documents and
contributor files are left out (ADR-006).
- Create the first administrator with
labelpipe artisan labelpipe:create-admin. A first-run setup page follows in M2.
0.3.0 2026-11-05
- Security:
- ZPL templates and the dynamic ZPL API refuse prefix changes (
^CC, ^CT, ^CD and their ~ forms) and
commands that store, load or erase objects on the printer (~DG, ^DF, ^IM …). Use ^GF for graphics.
A line break or other control character inside a command no longer hides it, and the error names the command
as you typed it.
- After 20 failed API logins from one address in a minute, that address gets 429 for the rest of the minute.
- API tokens expire: one year at most (
LABELPIPE_API_TOKEN_LIFETIME_DAYS); Issue token asks for the date.
- Allowed source IPs / CIDR on integration clients is enforced (403 from other addresses).
Behind a reverse proxy, set
LABELPIPE_TRUSTED_PROXIES.
- The development CUPS server allows administration only to a local system administrator; a production
configuration without the web interface is included.
- Safer development defaults:
APP_DEBUG=false, the app is reachable only from this computer (127.0.0.1:8080).
- Queue: a print job running close to the 120-second limit is no longer handed to a second worker.
- Organisations: more checks that forms accept only your own organisation’s records.
0.2.3 2026-11-05
- Brand: headings that show a name you typed (template designer, edit and view pages, an import or import
template) keep its case, e.g. “Design: Shelf A4 Promo”. Static page and section headings stay lowercase.
0.2.2 2026-11-05
- Production setup:
db:seed no longer creates the demo users, demo token or virtual printers unless APP_ENV is
local or testing; DevelopmentSeeder refuses to run anywhere else. ./dev update seeds only the reference data.
- New command
php artisan labelpipe:create-admin creates the first administrator (password asked with a hidden
prompt, at least 12 characters).
- Docs: production deploy guide for a single server (Caddy, Tailscale, Cloudflare Tunnel, host CUPS, backups,
updates):
docs/developer/15-production-deploy.md.
0.2.1 2026-11-05
- Organisations: forms accept only stores, printers, users and import templates of your own organisation, and
the import page accepts only files uploaded through it. Background maintenance (stuck-job checks, pruning) also
covers switched-off organisations.
0.2.0 2026-11-05
- Organisations (tenancy-ready schema): every row now belongs to an organisation. An install has one, the default
organisation, created by the database migration. Codes (stores, printers, templates, fields, images) are unique
per organisation. A user or integration client of an inactive organisation cannot sign in or call the API.
Files are stored under the organisation’s folder. Install fresh with
./dev setup (0.x schemas are not migrated).
- Product data:
origin, warranty, manufacturer and importer are back as plain fields (API, import
mapping, label fields {{manufacturer}} …), for EU product labelling.
- Price engine: the focus and sale thresholds are settings (
LABELPIPE_PRICING_*, defaults 1.9 % and 14.9 %).
- Client IDs now start with
lp_.
- Removed the unused Publish settings action on label templates and an old per-store setting.
- Brand: the labelpipe theme. Logo and favicons, Deep Charcoal dark mode as the default (light mode in the user
menu), Electric Cyan as the primary colour, Neon Mint for success, Vivid Amethyst for info and template fields.
Self-hosted fonts: Inter (interface), Geologica (headings), JetBrains Mono (code, ZPL, rule inputs). Every text and
button colour pair meets WCAG AA in both modes (
docs/design/theme-spec.md).
- Sign-in page with the stacked logo and the brand glow, the API documentation page with the charcoal brand bar, the
manual with brand headings and a logo on printouts. Label and document output is unchanged: print colours stay
whatever the template says.