WyzBooks WyzBooks v26.4.140658

API Server Operations

Operations guide for deploying and managing the WyzBooks REST API server. Covers architecture, deployment, server management, and troubleshooting.

Overview

The WyzBooks API is an Express.js HTTP server that provides REST access to the same accounting data used by the desktop Electron app. It supports real-time synchronization between multiple clients via Server-Sent Events (SSE).

  • Framework — Express 4.x (must stay on 4.x; 5.x has ESM-only deps)
  • Runtime — Node.js 16+
  • Default port — 3141
  • Bind address — 127.0.0.1 (localhost only)
  • Process ownerwyzbooks user (never root)
  • Process manager — PM2 with systemd boot persistence (pm2-wyzbooks service)
  • Auth — API key via X-API-Key header (required in multi-tenant mode)
  • Data format — Double-encoded JSON file
  • Dependenciesexpress, xlsx

Quick Reference

# Local development
npm run api                      # Start API on localhost:3141

# Deploy code changes to server
npm run deploy:api               # rsync + npm install + restart

# Server management (SSH as wyzbooks user — never use root)
ssh wyzbooks@web3
pm2 status wyzbooks-api          # Check if running
pm2 restart wyzbooks-api         # Restart after code changes
pm2 logs wyzbooks-api            # View logs
pm2 stop wyzbooks-api            # Stop
pm2 start wyzbooks-api           # Start
pm2 save                         # Save state for boot persistence

# Systemd (boot persistence — managed by pm2-wyzbooks service)
systemctl status pm2-wyzbooks
journalctl -u pm2-wyzbooks -f

Architecture

The server is deployed to /opt/wyzbooks-api/ with the following structure:

  • src/api/server.js — Express app and startup
  • src/api/openapi.js — OpenAPI 3.0 spec
  • src/api/middleware/ — DataLayer (JSON file I/O, locking, sync versioning), ApiError class, SSE client registry
  • src/api/routes/ — 18 route files: accounts, audit, bank-txns, bills, console, contacts, data, import, invoices, items, journals, messages, payments, reconcile, reports, sync, system, terms
  • src/lib/ — Shared accounting functions (reports, parsers, reconciliation) and merge utilities
  • .env — Environment variables (port, API key, data dir)

Data Storage

  • Data directory/var/lib/wyzbooks/ (default). Contains wyzbooks-data.json (double-encoded JSON).
  • Config/logs~/.wyzbooks/ contains wyzbooks.ini, audit.log (1 MB rotation), and audit-import.log (1 MB rotation).
  • Double encoding — The data file uses double-encoded JSON: the outer JSON object contains string values that are themselves JSON. Reading requires JSON.parse() twice. This matches the Electron app's storage format.

Request Flow

Client Request
  -> Express body parser (JSON 50MB / text 50MB)
  -> API key auth (if WYZBOOKS_API_KEY set)
  -> DataLayer injected as req.dl
  -> Route handler
  -> Response (JSON)
  -> Error handler (if thrown)

Real-Time Sync (SSE)

  • Connection — Clients connect via GET /sync/stream with X-Client-Id header. Server broadcasts to all clients except the sender.
  • Reconnection — Reconnecting clients use Last-Event-ID header for automatic catch-up of missed events.
  • Heartbeat — 30-second heartbeat keeps connections alive through proxies.
  • EventsPOST /sync/push triggers data-changed events. POST /messages/send triggers text-message events.

Environment Variables

