CHANGELOG
Every release, what changed, and why. Updates are Ed25519-signed and your installation verifies the signature before applying — an unsigned or altered package is refused.
Changelog
All notable changes. Releases are signed (Ed25519) and verified against the embedded vendor key before applying — see platformctl update --package.
[1.0.2] — Multi-plant sync could silently drop records (2026-07-31)
Recommended for everyone. Required if you run more than one plant. The fix existed in our source before this build did — 1.0.1 and earlier do not contain it, so upgrading is the only way to get it.
Fixed — silent cross-plant data loss at the sync boundary
- What went wrong. When one plant pulled changes from another, it recorded how far it had caught up
- Who was affected. Only installs running two or more plants with
multi_sitesync enabled. A - What changed. The catch-up marker is now derived from the data actually received — the newest
- Upgrading repairs the damage. The first sync after you upgrade performs a full catch-up pull
using its own clock at the moment of the pull, while the sending plant only returned records stamped strictly after that point. A record created in the same millisecond as a sync therefore fell into the gap between the two — and because the catch-up marker only ever moves forward, that record was never sent again. No error, no warning, no retry: the record simply existed at one plant and not the other, indefinitely.
single-site install was never at risk. The window is narrow — a record has to be stamped in the exact millisecond of a pull — but a busy plant syncing on a short interval will hit it eventually, and inventory quantities are exactly the kind of record that goes quietly wrong when one is missing.
timestamp in the batch — instead of from the wall clock, and it can never move past what was really seen. The boundary is now inclusive, so a record sharing that timestamp is re-sent rather than skipped; re-sending is safe because applying a record twice is a no-op.
from each peer plant, so records that were skipped previously are fetched and applied then. You do not need to re-enter anything or re-pair the plants.
Also in this build
- Everything in 1.0.1 (the zero-IT hardening) plus the above. No settings, licence keys or stored data
- Still unsigned: Windows SmartScreen will warn on first run. Verify the SHA-256 on the Downloads
- Windows portable
.ziponly (the.exeinstaller needs Inno Setup; Linux builds need a Linux host).
are changed by upgrading — your existing licence file and your data directory are used as-is.
page before installing.
[1.0.1] — Zero-IT hardening in the shipped build (2026-07-19)
_First rebuild since 1.0.0. The 1.0.0 artifacts predated the zero-IT hardening merge, so the published download did not contain it; this build does._
- Zero-IT hardening now in the binary: team-connect address + QR (
/api/v1/connect-info,/hello), - Still unsigned — Windows SmartScreen will warn on first run. Verify the SHA-256 below before
- Windows portable
.ziponly in this build (the.exeneeds Inno Setup, Linux needs a Linux host).
persisted runtime state with sleep-gap and LAN-IP-change detection, extended Health checks (clock plausibility, autostart, disk, off-machine backup), MFA recovery codes, the loopback-gated "Forgot the admin password?" flow, and the "Something's wrong" support export.
installing; see the Downloads page for the exact click path.
[1.0.0] — Packaged & launch-ready (built locally; not yet published)
_Prepared 2026-06-29. Everything below is free and non-public: artifacts stay local in dist/, Stripe stays in TEST mode, and the storefront stays gated until the owner decides to launch._
Packaging — 1-click install
- Self-contained binary (Node SEA): no Node.js, terminal, or config file for the end user —
- Installers built to
dist/installers/with adist/SHA256SUMSmanifest, all unsigned and
install, open https://localhost:8443/, set an admin password. Pure-Node self-signed TLS cert (no openssl) and per-OS data/config paths with first-run bootstrap.
local (nothing uploaded): a Windows portable zip (pure-Node ZIP writer — no toolchain, builds on any OS; verified it extracts byte-identical via native Expand-Archive), plus build paths for the Windows Inno-Setup installer, Linux .deb/.rpm/.tar.gz (systemd), and a one-container Docker image.
Commerce — proven in TEST mode, gated OFF
- Stripe self-serve loop proven end-to-end in test mode (test card →
checkout.session.completed LAUNCH_MODEgate (default off): public checkout + sign-up return "coming soon" and nothing
webhook → signed WFLIC1 license auto-issued → emailed with a .lic attachment).
is purchasable; the webhook + console issuance stay live. New launchmode test; /healthz reports launch_mode + storefront_open. Go-live runbook in sell/GO-LIVE-STRIPE.md.
Marketing site & hosting — deploy-ready, not deployed
- Static site build (
node sell/marketing-site/build.js→dist/site/): all pages, **7 legal pages - Hosting blueprints validated locally (no deploy):
render.yaml/docker-compose.ymlparse as YAML,
pre-rendered to static HTML, plus a self-contained coming-soon** holding page (dist/site-coming-soon/). One-command publish documented in sell/PUBLISH-SITE.md. Fixed the brand logo glyph and the contact email.
railway.json as JSON; /healthz returns signing_key_matches_build: true. Fixed a real bug in Dockerfile.marketing (it didn't copy sell/shared + legal/, so the server would crash); sell/DEPLOY.md gains a full env reference + launch-gate notes.
Legal & docs
- Legal pack finalized: product PlantStacks / entity PlantStacks LLC /
plantstacks.com; - New
docs/INSTALL-GUIDE.md(per-OS) anddocs/ADMIN-QUICKSTART.md.
customer-facing contact email moved off a personal address to support@plantstacks.com; business-address + sales-tax placeholders left as marked TODOs (sales-tax later resolved by the CPA — see the [1.1.0] entry). Added the internal legal/NOT-YET-FORMED-NOTICE.md (the LLC is not yet formed — do not represent it as registered).
Guardrails kept
- Core stays zero-runtime-dependency (
dependencies: {}; the only build tool ispostject),
offline/no-phone-home, RBAC deny-by-default, WFLIC1 frozen, security-map.md never shipped. Full test suite (20 core suites + vendor backend) green.
[Unreleased] — Rebrand to PlantStacks
- The product is now PlantStacks (domain
plantstacks.com). The brand was applied everywhere - Compatibility contracts kept (never named after the brand, so nothing to rename):
WFLIC1 - Legal entity remains PlantStacks LLC (not yet formed — a separate business decision).
the previous product name appeared — including every identifier that carried it: the UI, OpenAPI/docs, PWA manifest, TOTP issuer, seeded help content; Prometheus metrics (plantstacks_); environment variables (PLANTSTACKS_CONFIG, PLANTSTACKS_SECRET_, PLANTSTACKS_LICENSE_PUBKEY, PLANTSTACKS_API_RATE_PER_MIN, PLANTSTACKS__ override prefix); HTTP/webhook headers (X-PlantStacks-*); the plantstacks.log log file; and the npm package name (plantstacks). The bundled trial license re-signed to the same key/format (now "PlantStacks Trial").
license token format + signing keys, module ids, permission/role keys, store/collection namespaces, event-bus names, the YAML config schema, and the platformctl bin. Existing licenses and data keep working.
[Unreleased] — Support, observability & fleet
- Data-classification boundary (
src/core/diagnostics/): allowlisted diagnostic - Vendor diagnostic KB (
/support-kb) +ERR_####taxonomy + standardized - On-box support assistant: zero-dep BM25 retrieval over the KB; optional
- Opt-in support bridge (off by default) via signed
webhooks; air-gap file - Signed release feed + offline update package; verify-before-apply
- Observability: structured JSON logging with correlation ids + redaction
- Robustness: circuit breakers + retries with backoff/jitter on optional
reports (input shape, never values); local-only verbose log; secret-key drop.
error envelopes. security-map.md is vendor-internal (excluded via .buildignore).
customer-run LOCAL model; cloud LLM endpoints refused; security topics refused.
export; vendor-console intake + fleet dedup by error signature.
(ERR_6001/6002/6003); in-app "update available" notification.
(values only to the local verbose store), bounded rotation/retention, platformctl doctor, troubleshooting mode + diagnostic bundle.
outbound, bounded queues + backpressure, idempotency; load/chaos tests.
[2.0.0] — Manufacturing MES
business_inventoryexpanded into a full MES: movement ledger, BOM staging,
replenishment, traceability, quality holds, counts, costing/WIP, PLC feed.
[1.x] — Platform + enterprise
- All-features-all-tiers licensing (seats+sites, prorated upgrades), cloud
vendor backend, multi_site sync, integration API + OpenAPI, plc_connector, Postgres scale knob.