Features

Everything ravyn does, one page, grouped by area. All of it is configured from /settings (your own account) or /admin (instance-wide, admin only).

Accounts and access

The first account ever created, through /register or the CLI, automatically becomes the instance's admin. Everyone after that is gated by a registration mode an admin sets from /admin: closed (default, accounts only via the CLI or an invite), open (anyone can sign up), or invite (a single-use code, generated by an admin).

cargo run -p ravyn-api -- create-user alice hunter2

That command always creates an admin regardless of the current registration mode, useful for a headless setup or adding a second admin later. An API token (/settings) is what ShareX or a script authenticates with instead of a session cookie, and the settings page has a one-click "download ShareX config" button that fills in the token and this instance's URL already.

Two-factor authentication

Optional, per user, off by default. Standard TOTP, compatible with any authenticator app: setup shows a QR code and confirms with a real code before it's actually turned on, so an abandoned setup never leaves an account half-protected. Confirming mints eight single-use recovery codes, shown once. Once enabled, login becomes two steps (password, then a code), and turning it back off needs both the password and a valid code, so a stolen session cookie alone can't touch it either way.

Per-user storage quotas

An admin sets a cap in MB per account from /admin, or leaves it unlimited. An upload that would push someone over their cap is rejected outright rather than partially saved.

Deleting an account

An admin can remove a user entirely from /admin, behind an inline confirmation since it's irreversible. This deletes their file bytes from storage, any in-progress upload's leftover chunks, and the account itself, which cascades everything else that belonged to them (folders, sessions, tokens, short links). Activity log entries survive, since those already store a snapshot of the username rather than a live reference. An admin can't delete their own account this way, so an instance never ends up with nobody able to manage it.

Rate limiting

Login, two-factor codes, registration, and uploads are all protected against brute-forcing with a simple limiter keyed by IP (or by account, for uploads): 10 login attempts per 5 minutes, 10 two-factor attempts per 5 minutes (a 6-digit code only has a million possibilities, so this one matters more than the password check), 5 registrations per hour, 60 uploads per minute. A rejected request gets a plain "too many attempts, try again later." This lives in memory and resets on a restart; it's meant to blunt casual abuse on a self-hosted instance, not stand up to a determined, distributed attacker.

Uploading

Large files

At or above 32 MB, an upload automatically switches from a single request to 8 MB chunks sent one at a time. A chunk that fails is retried on its own, up to three times, so a network blip partway through a multi-gigabyte upload costs a few seconds of retrying that one chunk, not starting over. Reloading the page mid-upload does lose progress today; there's no resume-after-reload yet, though the server already tracks enough to build that later.

ShareX and scripts

Dropping (or selecting) several files at once sends them as one request with multiple parts. A single file upload gets back the plain {"id": ...} shape ShareX expects; more than one gets an array instead, so one bad file in a batch doesn't silently drop the ones that succeeded next to it.

Text snippets

The upload page's "paste text" tab turns pasted text into a file and sends it through the exact same upload path as a real file, so naming, quotas, and auto-delete all apply the same way. Any text file under 2 MB (not just ones made this way) renders as a proper in-page text view instead of a raw download when opened.

URL shortener

A third upload-page tab: paste a destination, get back a short link. Deliberately minimal, just a slug, a destination, and a click counter, since the file side already covers passwords, expiry, and tags for anything actually hosted here. Each account only ever sees and manages its own links.

Organizing files

Folders

Flat labels (no nesting) a file can belong to, each with its own optional password. A folder's contents are viewable at its own public link without logging in, gated by the password if it has one, the same sharing model as a single file.

Passwords

A file can also be password-protected on its own, independent of any folder. A direct link to a protected file shows a small password prompt instead of the raw bytes. The owner always sees their own file regardless, whether through a session or an API token.

Tags

Simple, free-text, per-file tags, no shared registry to manage. The browse page's search matches both filenames and tags, and clicking a tag on a file card drops it straight into the search box.

Bulk actions

The browse page's "select" button turns every card into a checkbox. With one or more selected, a bar above the grid moves or deletes all of them at once.

File naming and lifecycle

Naming scheme

By default a file keeps whatever name it was uploaded with. An admin can switch the instance-wide default to a random string (length configurable), a UUID, or a timestamp, all of which keep the original file extension. This only changes the display name, not the link, and anyone can rename their own file afterward regardless of the default.

Auto-delete

An admin sets an instance-wide default expiry (from 5 minutes up to a year, or never) applied at upload time. Anyone can override their own file's expiry afterward, including turning it off entirely. A background sweep checks every 5 minutes and deletes anything past its expiry, storage and database record together.

EXIF stripping

On by default, instance-wide: removes camera model, GPS coordinates, and timestamps from an uploaded image before it's ever written to storage. Only affects JPEG, PNG, and WebP; other formats upload untouched rather than failing over a privacy nice-to-have. The image container is rewritten in place, not recompressed, so a stripped file is byte-identical to the original apart from the removed metadata.

Sharing and embeds

Every share link already works as a direct image or video link, Discord previews it natively either way. Turning embeds on (per account, from /settings) additionally sets a custom title, description, and accent color that Discord, Slack, and Twitter read from the link, with template variables for the file's name, size, type, and the uploader's username.

Upload webhooks

Per account, off by default: every successful upload posts a notification (filename and link) to a URL you provide. Works for both Discord and Slack's webhook formats without needing to say which one it is. Sent in the background so a slow or unreachable target never delays the upload itself.

Visibility

Your own stats

Storage used against your quota, file count, and a breakdown by type, in /settings.

Instance stats

The same shape summed across every account, plus registration mode and invite management, in /admin.

Cost estimate

A fun, not-a-bill number on /admin: fill in a currency and a price per GB per month, and it's multiplied against the instance's real total storage on every load. Left unconfigured by default, since there's no sensible price to guess at, which hides the number entirely rather than showing a misleading zero. Storage only, no request or bandwidth cost, since this app doesn't track either.

Activity log

An admin-only tab showing the most recent 200 events on the instance: uploads, deletes, logins, registrations, password and 2FA changes, and anything an admin changed. Each entry keeps a snapshot of the acting username, so the log stays readable even for an account since renamed or deleted.

More on how any of this actually works internally is in the GitHub README, which stays closer to the code.