Skip to content

Releases: portixhq/portixone

PortixOne Runtime 0.1.1 — internal pilot

Choose a tag to compare

@PortixOne PortixOne released this 16 Jul 22:02

⚠️ Internal pilot build — not for commercial distribution

Same 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=internal to receive internal builds; the default
is stable, 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/latest also 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. Adds stable / beta / internal channels (inclusive downward, via PORTIX_UPDATE_CHANNEL, defaulting to stable). A release with no installer or no SHA256SUMS.txt is 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 itself

0.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

Choose a tag to compare

@PortixOne PortixOne released this 16 Jul 20:46

⚠️ Internal pilot build — not for commercial distribution

Approved 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 emits ESC t 16 (WPC1252) after INIT and encodes as latin1. Confirmed on a SICAR WL88S.
  • A disconnected printer silently reported success. With the printer unplugged, Get-Printer still reported PrinterStatus=Normal (the Generic / Text Only driver lies once a USB device is gone), so the pre-flight passed, the job reported completed, and the bytes sat in the spooler and printed late on reconnect. Win32_Printer.WorkOffline is now joined into detection and mapped to Offline, so PrinterOfflineError is raised before anything is queued.
  • Malformed request bodies returned HTTP 500 instead of 400.
  • Print jobs are now bounded: copies ≤ 100 and content ≤ 100,000 characters. Both were unbounded.
  • Pairing codes are generated with crypto.randomInt instead of Math.random() — a pending code is enough to retrieve its token from the unauthenticated GET /pairing/status.
  • StorageRepository writes 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 --test via tsx, zero new dependencies). 100 tests.

sdk-v0.3.4

Choose a tag to compare

@PortixOne PortixOne released this 12 Jul 04:55

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 returned preview string and every other behavior is unchanged.

0.3.3

  • A missing/unreachable Runtime now throws a clear RuntimeUnreachableError (message includes the portix.one/download link) instead of a raw TypeError: fetch failed. In a browser, connect() also best-effort opens the download page. RuntimeUnreachableError is 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, using appId/tenant from the constructor — no separate .pair() call needed for the common case. Instant from localhost (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 explicit apiKey.
  • A successful pairing is persisted (browser localStorage) so it's asked for once per appId/tenant, not on every connect().

npm install @portixone/sdk@0.3.4

@portixone/sdk@0.3.1

Choose a tag to compare

@PortixOne PortixOne released this 06 Jul 04:33

A code-review pass on the SDK:

  • print() in mock mode now returns preview (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.parse SyntaxError.

npm: https://www.npmjs.com/package/@portixone/sdk
Full changelog: sdk-js/CHANGELOG.md

@portixone/protocol@0.2.2

Choose a tag to compare

@PortixOne PortixOne released this 06 Jul 04:57

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.

npm: https://www.npmjs.com/package/@portixone/protocol

@portixone/protocol@0.2.1

Choose a tag to compare

@PortixOne PortixOne released this 06 Jul 04:33

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/shared@0.2.0

Choose a tag to compare

@PortixOne PortixOne released this 05 Jul 16:30

New hardware-specific error classes (PrinterOfflineError, PaperOutError, PrinterTimeoutError, PrinterBusyError, PrinterNotReadyError, and more) plus APP_VERSION. Purely additive.

npm: https://www.npmjs.com/package/@portixone/shared
Full changelog: packages/shared/CHANGELOG.md

@portixone/sdk@0.3.0

Choose a tag to compare

@PortixOne PortixOne released this 05 Jul 16:31

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

Choose a tag to compare

@PortixOne PortixOne released this 05 Jul 16:30

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