# Forking Meistertracker for your own lab Meistertracker was built for [Meisterpilze](https://www.meisterpilze.de) — a mushroom cultivation farm in Erlangen, Germany — and a few rough edges still reflect that. None of these block running it as-is, but if you fork or self-host you should know what's tuned to a specific operator vs. what's generic. If you only want to **run** Meistertracker (not modify it), follow [DEPLOYMENT.md](DEPLOYMENT.md) — none of this is required to start the server. --- ## 1. Branding & visible names By default the user-facing UI says `Meistertracker` (the product name). The text `Meisterpilze` is the original operator's brand and only appears in places where renaming would break compatibility with existing CalDAV clients: | File | What stays as `meisterpilze` | Why | |---|---|---| | `server.js` | `PRODID:-//Meisterpilze Lab Tracker//EN` in generated iCal | Some clients cache this and re-issuing breaks sync | | `server.js` | `X-MEISTERPILZE-TYPE`, `X-MEISTERPILZE-ASSIGNEE`, `X-MEISTERPILZE-BATCH` custom iCal properties | Used to round-trip task / batch metadata with subscribed clients | | `server.js` | UID suffix `@meisterpilze` (`batch-XYZ@meisterpilze`, `cev-NN@meisterpilze`) | iCal RFC 5545 — UIDs must remain stable across edits | | `server.js` | CalDAV shared-calendar slug `meisterpilze` (in URL `/caldav/calendars/.../meisterpilze`) | Apple Calendar / Thunderbird subscribe by URL; renaming forces every client to re-subscribe | | `server.js` | CalDAV `WWW-Authenticate: Basic realm="Meisterpilze CalDAV"` | Cosmetic | If you're starting **fresh** (no CalDAV clients yet) you can grep+replace these freely. If you're migrating from an existing instance, change them only with a synchronised re-subscribe across all calendar clients. The PWA cache name (`sw.js` — `meistertracker-v22`) is bumped on rebrand so existing clients re-fetch the shell. ## 2. Hardware assumptions ### Label printer — **read this carefully before forking the print code** The print pipeline is built tightly around one specific setup: > **ZPL II · 203 dpi · 50×30 mm landscape · Zebra GK420d (or compatible)** Everything else needs varying amounts of work. #### What works out of the box Any ZPL-II thermal printer at 203 dpi with 50×30 mm label stock. Verified models include the Zebra **GK420d / GK420t / GX420d / ZD220 / ZD230 / ZD420 / ZD500 / ZD620**. ZPL-clone printers (Argox CP-2140, Bixolon SLP-TX400 in ZPL mode) at the same dpi/label size also work. Set `PRINTER_NAME=` (env or `.env`) so the print bridge can find it. #### What "slightly different" labels can do The env vars `LABEL_WIDTH_DOTS` and `LABEL_HEIGHT_DOTS` only change the `^PW` / `^LL` headers the printer reads — they do **not** change the field layout inside the label. So they're useful when: - ✅ Your label feeder reports slightly different dots (e.g. 408×244 vs. 400×240) - ✅ You print the same 50×30mm content on slightly larger stock and accept blank margin They're **not** enough when: - ❌ Your labels are a different physical size (e.g. 40×20mm or 75×50mm) — text and barcodes will overflow or sit in the corner - ❌ Your printer is 300 dpi — every font and coordinate would need to double - ❌ Your labels are portrait-oriented #### What needs a layout refactor (~200 LOC) The 50×30mm canvas is the single source of truth. The layout assumes `width=400` everywhere — the comment at `app.js:9493` says so explicitly. Hard-coded coordinates live in: | File | Where | What | |---|---|---| | `app.js` | `bagLabelItems()` ~9673 | Bag label fields (y=155/185/215, blockW=400, fontH=28/24/24) | | `app.js` | `labLabelItems()` ~9743 | Lab/culture label fields (same scheme) | | `app.js` | `bcParams()` ~9482 | Barcode width + centering, hardcoded `> 400` and `(400 - w) / 2` | | `app.js` | `buildPreviewCell()` UI preview | `viewBox="0 0 400 240"` — the in-app preview is also pinned to 400×240 | | `mcp-server.js` | `itemsToZPL()` and `bagLabelItems()` ~121 | Mirror of the client logic for MCP-driven printing | Forking for a different label size means editing all five places (or introducing a label-template abstraction) and updating the preview viewBox. Not impossible, but more than a config tweak. #### What needs a new print backend (much bigger scope) Brother QL-series, Dymo LabelWriter, Citizen CL-S, and most non-Zebra thermal printers don't speak ZPL at all. To support them you'd add a new pipeline that emits raster bitmaps (PNG/PDF) and a corresponding print-bridge for those drivers. That's a separate project, not a config swap. #### Print bridge `scripts/print-bridge.ps1` is **Windows-only**. Linux/macOS hosts fall back to "ZPL download" — the UI emits a `.zpl` file the operator forwards to a print queue manually. Documented in [DEPLOYMENT.md §12](DEPLOYMENT.md#12-windows-print-bridge-optional). ### Cameras (optional `mushroom_camera/` module) Generic RTSP — works with any ONVIF camera (Reolink, Hikvision, generic). URLs are env-only (`CAM1_RTSP`, `CAM2_RTSP`, …). YOLOv8 + HSV thresholds are exposed as env vars; defaults work for the typical fruiting-tent lighting Meisterpilze runs. The Python module is **AGPL-3.0** (because Ultralytics YOLOv8 is). If you embed it in a non-AGPL project you need an Ultralytics commercial licence. ## 3. Geography / locale | Default | Where | How to change | |---|---|---| | UI language `de` | `app.js:59` `localStorage.mp-lang \|\| 'de'` | User picks via the language selector in the sidebar; default is per-browser | | Date format `DD.MM.YY` | `app.js` `fmtDt()` | Hardcoded — change in `app.js` if you need ISO or US format | | Server timezone | KPI snapshots, "due today" buckets are based on the **server's local timezone** at midnight | Run with `TZ=Europe/Berlin` (or your zone) in the service env | ## 4. Workflow / business-logic assumptions ### Default zones Migration v7 seeds `SPAWN`, `INC`, `TENT1`, `TENT2`, `TENT3`, `CONTAM` with English names. The dashboard maps the canonical IDs to localised display names via `KNOWN_ZONE_I18N` in `app.js`. Rename in **Settings → Zones** if your physical layout is different (e.g. greenhouse-A, greenhouse-B). The localisation falls away once your name no longer matches the i18n key. ### Inventory schema The `inventory` table has fixed columns for substrate stock: `stock_hardwood`, `stock_wheatbran`, `stock_gypsum`, `stock_grain`. Same for `batches.sub_*`. A lab that tracks straw pellets, coffee grounds, millet, or soy hulls **cannot** add those without a schema migration in `db.js:118-133` and corresponding column additions in `batches`. Display names ARE translatable; the underlying schema is not. ## 5. External services | Service | Status | Required? | |---|---|---| | **DuckDNS** | Hardcoded in `server.js:1023+` for built-in DDNS + Let's Encrypt DNS-01 | Optional — only if you want a public hostname with a real cert. For other DNS providers (Cloudflare, Route53, etc.), use the Nginx-reverse-proxy path in DEPLOYMENT.md §7 | | **Let's Encrypt** | Built-in via DuckDNS | Optional — self-signed cert works for LAN-only | | **GitHub webhook auto-deploy** | `/api/webhook/github` fetches `origin/main` and snapshots the database, then runs `git reset --hard`, `npm install`, the preflight and `pm2 restart`, one deploy at a time (DEPLOYMENT.md §9) | Optional — set `GITHUB_WEBHOOK_SECRET` to enable | ## 6. Deployment defaults | Variable | Default | Override | |---|---|---| | `PORT` | `3000` | env | | `DB_FILE` | `meistertracker.db` next to `server.js` | env | | `PRINTER_NAME` | `ZDesigner GK420d` | env | | `LABEL_WIDTH_DOTS` / `LABEL_HEIGHT_DOTS` | `400` / `240` | env | | `PM2_PROCESS_NAME` | `meisterpilze` | env (also `update_server.sh` and `START.bat`) | | `BACKUP_FILENAME_PREFIX` | `meisterpilze_backup_` | env — pick a fork-specific value **before** the first auto-backup, otherwise existing files won't match the rotation regex | | `PUBLIC_HOSTNAME` | derived from `Host:` header | env — recommended in production behind a proxy | | `TRUST_PROXY` | `false` | env — set to `true` when behind an nginx/cloudflare/traefik proxy so rate limits track the real client IP | | `GITHUB_WEBHOOK_SECRET` | unset → webhook disabled | env | ## 7. Documentation references These docs assume the original repo URL (`github.com/meisterpilze/meistertracker`): - `README.md` - `CONTRIBUTING.md` - `SECURITY.md` - `CODE_OF_CONDUCT.md` - `package.json` (`homepage`, `repository`, `bugs`) - `.github/ISSUE_TEMPLATE/config.yml` Forks should rewrite these links to point at their own repository — the upstream issue tracker only handles upstream bugs. ## 8. Trademark `Meistertracker`, `Meisterpilze`, and the Meisterpilze logo are trademarks of Meisterpilze UG. The AGPL-3.0 licence covers the source code only and does **not** grant rights to use these names or marks for your fork. Please pick your own name (and your own logo via `scripts/make_icons.py`) before publishing a fork.