Configuration Matrix¶
This page is the exhaustive operator reference: for practically every Kamerplanter feature, it shows which services must run for it, the exact environment variable or Helm switch that turns it on or off, which secrets are mandatory, how much CPU/RAM/storage it adds, and whether a missing prerequisite blocks startup.
Start with bundles
If you do not want to assemble every feature individually, start with the five ready-made bundles under Deployment Profiles. This page is the exhaustive per-feature reference behind them.
How to read this matrix¶
Every table below follows the same column layout:
| Column | Meaning |
|---|---|
| Feature | The feature or sub-feature, named in plain language. |
| Required services/components | Which pods/containers must run for it — referring to helm/kamerplanter/values.yaml controller names or external services. |
| Activation/deactivation | The exact environment variable from src/backend/app/config/settings.py (or src/knowledge-service/app/config.py / src/inference-service/app/config.py) or the Helm toggle path. |
| Mandatory secrets/prerequisites | Values that must be set for the feature to actually work (not just "not crash"). |
| Resource impact | CPU/RAM/storage delta over the core, from resources.requests/resources.limits in values.yaml (or values-dev-ki.yaml, where no chart defaults exist for production — see the note there). |
| Startup gate? | Does insecure_default_secrets() (src/backend/app/main.py) or the respective microservice's check_insecure_config() refuse to start when a prerequisite is missing? |
Two separate on/off layers — do not confuse them¶
Kamerplanter has two independent visibility/activation layers that are frequently confused in support conversations:
| Layer | Who sets it? | Effect | Example |
|---|---|---|---|
| Operator env gate (this page) | The instance operator, via environment variable/Helm value, applies instance-wide | Fully disables a backend endpoint (HTTP 404) or prevents a pod from starting at all. No user can bypass it. | AI_FEATURES_ENABLED=false makes /ai/* answer with HTTP 404 instance-wide. |
| Personal module visibility (REQ-021/042) | Every user for themselves, in Account Settings under "Modules & Features" | Pure display preference — hides a navigation area in the UI, changes nothing about data or API availability. | A user hides "Tank Management" even though the feature is active instance-wide. |
flowchart LR
A["Operator env gate<br/>(this page, instance-wide)"]
B["Personal module visibility<br/>(REQ-021/042, per user)"]
C["Effective availability<br/>for a given user"]
A -->|"must be active, otherwise"| X["API answers 404 / feature effectively does not exist"]
A -->|"active"| B
B -->|"controls display only"| C
style A fill:#4CAF50,color:#fff
style B fill:#8D6E63,color:#fff
style C fill:#388E3C,color:#fff A feature disabled via the operator env gate can not be re-enabled by any user through module visibility — the two layers work in series, not in parallel. Details on the user-level layer: Modules & Features.
Mandatory secrets per enabled feature¶
This overview bundles the boot blockers across all three processes — backend, Knowledge Service, Inference Service. All three check their own secrets at startup and abort with SystemExit when they are missing (effective only when DEBUG=false, see Environment Variables — Operating Mode).
| Secret | Process | When mandatory? | Check |
|---|---|---|---|
JWT_SECRET_KEY | Backend | Always, regardless of enabled features | Value must no longer be change-me-in-production-use-openssl-rand-hex-32 |
ARANGODB_PASSWORD | Backend | Always | Value must no longer be rootpassword |
FERNET_KEY | Backend | Always — regardless of whether OIDC providers are used | Must not be empty; must be a valid Fernet key (32 bytes, url-safe base64, 44 characters) |
ERASURE_TOMBSTONE_SALT | Backend | Always — regardless of whether GDPR erasure requests are actively used | Must be at least 32 characters long |
LOG_PSEUDONYM_SALT | Backend + Celery Worker | Always — regardless of which features are active; the backend and the worker need the same value | Must be at least 32 characters long |
TIMESCALEDB_PASSWORD | Backend | Only when TIMESCALEDB_ENABLED=true | Value must no longer be changeme |
INTERNAL_SERVICE_TOKEN | Backend | Only when KNOWLEDGE_SERVICE_ENABLED=true or INFERENCE_SERVICE_ENABLED=true | Must not be empty |
INTERNAL_SERVICE_TOKEN + INFERENCE_SERVICE_ENABLED/-URL | Celery Worker | Only when INFERENCE_SERVICE_ENABLED=true — must be identical to the backend's value, or the worker holds the GDPR erasure of contributed reference images as a configuration error (see the warning below) | Must not be empty |
INTERNAL_SERVICE_TOKEN | Knowledge Service | Always, whenever the process runs at all (its own gate, independent of the backend gate) | Must not be empty |
VECTORDB_PASSWORD | Knowledge Service | Always, whenever the process runs at all | Value must no longer be changeme |
INTERNAL_SERVICE_TOKEN | Inference Service | Always, whenever the process runs at all | Must not be empty |
VECTORDB_PASSWORD | Inference Service | Always, whenever the process runs at all | Value must no longer be changeme |
The first five rows affect EVERY production instance
JWT_SECRET_KEY, ARANGODB_PASSWORD, FERNET_KEY, ERASURE_TOMBSTONE_SALT, and LOG_PSEUDONYM_SALT are not feature flags — they are checked regardless of which of the optional features listed below are active. A fresh production instance without these five values simply refuses to start (SystemExit) once DEBUG=false is set — for LOG_PSEUDONYM_SALT this applies to both the backend and the Celery worker (Celery Beat does not check it).
INTERNAL_SERVICE_TOKEN must be identical everywhere
The backend, Celery worker/beat, Knowledge Service, and Inference Service must all receive the same INTERNAL_SERVICE_TOKEN value (one Kubernetes secret, injected into all four controllers via envFrom/secretKeyRef) — it is a shared M2M secret, not a per-service token.
Plant Identification ¶
| Feature | Required services | Activation/deactivation | Mandatory secrets/prerequisites | Resource impact | Startup gate? |
|---|---|---|---|---|---|
| Pl@ntNet (cloud, free tier ≤ 500/day) | Backend (external HTTP call) | PLANTNET_API_KEY set and PLANTNET_ENABLED=true (default) | PLANTNET_API_KEY | no additional pods | No |
| Plant.id / Kindwise (cloud, operator opt-in) | Backend (external HTTP call) | PLANT_ID_API_KEY set | PLANT_ID_API_KEY | no additional pods | No |
| Self-hosted DINOv2 recognition | inference-service + vectordb (dedicated Helm controllers) | INFERENCE_SERVICE_ENABLED=true and controllers.vectordb.enabled=true and controllers.inference-service.enabled=true | INTERNAL_SERVICE_TOKEN, POSTGRES_PASSWORD (one secret key, shared by vectordb and inference-service) | inference-service: 250m/2 CPU, 512Mi/2Gi RAM; vectordb: 50m/500m CPU, 128Mi/512Mi RAM + 5Gi PVC | Yes — internal_service_token (backend gate) + the inference-service/vectordb process's own gate |
| Allow the external recognition path in Light Mode | Backend | IDENTIFICATION_EXTERNAL_IN_LIGHT_MODE=true (default false) | — | — | No |
| Species identity resolution/deduplication | Backend (part of the adapters above) | No dedicated switch — always runs once an identification adapter is active | — | — | No |
Full setup (activation order, populating the reference index): Setting Up Plant Identification.
INFERENCE_SERVICE_ENABLED also belongs on the celery-worker (GDPR erasure, internal reference: issue #1753)
The row above lists only the inference-service/vectordb stack itself. Once users contribute their own photos as reference images, INFERENCE_SERVICE_ENABLED/INFERENCE_SERVICE_URL must also carry the same value on the celery-worker — it runs the scheduled Art. 17 erasure of those contributions. If it is missing there, the worker holds due erasures as a configuration error, and a tenant deletion answers with HTTP 503. Details: Setting Up Plant Identification.
Pest Detection ¶
| Feature | Required services | Activation/deactivation | Mandatory secrets/prerequisites | Resource impact | Startup gate? |
|---|---|---|---|---|---|
| Master switch | — | PEST_DETECTION_ENABLED=true (default false) | — | — | No |
| Symptom-pattern recognition (mode 2, default adapter) | inference-service (shared with plant identification) | PEST_DETECTION_SYMPTOM_ENABLED=true (default) — only effective when PEST_DETECTION_ENABLED=true | Same as the inference service above | shares the inference-service pod, no extra requirement | Yes (via the shared inference service gate) |
| Direct detector (mode 1, phase 2, D-FINE/RF-DETR ONNX) | inference-service | PEST_DETECTION_DETECTOR_ENABLED=true (default false) | Trained ONNX detector baked into the service image | shares the inference-service pod | Yes (via the shared inference service gate) |
| Demo adapter (placeholder findings, no real model) | none | PEST_DETECTION_DEMO_ENABLED=true (default false) — not for real decisions | — | none | No |
Cloud adapter (Kindwise plant.health) | Backend (external HTTP call) | PEST_DETECTION_CLOUD_ENABLED=true and PEST_DETECTION_CLOUD_API_KEY set | PEST_DETECTION_CLOUD_API_KEY | no additional pods | No |
PEST_DETECTION_ENABLED/INFERENCE_SERVICE_ENABLED also belongs on the celery-worker (GDPR erasure, internal reference: issue #1759)
Once a platform admin has promoted a contributed user pest photo into the recognition base, PEST_DETECTION_ENABLED or INFERENCE_SERVICE_ENABLED together with INFERENCE_SERVICE_URL must also be set on the celery-worker — it runs the scheduled Art. 17 erasure of those contributions (regardless of whether the promotion is still active). If it is missing there, the worker holds due erasures as a configuration error, and a tenant deletion answers with HTTP 503. If the inference-service is merely unreachable, it instead answers with HTTP 502. Details: Setting Up Plant Identification.
CV Disease Diagnosis ¶
| Feature | Required services | Activation/deactivation | Mandatory secrets/prerequisites | Resource impact | Startup gate? |
|---|---|---|---|---|---|
| Self-hosted CV diagnosis (ONNX PlantDoc classifier) | inference-service (shared) | CV_DIAGNOSIS_ENABLED=true (default false) | Same as the inference service above — reuses the existing INFERENCE_SERVICE_URL/INTERNAL_SERVICE_TOKEN connection, no dedicated connection variables | shares the inference-service pod | Yes (via the shared inference service gate) |
| PlantCV phenotype panel (leaf area, greenness index) | inference-service (shared) | CV_PHENOTYPE_ENABLED=true (default) — only effective when the inference service has PlantCV installed | — | shares the inference-service pod | No |
As of this version there is no cloud adapter for CV disease diagnosis — distinct from pest detection above.
AI Assistant, Knowledge Service, and Language-Model Providers ¶
| Feature | Required services | Activation/deactivation | Mandatory secrets/prerequisites | Resource impact | Startup gate? |
|---|---|---|---|---|---|
| Unlock the AI API instance-wide (stage 1) | Backend | AI_FEATURES_ENABLED=true (default false) — false makes /ai/* answer with HTTP 404 | — | ~no extra requirement on the backend itself | No |
| Connect to the Knowledge Service | Backend + Knowledge Service (controllers.knowledge-service) | KNOWLEDGE_SERVICE_ENABLED=true + KNOWLEDGE_SERVICE_URL | INTERNAL_SERVICE_TOKEN | knowledge-service: ~100m/1 CPU, 128Mi/512Mi RAM (orientation value from values-dev-ki.yaml — no chart default exists for production, see the note below) | Yes — backend gate + the Knowledge Service process's own gate |
| Language model: Ollama (self-hosted) | Ollama subchart + Embedding Service + VectorDB | LLM_PROVIDER=ollama + LLM_API_URL + LLM_MODEL on the Knowledge Service, not the backend | — | Ollama 4–16Gi RAM (model-dependent); embedding-service ~100m/2 CPU, 1.5–4Gi RAM (dev orientation); vectordb 50m/500m CPU, 128Mi/512Mi RAM + 5Gi PVC | No (Ollama itself has no Kamerplanter startup gate) |
| Language model: Anthropic (cloud) | Knowledge Service | LLM_PROVIDER=anthropic + LLM_API_KEY on the Knowledge Service | LLM_API_KEY | no additional pods | No |
| Language model: OpenAI-compatible (cloud, e.g. OpenAI) | Knowledge Service | LLM_PROVIDER=openai_compatible + LLM_API_URL + LLM_API_KEY on the Knowledge Service | LLM_API_KEY | no additional pods | No |
| Re-ranking (higher RAG precision, ADR-007) | Reranker Service (controllers.reranker-service) | RERANKER_URL set on the Knowledge Service (empty = disabled) | — | reranker-service ~100m/2 CPU, 1.5–4Gi RAM (dev orientation) | No |
| AI terminology glossary | shares the AI assistant stack | No dedicated switch — follows AI_FEATURES_ENABLED | — | — | No |
| Structured AI diagnosis assistant (symptom catalogue) | shares the AI assistant stack | No dedicated switch — follows AI_FEATURES_ENABLED; only available in Full mode | — | — | No |
| Daily budget for AI calls | Backend + Valkey (counters per UTC day) | Always on; AI_BUDGET_USER_CALLS_PER_DAY (default 50 per account and tenant), AI_BUDGET_TENANT_CALLS_PER_DAY (500 per tenant), AI_BUDGET_TENANT_TOKENS_PER_DAY (2000000 tokens per tenant); 0 switches a budget off. Above that 429 AI_BUDGET_EXCEEDED with Retry-After until the day ends; RATE_LIMIT_INFERENCE per account and minute on top | Valkey from REDIS_URL — unreachable: no AI call (503 AI_BUDGET_UNAVAILABLE, fail-closed) | ~no extra demand | No |
No automatic cloud fallback
There is no runtime fallback between Ollama and a cloud provider. LLM_PROVIDER is a single configured value (ollama, anthropic, or openai_compatible) — switching it is a Knowledge Service redeploy, not an automatic failover on unreachability.
No pre-stubbed enabled blocks for the Knowledge/Embedding/Reranker Service
Unlike vectordb and inference-service, helm/kamerplanter/values.yaml contains no commented-out or disabled controller blocks for knowledge-service, embedding-service, and reranker-service. The resource figures shown here come from the Skaffold dev overlay values-dev-ki.yaml — operators must supply the full controller block (image, resources, probes) themselves in the ArgoCD valuesObject. An example is shown in Deployment Profiles → Professional.
Unlocking the instance (AI_FEATURES_ENABLED=true) is not sufficient on its own: a given tenant also needs tenant.settings.ai_features_enabled (stage 2), and the user needs to grant consent (stage 3). Details: AI Assistant — For Technical Users / Self-Hosters.
MCP Server ¶
| Feature | Required services | Activation/deactivation | Mandatory secrets/prerequisites | Resource impact | Startup gate? |
|---|---|---|---|---|---|
| MCP tool interface for external LLM clients | Backend (runs in the existing process) | MCP_SERVER_ENABLED=true (default false) — false makes /mcp/* answer with HTTP 404 | Service-account API keys (per client, via user management — not a global env secret) | no additional pods, no separate process | No |
Weather, Frost Early-Warning, Climate Normals, and Irrigation Demand ¶
| Feature | Required services | Activation/deactivation | Mandatory secrets/prerequisites | Resource impact | Startup gate? |
|---|---|---|---|---|---|
| Weather source fetching (master switch) | Backend + Celery Beat | WEATHER_ENABLED=true (default false) | — | additional Celery Beat tasks, no extra pod | No |
| Public source: Open-Meteo | Backend | OPEN_METEO_ENABLED=true (default) — instance default, overridable per location by the platform admin | — | — | No |
| Public source: DWD/Bright Sky | Backend | DWD_ENABLED=true (default) — instance default | — | — | No |
| Public source: OpenWeatherMap | Backend | OPENWEATHERMAP_ENABLED=true (default) — instance default | — | — | No |
| Proactive frost early-warning (forecast-based) | Celery Beat | Requires WEATHER_ENABLED=true; thresholds via FROST_FORECAST_THRESHOLD_CELSIUS/FROST_FORECAST_HORIZON_DAYS | — | — | No |
| Reactive frost warning (current reading) | Backend | Always active, no switch — only FROST_WARNING_THRESHOLD_CELSIUS is configurable | — | — | No |
| Climate normals (NASA POWER) | Celery Beat | Requires WEATHER_ENABLED=true and NASA_POWER_CLIMATE_ENABLED=true (default) | — | monthly Celery task | No |
| Hardiness zone refresh (USDA) | Celery Beat | Requires climate normals (see above) and HARDINESS_ZONE_REFRESH_ENABLED=true (default) | — | quarterly Celery task | No |
| Irrigation demand (ET₀, FAO-56) | Celery Beat | Requires WEATHER_ENABLED=true and IRRIGATION_DEMAND_ENABLED=true (default) | — | daily Celery task | No |
| Season & overwintering automation | Celery Beat | SEASON_STATE_EVAL_ENABLED=true (default) — additionally uses the live frost forecast when WEATHER_ENABLED=true is set | — | daily Celery task | No |
Sensors and Time-Series Data ¶
| Feature | Required services | Activation/deactivation | Mandatory secrets/prerequisites | Resource impact | Startup gate? |
|---|---|---|---|---|---|
| Manual readings | Backend + ArangoDB | Always active, no switch | — | — | No |
| Home Assistant connection (semi-automatic, live) | Backend | HA_URL + HA_ACCESS_TOKEN set | HA_ACCESS_TOKEN | — | No |
| Time-series storage with downsampling | timescaledb controller (off by default) | controllers.timescaledb.enabled: true (also sets TIMESCALEDB_ENABLED=true on backend, worker and beat) | TIMESCALEDB_PASSWORD | ~250m/1 CPU, 512Mi/1Gi RAM + 10Gi PVC | Yes — timescaledb_password default check (only when TIMESCALEDB_ENABLED=true) |
TimescaleDB with one switch
In the chart, controllers.timescaledb.enabled: true switches on the database, its Service, its NetworkPolicy and TIMESCALEDB_ENABLED on backend, worker and beat together; add TIMESCALEDB_PASSWORD to kamerplanter-secrets first (see TimescaleDB). Docker Compose has its own profile for it: docker-compose --profile timescaledb up.
Environment Control & Actuators ¶
| Feature | Required services | Activation/deactivation | Mandatory secrets/prerequisites | Resource impact | Startup gate? |
|---|---|---|---|---|---|
| Automatic rule/schedule loop | Celery Beat | ACTUATOR_CONTROL_LOOP_ENABLED=true (default false) | — | three additional periodic tasks (30s/hourly/5min) | No |
| Manual actuator control via the API | Backend | Always available — independent of the rule loop | — | — | No |
| Talking to Home Assistant actuators | Backend | HA_URL + HA_ACCESS_TOKEN set; optionally HA_ALLOW_PRIVATE_ENDPOINT=true for LAN addresses | HA_ACCESS_TOKEN | — | No |
Notification System ¶
| Feature | Required services | Activation/deactivation | Mandatory secrets/prerequisites | Resource impact | Startup gate? |
|---|---|---|---|---|---|
| Email channel (console, development) | Backend | EMAIL_ADAPTER=console (default) | — | — | No |
| Email channel (SMTP) | Backend + external SMTP server | EMAIL_ADAPTER=smtp + SMTP_HOST/SMTP_USERNAME/SMTP_PASSWORD | SMTP_PASSWORD | — | No |
| Email channel (Resend) | Backend | EMAIL_ADAPTER=resend + RESEND_API_KEY (+ optional RESEND_FROM_EMAIL) | RESEND_API_KEY | — | Yes — refuses to start without RESEND_API_KEY once EMAIL_ADAPTER=resend is set (Pydantic validation while loading the configuration, independent of DEBUG; not an insecure_default_secrets() check) |
| Browser push (Web Push / VAPID) | Backend | VAPID_PUBLIC_KEY + VAPID_PRIVATE_KEY + VAPID_CONTACT_EMAIL all three set | VAPID_PRIVATE_KEY | — | No |
| Home Assistant channel (persistent notifications, mobile push, TTS) | Backend | HA_URL + HA_ACCESS_TOKEN set | HA_ACCESS_TOKEN | — | No |
| Apprise channel (multi-backend push) | Backend image | Always active as long as the optional apprise Python package is installed in the image (no env switch) | — | larger backend image | No |
InvenTree Integration ¶
| Feature | Required services | Activation/deactivation | Mandatory secrets/prerequisites | Resource impact | Startup gate? |
|---|---|---|---|---|---|
| Equipment/inventory connection | Backend + external InvenTree instance | INVENTREE_ENABLED=true (default false) | InvenTree API token (configured via the REST API, not an env secret) | no additional pods | No |
| InvenTree with a private/LAN address | Backend | Additionally INVENTREE_ALLOW_PRIVATE_ENDPOINT=true | — | — | No |
Object Storage (Photos, Imports, Exports) ¶
| Feature | Required services | Activation/deactivation | Mandatory secrets/prerequisites | Resource impact | Startup gate? |
|---|---|---|---|---|---|
| Local filesystem (default) | Backend + Celery Worker + backend-attachments PVC | STORAGE_BACKEND=local-fs (default) | STORAGE_LOCALFS_SIGNING_SECRET — mandatory with more than one backend replica | 20Gi PVC (chart default, helm.sh/resource-policy: keep) | No (no backend boot gate; without the signing secret, downloads fail on multi-replica) |
| S3-compatible storage | Backend + Celery Worker + external S3 endpoint | STORAGE_BACKEND=s3 + STORAGE_S3_ENDPOINT_URL/STORAGE_S3_REGION/STORAGE_S3_BUCKET | STORAGE_S3_ACCESS_KEY_ID, STORAGE_S3_SECRET_ACCESS_KEY (from the External Secrets Operator) | no PVC needed | No |
| Virus scanning (ClamAV REST wrapper) | External ClamAV service | STORAGE_VIRUS_SCAN_ENABLED=true + STORAGE_VIRUS_SCAN_ENDPOINT | — | external, not part of the chart | No |
| Plant photo gallery | shares the object storage stack | No dedicated switch — STORAGE_MAX_PHOTOS_PER_INSTANCE caps the count | — | — | No |
| Pixel ceiling for images | Backend + Celery Worker | Always on, no switch: every upload and every image analysis refuses images above 40 megapixels with 413 (IMAGE_PIXEL_LIMIT_EXCEEDED) before a single pixel is decoded | — | caps the memory of one image decode at about 160 MB (40 MPx RGBA) | No |
| Image types without EXIF stripping (HEIC/HEIF, GIF) | Backend | Not admitted while STORAGE_STRIP_EXIF=true (default) — not even through STORAGE_ALLOWED_MIME_TYPES_<CATEGORY>; an upload answers 415 | — | — | No |
| Thumbnail generation | Celery Worker | Always on: at most one job per attachment in five minutes (lock in Valkey, per process during an outage); thumbnails that failed for good answer 404 | — | — | No |
| Per-account budgets for expensive routes | Backend + Valkey (counters) | RATE_LIMIT_UPLOAD (default 30/minute: uploads, CSV import), RATE_LIMIT_INFERENCE (20/minute: CV diagnosis, pest detection, plant identification, reference contribution, every route that calls an LLM), RATE_LIMIT_EXPORT (20/minute: PDF printing) — per route and account; above that 429 with Retry-After | Valkey from REDIS_URL or RATE_LIMIT_STORAGE_URL (falls back to counting per process during an outage) | — | No |
Details: Configure Storage, Helm Charts — Storage Configuration.
Privacy, Multi-Tenancy, and Operating Mode ¶
| Feature | Required services | Activation/deactivation | Mandatory secrets/prerequisites | Resource impact | Startup gate? |
|---|---|---|---|---|---|
| Light Mode (no login, single-user) | Backend + Frontend | KAMERPLANTER_MODE=light (backend) and KAMERPLANTER_MODE=light (frontend init container) | — | — | No |
| Full mode (auth + multi-tenant) | Backend + Frontend | KAMERPLANTER_MODE=full (default) | JWT_SECRET_KEY, FERNET_KEY (both already mandatory regardless, see above) | — | Yes (via the general backend secrets) |
| GDPR erasure/anonymization | Backend + Celery Beat | Always active, cannot be disabled | ERASURE_TOMBSTONE_SALT | one daily Celery task | Yes (erasure_tombstone_salt, always checked) |
| Log pseudonymization (subject references, email digests) | Backend + Celery Worker | Always active, cannot be disabled | LOG_PSEUDONYM_SALT — rotatable, independent of ERASURE_TOMBSTONE_SALT | — | Yes (log_pseudonym_salt, always checked; Celery Beat does not check it) |
| Email verification at registration | Backend | Active by default (REQUIRE_EMAIL_VERIFICATION=true); false switches it off | Outbound mail (EMAIL_ADAPTER=smtp or resend); without it set false | — | No |
| Registration mode | Backend + frontend (reads GET /mode) | REGISTRATION_MODE=open (default) | invite_only | closed; optional REGISTRATION_ALLOWED_DOMAINS | — | — | Yes (an unknown mode refuses startup) |
| Member limit per tenant | Backend | Always active; platform ceiling TENANT_MAX_MEMBERS_CEILING (default 50) | — | one count query per join | No (a value below 1 refuses startup) |
| Service accounts per tenant | Backend | Always active (full mode); quota TENANT_MAX_SERVICE_ACCOUNTS (default 20) | — | one count query per creation | No (a value below 1 refuses startup) |
| "Have I Been Pwned" check | Backend | HIBP_ENABLED=true (default false) | — | outbound HTTPS requests on password change | No |
External Data Enrichment ¶
| Feature | Required services | Activation/deactivation | Mandatory secrets/prerequisites | Resource impact | Startup gate? |
|---|---|---|---|---|---|
| GBIF (taxonomic data) | Backend | Always active, keyless public API | — | — | No |
| Perenual | Backend | PERENUAL_API_KEY set | PERENUAL_API_KEY | — | No |
| Tréflé | Backend | TREFLE_API_KEY set | TREFLE_API_KEY | — | No |
mDNS / Zeroconf Discovery¶
| Feature | Required services | Activation/deactivation | Mandatory secrets/prerequisites | Resource impact | Startup gate? |
|---|---|---|---|---|---|
| LAN auto-discovery for Home Assistant | Backend | MDNS_ENABLED=true (default false) — ineffective in standard Kubernetes clusters, see Environment Variables — mDNS and Kubernetes | — | — | No |
Diagnostics: Build Identity on the Health Endpoint¶
| Feature | Required services | Activation/deactivation | Mandatory secrets/prerequisites | Resource impact | Startup gate? |
|---|---|---|---|---|---|
GET /api/health reports which commit the running image was built from | Backend | HEALTH_EXPOSE_BUILD_REVISION=true (application default false, chart default true since #1236) — with false the build_revision key is absent entirely, see Environment Variables — Health endpoint | An image with a BUILD_REVISION baked in at build time; otherwise the answer is "unknown" | — | No |
Deliberately off, because the endpoint is unauthenticated
The mapping this host runs that commit reveals the exact lag behind the development state, and with it the list of fixes this instance is missing. So enable the disclosure deliberately. GET /api/health is additionally rate-limited per client IP (RATE_LIMIT_HEALTH, default 60/minute); the Kubernetes probes use /api/v1/health/live and /api/v1/health/ready and are not affected.
For a production instance whose delivery state needs to be auditable, HEALTH_EXPOSE_BUILD_REVISION=true is nonetheless the recommended setting — details and the measured cost of the alternative are under CI/CD — How do I see which image version is currently running?. Since #1236 helm/kamerplanter/values.yaml sets it as a **chart default of
true**; it therefore travels with the chart and needs no action in the GitOps repository. The state this unlocks can be checked with task verify:deployed-build INSTANCE_URL=… CHART_VERSION=….
Core Features Without a Dedicated Operator Switch¶
The following features are part of the core application (backend + frontend + ArangoDB + Valkey + Celery worker/beat, see Deployment Profiles — Core) and have no dedicated activation/deactivation switch — they always run once the instance is up. Personal show/hide happens exclusively through module visibility per user, not through this page.
| Feature | Note |
|---|---|
| Plant master data | — |
| Location management | GPS detection uses the browser Geolocation API (client-side, no backend switch). |
| Phase control | — |
| Fertilization logic | — |
| Task planning | — |
| Harvest management | — |
| Post-harvest | — |
| Dashboard | Personalization is a pure frontend/user setting. |
| IPM system (rule-based) | Distinct from photo-based pest detection, which has its own switch. |
| Master data import | — |
| Planting run | — |
| Tank management | — |
| Calendar view | — |
| Propagation management | — |
| Substrate management | — |
| Onboarding wizard | — |
| UI experience levels | Controls the defaults for module visibility — itself a user setting, not an operator switch. |
| Care reminders | — |
| Aquaponics management | — |
| Companion planting | — |
| Print views & export | Needs a correctly set APP_BASE_URL for QR codes on labels, see Environment Variables — Operating Mode. |
| Modular feature visibility | The user-level layer itself — see "Two separate on/off layers" above. |
Not Yet Implemented Features (no operator switch exists)¶
Not yet implemented
The following specified features are still at the draft stage and have no implementation in the code — there is (as yet) no environment variable or Helm configuration to document for them.
- Knowledge-base enrichment via OpenFarm/Growstuff
- Holistic, photo-based plant health assessment
Open Architecture Documentation (a gap, not closed by this remediation)¶
No ADR for the deployment-profile / light-full architecture
Although five deployment profiles and a light/full operating mode are documented, there is currently no Architecture Decision Record justifying this decision (separate operating modes instead of a single configuration dimension, a bundle approach with five profiles) — see the ADR overview. This gap is recorded here but not closed as part of this configuration-matrix remediation; a dedicated ADR is planned as follow-up work.
See Also¶
- Deployment Profiles — Five recommended bundles for typical use cases
- Environment Variables — Complete alphabetical variable reference with defaults
- Setting Up Plant Identification — Detailed setup of self-hosted DINOv2 recognition
- Helm Charts — Chart structure, storage configuration, security context
- Modules & Features — The personal per-user visibility layer
- Data Retention — Retention periods and downsampling stages (NFR-011)