The WooCommerce POS desktop client for Windows, macOS and Linux.
About — How it works — Development — Building — Download
WCPOS Desktop is the Electron shell that packages the cross-platform WooCommerce POS client as a native desktop app. It wraps the shared React Native / Expo web app (@wcpos/main) and adds the native capabilities a point-of-sale terminal needs but a browser cannot provide:
- Receipt printing to USB, serial and network (ESC/POS) printers, plus the system print dialog.
- Printer discovery on the local network via Bonjour/mDNS.
- Local-first data stored on disk by RxDB's filesystem-node storage in the main process, exposed to the renderer over an IPC storage bridge.
- OAuth deep-linking via a custom
wcpos://protocol handler. - Auto-updates delivered straight from GitHub releases.
WCPOS requires WooCommerce and the WooCommerce POS plugin for WordPress as its backend. This repository builds and ships the desktop installers — see the latest release to download.
The app is split across Electron's three process types. The renderer is the WCPOS Expo web build — in development it points at the local Expo dev server, and in production it serves the static expo export bundle from disk via electron-serve under the wcpos:// scheme. The main process owns all native integrations and talks to the renderer over a tightly whitelisted preload bridge.
flowchart LR
subgraph Renderer["Renderer — @wcpos/main (Expo web)"]
UI["POS UI"]
end
subgraph Preload["Preload (context-isolated bridge)"]
IPC["whitelisted ipcRenderer\nsend / invoke / on"]
end
subgraph Main["Main process (src/index.ts)"]
DB["RxDB storage bridge\n(filesystem-node)"]
PRINT["Printing\nUSB · Serial · TCP/ESC-POS · system"]
DISCO["Printer discovery\n(Bonjour / mDNS)"]
PROTO["wcpos:// protocol\n(OAuth deep links)"]
UPD["Auto-updater"]
HTTP["HTTP bridge (net.fetch)\n+ image cache"]
end
UI <--> IPC
IPC <--> DB & PRINT & DISCO & PROTO & UPD & HTTP
Communication is locked down for security: contextIsolation is on, nodeIntegration is off, and src/preload.ts only forwards an explicit allow-list of IPC channels (e.g. rxStorage, http-request, print-raw-tcp, printer-discovery, auth:prompt).
| Area | Files | Notes |
|---|---|---|
| App bootstrap | src/index.ts |
Window lifecycle, power-monitor events, startup sequence |
| Storage | src/main/rxdb-storage.ts |
RxDB v17 over getRxStorageFilesystemNode, served to the renderer via the RxDB remote IPC bridge (with attachment serialization and targeted corruption recovery) |
| Printing | src/main/print-raw-tcp.ts, src/main/usb-printer.ts, src/main/serial-printer.ts, src/main/winspool-printer.ts, src/main/print-external-url.ts |
Raw ESC/POS over TCP, USB and serial; Windows USB routes through the print spooler |
| Discovery | src/main/printer-discovery.ts, src/main/bluetooth-select.ts |
mDNS network printer discovery and Bluetooth device selection |
| Networking | src/main/http-bridge.ts, src/main/image-cache.ts |
The http-request IPC channel over Chromium net.fetch (axios-shaped payloads — the renderer's client library is axios) and image caching |
| Auth | src/main/auth-handler.ts, src/main/protocol.ts |
wcpos:// deep-link handling for OAuth flows |
| Updates | src/main/update.ts |
Checks https://updates.wcpos.com and applies GitHub release assets |
| Shell | src/main/window.ts, src/main/menu/ |
Browser window and native application menus |
src/
index.ts # Main-process entry point
preload.ts # Context-isolated IPC bridge (renderer <-> main)
renderer.ts # Renderer entry (loads the Expo web bundle)
main/ # Main-process services (storage, printing, auth, update, …)
menu/ # Native application menu definitions
translations/ # Bundled i18n strings for native chrome (menus, dialogs)
forge.config.ts # Electron Forge: packaging, makers, publishers, signing
webpack.*.config.ts # Webpack config for the main & renderer bundles
icons/ # App icons per platform (.ico / .icns / .png / .svg)
flathub/ # Flathub manifest & metadata for the Linux Flatpak
This repo is standalone, no longer a submodule. The renderer (@wcpos/main) is built in a separate WCPOS monorepo checkout; this repo only consumes its prebuilt export in dist/ for packaging.
Prerequisites
- Node.js 22+
- pnpm (the version is pinned via
packageManagerinpackage.json) - Platform build tools for native modules (
usb,serialport) — Xcode CLT on macOS, build-essential/libudev on Linux, the relevant MSVC toolchain on Windows - An RxDB Premium license token (the app depends on
rxdb-premium). CI injects it intopackage.jsonunderaccessTokens["rxdb-premium"]; do the same locally for installs to succeed.
Run the app
In the monorepo checkout, start the renderer (port 8088, with ELECTRON=true):
pnpm dev:electron-rendererIn this repo, in a separate terminal:
pnpm install
pnpm rebuild:all
pnpm devElectron loads http://localhost:8088; override its port with EXPO_PORT to match the renderer server.
Useful scripts
| Script | Description |
|---|---|
pnpm dev |
Launch Electron against the separately running renderer |
pnpm lint / pnpm lint:fix |
Lint the src/ tree |
pnpm ts:check |
Type-check with tsc --noEmit |
pnpm test |
Run the main/preload test suite (see below) |
pnpm rebuild:all |
Rebuild usb for Electron |
pnpm package |
Package the app without producing installers |
pnpm make |
Build platform installers |
pnpm publish-app |
Build and publish a release to GitHub |
Tests are lightweight ts-node scripts that exercise the main-process and preload logic without a full Electron runtime. pnpm test runs them all:
test:preload— IPC channel allow-listingtest:rxdb-ipc-attachments— RxDB attachment serialization over IPCtest:image-cache— image cache behaviourtest:printer-discovery/test:bluetooth-select— discovery and device selectiontest:winspool-printer/test:serial-printer— Windows spooler and serial printingtest:package-runtime-externals— verifies runtime-external native deps are bundled
CI (.github/workflows/test.yml) runs pnpm lint, tsc, and pnpm test on every push and PR to main.
Packaging is driven by Electron Forge (forge.config.ts). Because the UI is built separately, a production build is a two-step process:
- Build the renderer in the monorepo, then import its export into this repo's
dist/:Forge bundles# In the monorepo checkout pnpm --filter @wcpos/main build:electron # In this repo (replace <monorepo> with your checkout path) pnpm renderer:import <monorepo>/apps/main/electron-build
dist/into the app viaextraResource, andelectron-serveserves it underwcpos://. - Make the installers:
pnpm make
Targets
| Platform | Maker | Output |
|---|---|---|
| Windows | Squirrel | .exe installer |
| macOS | DMG + ZIP | signed & notarized .dmg / .zip |
| Linux | Deb / Rpm | .deb, .rpm |
| Linux | Flatpak | Flatpak bundle / Flathub (opt-in via WCPOS_FLATPAK=1) |
The desktop build's renderer comes from wcpos/monorepo at the ref in .monorepo-ref; bump it in the same PR as the version bump.
Pushes to main trigger the Publish workflow: it builds the Expo renderer, then builds, signs and publishes platform installers to GitHub Releases. Installed apps check for updates against https://updates.wcpos.com and apply new release assets in place. Changes are tracked in CHANGELOG.md.
Issues and pull requests are welcome. Please run pnpm lint, pnpm ts:check and pnpm test before opening a PR. Project-specific agent and review guidelines live in AGENTS.md.
- 🌐 Website — wcpos.com
- 📦 Monorepo — github.com/wcpos/monorepo
- 🔌 WordPress plugin — github.com/wcpos/woocommerce-pos
- 💬 Discord — wcpos.com/discord
MIT © Paul Kilmurray