Ctrl K
ComunidadGuías…

SECURITY.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Security

## Reporting a vulnerability

Please report security issues privately through
[GitHub's private vulnerability reporting](https://github.com/rancur/fade-out/security/advisories/new)
rather than opening a public issue.

Expect an initial response within a few days. This is a hobby project maintained
by one person — please size your expectations accordingly.

## The single most important thing to know

**fade-out has no authentication. Do not expose it directly to the internet.**

There is no login, no API key, no session — every endpoint is reachable by
anyone who can reach the port. That includes endpoints that read and write
stored credentials:

- `GET /api/auth/credentials` lists which credentials are configured and returns
  masked values (first 6 and last 4 characters).
- `PUT /api/auth/credentials` overwrites stored credentials.
- `POST /api/mixes/{id}/upload` and the pipeline endpoints publish to your real
  SoundCloud and YouTube accounts.

This is a deliberate design point, not an oversight: fade-out is built as a
single-user tool on a trusted home network. But it means the deployment model is
your only security boundary.

### Deploy it behind something

Safe:

- Bound to a LAN the app can trust, reachable only from inside it.
- Behind a reverse proxy that enforces authentication (Authelia, oauth2-proxy,
  Cloudflare Access, basic auth).
- On a private overlay network such as Tailscale or WireGuard.

Not safe:

- Port-forwarded straight from your router.
- A Cloudflare Tunnel with no Access policy in front of it.
- Any public hostname that resolves to the container without an auth layer.

### The OAuth callback exception

YouTube's OAuth flow requires a real, publicly resolvable domain, which is why
`PUBLIC_URL` exists. Publishing a hostname for the OAuth callback does **not**
mean publishing the dashboard: put the auth layer in front of everything, and if
your proxy needs an exception for the callback, scope it to the
`/api/auth/*/callback` paths only.

### CORS

`CORS_ALLOW_ORIGINS` defaults to the local Vite dev-server origins, with
`PUBLIC_URL` folded in automatically. The dashboard is served from the same
origin as the API, so most deployments need no additional entry.

Setting it to `*` is strongly discouraged. With no authentication in front of
the API, a wildcard lets any website you happen to visit read your stored
credential metadata and drive your pipeline from your own browser. If `*` is
set, credentialed CORS is disabled automatically, but the request-forgery
exposure remains.

## Secrets

- Secrets come from the environment or the settings database. Nothing is
  committed — `.env` is gitignored, and `gitleaks` runs as a pre-commit hook
  (`.pre-commit-config.yaml`).
- Credential values are masked on read, never returned in full.
- `GITHUB_TOKEN` is only ever used to read release and tag metadata for the
  deployment-freshness check. Use a fine-grained token scoped to this one
  repository with *Contents: Read-only*, and nothing else.

## Container

The image runs as root so that bind-mounted NAS shares keep working out of the
box across the many UID/GID setups people deploy on. If your mounts permit it,
running unprivileged is better — uncomment the `user:` line in
`docker-compose.yml` and set it to a UID/GID that can read the watch folders and
write the output folders.

## Automation

The workflows under `.github/workflows/` run Claude with write access to this
repository. `claude.yml` is gated on `author_association`, so only the owner,
org members, and invited collaborators can trigger it — pull requests from forks
do not receive repository secrets. Please keep that gate in place in any PR that
touches those files.