Skip to content
Coming soon

Remote Access

Putting an access layer in front of your Sovereign server means a request has to prove who sent it, or which network it came from, before it reaches the server at all. This page covers the two kinds in common use, what each one changes for the clients, and the bypass the Roku channel needs for artwork.


An identity-aware proxy sits in front of a public hostname and answers any request it doesn’t recognise with its own login page. Cloudflare Access, Pomerium, oauth2-proxy, and Authelia or Authentik in forward-auth mode all work this way. The server stays reachable from the internet, and the proxy decides who gets through.

A private network takes the server off the public internet instead. Tailscale, ZeroTier, NetBird and plain WireGuard all work this way: only devices that have joined the network can reach the server, and nothing answers anyone else.

Identity-aware proxy Private network
Who gets through Anyone who signs in, or presents a credential Any device that has joined the network
A browser Signs in to the proxy once, then carries its session Nothing extra, once the device has joined
Roku Custom headers, plus a bypass for artwork Can’t join. Reaches the server on the same local network, or through a router that has joined
Android, Fire TV, Google TV Custom headers The network’s own app, where one exists for the device

The next four sections cover identity-aware proxies, and Private Networks follows them.


The proxy answers any request it doesn’t recognise with its login page. A browser gets past that once, interactively, and the proxy gives it a session cookie the browser then sends on everything after. Every failure on this page comes from that. Anything that can’t complete an interactive login, and anything fetched by a component that won’t send credentials, receives the login page where it expected JSON, an image, or a video segment.

It rarely announces itself as an error. A television client reports that it can’t reach the server, or signs in and shows a library with no artwork in it. Underneath, the request got a redirect and a page of HTML where the client wanted data.

Each kind of client gets past the proxy its own way, except Roku’s artwork, which needs a bypass:

sequenceDiagram
  participant B as Browser
  participant T as TV client
  participant A as Access proxy
  participant S as Sovereign

  B->>A: First request, no session
  A-->>B: Login page
  B->>A: Sign in
  A-->>B: Session cookie
  B->>A: Requests, with the cookie
  A->>S: Forwarded
  T->>A: Requests, with its headers
  A->>S: Forwarded, no prompt
  T->>A: Roku artwork, no credentials
  alt A bypass covers the path
    A->>S: Forwarded
    S-->>T: The image, if its signature is valid
  else No bypass
    A-->>T: Login page, so nothing is drawn
  end

Your issuer doesn’t change. Sovereign__Authentication__Issuer stays the public hostname the proxy protects, as Authentication describes it. Pointing it at an internal address to route around a blocked request breaks sign-in a different way, because tokens are then issued for one origin and presented to another.

Protect the Sovereign hostname as a single application, whatever sits behind it. A tunnel from the provider and a proxied DNS record pointing at your own reverse proxy are equivalent here: the access layer protects the hostname, so neither the tunnel nor your reverse proxy configuration changes.

Start with one policy, which allows the people who may sign in from a browser: email addresses, an email domain, or a group from your identity provider. The television clients get a policy of their own, described below.

Session length is worth more thought here than on an ordinary web application. A browser session that expires part-way through a film turns the next segment request into a login redirect, and the player stalls on what looks like a broken stream rather than prompting anyone to sign in again. A session measured in days costs you very little, because the identity check still happens the first time and the cookie is scoped to the one hostname.


Interactive login only works where there’s a browser. The Roku channel and the Android apps talk to the server directly, so they need a credential they can present on every request instead: one or more HTTP headers the proxy checks.

Most identity-aware proxies have a name for this. Cloudflare Access calls it a service token and checks it as two headers, CF-Access-Client-Id and CF-Access-Client-Secret. A forward-auth setup in Authelia or Authentik, or your own reverse proxy, can check a static header you choose. The clients send whatever name and value pairs you give them, so any of these is configured the same way.

Give the credential a policy of its own, separate from the browser policy, that admits that credential alone and never prompts for a login. Name the specific credential rather than admitting any machine credential in the account, and leave the browser policy alone: the two serve different populations, and widening one to cover the other is how a television credential ends up granting more than it should.

Many providers show the secret half once, at creation, and never again, and many put an expiry on it. Note when yours runs out. A device holding an expired credential starts receiving login pages, with nothing on screen to explain why.

Both halves change together, and a device holding the old pair is refused the moment that pair stops being admitted. Roll a new one out in this order:

  1. Create the new credential and record it.
  2. Admit it in the policy alongside the old one.
  3. Update each device.
  4. Remove the old credential from the policy, then revoke it.

