# Nest storage: File mode vs. Database mode The desktop app can save nests two ways: - **File mode** (default, unchanged behavior): Save/Save As write a `.nest` ZIP archive to disk via the normal file dialog. See [nest-file-format.md](nest-file-format.md). - **Database mode**: Save uploads the nest to a central `OpenNest.Server` instance shared by every shop PC, instead of writing a local file. A separate **File > Export .nest...** command is always available (in both modes) for producing a local file to share or back up. The mode and server address are stored per-PC at `%APPDATA%\OpenNest\storage.json` (`OpenNest.Data.NestStorageSettings`), defaulting to File mode so existing installs are unaffected until an operator opts in via **File > Storage Mode...**. ## Wire contract `OpenNest.Data.NestRecord` is the metadata DTO shared by the client (`RemoteNestRepository`) and the server (`OpenNest.Server`). JSON is camelCase with enums serialized as strings (`JsonSerializerDefaults.Web` + `JsonStringEnumConverter(JsonNamingPolicy.CamelCase)`), matching the rest of `OpenNest.Data`'s local JSON stores. | Field | Type | Notes | |---|---|---| | `id` | guid | Client-generated on first save; kept on updates. | | `name` | string | | | `customer` | string | | | `dateCreated` / `dateModified` | datetime | Set by the client from the nest's own metadata. | | `material` | string | | | `thickness` | number | | | `status` | `"quote"` \| `"toBeCut"` \| `"hasBeenCut"` | Default `quote`. | | `plateCount` / `partCount` | integer | Computed client-side at save time (non-cutoff parts only). | | `comments` | string | | | `madeBy` | string | | | `fileSize` | integer | Server-computed; ignored on upload. | | `savedAt` | datetime | Server-computed; ignored on upload. | ## Endpoints | Method | Path | Body | Response | |---|---|---|---| | GET | `/healthz` | — | `{ "status": "ok" }` | | GET | `/api/nests` | — | `NestRecord[]`, newest `savedAt` first | | GET | `/api/nests/{id}` | — | `NestRecord` or 404 | | GET | `/api/nests/{id}/file` | — | `.nest` archive bytes (`application/zip`) or 404 | | POST | `/api/nests` | multipart: `metadata` (JSON `NestRecord`) + `file` (`.nest` bytes) | `NestRecord` with server-assigned `id` when the client sends an empty guid | | PUT | `/api/nests/{id}/file` | multipart: `metadata` + `file` | Updated `NestRecord`, or 404 if `id` is unknown | | PUT | `/api/nests/{id}/metadata` | JSON `NestRecord` | Updated `NestRecord` (archive untouched), or 404 | | DELETE | `/api/nests/{id}` | — | 204, or 404 | There is no authentication; this is a LAN-only service. Do not expose it outside the shop network without adding one. ## Storage `OpenNest.Server.NestDatabase` uses SQLite (`Microsoft.Data.Sqlite`) with `journal_mode=WAL`. Each row holds the metadata columns plus the `.nest` archive as a `BLOB`. The database file path comes from `--database=`, then `OPENNEST_DB`, defaulting to `./data/nests.db`. ## Running Locally: ```sh dotnet run --project OpenNest.Server/OpenNest.Server.csproj ``` Listens on `ASPNETCORE_URLS` (default Kestrel ports) unless overridden. Docker (built from the repository root so it can see `OpenNest.Data`): ```sh docker build -f OpenNest.Server/Dockerfile -t opennest-server . docker run -d -p 8090:8090 -v opennest-data:/app/data opennest-server ``` The image listens on `:8090` and stores `nests.db` under `/app/data`, which should be a named volume or bind mount so nests survive container recreation. Point the desktop app's **File > Storage Mode...** server URL at `http://:8090` (no trailing slash required; `RemoteNestRepository` trims it).