In this series

  1. Why I modernized my homelab instead of upgrading it
  2. Making a homelab repository agent-friendly
  3. Building the new K3s foundation
  4. Proving the entire platform with one blog
  5. Handing PostgreSQL over to Amp
  6. Modernizing an application, not merely moving it
  7. Replacing a surveillance media chain, not just a recorder
  8. Proving Frigate before the cutover (this post)
System design showing four active cameras, a retired fifth camera, a 24-hour test, storage and secure browser access feeding a closed production gate while the old recorder stays active

The four-camera design and access checks passed. The broken 24-hour run kept the production gate closed.

Part VII ended with a working Frigate beta. Four cameras were streaming and recording, the storage projection made sense and the container ran with a small, documented security exception. That proved the design. It did not prove that I could point my family at it, because the browser had no proper sign-in yet, and it did not prove that the recorder would still be recording next week.

So I needed two more things before I could even think about a cutover: sign-in that I trusted, and a test that lasted longer than a demo. A working beta is not the same as a safe cutover. Surveillance is the one application where I care most about the boring hours.

Put Auth0 in front of Frigate

The identity provider was never in question. Back in the original surveillance post I had already decided that I did not want to run a user database at home, and Auth0 has protected the old feed since then. Baby Tracker joined it in Part VI. The question this time was how to put it in front of an application I did not write.

Frigate has its own login, but I did not want yet another password to look after. It can instead trust a proxy to tell it who the user is, so Amp gave Frigate a dedicated oauth2-proxy and configured the Traefik route to ask that proxy before forwarding anything. Auth0 puts the user’s roles into the ID token. oauth2-proxy refuses any session that carries none of the approved roles. Frigate then maps the roles it does receive onto its own built-in admin and read-only viewer roles. If a request somehow reaches Frigate with no usable role at all, it lands in a deliberately useless default role whose only camera does not exist, so it can see precisely nothing. Failing closed twice felt about right for a camera system.

One detail matters more than it looks. Before the route asks oauth2-proxy anything, it strips every identity header a browser could have supplied, and afterwards it copies only the two headers from oauth2-proxy itself through to Frigate. Without that, a request could simply declare its own role and Frigate would have no way to tell the difference.

Frigate is also not a single web page. Live video, recording playback, the API and the WebSocket traffic behind the live view all had to go through the same boundary, and then straight to Frigate’s authenticated port rather than being relayed byte by byte through the proxy. The internal health port from Part VII stayed Pod-local. A neat login page in front of the UI is worth very little if the video endpoints answer without it.

System design showing a browser request crossing an identity gate before reaching Frigate, where approved control and viewing paths continue while unauthorized access is blocked

The browser must pass the identity gate before it can reach Frigate.

I did find one bug myself. Signing out of the beta site sent me to the wrong place, so I told Amp: “Redirection should follow the beta and non-beta split.” Then, because a login page that looks right is exactly the kind of thing that makes me nervous, I followed up with: “Make a thorough check that frigate won’t expose recordings or live streams to unauthorized persons.”

Amp tested the role mapping in the pinned Frigate image, checked that a synthetic viewer could reach a media route but not an admin route, and attacked the live beta without a session. Requests for the UI, API, recordings, live media and WebSockets all went to Auth0 instead of returning content. Forged identity headers changed nothing. It also verified the corrected sign-out path, which returned beta to the beta Home Portal rather than production.

To be clear about what this is and is not: the beta boundary behaves this way today. Production still needs one final pass with disposable test accounts for every role, including authenticated live view and playback. The configuration exists; that last production check does not.

Keep the first checks quick

I did not want to wait 24 hours to find out that I had broken something small. So Amp built a short loop first: deploy to beta, wait for Frigate to become ready, confirm that every configured stream is producing frames, confirm that recording files exist, and request the browser route without a session to make sure it still redirects to Auth0. It lives in the Justfile now, so deploying and checking the beta is one command rather than a list I would forget. If something basic was wrong, we knew within minutes rather than the following day.

None of this required looking at private footage. Amp used Frigate’s stream counters, the timestamps on the recording files and FFmpeg’s description of a few sample segments. The four available feeds ran at about five frames per second, new files kept appearing on the QNAP, and the sample segments contained H.264 video with AAC audio, exactly as designed.

That loop proves the path from camera to recording to browser. It does not prove the recorder can stay healthy, which is a different question entirely. Once the short checks passed, the same build could move on to the long test without anyone touching it.

Let 24 hours decide

At this point the quick checks looked good. However, I was not going to replace the old recorder after only a few minutes, so I asked Amp to keep the same beta running and check it over 24 hours.

The test covered only the four active cameras, as I had already told Amp to ignore the one I removed. It still failed because Frigate restarted, recordings had gaps and RTSP errors kept returning. If I had opened the beta during one of the good hours, I would have said it worked and missed those problems.

Now I need Amp to find out why it restarted and why the recordings had gaps. Then I will run the same test again with all four active cameras. Object detection can wait until this basic job works properly.

The old recorder keeps running until then. I am not cutting over until the 24-hour check passes.