diff --git a/ansible/playbooks/indri.yml b/ansible/playbooks/indri.yml
index f1c7d11..3042366 100644
--- a/ansible/playbooks/indri.yml
+++ b/ansible/playbooks/indri.yml
@@ -175,5 +175,3 @@
tags: jellyfin_metrics
- role: caddy
tags: caddy
- - role: frigate_detector
- tags: frigate_detector
diff --git a/ansible/roles/frigate_detector/defaults/main.yml b/ansible/roles/frigate_detector/defaults/main.yml
deleted file mode 100644
index 71af21d..0000000
--- a/ansible/roles/frigate_detector/defaults/main.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-frigate_detector_repo: https://forge.ops.eblu.me/eblume/apple-silicon-detector.git
-frigate_detector_dir: "{{ ansible_env.HOME }}/code/3rd/apple-silicon-detector"
-frigate_detector_endpoint: "tcp://*:5555"
-frigate_detector_model: AUTO
-frigate_detector_log_dir: "{{ ansible_env.HOME }}/Library/Logs"
-frigate_detector_uv_binary: "{{ ansible_env.HOME }}/.local/share/mise/installs/uv/latest/uv-aarch64-apple-darwin/uv"
-frigate_detector_python: "3.12"
diff --git a/ansible/roles/frigate_detector/handlers/main.yml b/ansible/roles/frigate_detector/handlers/main.yml
deleted file mode 100644
index 850c66c..0000000
--- a/ansible/roles/frigate_detector/handlers/main.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-- name: Restart frigate-detector
- ansible.builtin.shell: |
- launchctl unload ~/Library/LaunchAgents/mcquack.eblume.frigate-detector.plist 2>/dev/null || true
- launchctl load ~/Library/LaunchAgents/mcquack.eblume.frigate-detector.plist
- changed_when: true
diff --git a/ansible/roles/frigate_detector/tasks/main.yml b/ansible/roles/frigate_detector/tasks/main.yml
deleted file mode 100644
index 07b5ffc..0000000
--- a/ansible/roles/frigate_detector/tasks/main.yml
+++ /dev/null
@@ -1,55 +0,0 @@
----
-# Apple Silicon ZMQ detector for Frigate
-# Runs natively on macOS, using CoreML / Neural Engine for ~15ms inference.
-# Communicates with Frigate via ZMQ over TCP.
-# Dependencies managed inline by uv — no venv or make install needed.
-#
-# ONE-TIME SETUP (before running ansible):
-#
-# 1. Clone the repo (use localhost:3001 - hairpinning doesn't work):
-# ssh indri 'git clone http://localhost:3001/eblume/apple-silicon-detector.git ~/code/3rd/apple-silicon-detector'
-#
-# 2. Run ansible to deploy LaunchAgent:
-# mise run provision-indri -- --tags frigate_detector
-
-- name: Verify apple-silicon-detector repo exists
- ansible.builtin.stat:
- path: "{{ frigate_detector_dir }}/detector/zmq_onnx_client.py"
- register: frigate_detector_stat
-
-- name: Fail if apple-silicon-detector not found
- ansible.builtin.fail:
- msg: |
- apple-silicon-detector not found at {{ frigate_detector_dir }}.
- Please clone first:
- ssh indri 'git clone {{ frigate_detector_repo }} {{ frigate_detector_dir }}'
- when: not frigate_detector_stat.stat.exists
-
-- name: Verify uv binary exists
- ansible.builtin.stat:
- path: "{{ frigate_detector_uv_binary }}"
- register: frigate_detector_uv_stat
-
-- name: Fail if uv not found
- ansible.builtin.fail:
- msg: "uv not found at {{ frigate_detector_uv_binary }}. Install via mise: mise use uv@latest"
- when: not frigate_detector_uv_stat.stat.exists
-
-- name: Deploy frigate-detector LaunchAgent plist
- ansible.builtin.template:
- src: mcquack.eblume.frigate-detector.plist.j2
- dest: ~/Library/LaunchAgents/mcquack.eblume.frigate-detector.plist
- mode: '0644'
- notify: Restart frigate-detector
-
-- name: Check if frigate-detector LaunchAgent is loaded
- ansible.builtin.command: launchctl list mcquack.eblume.frigate-detector
- register: frigate_detector_launchctl_check
- changed_when: false
- failed_when: false
-
-- name: Load frigate-detector LaunchAgent if not loaded
- ansible.builtin.command: launchctl load ~/Library/LaunchAgents/mcquack.eblume.frigate-detector.plist
- when: frigate_detector_launchctl_check.rc != 0
- changed_when: true
- failed_when: false
diff --git a/ansible/roles/frigate_detector/templates/mcquack.eblume.frigate-detector.plist.j2 b/ansible/roles/frigate_detector/templates/mcquack.eblume.frigate-detector.plist.j2
deleted file mode 100644
index 87921ac..0000000
--- a/ansible/roles/frigate_detector/templates/mcquack.eblume.frigate-detector.plist.j2
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
- Label
- mcquack.eblume.frigate-detector
- ProgramArguments
-
- {{ frigate_detector_uv_binary }}
- run
- --python
- {{ frigate_detector_python }}
- --with
- numpy==1.26.*
- --with
- opencv-python-headless==4.11.0.*
- --with
- opencv-contrib-python==4.11.0.*
- --with
- onnxruntime==1.22.*
- --with
- pyzmq==26.2.*
- --with
- pydantic==2.10.*
- detector/zmq_onnx_client.py
- --model
- {{ frigate_detector_model }}
- --endpoint
- {{ frigate_detector_endpoint }}
-
- WorkingDirectory
- {{ frigate_detector_dir }}
- RunAtLoad
-
- KeepAlive
-
- StandardOutPath
- {{ frigate_detector_log_dir }}/mcquack.frigate-detector.out.log
- StandardErrorPath
- {{ frigate_detector_log_dir }}/mcquack.frigate-detector.err.log
-
-
diff --git a/argocd/apps/mosquitto.yaml b/argocd/apps/mqtt.yaml
similarity index 84%
rename from argocd/apps/mosquitto.yaml
rename to argocd/apps/mqtt.yaml
index 976dd5c..61959aa 100644
--- a/argocd/apps/mosquitto.yaml
+++ b/argocd/apps/mqtt.yaml
@@ -2,7 +2,7 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
- name: mosquitto
+ name: mqtt
namespace: argocd
spec:
project: default
@@ -11,7 +11,7 @@ spec:
targetRevision: main
path: argocd/manifests/mosquitto
destination:
- server: https://kubernetes.default.svc
+ server: https://ringtail.tail8d86e.ts.net:6443
namespace: mqtt
syncPolicy:
syncOptions:
diff --git a/argocd/apps/ntfy.yaml b/argocd/apps/ntfy.yaml
index 8846b7f..13927bc 100644
--- a/argocd/apps/ntfy.yaml
+++ b/argocd/apps/ntfy.yaml
@@ -11,7 +11,7 @@ spec:
targetRevision: main
path: argocd/manifests/ntfy
destination:
- server: https://kubernetes.default.svc
+ server: https://ringtail.tail8d86e.ts.net:6443
namespace: ntfy
syncPolicy:
syncOptions:
diff --git a/argocd/manifests/ntfy/deployment.yaml b/argocd/manifests/ntfy/deployment.yaml
index 7bff658..8515950 100644
--- a/argocd/manifests/ntfy/deployment.yaml
+++ b/argocd/manifests/ntfy/deployment.yaml
@@ -16,7 +16,7 @@ spec:
spec:
containers:
- name: ntfy
- image: registry.ops.eblu.me/blumeops/ntfy:v1.0.0
+ image: registry.ops.eblu.me/blumeops/ntfy:v1.1.0-nix
args: ["serve", "--config", "/etc/ntfy/server.yml"]
ports:
- containerPort: 80
diff --git a/containers/ntfy/default.nix b/containers/ntfy/default.nix
new file mode 100644
index 0000000..47a43ab
--- /dev/null
+++ b/containers/ntfy/default.nix
@@ -0,0 +1,27 @@
+# Nix-built ntfy push notification server
+# Replaces the multi-stage Dockerfile (Node + Go + Alpine) with nixpkgs ntfy-sh
+# Built with dockerTools.buildLayeredImage for efficient layer caching
+{ pkgs ? import { } }:
+
+pkgs.dockerTools.buildLayeredImage {
+ name = "blumeops/ntfy";
+ tag = "latest";
+
+ contents = [
+ pkgs.ntfy-sh
+ pkgs.cacert
+ pkgs.tzdata
+ ];
+
+ config = {
+ Entrypoint = [ "${pkgs.ntfy-sh}/bin/ntfy" ];
+ Env = [
+ "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
+ "TZDIR=${pkgs.tzdata}/share/zoneinfo"
+ ];
+ ExposedPorts = {
+ "80/tcp" = { };
+ };
+ User = "65534";
+ };
+}
diff --git a/docs/changelog.d/feature-port-mqtt-ntfy-ringtail.infra.md b/docs/changelog.d/feature-port-mqtt-ntfy-ringtail.infra.md
new file mode 100644
index 0000000..f58dac9
--- /dev/null
+++ b/docs/changelog.d/feature-port-mqtt-ntfy-ringtail.infra.md
@@ -0,0 +1 @@
+Port Mosquitto (MQTT) and ntfy to ringtail k3s; retire Apple Silicon Detector from indri.
diff --git a/docs/reference/services/frigate.md b/docs/reference/services/frigate.md
index edd93af..0e661fe 100644
--- a/docs/reference/services/frigate.md
+++ b/docs/reference/services/frigate.md
@@ -27,12 +27,10 @@ Open-source network video recorder (NVR) with object detection. Runs cloud-free
ReoLink Camera (GableCam)
│ RTSP
▼
-Frigate pod (minikube)
+Frigate pod (ringtail k3s)
├── go2rtc — RTSP restream proxy
├── FFmpeg — stream decoding
- ├── ZMQ detector ──tcp://host.minikube.internal:5555──→ apple-silicon-detector
- │ ├── CoreML / Neural Engine
- │ └── LaunchAgent (mcquack.eblume.frigate-detector)
+ ├── detector — GPU-accelerated (RTX 4080, pending migration)
├── /media/frigate — NFS recordings (sifaka)
└── /db — SQLite (local PVC)
│
@@ -49,7 +47,7 @@ Camera credentials are stored in 1Password and synced via [[external-secrets]] t
## Detection
-Object detection uses the [apple-silicon-detector](https://github.com/frigate-nvr/apple-silicon-detector) with a YOLOv9-m model (`yolo-generic`, 320x320), running natively on [[indri]] as a LaunchAgent (`mcquack.eblume.frigate-detector`). It communicates with Frigate via ZMQ over TCP (`tcp://host.minikube.internal:5555`), using CoreML with partial Neural Engine acceleration (~100-170ms inference). Model ONNX files are stored on the NFS volume at `/media/frigate/models/`.
+Object detection will use GPU-accelerated inference on [[ringtail]]'s RTX 4080 (migration pending). The previous Apple Silicon Detector on [[indri]] has been retired.
Two zones are configured: `driveway_entrance` (triggers review alerts for person/car) and `driveway` (triggers review detections).