Skip to content

Security and Secrets

SyncTV has three high-impact secrets. They have different purposes and should not be reused.

SettingPurposeMust remain stableImpact if lost or leaked
jwt.secretSigns access, refresh, and guest tokensCan be rotated, but affects sessionsA leaked value may allow forged tokens
security.opaque_server_setup_secretServer setup secret for OPAQUE password authYesChanging it can make OPAQUE password records unverifiable
security.credential_encryption_keyEncrypts provider credentialsYesLosing it can make encrypted provider credentials unreadable

Use environment variables or secret files in production. Do not commit these values.

Purpose: signs login tokens.

Requirements:

  • At least 32 characters.
  • High entropy.
  • Not a placeholder, project name, domain, date, or common word.

Generate:

Terminal window
openssl rand -base64 32

Recommended YAML:

jwt:
secret_file: "/run/secrets/jwt_secret"

Environment variables:

Terminal window
SYNCTV_JWT_SECRET=...
SYNCTV_JWT_SECRET_FILE=/run/secrets/jwt_secret

Rotating it can invalidate existing access and refresh tokens. Rotate immediately if it may have leaked.

FieldDefaultPurpose
jwt.access_token_duration_hours1Access token lifetime
jwt.refresh_token_duration_days30Refresh token lifetime
jwt.guest_token_duration_hours4Guest token lifetime
jwt.clock_skew_leeway_secs60Clock skew tolerance

For internet-facing deployments, keep access tokens short and rely on refresh rotation. Use NTP instead of increasing clock skew to hide server time drift.

Purpose: encrypts sensitive provider credentials such as tokens, API keys, and provider secrets.

Format:

  • 64 hexadecimal characters.
  • Equivalent to a 32-byte AES-256-GCM key.

Generate:

Terminal window
openssl rand -hex 32

Recommended YAML:

security:
credential_encryption_key_file: "/run/secrets/credential_encryption_key"

Environment variables:

Terminal window
SYNCTV_SECURITY_CREDENTIAL_ENCRYPTION_KEY=...
SYNCTV_SECURITY_CREDENTIAL_ENCRYPTION_KEY_FILE=/run/secrets/credential_encryption_key

Back it up securely before storing encrypted provider credentials. Do not rotate it casually without a migration plan.

Purpose: stable server secret used by OPAQUE password authentication.

Generate:

Terminal window
openssl rand -base64 48

Recommended YAML:

security:
opaque_server_setup_secret_file: "/run/secrets/opaque_server_setup_secret"

Environment variables:

Terminal window
SYNCTV_SECURITY_OPAQUE_SERVER_SETUP_SECRET=...
SYNCTV_SECURITY_OPAQUE_SERVER_SETUP_SECRET_FILE=/run/secrets/opaque_server_setup_secret

Important constraints:

  • Do not reuse jwt.secret.
  • Do not generate a new value on every container start.
  • Keep the value stable across Helm upgrades and redeployments.
  • Changing it incorrectly can break existing password login records.

Default:

password_complexity:
min_length: 8
require_uppercase: true
require_lowercase: true
require_digit: true
require_special: false
max_repeated_chars: 3

This applies to user account passwords, not room passwords.

Production policy usually benefits more from longer passwords and rate limits than from forcing many special characters on clients with poor text input.

max_repeated_chars=0 disables the repeated-character check.

CORS controls which browser origins may call the API.

server:
cors_allowed_origins:
- "https://app.example.com"

Origins must not include paths, query strings, or fragments.

Trusted proxies control whether SyncTV trusts forwarding headers such as X-Forwarded-For.

server:
trusted_proxies:
- "10.0.0.0/8"

Only add proxy IPs or CIDRs that you control. If this list is empty, SyncTV uses the socket peer address and does not trust forwarded client IP headers.

WebAuthn/passkey configuration is documented in WebAuthn and Passkeys.

User-level two-factor authentication is a preference, not a global YAML switch. Before enabling 2FA, a user must have at least two usable local verification methods:

  • Password.
  • WebAuthn/passkey.
  • Verified email.

OAuth2 does not participate in local 2FA, but a user with 2FA enabled may still log in through OAuth2. OAuth2 sessions are treated as an accepted authentication context for token refresh.

When 2FA is enabled, local one-factor tokens should not be accepted as enough for refresh or security-sensitive preference changes.