# Installation and recovery

## Architecture

Browser / Zebra keyboard scanner → local business network → HTTPS reverse proxy → Stockroom Node server → local SQLite database. Static fonts, icons, and application assets are served locally. Internet access is not needed for ordinary LAN operations while the server and local network remain available. A disconnected handheld can retain receipt drafts in that browser's local storage; it cannot approve counts or change stock until reconnected.

Use a dedicated service account, a persistent local SSD data directory, HTTPS, a protected management network, and an off-machine backup destination. The supplied service limits the process to one CPU and 512 MB; those are preview guardrails, not a customer capacity guarantee. Size and load-test against the actual catalog, daily transaction rate, user count, retention policy, and recovery objectives before production handover.

## Configuration

| Variable            | Default                 | Purpose                                                                               |
| ------------------- | ----------------------- | ------------------------------------------------------------------------------------- |
| HOST                | 127.0.0.1               | Listener; use 0.0.0.0 only inside the supplied container                              |
| PORT                | 4188                    | Application port                                                                      |
| DB_PATH             | ./data/stockroom.sqlite | Persistent database, including accounts and stock                                     |
| SANDBOX_ENABLED     | 1                       | Enables anonymous demos and named sample-workspace registration; set 0 for production |
| MAX_DEMO_WORKSPACES | 200                     | Stops creating sample workspaces at the configured capacity                           |
| COOKIE_SECURE       | 0                       | Set 1 when served through HTTPS                                                       |
| TRUST_PROXY         | 0                       | Set 1 only behind one trusted reverse proxy, with direct app traffic blocked          |

Production mode disables both demo creation endpoints and the corresponding sign-in buttons. Existing sample workspaces are not deleted by this setting. Provision production into a new database; never turn a customer sandbox into live stock by merely changing the banner.

## On-premise service

Install the release under `/opt/stockroom/releases/<version>`, create `/opt/stockroom/current` pointing to it, and create `/var/lib/stockroom` owned by the `stockroom` service account. Install Node 24 and run `npm ci --omit=dev` in the release. The release archive includes the compiled `dist/`, so no server-side frontend build is required.

Use `deploy/stockroom.service` as the systemd template and `deploy/Caddyfile.example` for the customer hostname. Verify HTTPS and proxy isolation before setting the service live. The example domain is a placeholder; this package does not modify DNS or existing production proxy configurations.

For containers, `compose.yaml` binds the application only to host loopback and keeps SQLite in a named Docker volume. The Dockerfile is supplied as an installation option; the verified preview deployment uses the native Node service. Do not claim container commissioning without building and testing it on the intended host.

## Provision the first administrator

Run with the intended `DB_PATH` set:

```sh
node scripts/provision.mjs email=admin@example.com business="Customer Business" name="Site Administrator" output=/var/lib/stockroom/private-bootstrap.json
```

`email`, `business`, and `name` identify the new administrator and workspace. `output` is a private file containing the generated initial password and recovery code. The script refuses to overwrite an existing credential file. It writes no credentials to standard output. File mode is 0600 on POSIX; use restrictive NTFS permissions on Windows. Deliver these credentials through the customer's approved secure channel and remove the bootstrap file after secure handoff. Users can change passwords in Settings. Add `sample=yes` only for a separate fictional evaluation workspace.

After provisioning, sign in and create suppliers, locations, then products. Receive opening stock using a documented opening-balance reference. Confirm physical totals before accepting sales imports.

## Backup

```sh
node scripts/backup.mjs /secure-backups/stockroom
```

The command uses SQLite's online backup API for a consistent snapshot, including WAL state, while the application runs. It checks database integrity, writes a SHA-256 sidecar, and reports record counts. Backups contain account password hashes and operational data; protect them as sensitive business records. Copy the completed snapshot and checksum to the approved off-machine destination. The package does not silently enable an external backup service or assume a retention policy.

Schedule this command with the customer's system scheduler once recovery objectives and retention are agreed. Test restoration regularly; a file existing on disk is not a recovery test.

## Restore without overwriting the active database

```sh
node scripts/restore.mjs /secure-backups/stockroom/snapshot.sqlite /var/lib/stockroom/restored.sqlite
```

The destination must not exist. The script checks the checksum when present, database integrity, foreign keys, and each lot's balance against its movement history. It never overwrites the source or active database. Stop the application, preserve the previous database and release, point `DB_PATH` to the restored file, and restart. Then sign in and verify a known product quantity, recent movement, purchase order, and team account through the actual UI.

The automated recovery test performs receipt → online backup → restore into a new file → restart against restored data → real browser sign-in → lot readback. It uses fictional temporary data.

## Updates and rollback

Build and test a new release separately. Back up first. Stop the service, point `current` to the new release, and restart. Confirm `/api/health`, sign in, and complete a safe test receipt in an isolated workspace. Browser reload fetches the installed version; there is no separate desktop updater. To roll back, stop the service and restore the previous release; if a future release changes the schema incompatibly, restore its matching pre-update database into a new path as above. This release's schema version is 1.

## Zebra and printer commissioning

Configure a DataWedge profile for the chosen browser, enable keyboard/keystroke output, and append Enter. Stockroom's scan field resolves exact barcode or SKU values. See [Zebra's keystroke documentation](https://techdocs.zebra.com/datawedge/14-1/guide/output/keystroke/). Test real labels, scanner focus, accidental double-scans, Wi-Fi roaming, sleep/wake, connectivity loss, and screen orientation on the selected model. The printed product label uses Code 128; calibrate paper dimensions and printer scaling on the chosen hardware. Generated SVG bars were tested in software; physical scan/print tests remain part of installation.

## POS import contract

CSV headers: `external_id,sku,location,quantity,revenue,date`.

`external_id` identifies a unique sale line, not an entire receipt containing multiple products. `sku` must exist in this workspace. `location` is a Stockroom location ID. `quantity` is a positive integer in the product's base unit. `revenue` is an integer number of cents. `date` is YYYY-MM-DD and must not be in the future. Up to 500 rows per import. Unknown products or conflicting duplicate IDs reject the whole batch. A shortage creates an exception without partial stock deduction. Receive or transfer sufficient unexpired stock into the specified location, then retry the exception.

Returns, financial settlement, tax calculation, live vendor synchronization, and automatic supplier messaging are outside this inventory preview. Orders are downloaded and sent through the existing supplier channel. Replenishment projections use a transparent moving average; no AI demand model is represented as deployed.