Every device keeps working throughout, because for the overlap both are admitted. Revoking first locks out every television in the house at once, and each one is recovered by typing a long secret with a D-pad.

A television credential is long-lived, and whoever holds it gets past your proxy to Sovereign’s own sign-in, so it belongs in the same category as the asset signing key on Production Hardening. Keep it in the secret store the rest of your deployment’s secrets live in.


Nothing to configure. The browser holds the proxy’s session cookie and sends it on same-origin requests, which covers the API, the signed asset URLs, and every image on the page.

Do check the live updates. Surfaces such as the admin log stream and active streams update over a WebSocket, and a proxy that mishandles the upgrade shows no error: the pages load, but those surfaces never change. Open one and check that it updates, because nothing on screen says the connection was refused.

The channel ships an editor for this. On the server setup screen, before you sign in, select Custom headers and add each pair one at a time.

They live on the setup screen rather than in Account because the channel’s first request, an anonymous probe of the address you typed, already has to get past the proxy. There’s no session yet at that point, and there won’t be one until the probe succeeds.

  • Up to five pairs, stored on the device beside the server address.
  • Applied to the next request, with no reconnect and no restart.
  • Names are validated as you enter them, so a space or a colon is refused rather than producing a malformed request.
  • Names the channel sets for itself (Authorization, Content-Type, Content-Length, Host, X-Sovereign-Device-Id) can’t be overridden.

Artwork is the exception, and it’s the reason for the bypass below. Everything else the channel asks for carries the headers, including video segments.

The same place and the same limits: Custom headers on the server setup screen, before sign-in, up to five pairs with the same reserved names.

On Android the API, the artwork and the video player share one HTTP client, so a header entered here is sent on all three. Posters load, scrub previews load, playback works, and there’s nothing to bypass.


On Roku the custom headers reach the API, the token endpoint and every video segment. They can’t reach artwork. Roku’s image node accepts a URL and nothing else, with no header, cookie, or request hook of any kind, so those fetches arrive at the proxy carrying no credential. The proxy answers with its login page and the channel draws nothing.

The install looks like it’s working, which makes this hard to spot. The channel connects, signs in, browses and plays; every poster, backdrop, still, headshot, scrub preview and profile picture is blank, and an image-based subtitle track never draws a line.

Add a rule that lets these paths through the proxy without a login (Cloudflare Access calls it a Bypass policy; other proxies call it a public path, an allow list or an exception):

Path Serves
/media/images Posters, backdrops, stills and headshots
/media/trickplay Scrub-preview sprite sheets
/media/subtitles Caption images for image-based subtitle tracks
/users Profile pictures

/media/videos doesn’t belong on that list. Segments carry the headers already, so bypassing it widens what’s reachable and gains nothing.

Only Roku needs this. Android sends the headers on artwork like everything else, and a browser sends the proxy’s cookie on same-origin subresources, so neither needs it.

Sovereign checks every request on the paths in that table itself, with or without a proxy in front of it. Each link is handed out only to a session that has already signed in, works for one file, and expires, and a request without a valid one is refused. Signed Asset URLs covers what those links guarantee.

The cost is that anyone holding a valid signed URL can fetch that one asset until the signature expires, with no proxy identity behind it. That’s the same exposure the signing model already assumes, and it’s only safe while Sovereign__Obfuscation__AssetSecuritySecretKey is a real secret. If you’ve never pinned it, read The Asset Signing Key before you bypass anything.

/api/graphql carries every piece of application data Sovereign holds and stays protected, as does the interface itself. Don’t bypass either.

Two paths need a decision rather than a default:

  • /health returns an empty 200 and nothing else. Bypass it when your uptime monitor or container platform probes it from outside, since a probe has neither a cookie nor a credential and would otherwise record your server as permanently down.
  • /connect handles sign-in and device pairing, and needs no bypass provided the clients carry their headers. Pairing works because both halves are authenticated: the television polls the token endpoint with its headers, and you approve the code in a browser that has the cookie. See Device Pairing.

A private network doesn’t intercept anything. A device that has joined reaches the server as if it were on the same local network, and a device that hasn’t gets no answer at all, so there’s no login page to land on, no credential for the clients to carry, and no artwork bypass to add. Custom headers stay empty.

