Security and data: your work stays local
PanelCraft is an offline-first desktop application. Your projects, price lists, client records and decor images are stored on your own machine, and the application is built so that a hostile file or a faulty component cannot reach beyond what it needs.
Questions about security: hello@panelcraft.store.
Offline-first by design
Designing, cutting, pricing, drawings, documents, machining export and room view all run locally. The interface of the application is allowed to load nothing from the network: its content policy limits every kind of source to the application itself, and the default session cancels every request except local files, data and blob URLs.
Only one thing needs the internet: the licence. The application activates once and then checks the licence online when a check is due. If it cannot reach the licence service it keeps working for a grace period of several days, so a workshop with a bad connection is not interrupted. Network and server errors never remove an existing licence; only an explicit refusal from the service does.
No project, price list, client record, decor image or room photograph is ever uploaded.
Where your data lives
Everything is stored on your computer, in places you can find and back up.
| Data | Where | Notes |
|---|---|---|
| Projects | Wherever you save the .panelcraft file | Written atomically (temporary file, then rename). Opening reads at most 50 MB. |
| Projects index, clients, quotes | A local SQLite database in the application data folder | Held in memory and saved atomically after every transaction. An unreadable file is renamed aside, never deleted. |
| Libraries and stock | JSON files in the application data folder | Read at most 10 MB, validated before use. |
| Decor images | The application data folder, stored by content hash | Validated by content: 8 MB, 16 to 8192 px, 36 megapixels per image. |
| Room view photo | A downscaled copy in the application data folder | At most 10 MB, validated by magic bytes, folder named by a hash of the project id. |
| Licence | Two copies of a licence state file in the application data folder | The valid copy with the newest check time wins, so deleting or editing one copy does not reset the trial clock. |
Exports leave the application as plain files: PDF, CSV, DXF, SVG and images. You choose where they are written, and the application never decides a path for you.
What the design defends against
The threat model names four adversaries. A malicious or corrupt file that you open or import (a project, a CSV, a library, an image or a database) is treated as untrusted data. A compromised interface, for example through a flaw in a dependency, is treated as untrusted too: it must not be able to read or write arbitrary files, run code in the main process, bypass licensing or reach the network. A local user who tampers with licence files or the clock is handled to a reasonable level. And the supply chain, the npm dependencies and Electron itself, is kept small and reviewed.
Out of scope are an attacker who already runs code as you, a malicious operating system and physical access to the machine. No desktop application can defend against those, and we do not claim to.
The controls
| Area | Control |
|---|---|
| Renderer isolation | Sandboxed renderer with context isolation and no Node integration. No remote content and no embedded web views. Application sandbox enabled for every window. |
| Calls to the main process | Every call is checked: the sender must be the top frame of the packaged page, every argument is type-checked and size-bounded, unknown fields are dropped, and export extensions are whitelisted. |
| File paths | The interface never chooses where to read or write. Reads come from native open dialogs, writes from native save dialogs or application-owned files. A path is accepted for saving only if you picked it in a dialog this session or it is already in the index the main process wrote. |
| Bounded file IO | Reads are size-capped (project 50 MB, CSV 10 MB, stores 10 MB, database 512 MB, licence 64 KB) and must be regular files, not devices or directories. Writes use a random temporary name opened exclusively and then renamed, so a planted link is never followed. |
| Malformed input | Project, library and stock text must parse as a JSON object or array before use. Parse errors become a clean message, never a crash. |
| Content policy | Production policy: script, style, image, font and connection sources limited to the application itself; no eval; no inline script or style; no frames or objects; no base or form targets. |
| PDF rendering | A hidden window with JavaScript off, its own in-memory session that cancels everything except data URLs, permissions denied, navigation and pop-ups denied, and a 60-second timeout. The markup is sanitised again in the main process, a policy is injected as the first element, and scripts, frames, objects, embeds, base and link elements and event handlers are stripped. User text is escaped when documents are generated. |
| Navigation | The window can navigate only to its own page; new windows are always denied; web links open in the system browser; web views and permission requests are denied. |
| Images | Decor and room photos are validated by content (magic bytes and the pixel size in the header), refused if they are links, directories or oversized, stored under a hash, and decoded without loosening the content policy. |
| Database | Every query is a prepared statement with bound parameters. The one place that builds a column list uses fixed literals. Wildcards are escaped, and adversarial tests cover injection payloads. |
| Runtime hardening | The packaged application flips the runtime fuses: the node run mode is off, the NODE_OPTIONS variable and the inspect flags are off, cookie encryption is on, only the packaged archive can be loaded, and its integrity is checked. |
Licensing without surveillance
A licence is a signed token: a set of claims (plan, features, expiry, device limit, grace days) and an Ed25519 signature over them. The application verifies the signature on your machine with a public key built into it; the private key lives only on the licence server. Feature checks run in the main process, inside the handlers that save, export and read data, so a modified interface cannot switch features on.
Time handling is monotonic. The application remembers the latest time it has seen and never goes back, so winding the clock back does not extend a trial or a grace period. The trial and the grace period are counted from that clock.
The licence call sends a licence key and a device identifier, and nothing about your designs.
Dependencies and testing
The shipped JavaScript is small: React, three.js, a small state library, an SQLite engine compiled to WebAssembly and the project’s own packages. The SQLite engine needs no native build, which removes a class of per-platform build problems.
Security behaviour is covered by tests: sender checks, path and argument validation, traversal and link writes, oversize and malformed JSON, print-markup hardening, the URL allow-list and the content policy; SQL injection payloads; decor magic bytes, header sizes, decompression bombs, traversal, links and tampering; and end-to-end tests that forge paths, attempt unsafe exports, violate the content policy, navigate, open windows, send calls from a foreign window and feed hostile files and hostile PDF markup.
If you find a security issue, write to hello@panelcraft.store with the details.
The program in pictures
Try it on your own machine
Download PanelCraft for Windows or macOS and run a real job through it, from drawing to quote. The 14-day trial includes every Studio feature.
Questions first? Write to hello@panelcraft.store or see the FAQ.





