Skip to content

Security

  • Run PodVault on your local or home network.
  • Turn on the login (PODVAULT_USERNAME and PODVAULT_PASSWORD) whenever port 8000 is reachable from other machines.
  • Never expose it directly to the internet: no port forwarding, no public server.
  • To use it away from home, connect to your home network through a VPN such as WireGuard or Tailscale.
  • Treat podcast feed URLs that contain ?token= like a password.

PodVault is a single-user app built for a trusted network. It hasn’t been through any security audit or penetration testing. Without a login, anyone who can reach the port has full control of it: they can add feeds, start downloads and change settings. The optional login keeps other people on your network out. It isn’t meant to make PodVault safe to publish.

A VPN gives you the same access from your phone or laptop away from home without opening PodVault to everyone else.

By default there’s no login, which is fine when PodVault only listens on 127.0.0.1. To require one, set both variables:

Terminal window
PODVAULT_USERNAME=admin PODVAULT_PASSWORD='a-long-password' podvault serve --host 0.0.0.0

or in Docker, -e PODVAULT_USERNAME=… -e PODVAULT_PASSWORD=….

With the login on:

  • Every page, action, audio stream and export requires it. The only exceptions are /healthz (for Docker’s health check) and the fonts the sign-in page needs.
  • Opening any page sends you to the sign-in page first, then back to the page you asked for.
  • Sign out is at the bottom of the sidebar.
  • Credentials are compared in constant time, and the password is never logged or shown. The Settings page only shows whether one is set.
  • podvault serve warns you if you bind to a non-local address without a login.

If you set only one of the two variables, PodVault refuses every request until both are set. podvault serve names the missing one when it starts, and the sign-in page says “Login is misconfigured”. It never falls back to running without a login.

  • “Keep me signed in for 30 days” is on by default and keeps you signed in across browser restarts. Untick it and the session ends when the browser closes, or after 12 hours at most.
  • Sliding sessions: the session renews itself while you use PodVault, including during long audio playback, so it only expires after that long without using it.
  • When a session expires, the next click takes you to the sign-in page and back. Background actions (job progress, the player, the Settings preview) do the same instead of failing silently.
  • Changing the password or username signs every browser out.
  • The session lives in a signed podvault_session cookie that’s HttpOnly and SameSite=Lax (which protects PodVault’s forms from cross-site requests), and Secure when an HTTPS reverse proxy reports it through X-Forwarded-Proto. There’s no session store on the server.

After 5 wrong passwords from the same address, sign-in is locked for 30 seconds, doubling with each further miss up to 15 minutes. A successful sign-in clears it, and it resets when PodVault restarts.

The lockout is per IP address. Behind a reverse proxy everyone shares the proxy’s address, so a lockout there affects everyone who isn’t already signed in. Browsers that are already signed in aren’t affected.

Scripts can still send the credentials as HTTP Basic auth:

Terminal window
curl -u admin:a-long-password http://podvault.local:8000/export -o library.json

Browsers can’t use Basic auth, so Sign out always really signs you out. A script that sends a browser-like Mozilla/… User-Agent has to use the sign-in page too.

Podcast apps can’t fill in a sign-in page, so when the login is on, your podcast app feeds use a token in the URL (?token=…):

  • The token opens only the feeds and the audio and artwork they link to. It can’t open the web interface, exports or any action.
  • It’s derived from your password, so changing the password revokes every feed URL handed out before. Set PODVAULT_FEED_TOKEN if you’d rather manage the token yourself.
  • Tokens travel in URLs, so they can end up in proxy logs and in your podcast app’s sync service. Treat feed URLs as secrets.

PodVault serves plain HTTP. On plain HTTP the password isn’t encrypted on its way to the server. On a home network that’s usually acceptable. If you run PodVault on a network you don’t fully trust, put it behind an HTTPS reverse proxy (Caddy, Traefik, nginx…). That still doesn’t make it suitable for the public internet.

  • Untrusted input is validated. Naming templates can’t write outside the downloads folder, database table names are validated, and queries are parameterized.
  • Redirects after sign-in only go to local paths.
  • OPML files are checked first: files over 5 MB, or with DOCTYPE or entity declarations, are rejected before parsing.
  • Feed HTML is never rendered as HTML. Episode descriptions are converted to plain text and escaped.

Open an issue on GitHub. Please don’t include passwords, tokens or feed URLs.

PodVault v1.3.1 · GitHub · Docker Hub ·Report an issue

Built for home networks · No trackers · MIT License · © Carlos Medina