Instead, every device has to join the network, and not every client can:

  • Browsers, phones and computers join by running the network’s own app on the device, and then reach the server like any other address.
  • Android, Fire TV and Google TV can join where the network publishes an app for that device. Where it doesn’t, they’re in the same position as Roku.
  • Roku can’t run any network’s app. It reaches the server only when it’s on the same local network as the server, or through a router on its own network that has joined the private network on its behalf.

Set the issuer to the address every client uses to reach the server over that network, usually the server’s name on it, and keep serving it over HTTPS as Getting Started describes. The rules in Authentication apply unchanged: one address, reachable by every client.

When two devices can’t connect to each other directly, some private networks fall back to relaying their traffic through the provider’s own servers. That’s slower, and it’s where a film library’s worth of video runs into the provider’s fair-use limits (see the warning at the top of this page). Check whether your devices connect directly before you rely on the network for playback.


Some access layers are part of a content delivery network, and cache whatever passes through them that they’re allowed to. Cloudflare’s service-specific terms for its Free, Pro and Business plans, for example, restrict using the CDN to serve video, or a disproportionate share of pictures, audio or other large files, without one of its paid media services. Other providers have terms of their own.

Sovereign marks every video segment and playlist it serves no-store, so a provider that honours the server’s cache headers stores none of your video. Keep it that way:

  • Add a rule that bypasses the cache for /media/videos, if your provider lets you write caching rules. Everything Sovereign streams is under that path, and the rule holds even if a broader one is added later. On Cloudflare it’s a Cache Rule with the expression starts_with(http.request.uri.path, "/media/videos") and Bypass cache as the action.
  • Don’t cache everything on the hostname, or add any rule that overrides the server’s cache headers there. That’s the setting that would put your library’s video in the provider’s cache.

Artwork, scrub-preview sheets and caption images are small, and Sovereign marks them cacheable for a month. Leaving the provider to cache those is fine, and it saves your server answering the same poster twice.


For an identity-aware proxy, check these from outside your network, with no session, before you touch a client. It’s the quickest way to tell “the proxy is misconfigured” from “Sovereign is misconfigured”: an interception is a redirect toward the proxy’s login, or a page of HTML where you asked for data or an image. A response from Sovereign, even a rejection, means the request got through.

Terminal window
# Protected path, no credentials. Expect a redirect to the proxy's login.
curl -sI https://media.example.com/api/graphql
# Same path with the television credential. Expect a response from
# Sovereign rather than a redirect, which proves the credential policy
# matches. Add one -H per header your proxy checks.
curl -sI https://media.example.com/api/graphql \
-H "<header-name>: <header-value>"
# Bypassed artwork path, no credentials. Expect 401 from Sovereign,
# which is the proxy letting it through and Sovereign refusing it for
# want of a signature. A redirect here means the bypass isn't matching.
curl -sI https://media.example.com/media/images/anything.jpg
# Health, if you bypassed it. Expect 200 and an empty body.
curl -s -o /dev/null -w '%{http_code}\n' https://media.example.com/health

For a private network, run the same health check from a device that has joined (expect 200) and from one that hasn’t (expect no answer at all).

Then walk one device: connect, sign in, open a library and confirm the artwork appears, and play something for longer than a few segments.


Symptom Cause
The browser bounces between the proxy’s login and sign-in The issuer no longer matches the hostname the proxy protects. Set it back to the public origin.
A television reports it can’t reach the server Behind a proxy: the headers are missing, mistyped, or the credential has expired. Re-enter them on the setup screen; header names are case-insensitive but values aren’t. On a private network: the device isn’t on the network or the local network the server is on.
The channel connects but every image is blank The artwork bypass is missing or its paths don’t match. Roku behind a proxy only.
A pairing code is approved but the device never signs in The device’s poll of /connect/token is being intercepted, so its headers aren’t reaching the server.
Playback starts and stalls a minute or two in A browser session expired mid-stream, or a segment request is being intercepted. Lengthen the session, and confirm /media/videos isn’t caught by a stricter rule.
Playback is slow or stutters on a private network The devices are relaying through the provider rather than connecting directly.
Admin pages load but never update The WebSocket upgrade isn’t surviving the proxy.
Artwork works on the web and not on a television Expected on Roku without the bypass. On Android it means the headers weren’t entered on that device.

© 2026 Sovereign Media LLCdocs/v1.0.5@98b57ac305fc

Titles, artwork, people and descriptions shown in screenshots are fictional and were created for illustration. Sovereign doesn't include or supply any media: it plays the library you bring.