Repository navigation
Ops Matter
The Matter server puts every mower into Apple Home, Google Home or Home Assistant as its own Matter device (shown as a robot vacuum: Matter has no mower type yet): start, pause, return to the dock, state and battery. There is no emergency stop over Matter. Design and decisions: ADR 0063. User guide: Apple Home and Google Home.
It is optional and off by default: Compose profile matter, Helm services.matterServer.enabled.
- Same network as the controller. Matter finds devices with mDNS and talks IPv6 link-local. The MowDeck host and the Matter controller (Apple TV or HomePod as home hub, a Google Nest hub, or Home Assistant) must be on the same layer-2 network (VLAN), or you must bridge mDNS (an mDNS reflector) and route IPv6 between them. The iPhone pairing a device must be on that network too.
-
IPv6 on the host. Link-local addresses are enough (
ip -6 addrshows anfe80::address on the LAN interface); do not disable IPv6 in the kernel. -
Host networking. The container runs with
network_mode: host(Kubernetes:hostNetwork: true). It cannot work on Docker's bridge network. - One API token per account whose mowers should appear.
| Port | Protocol | From | Purpose |
|---|---|---|---|
| 5353 | UDP | LAN (IPv4 multicast 224.0.0.251, IPv6 ff02::fb) |
mDNS: the mowers announce themselves |
MATTER_PORT_BASE ... +MATTER_MAX_NODES-1 (5540-5555) |
UDP | LAN, IPv4 and IPv6 link-local | One Matter node per mower |
MATTER_HTTP_PORT (8790) |
TCP | Docker's bridge networks only | Admin API (pairing codes) for Traefik and Identity's module descriptor |
The admin API checks every request with Identity, but it has no business on the LAN: Traefik serves it at /api/v1/matter.
-
API token. Sign in as an account admin, Einstellungen › API-Tokens › Token erstellen: name
Matter-Server, Berechtigungen: Home Assistant – Grundfunktionen (exactlymower.viewandmower.controlon the whole account), nothing more. Copy the token (it is shown once). -
Token file (one token per line, one line per account;
#starts a comment):cd infra/compose install -d -m 0750 secrets/matter (umask 077 && $EDITOR secrets/matter/api-tokens) # paste the token sudo chown 1000:1000 secrets/matter/api-tokens && sudo chmod 0400 secrets/matter/api-tokens
The directory is git-ignored. The token never appears in a log.
-
.env:COMPOSE_PROFILES=matter # add to existing profiles: relay,matter IDENTITY_EXTRA_MODULE_URLS=http://host.docker.internal:8790 # optional: MATTER_MDNS_INTERFACE=enp2s0, MATTER_LANGUAGE=en, MATTER_PORT_BASE=5540
MATTER_API_URLdefaults to Traefik on the host (http://127.0.0.1:<GATEWAY_HTTP_PORT>); set it only when the API is elsewhere. -
Firewall (below), then start:
docker compose up -d --wait(with a release: the usualdeploy.sh; the image is signed and verified like the others). -
Check:
docker compose logs matter-servershowsmatter.node.startedonce per mower (with its port, never a code). In the web app, Einstellungen › Matter lists the mowers; pairing: guide.
Allow mDNS and the node ports from the LAN only (here 192.168.0.0/16 and IPv6 link-local), and the admin port from Docker's bridges only. Merge into your inet filter table's input chain (it must have policy drop or end with a drop for this to matter):
table inet filter {
chain input {
# ... existing rules (established/related, loopback, ssh) ...
# Matter (MowDeck matter-server, ADR 0063)
ip saddr 192.168.0.0/16 udp dport 5353 accept comment "mDNS from the LAN"
ip6 saddr fe80::/10 udp dport 5353 accept comment "mDNS (IPv6) from the LAN"
ip saddr 192.168.0.0/16 udp dport 5540-5555 accept comment "Matter nodes"
ip6 saddr fe80::/10 udp dport 5540-5555 accept comment "Matter nodes (IPv6)"
ip saddr 172.16.0.0/12 tcp dport 8790 accept comment "Matter admin API from Docker bridges"
tcp dport 8790 drop comment "Matter admin API: nobody else"
}
}
If your LAN uses IPv6 unique-local or global addresses for the hub, add their prefix next to fe80::/10. Docker publishes no port of this service (host network), so Docker's own nftables rules do not open anything here. Reload with sudo nft -f /etc/nftables.conf and check with sudo nft list ruleset.
Avahi on the host can stay: matter.js shares UDP 5353.
services:
matterServer:
enabled: true
nodeSelector: {kubernetes.io/hostname: ms01} # a node in the hub's LAN
secrets:
MATTER_API_TOKENS: "mdk_..." # --set-file or SOPS, one per line
config:
MATTER_API_URL: "" # default https://<ingress.host>The pod uses hostNetwork with the Recreate strategy and a 64 MiB PersistentVolumeClaim (persistence). The Ingress routes /api/v1/matter to it and Identity lists its descriptor. Open the same ports on the node as above.
-
Backups: the
matter-datavolume (nodes.jsonwith the setup codes,matter/with the fabrics). Without it every mower must be paired again everywhere. It contains secrets: back it up like the.env. - New mower: appears within a minute.
- Removed mower: its node is factory-reset after two minutes without it in a complete answer; remove the tile in the Home app.
- Revoking access: revoke the API token (Einstellungen › API-Tokens): the server can no longer read or command that account's mowers (the tiles show an error). Reset a mower's pairing in Einstellungen › Matter to throw every controller out.
-
More than 16 mowers: raise
MATTER_MAX_NODESand widen the firewall range. -
Logs: JSON lines;
matter.commandper controller command (intent and plan, no code). Pairing codes and passcodes are never logged.
Installations that already run Home Assistant with the MowDeck integration can expose its lawn_mower entity through Home Assistant Matter Hub instead (it maps lawn_mower to the same robot vacuum type; use its server mode for Apple Home). The MowDeck Matter server needs no Home Assistant.
Mirrored from docs/ops/matter.md at commit 81d429e8e971359e0400808368c2358112616112. Edit the file in the repository, not this wiki page.
Mirrored from the docs/ directory at commit 81d429e8e971359e0400808368c2358112616112. Edit the files in the repository, not this wiki.
MowDeck documentation
- Home
- Phases
- Architecture
-
Decisions (ADR)
- Architecture decisions
- ADR 0001: OpenFGA instead of self-built authorization
- ADR 0002: Self-built Identity service in FastAPI instead of Keycloak or Zitadel
- ADR 0003: SvelteKit for the web app
- ADR 0004: Monorepo with a uv workspace
- ADR 0005: AGPL-3.0 for services and apps, Apache-2.0 for libraries
- ADR 0006: PostgreSQL for all persistence, no SQLite
- ADR 0007: NATS JetStream as event bus
- ADR 0008: Traefik as gateway
- ADR 0009: Command and event envelopes, subject naming and de-duplication
- ADR 0010: Python 3.13 for all services and libraries
- ADR 0011: Separate GPL-3.0 package for the PyMammotion adapter
- ADR 0012: Mower control interface and safety rules
- ADR 0013: Device-Gateway registry, sharding and per-mower concurrency
- ADR 0014: Authorization model, tuple ownership and the mowdeck-authz client
- ADR 0015: Tokens, sessions and key rotation
- ADR 0016: Fleet commands and authorization per command kind
- ADR 0017: Telemetry storage and retention
- ADR 0018: Realtime WebSocket protocol
- ADR 0019: Mammotion adapter transports, manual drive and secrets
- ADR 0020: Web app architecture
- ADR 0021: Browser authentication flow
- ADR 0022: Notifications: rules, recipients and delivery
- ADR 0023: Map rendering and projection
- ADR 0024: Scheduler: recurrence, rain pause and command issuing
- ADR 0025: Home Assistant integration as a pure API client
- ADR 0026: Manual drive channel over the Realtime WebSocket
- ADR 0027: Health history and theft alarm
- ADR 0028: Entitlements: plans as data and enforcement
- ADR 0029: Helm chart and release pipeline
- ADR 0030: Mowing patterns as plug-ins and the Planner
- ADR 0031: Billing with a Stripe adapter
- ADR 0032: Live camera over Agora, joined directly by the browser
- ADR 0033: Vision-assisted recovery with human approval
- ADR 0034: Linked cloud accounts and device discovery
- ADR 0035: Server-side camera frames and automatic AI analysis on errors
- ADR 0036: Native app shells with Capacitor, loading the web app from the server
- ADR 0037: Camera panorama calibration in the browser, with the AI supplying points only
- ADR 0038: Dual link per mower and firmware updates
- ADR 0039: Spot mowing by MowDeck-driven paths
- ADR 0040: Link heartbeat for online status
- ADR 0041: Spot runs from the dock: undock, transit and return
- ADR 0042: Drive calibration
- ADR 0043: Turn styles
- ADR 0044: Drive view with camera
- ADR 0045: Camera relay
- ADR 0046: Device settings as capabilities
- ADR 0047: The RTK base station as a Fleet device
- ADR 0048: Maintenance commands
- ADR 0049: Manual driving over the Mammotion cloud
- ADR 0050: Passkeys and the app login hand-off
- ADR 0051: Sign in with GitHub
- ADR 0052: Sign in with Apple
- ADR 0053: Retention and downsampling of the position track
- ADR 0054: Aerial photo under the mower map
- ADR 0055: Deploy maintenance window
- ADR 0056: Camera photo in push notifications for errors and theft alarms
- ADR 0057: Step-up re-authentication for critical actions
- ADR 0058: Theft tracking mode
- ADR 0059: iOS widgets and the mowing Live Activity
- ADR 0060: Apple Watch app
- ADR 0061: Mowing angle rotation
- ADR 0062: Setup tour for new users
- ADR 0063: Matter server (mowers in Apple Home and Google Home)
- ADR 0064: Personal access tokens and the public API documentation
- ADR 0065: Security center and new-login alerts
- Development
-
Operations
- Operating the Compose stack
- Operating MowDeck on Kubernetes (Helm)
- Building the Bluetooth bridge (ESPHome on a XIAO ESP32-S3)
- Releases
- Billing operations
- Telemetry: retention and position downsampling
- iPhone and Android apps
- Passkeys
- Sign in with GitHub
- Aerial photo under the map
- Sign in with Apple
- Theft tracking mode
- Matter server
- Source code offer (AGPL-3.0 section 13)
- Spikes
-
Guides
- Guides
- ESP32 bridge: purchase and setup
- Signing in with a passkey
- Signing in with GitHub
- Aerial photo under the map
- Signing in with Apple
- Confirming critical actions
- Your security page
- iPhone widgets and the mowing Live Activity
- Apple Watch
- The setup tour
- Apple Home and Google Home (Matter)
- Scripting MowDeck
- API reference
- Original plan (German)