انتقل إلى المحتوى

Security

Passwords

  • Hashed with Argon2 (the argon2 crate's default parameters, which are Argon2id) with a random salt, and stored in PHC format.
  • Must be at least 8 characters.
  • Changing your password needs the current one.
  • Changing or resetting a password deletes all of that user's sessions.

Sessions

Property Value
Cookie vistilla_session
Value 40 random characters
Stored as SHA-256 of the value (a database leak does not leak live sessions)
Attributes Path=/; HttpOnly; SameSite=Lax, plus Secure when VISTILLA_SECURE_COOKIES=true
Lifetime VISTILLA_SESSION_TTL_DAYS, default 30 days

Sign-in failures always answer "incorrect email or password", whether the email exists or not.

Sign-up verification

New accounts are only created after the emailed 6-digit code is typed:

  • the code is stored hashed;
  • it expires after 15 minutes;
  • it allows 5 attempts;
  • a new code can be requested every 30 seconds.

Invited people skip the code, because the invitation link was already sent to their address and only that address can accept it.

VISTILLA_SIGNUP_ENABLED=false closes open sign-up: then only invited people can create accounts.

Tokens

All random values come from the operating system's secure random generator.

Token Length Stored as Lifetime
Session 40 SHA-256 30 days
Password reset 40 SHA-256 1 hour, one use
Sign-up code 6 digits SHA-256 15 minutes, 5 tries
Team invitation 28 as is 14 days
Share link 22 as is until revoked or expired
Guest cookie (vistilla_guest) 32 SHA-256 365 days

Roles and access

  • Roles are ordered viewer < member < admin < owner:
    • members and above can upload;
    • admins and above manage people, moderate comments and change AI settings.
  • Workspace owners and admins hold their role on every team.
  • Video visibility:
    • team: team members;
    • private: the uploader and team admins;
    • org: every workspace member.
  • Anyone without access gets 404, so the video's existence is not revealed.
  • Editing a video (rename, delete, share, trim, chapters, Insights) is for the uploader and admins.
  • Share links give access to one video without an account. A revoked or expired link answers 403.
  • Password-protected links (an API option):
    • unlocking sets a 12-hour cookie tied to the current password, so changing the password locks everyone out again;
    • signed-in people who can already see the video skip the password.

Encrypted AI keys

Workspace AI keys are sealed with XChaCha20-Poly1305:

  • a fresh 24-byte nonce for each key;
  • stored as base64(nonce ‖ ciphertext).

The master key comes from VISTILLA_SECRET_KEY (base64, 32 bytes). Otherwise it comes from a secret.key file in VISTILLA_SECRET_DIR (by default the database's folder), which is created with mode 0600 on first start.

Keep the secret key

If the master key changes, stored AI keys can no longer be decrypted, and admins must paste them again. The hosted instance sets VISTILLA_SECRET_KEY in its environment so it survives restarts.

HTTP headers

Every response carries:

X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: default-src 'self'; img-src 'self' data: blob:;
  media-src 'self' blob: [bucket origin]; script-src 'self' 'unsafe-inline';
  style-src 'self' 'unsafe-inline'; connect-src 'self';
  frame-ancestors 'self'; base-uri 'none'
  • The bucket's origin (VISTILLA_S3_PUBLIC_ENDPOINT) is added to media-src only when streams are redirected to presigned URLs. A download proxy (VISTILLA_S3_DOWNLOAD_PROXY) is not added: on the hosted instance it is the Worker's /_b2/, on the same origin as the app, so 'self' covers it. A proxy on another origin would need adding there.
  • Inline scripts and styles are allowed because the UI has no build step.
  • Nothing else from another origin can load.

Profile photos

GET /api/users/{id}/avatar needs no sign-in, so a photo can show beside a comment on a public share page. User ids are random and not listed anywhere public, and a photo is only as exposed as the name next to it. The answer is cached for a year (public, max-age=31536000, immutable); a new photo gets a new address (?v=), and a removed one stops being linked, though a browser that already has it may keep it.

Other safeguards

  • Uploads:
    • content types are checked;
    • sizes are checked against the limit before and during upload;
    • the filesystem store rejects .. and absolute keys.
  • Internal errors are logged in full, but the client only sees "internal error".

See Known limitations for what is not covered yet, such as rate limiting.