Variable Default Description
WYZBOOKS_API_PORT 3141 Listen port
WYZBOOKS_API_KEY (none) API key for auth. If set, all /api/* requests require X-API-Key header
WYZBOOKS_DATA_DIR ~/Library/Application Support/WyzBooks/data (macOS) Data file directory
HOME system default Used for ~/.wyzbooks/ config and audit logs

On the server, these are typically set in /opt/wyzbooks-api/.env (loaded by systemd EnvironmentFile or pm2 ecosystem file).

First-Time Server Setup

# 1. Deploy files to server
./scripts/cloud-sync/deploy.sh user@myserver.com

# 2. SSH in and run setup
ssh user@myserver.com
cd /opt/wyzbooks-api
sudo bash scripts/cloud-sync/setup-server.sh \
  --api-key "$(openssl rand -hex 32)" \
  --api-port 3141 \
  --data-dir /var/lib/wyzbooks \
  --user wyzbooks

The setup script installs Node.js 18+ if missing, creates a wyzbooks system user (no-login), creates the data directory with correct ownership, writes the .env file with API key and port, runs npm install --omit=dev, configures systemd or pm2, and optionally configures Apache reverse proxy.

Deploying Code Changes

npm run deploy:api

This single command rsyncs src/api/, src/lib/, and package.json to the server, runs npm install --omit=dev on the server, and restarts the API via pm2 restart wyzbooks-api.

When to deploy: After any change to files under src/api/, src/lib/, or package.json. Changes to src/app.jsx, src/main.js, src/preload.js, or src/cloud-sync.js are client-side only and do not require a server deploy.

Server Management

  • Start (pm2)pm2 start src/api/server.js --name wyzbooks-api
  • Start (systemd)sudo systemctl start wyzbooks-api
  • Stoppm2 stop wyzbooks-api or sudo systemctl stop wyzbooks-api
  • Restartpm2 restart wyzbooks-api or sudo systemctl restart wyzbooks-api. A restart is required after deploying code changes — Node.js loads modules once at startup.
  • Logs (pm2)pm2 logs wyzbooks-api or pm2 logs wyzbooks-api --lines 200
  • Logs (systemd)sudo journalctl -u wyzbooks-api -f (follow) or sudo journalctl -u wyzbooks-api --since today

Health Check

curl http://127.0.0.1:3141/api/v1/system/health

Returns { ok: true, data: { version, collections: { accounts, contacts, ... } } }.

Check connected clients:

curl -H "X-API-Key: YOUR_KEY" http://127.0.0.1:3141/api/v1/sync/status

Returns { ok: true, data: { syncVersion, connectedClients, clientIds: [...] } }.

Data Layer

  • File locking — Atomic mkdir (NAS-safe). Lock path: {dataFile}.lock/ directory. 10 retries, 50ms between attempts. Stale locks older than 10 seconds are auto-removed. HTTP 423 DATA_LOCKED if lock cannot be acquired.
  • Sync versioning_syncVersion is a monotonic counter incremented on each push. _rev is a per-record revision number. _deletions is an array of tombstones (max 1000). Conflict detection returns HTTP 409 if client's baseVersion < server's _syncVersion.
  • Audit logging — Data changes: ~/.wyzbooks/audit.log. Import operations: ~/.wyzbooks/audit-import.log. Both rotate at 1 MB. Format: [ISO-timestamp] ACTION entity detail.

Apache Reverse Proxy

The API binds to localhost only. Apache provides TLS termination and external access.

# Required modules
sudo a2enmod proxy proxy_http headers

# Add to existing vhost
<Location /api/>
    ProxyPass        http://127.0.0.1:3141/api/
    ProxyPassReverse http://127.0.0.1:3141/api/
    SetEnv proxy-sendchunked 1
    SetEnv proxy-initial-not-pooled 1
    RequestHeader set X-Forwarded-Proto "https"
</Location>
Tip

Critical SSE settings: proxy-sendchunked and proxy-initial-not-pooled must be set, and ProxyTimeout should be 3600 (default 60s is too short for SSE). Without these, SSE connections buffer and fail.

Security

  • Localhost only — The API never accepts connections from external IPs directly.
  • API key auth — When WYZBOOKS_API_KEY is set, all requests require X-API-Key header. Without it, any local process can access the API.
  • TLS — Handled by Apache/Nginx, not by the API server.
  • Stateless — No session state. Every request is independently authenticated.
  • Key storage — Plain text in .env file, wyzbooks.ini, and app preferences. Treat as a password.

Multi-Tenant Mode & Admin Console

The API server supports multi-tenant isolation. When ~/.wyzbooks/tenants.json exists, each API key maps to a separate tenant with its own data directory.

  • Admin Console — Navigate to /console on your server (e.g. https://wyzbooks.com/console). Login with your admin key (Enter key submits). The console supports light and dark themes (toggle in header), configurable auto-refresh interval, and show/hide password toggle on the login form.
  • Tenant Detail — Click a tenant name to expand a detail panel showing: sync version, data checksum, deletion count, collection record counts, the 30 most recent change stack entries with version badges, and connected SSE clients with IP address and User-Agent.
  • Client Management — The tenant detail panel shows all connected SSE clients. Each client displays its client ID, IP address, and User-Agent string. A Disconnect button lets you forcibly close a specific client's SSE connection.
  • Key Management — Change the admin key from the console header menu. Set or regenerate individual tenant API keys from the tenant detail panel. Logout button in the header.
  • Purge Stack — Resets a tenant's sync version to 0, strips all _rev stamps, and clears deletions. After purging, the next client to connect should use Push First or Pull First to re-establish a baseline.
  • Tenant CRUD — Add, update, and delete tenants. Regenerate API keys. Migrate legacy data from the default data directory to a tenant's directory.

wbctl — CLI Management Tool

wbctl is a Perl CLI tool for managing the WyzBooks API server from the command line. It works locally via HTTP or remotely via SSH.

# Install (copy to a directory in your PATH)
cp scripts/cloud-sync/wbctl /usr/local/bin/
cp scripts/cloud-sync/wbctl.conf.sample ~/.wbctl.conf

# Configure
export WYZBOOKS_URL=https://wyzbooks.com
export WYZBOOKS_ADMIN_KEY=your-admin-key
export WYZBOOKS_SSH=user@myserver.com
  • Server controlwbctl start, stop, restart, status, logs — manage the API process via pm2 or systemd over SSH.
  • Health & syncwbctl health, sync, changes, clients — check server health, sync status, recent changes, and connected SSE clients.
  • Tenant managementwbctl tenants, tenant-add, tenant-update, tenant-delete, tenant-migrate — full CRUD for multi-tenant deployments.
  • Data operationswbctl export, purge, stats, audit — export tenant data, purge sync stack, view collection stats, and read audit logs.
  • Key managementwbctl set-key, regen-key, admin-key — change tenant API keys or the admin key.
  • Deploymentwbctl deploy, setup — rsync code to server or run initial server setup.

Run wbctl help for the full command list, or wbctl <command> --help for usage details. Configuration can be stored in ~/.wbctl.conf or set via environment variables.

Troubleshooting

  • API won't start — Check if port is in use (lsof -i :3141), verify Node.js 18+ (node --version), check dependencies (cd /opt/wyzbooks-api && npm ls), and verify .env file.
  • SSE connections drop — Verify Apache has proxy-sendchunked and proxy-initial-not-pooled set. Verify ProxyTimeout 3600. Check pm2 logs for reconnection messages.
  • Data file locked — Stale locks auto-recover after 10 seconds. Manual recovery: rmdir /var/lib/wyzbooks/wyzbooks-data.json.lock
  • Permission errors — Fix ownership: sudo chown -R wyzbooks:wyzbooks /var/lib/wyzbooks and /opt/wyzbooks-api. For audit logs: sudo mkdir -p ~wyzbooks/.wyzbooks && sudo chown wyzbooks:wyzbooks ~wyzbooks/.wyzbooks

Dependencies

Package Version Purpose
express ^4.22.1 HTTP framework (must stay on 4.x)
xlsx ^0.18.5 Excel export for reports

No other runtime dependencies. Node.js built-in modules (http, https, fs, path, crypto, os) are used for everything else.