Meistertracker
БесплатноНе проверенSelf-hosted lab management for specialty mushroom cultivation — barcode scanning, batch / culture / harvest tracking, label printing, CalDAV sync, MCP integrati
Описание
Self-hosted lab management for specialty mushroom cultivation — barcode scanning, batch / culture / harvest tracking, label printing, CalDAV sync, MCP integration. Built at meisterpilze.de.
README
Website License: AGPL-3.0-or-later CI Built at Meisterpilze Node 22+
Meistertracker is a self-hosted lab management system for mushroom cultivation. Workers walk the lab with phones or tablets, scan barcodes on bags and cultures with the device camera, and the software tracks every fruiting block from inoculation to harvest — across the four phases of the cultivation cycle (spawn run → incubation → fruiting → contamination triage).
Print barcode and QR labels at the workbench, file contamination reports with photos, weigh harvests against KPI dashboards, manage cultures and inventory, and sync tasks and due dates to any CalDAV calendar (Apple Calendar, Google Calendar, Thunderbird, DAVx5). Multi-user with role-based access (worker / admin), MCP integration for Claude Desktop and other LLM clients, offline-capable as a PWA. One Node.js process, SQLite database, no cloud dependencies — runs on Windows, macOS, or Linux (including a Raspberry Pi).
Meistertracker is the operational backbone of Meisterpilze, an urban mushroom farm in Erlangen, Germany, growing shiitake, oyster, king oyster, lion's mane, and blue oyster mushrooms for restaurants, retail, and home growers. Every fruiting block, every culture transfer, and every harvest gram in the lab is tracked through this software — released under AGPL-3.0-or-later so other labs can run, modify, and self-host it freely.
🍄🟫 About
Meistertracker is developed and maintained at Meisterpilze UG in Erlangen, Germany — an urban specialty-mushroom farm founded in June 2024 by Dr. Jonas Hahn (research, biologist) and Luis Veloso (production, chemist). In their own words: „Eine Verbindung von Wissenschaft und Natur" — a blend of science and nature in service of better food.
The software was built in-house because no off-the-shelf tool fit how a real mushroom lab actually works: barcode scanning over typing, lifecycle phases that match the fungal biology, and a label printer right at the workbench. It runs daily in our lab and is published under AGPL-3.0-or-later so other labs can build on it freely.
The software is provided without warranty of any kind and the authors accept no liability for damages arising from its use.
AGPL §13 reminder: if you operate this software as a network service for users other than yourself, you must offer them the corresponding source code (including any modifications). The unmodified upstream is at https://github.com/meisterpilze/meistertracker — linking back is usually enough to comply.
See LICENSE for the full terms.
Legal notice: the meistertracker.com domain that redirects to this repository is operated privately by Julian Zienert (Netherlands). See its imprint & privacy notice for how visits to the domain and emails to @meistertracker.com addresses are handled.
✨ Features
Core lab workflow
- Barcode scanning — ADD, MOVE, REMOVE, HARVEST actions via USB-keyboard scanner or phone camera
- Batch management — fruiting blocks and grain spawn bags with full lifecycle tracking
- Culture library — mother cultures, petri dishes, liquid cultures, grain-to-grain spawn with lineage tracing
- Harvest logging — per-bag weight tracking with flush numbers and yield analytics
- Inventory ledger — substrate stock, delivery logging, low-stock alerts, audit trail per change
- Contamination reports — photo upload + on-screen annotations, optional auto-MOVE to CONTAM zone, follow-up tasks
- Task management — auto-generated batch tasks plus manual tasks with team assignment
- CalDAV calendar sync — built-in CalDAV server consumed by Apple Calendar, Thunderbird, DAVx5
- Dashboard — KPIs, production pipeline chart, harvest analytics, rack occupancy, contamination rate
- Label printing — Code 128 + QR labels for Zebra GK420d (50×30 mm, 203 dpi)
- PWA — installable on phones / tablets, offline scan queue replays on reconnect
- Multi-language UI — German, English, Portuguese
Optional modules
- MCP integration — expose batches, cultures, scans, harvests, and maintenance to Claude Desktop via the Model Context Protocol with OAuth + PKCE
- Camera AI (in active development) (mushroom_camera/) — Python sidecar for RTSP-based fruiting and incubation monitoring, writing hourly snapshots back to the same SQLite database
- Print bridge — HTTPS-secured Windows service that forwards label prints from a Linux server to a USB-attached Zebra GK420d
- DuckDNS + Let's Encrypt — built-in dynamic DNS and automatic free TLS for self-hosted public access (no Nginx required)
- Harvest feed — signed, outbound-only push of what you harvested and what is coming, to a URL you choose, so a shop or listing page can answer "what's available today?" without the lab machine being reachable
👥 Who is this for?
- Specialty mushroom farms with 5-50 fruiting tents who have outgrown spreadsheets
- University and commercial fungal labs that need traceable culture lineage, contamination logs, and audit trails
- Mushroom growkit producers doing per-bag QC and harvest analytics
- Fungal R&D labs experimenting with substrates, strain crosses, and yield optimisation
You probably do not need this if you are hobby-growing one or two bags at home — a notebook is fine. If you are tracking 100+ bags across multiple zones with multiple workers, label printers, and offline phone scanners, this is built for you.
🚀 Quick Start
git clone https://github.com/meisterpilze/meistertracker.git
cd meistertracker
bash update_server.sh
On Windows, double-click START.bat instead.
Open https://localhost:3000 in your browser. The server upgrades plain HTTP automatically and (best-effort) binds port 80 for the redirect. For other devices on the same WiFi, use https://<your-ip>:3000 and accept the self-signed certificate warning on first connect.
Prerequisites
- Node.js v22+ — nodejs.org
- Git — repo must be cloned (not just copied)
Setting up a fresh Linux server? See DEPLOYMENT.md for a step-by-step guide covering Node install, PM2, TLS, DuckDNS + Let's Encrypt, and security hardening.
⚙️ Configuration
Create a .env file in the project root to override defaults:
PORT=3000
PRINTER_NAME=ZDesigner GK420d
🖥️ Server Management
Linux / macOS
bash update_server.sh # Update code, back up data, restart
bash update_server.sh start # Start (without pulling updates)
bash update_server.sh stop # Stop the server
bash update_server.sh status # Show PM2 process status
Both scripts use PM2 for process management and auto-restart, so commands like pm2 logs meisterpilze, pm2 monit, and pm2 list work identically on either platform.
Windows
START.bat does the same job as update_server.sh: it pulls the latest code, installs deps, backs up the DB (using sqlite3 .backup if available, otherwise a file copy), generates a TLS cert if missing, and (re-)starts the PM2 process. Double-click it or run it from a terminal.
Auto-start on boot
Linux — pm2 startup systemd generates a systemd unit, then pm2 save freezes the current process list:
pm2 startup
# copy and run the printed `sudo env PATH=...` line
pm2 save
Windows — two equally valid options:
Startup folder shortcut (per-user, runs at logon)
Win + R→shell:startup→ Enter- Right-click in the folder → New → Shortcut → point at
C:\path\to\meistertracker\START.bat - Optional: in the shortcut Properties, set "Run" to Minimized so the console window doesn't pop into focus.
Task Scheduler (more robust — works even without an interactive logon)
- Open Task Scheduler → Create Basic Task
- Trigger: At log on (or At startup, if you want it before login)
- Action: Start a program
- Program:
C:\path\to\meistertracker\START.bat - "Start in":
C:\path\to\meistertracker
- Program:
- Optional: in the task's Settings tab, enable "Run task as soon as possible after a scheduled start is missed".
After either setup, PM2 needs to know the process list to restore. Run once after starting the server normally:
pm2 save
PM2 then writes %USERPROFILE%\.pm2\dump.pm2 and START.bat reads it on the next launch to restore the meisterpilze process.
📷 Scanning Workflow
- Print the Reference Barcodes page (Print tab) and hang it at your station
- Scan ADD → scan a location (INC, TENT1, etc.) → scan bag barcodes
- Scan MOVE → scan FROM → scan TO → scan bags
- Scan REMOVE → scan bags
- Scan HARVEST → scan a bag → enter weight
The scan bar works on every tab. Scanners must be in USB Keyboard mode.
🏷️ Label Printing (Zebra ZPL · 50×30 mm · 203 dpi)
The server sends ZPL directly to the printer via the Windows print spooler — no browser dialog needed.
- Connect a ZPL-compatible Zebra (GK420d, ZD420, ZD230, …) via USB and ensure it is powered on
- Labels are 50 × 30 mm, Code 128 / QR, designed for 203 dpi (
^PW400 ^LL240)
On non-Windows systems, use the "Download ZPL" fallback to send labels manually.
Compatibility: the layout is tuned for ZPL II at 203 dpi with 50×30 mm landscape labels. Different Zebras at the same dpi/label size work as-is; different label sizes, 300 dpi printers, or non-ZPL printers (Brother QL, Dymo LabelWriter, …) need either a layout refactor or a new print backend. See FORKING.md §2 for the full breakdown.
🔐 Authorization
The app has two user roles: worker and admin.
Workers can:
- Create batches, harvests, scans, cultures, and calendar events
- Log lab work and inventory consumption from batch creation
- Create and complete their own tasks
- Modify/delete tasks they are assigned to, or tasks with no assignee
Admins can:
- Everything workers can
- Delete calendar events and suppliers
- Manage users (create/delete/reset password)
- Adjust inventory manually (thresholds, composition config)
- Manage zones, racks, OAuth clients, CalDAV config
- Download/restore the encrypted database backup
Tasks belong to the people listed in their assignee field. An unassigned task (empty assignee) is considered "for everyone" and any authenticated worker may modify or delete it.
💾 Data & Backups
All data is stored in meistertracker.db (SQLite) on the server (shared by all devices automatically). Connected clients receive changes in near-real-time via Server-Sent Events; offline scans queue inside the service worker and replay automatically on reconnect.
- Auto-backup: daily at midnight to
backups/(keeps last 30 days). Uses SQLiteVACUUM INTOso the backup is WAL-consistent even while the server is writing. Each run writesbackups/.backup-status.jsonwith success/failure and size, and the latest file is verified to have a valid SQLite header before the status is marked successful. - Manual backup: use the Backup tab in the app to export/import an encrypted archive (requires admin).
- Remote backup:
scp user@host:~/meistertracker/meistertracker.db ./backup.db - Off-machine (REQUIRED on production): set up an
rsync-over-SSH cron that touchesbackups/.offsite-sync.jsonon each successful run — the marker is read by/api/healthandscripts/check-backup-health.js. See DEPLOYMENT.md → Off-site backups (REQUIRED) for the canonical setup. As a Windows-only convenience, placing the project folder under a cloud-synced directory (OneDrive, Dropbox, iCloud Drive, etc.) cloud-syncsbackups/automatically.
Monitoring backup health
Two commands check that the daily backup is actually running and producing valid output:
# Quick health check — exits 0 if OK, 1 if stale / missing / corrupt
node scripts/check-backup-health.js
# Full end-to-end verification — takes a fresh backup into a scratch dir,
# re-opens it, compares row counts, runs PRAGMA integrity_check, then deletes.
node scripts/verify-backup.js
The authenticated /api/health endpoint also includes a backup section with status, ageHours, and the last success / failure / attempt timestamps so any uptime monitor can watch it.
Restoring from a backup
- Stop the server so nothing writes to the DB during the swap:
bash update_server.sh stop # Linux / macOS # Windows: close the START.bat window - Pick the backup you want to restore. Files are in
backups/meisterpilze_backup_YYYY-MM-DD.db. Pick the most recent one that predates the corruption or data loss. - Move the current DB aside (keep it — do not delete):
mv meistertracker.db meistertracker.db.broken rm -f meistertracker.db-wal meistertracker.db-shm - Copy the backup into place:
cp backups/meisterpilze_backup_YYYY-MM-DD.db meistertracker.db - Start the server and log in to confirm data is present:
bash update_server.sh start # Linux / macOS # Windows: double-click START.bat - Verify the restore by opening the app and checking recent batches, harvests, and users. If anything is missing, stop the server and repeat step 3 onwards with an older backup.
- Once you have confirmed the restore is good, you can delete
meistertracker.db.broken.
For encrypted restores initiated from the admin UI, use Settings → Backup → Restore and provide the password that was used when the backup was downloaded.
For full deployment context (off-site backups, WAL-only recovery, manual file swap on a server with no UI access), see DEPLOYMENT.md → Restoring from a backup and → Off-site backups (REQUIRED).
🥧 Raspberry Pi Deployment
For a dedicated always-on server (Pi 4/5 recommended):
- Flash Raspberry Pi OS Lite (64-bit) with SSH enabled
- Install Node.js:
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - sudo apt install -y nodejs - Clone and start:
git clone https://github.com/meisterpilze/meistertracker.git cd meistertracker && bash update_server.sh - Enable autostart:
pm2 startup systemd # run the command it prints pm2 save - Assign a static IP in your router's DHCP settings
🧩 Optional Modules
The Core features above are everything you need to run a lab. The three pieces below are entirely optional — none of them is required for the main app to work, and any combination of them can be enabled per deployment.
MCP integration (Claude Desktop)
mcp-server.js exposes the lab state as a Model Context Protocol tool surface so an LLM client (Claude Desktop, Claude Code, …) can read and mutate batches, cultures, scans, harvests, inventory, tasks, contamination reports, zones, racks, and maintenance schedules.
- Transport — HTTPS, OAuth 2.0 with PKCE; clients register dynamically per RFC 7591.
- Auth — every tool call carries the OAuth-derived user identity; admin-only operations are gated server-side.
- Setup — open
Settings → MCPin the admin UI and paste the connection URL into your MCP client. A legacy static-token transport remains available for headless integrations under the same tab.
For the full tool list see mcp-server.js; the OAuth flow is documented in openapi.yaml.
Camera AI module — mushroom_camera/ (in active development)
A Python sidecar that watches RTSP cameras placed in the fruiting tents and incubation room and writes hourly snapshots back to the same SQLite database. The main Node.js app reads those tables to surface a live camera dashboard at Settings → Camera. The detection pipeline is still being trained on real lab data — current results should be treated as experimental.
cd mushroom_camera
pip install -r requirements.txt
export CAM1_RTSP="rtsp://user:[email protected]/stream1"
python -m mushroom_camera # APScheduler daemon — runs every hour
python -m mushroom_camera --now # one-shot cycle (good for cron)
The sidecar is independent: the main app keeps working fine if mushroom_camera/ is never started — the Camera tab simply reports "no measurements yet". Tuning thresholds live in mushroom_camera/config.py; see DEPLOYMENT.md for deploying it as a systemd service alongside the main process.
Print bridge (Windows)
If your Linux server can't talk to the Zebra directly (very common when the printer is in a different room from the server), run scripts/print-bridge.ps1 on a Windows PC that has the GK420d attached via USB. The Linux server forwards /api/print and /api/printer-status calls to the bridge over HTTPS with token authentication, so labels go straight to the printer with live status feedback.
# On the Windows PC — one-time setup (auto-elevates via UAC):
powershell -ExecutionPolicy Bypass -File print-bridge.ps1 -Install -Token "long-random-string"
The installer handles TLS certificate, URL ACL, inbound firewall rule, scheduled task, and immediate start in one step. Then enter the URL + token in the admin UI under Settings → Drucker. Without a print bridge configured, the app falls back to a "Download ZPL" workflow — no driver setup needed but one extra click per print.
Full setup walkthrough plus troubleshooting in DEPLOYMENT.md → Section 10.
Harvest feed (outbound)
harvest-feed.js posts a small, signed summary of your harvest situation to a URL you configure. The problem it solves: the numbers already live in this database, but the systems that need them — your own website, a CSA or box scheme, a co-op listing, a chat bot answering "what do you have today?" — live elsewhere. Copying them by hand goes stale within a day, and pointing those systems at the lab machine means exposing it to the internet.
One direction only. This module opens connections; it never accepts any. No inbound endpoint is added, no port needs opening, and a changing home IP does not matter. If the lab machine is off, the receiver keeps the last payload — its consumers see older numbers rather than an outage.
{
"version": 1,
"generatedAt": "2026-07-30T16:00:00.000Z",
"freshDays": 3,
"harvested": [{ "species": "Oyster", "strain": "Blue", "grams": 4700, "lastHarvest": "2026-07-30T07:30:00" }],
"planned": [{ "species": "Lion's Mane", "strain": "LM1", "expectedFrom": "2026-08-05" }]
}
Species, strain, gram totals and dates. No batch ids, no bag ids, no customers, no scan history, no notes — a summary is far easier to reason about than a dump, and everything that leaves is something you have to reason about.
Two things it deliberately does not do:
- It does not estimate yields. Planned entries carry a species and a date, never an amount. How much a block gives varies too much between flushes, and a number that reaches a customer becomes a promise. Recorded harvests are measured, so those do carry grams.
- It does not subtract reservations. If half of Thursday's harvest is already promised to a restaurant, publish the remainder — but what counts as promised differs per lab and is not tracked here. Do that subtraction in the receiving system, where the commitments live, or use release mode below, which asks the question the other way round.
Release mode: what may be sold, not what was harvested
harvested is a production fact, and it only ever grows. That makes it the wrong number for a shop the moment you sell anything anywhere else: three kilos over the counter on Saturday, and the feed still reports Friday's harvest until it ages out of the window. Recording every sale would fix the arithmetic and will not happen — a market stand takes cash, not keystrokes.
Release mode publishes a set-aside instead. In Settings → Harvest feed → Released for sale you enter, per species, how much a shop may sell and until when. Put that amount in its own crate and sell everything else from the rest, and no walk-in customer can make the published figure wrong — there is nothing to keep up to date. The payload then calls itself version 2 and carries a second list:
{
"version": 2,
"harvested": [{ "species": "Oyster", "grams": 6200 }],
"released": [{ "species": "Oyster", "grams": 2000, "validUntil": "2026-08-02" }]
}
The version bump is the point: a receiver that publishes harvested and ignores released would offer produce you deliberately kept back. That is a change of meaning, not a new optional field, so a receiver built for version 1 is expected to reject it rather than guess. Labs that leave release mode off keep sending version 1 and their receivers notice nothing.
validUntil is optional and worth setting. Fresh produce does not keep, and the realistic mistake is not a wrong number but a forgotten one — last week's release quietly selling mushrooms that were eaten days ago. An expired release counts as zero here; a receiver holding an old payload should apply the same rule at serving time, since the lab machine may simply be switched off.
A release outlives its harvest window on purpose. Set two kilos aside on Monday for Saturday's market and by Thursday the harvest has dropped out of freshDays while the crate is still standing there. The person who put it there is the better source than the window arithmetic.
Every request is signed: X-Meistertracker-Signature: sha256=<HMAC-SHA256 of "<timestamp>.<body>"> plus X-Meistertracker-Timestamp. Signing the timestamp together with the body is what makes a captured request useless later — reject anything outside your tolerance window and it cannot be replayed. The secret is not optional; the feed refuses to start without one, because a forged "we have 40 kg" is worse than no feed at all.
Set it up in Settings → Harvest feed. Receiver URL, a generated secret, how often, and how long a harvest counts as fresh. Two buttons that matter: Show what would be sent builds the payload and displays it without sending — the answer to "is this going to leak something?" is to look at it, not to trust the description above. Send one now delivers it and reports what came back, which is the difference between saved and working. The last outcome stays on the screen, so a feed that quietly stopped delivering is visible instead of silent.
The same knobs exist as environment variables (HARVEST_WEBHOOK_URL, HARVEST_WEBHOOK_SECRET, …) for installs that bake configuration into an image — see DEPLOYMENT.md → Section 15. The stored settings win when enabled; environment variables apply otherwise, and Settings says which of the two is in charge. Off is the default, and an upgrade never starts sending on its own.
node harvest-feed.js --dry-run # print exactly what would be posted, post nothing
node harvest-feed.js --once # build, sign, POST, report the result
🔌 API
The full REST surface (40+ operations covering auth, scanning, batches, cultures, harvests, inventory, tasks, contamination reports, photos, users, OAuth, MCP, CalDAV, DuckDNS, Let's Encrypt, backups, health, and webhook auto-deploy) is specified in openapi.yaml.
Notable surfaces worth knowing about:
| Path | Description |
|---|---|
GET /api/health |
Public liveness + uptime |
GET /api/health/full |
Admin-only ops view (disk, printer, DuckDNS, LE expiry, backup …) |
POST /api/data |
Full-state save (admin) — used by the SPA |
POST /api/print |
Send ZPL to printer (or print bridge) |
/caldav/calendars/ |
CalDAV endpoint for Apple Calendar / Thunderbird / DAVx5 |
/oauth/authorize |
OAuth 2.0 with PKCE for MCP clients |
/mcp |
Model Context Protocol transport |
📁 Project Structure
server.js HTTP+HTTPS server, CalDAV, OAuth, printer integration
db.js SQLite schema, migrations, queries, sessions, KPI snapshots
mcp-server.js Model Context Protocol tool surface
harvest-feed.js Outbound-only signed harvest summary (optional)
index.html SPA shell
app.js Frontend application logic
styles.css Stylesheet
sw.js Service worker (PWA, offline scan queue)
manifest.json PWA manifest
login.html, login.js Login + first-admin setup page
openapi.yaml REST API specification
lang/ Language packs (de, en, pt)
lib/ Vendored libraries (Chart.js, JsBarcode, html5-qrcode, qrcode)
mushroom_camera/ Optional Python AI camera module — see DEPLOYMENT.md
scripts/ Utilities: backup health, photo capture, print bridge, i18n audits
test/ Test suite (db, mcp-server, backup, perf, photo-cap)
update_server.sh Linux / macOS setup, update, and process management
START.bat Windows launcher (mirrors update_server.sh)
gen-cert.sh, .ps1 Self-signed TLS certificate generators
Dockerfile Containerized deployment
🍴 Running it for your own lab?
Meistertracker was built for one specific operator and a few rough edges still reflect that — a fixed inventory schema, a CalDAV slug baked in for compatibility, and a print pipeline tuned for Zebra ZPL at 203 dpi with 50×30 mm labels. Most of these are configurable via env vars; some need a small fork. Read FORKING.md for the full inventory of what's tuned to Meisterpilze vs. what's generic, and the env-var matrix for the configurable bits.
🤝 Contributing
Issues and pull requests are welcome at https://github.com/meisterpilze/meistertracker/issues. By submitting a contribution you agree that your code is licensed under the AGPL-3.0-or-later — the same terms as the rest of the project.
Local development:
git clone https://github.com/meisterpilze/meistertracker.git
cd meistertracker
npm install
npm test # ~211 unit tests
npm run lint # eslint
npm run format # prettier --write
The CI workflow (.github/workflows/ci.yml) runs lint, format check, and tests on every PR against main.
📜 License
Released under the GNU Affero General Public License v3.0 or later.
Copyright © 2026 Meisterpilze UG and contributors.
Vendored third-party libraries in lib/ ship under their own permissive licenses (Chart.js — MIT, JsBarcode — MIT, html5-qrcode — Apache-2.0, qrcode-generator — MIT). See NOTICE for full attribution and license texts.
Установка Meistertracker
У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.
▸ github.com/meisterpilze/meistertrackerFAQ
Meistertracker MCP бесплатный?
Да, Meistertracker MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для Meistertracker?
Нет, Meistertracker работает без API-ключей и переменных окружения.
Meistertracker — hosted или self-hosted?
Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.
Как установить Meistertracker в Claude Desktop, Claude Code или Cursor?
Открой Meistertracker на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
wenb1n-dev/SmartDB_MCP
A universal database MCP server supporting simultaneous connections to multiple databases. It provides tools for database operations, health analysis, SQL optim
автор: wenb1n-devPostgres Server
This server enables interaction with PostgreSQL databases through the Model Context Protocol, optimized for the AWS Bedrock AgentCore Runtime. It provides tools
автор: madhurprashPostgres
Query your database in natural language
автор: AnthropicPostgreSQL
Read-only database access with schema inspection.
автор: modelcontextprotocolCompare Meistertracker with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории data
