Releases: portixhq/portixone
Release list
PortixOne Runtime 0.1.1 — internal pilot
⚠️ Internal pilot build — not for commercial distributionSame scope as 0.1.0: Nerion and Kubia installs, controlled tests, clean-machine validation, and
pilots with no active commercial licensing and zero paying Creator customers. Licensing
ships inert (see SEC-LIC-001, #4).0.1.0 cannot update itself to this release
0.1.0 shipped the broken updater, so it will never discover 0.1.1 on its own. Machines on the
0.1.0 pilot need one manual reinstall. From 0.1.1 onward, updates work. Config, pairings and
printer selection survive — they live in.data/, which the installer doesn't touch.Pilot machines must set
PORTIX_UPDATE_CHANNEL=internalto receive internal builds; the default
isstable, which deliberately ignores pre-releases like this one.
2026-07-16 — 0.1.1: the update channel actually works
Fixed
-
The Runtime update check had never worked. It asked GitHub for
/releases/latest, which answers with the newest release of any product in this repo — so it kept returning an npm package tag (sdk-v0.3.4), a release with no installer and a version that isn't the Runtime's. The parser then failed on that tag and every check died with "Could not parse a version number to compare", silently, on every install./releases/latestalso hides pre-releases, so a pilot build was invisible to it regardless.Updates are now discovered by the Runtime's own tag convention (
runtime-v<semver>) across the full release list, so publishing an npm package tag can never break the updater again. Addsstable/beta/internalchannels (inclusive downward, viaPORTIX_UPDATE_CHANNEL, defaulting tostable). A release with no installer or noSHA256SUMS.txtis refused outright — an installer that can't be verified is worse than no update. Every passed-over release is logged with a reason, so a check that finds nothing is diagnosable instead of mute.
⚠️ 0.1.0 cannot update itself0.1.0 shipped with the broken updater, so it will never discover this release on its own. Machines
already running the 0.1.0 pilot need one manual reinstall to reach 0.1.1. From 0.1.1 onward,
updates work. Config, pairings, and printer selection survive the reinstall (they live in.data/,
which the installer does not touch).
PortixOne Runtime 0.1.0 — internal pilot
⚠️ Internal pilot build — not for commercial distributionApproved for: installs on Nerion and Kubia, controlled tests, clean-machine validation, and
pilots with no active commercial licensing and zero paying Creator customers.Not approved for: commercial Creator distribution, uncontrolled external customers, or
enabling licensing as a payment barrier. Licensing ships inert in this build; it is not an
enforcement boundary yet (see SEC-LIC-001).The point of this build is to learn whether Portix really installs, starts, prints, recovers, and
uninstalls on real machines — not to enforce a licence.
2026-07-16 — Print-path fixes and the licensing layer
The first Runtime installer release. Two of the fixes below were found by printing on real hardware, not by reading code — and both had to land before anything could be published.
Fixed
- Accented text printed as mojibake.
EscposBuilder.text()encoded content as UTF-8, but the printer reads one byte per character against its selected code table, soá é í ó ú,ñ Ñ, and¿ ¡ °each arrived as two bytes and printed as two garbage glyphs. Plain ASCII looked fine, which is why it survived earlier testing — and why Spanish receipts were unusable. The builder now emitsESC t 16(WPC1252) afterINITand encodes aslatin1. Confirmed on a SICAR WL88S. - A disconnected printer silently reported success. With the printer unplugged,
Get-Printerstill reportedPrinterStatus=Normal(the Generic / Text Only driver lies once a USB device is gone), so the pre-flight passed, the job reportedcompleted, and the bytes sat in the spooler and printed late on reconnect.Win32_Printer.WorkOfflineis now joined into detection and mapped toOffline, soPrinterOfflineErroris raised before anything is queued. - Malformed request bodies returned HTTP 500 instead of 400.
- Print jobs are now bounded:
copies≤ 100 andcontent≤ 100,000 characters. Both were unbounded. - Pairing codes are generated with
crypto.randomIntinstead ofMath.random()— a pending code is enough to retrieve its token from the unauthenticatedGET /pairing/status. StorageRepositorywrites are atomic (serialize → temp → fsync → rename, with orphan sweep and a Windows/OneDrive-safe retry). A failed write can no longer destroy the last valid file.
Added
- Licensing layer (runtime side). Offline ES256 license-token verification with a fail-closed keyring (production trusts only the production keyring and refuses to boot if a development key is present), the grace/posture state machine (72h technical grace counted from token expiry; commercial grace kept separate), a best-effort 12h heartbeat that only honors an authenticated revocation over TLS, one-time installation-token exchange, and an admin-only
GET /license. Licensing never gates printing — the print layer doesn't even import it, and an architecture test freezes that. Design and terms: LICENSING_PLAN.md, docs/licensing/. - Pricing settled as Free / Creator ($24/mo or $240/yr) / Founder ($240 one-time, 100 seats).
- A test runner (
node --testviatsx, zero new dependencies). 100 tests.
sdk-v0.3.4
First npm release since 0.3.1 — this publish ships 0.3.2, 0.3.3, and 0.3.4 together (0.3.2 and 0.3.3 were committed but never published, so they reach npm now).
0.3.4
- Mock-mode preview box now aligns: content rows were one column short of the frame, so the right edge never lined up with the corners. Purely cosmetic —
print()'s returnedpreviewstring and every other behavior is unchanged.
0.3.3
- A missing/unreachable Runtime now throws a clear
RuntimeUnreachableError(message includes theportix.one/downloadlink) instead of a rawTypeError: fetch failed. In a browser,connect()also best-effort opens the download page.RuntimeUnreachableErroris exported for apps that want to handle it with their own UI.
0.3.2
connect()now pairs automatically when it isn't authorized yet, usingappId/tenantfrom the constructor — no separate.pair()call needed for the common case. Instant fromlocalhost(the runtime's auto-trust); otherwise it waits for a human to approve it from the PortixOne tray's "Pairing Requests" menu. Skipped entirely if you pass an explicitapiKey.- A successful pairing is persisted (browser
localStorage) so it's asked for once perappId/tenant, not on everyconnect().
npm install @portixone/sdk@0.3.4
@portixone/sdk@0.3.1
A code-review pass on the SDK:
print()in mock mode now returnspreview(the rendered receipt text) on the result, not just a console.log side effect.- Fixed unclear errors when the runtime responds with something non-JSON (empty body, HTML from a proxy/captive portal) — throws an actionable message instead of a raw
JSON.parseSyntaxError.
npm: https://www.npmjs.com/package/@portixone/sdk
Full changelog: sdk-js/CHANGELOG.md
@portixone/protocol@0.2.2
Adds lastUsedAt to PairingRecord and recentJobCount to PairedAppSummary — foundational fields for the runtime's new pairing auto-trust and revoke support. Additive, no breaking changes.
@portixone/protocol@0.2.1
Adds preview?: string to PrintJobResult — the rendered text preview, present only for mock-mode jobs. Additive, no wire changes for real prints.
npm: https://www.npmjs.com/package/@portixone/protocol
Full changelog: packages/protocol/CHANGELOG.md
@portixone/sdk@0.3.0
The full Local API surface, published for the first time: pair(), disconnect(), listPrinters(), getPrinter(), getJobs(), cancel(), ping(), on() (real-time job events over WebSocket), and getMetrics() (Milestone 4's measurement layer — pairing duration, print latency, failures, WebSocket disconnects).
Requires @portixone/protocol@^0.2.0 and @portixone/shared@^0.2.0.
npm install @portixone/sdk
npm: https://www.npmjs.com/package/@portixone/sdk
Full changelog: sdk-js/CHANGELOG.md
@portixone/protocol@0.2.0
Breaking wire change, pre-1.0: JobStatus changed from 'queued' | 'printed' | 'error' to 'pending' | 'printing' | 'completed' | 'failed' | 'cancelled' to match the persisted-queue lifecycle from Milestone 3.
Also new: JobRecord, JobOwner, PrinterInfo, the pairing request/status contract (pairing.types.ts/pairing.schema.ts), and the GET /metrics contract (metrics.types.ts).
npm: https://www.npmjs.com/package/@portixone/protocol
Full changelog: packages/protocol/CHANGELOG.md