Skip to content

Latest commit

Β 

History

164 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ¦€ CrabS3

Send big files and secrets from your own S3 bucket. No cloud vendor, no monthly bill, no upload limit but the one you set.

Status Uptime License: Apache 2.0

Quick start Β· Configuration Β· How it works Β· API


CrabS3 is a self-hosted transfer platform for any S3-compatible storage. Drop files in the browser or push them through the API, get a shareable link back, and let the file delete itself after a deadline or a download count you choose.

It runs on RustFS by default, but any S3-compatible backend works β€” AWS S3, OVH Object Storage, MinIO, Ceph, etc. CrabS3 never stores your data itself; it only keeps metadata in Postgres.

Upload  β†’  bucket (hot)  β†’  share link  β†’  N downloads or T days  β†’  gone
                ↓
        marked cold in DB, per your bucket's own lifecycle rule (optional)

Why

πŸ“¦ Your storage Any S3-compatible backend. Your keys, your bucket, your retention rules.
πŸš€ Big files Resumable multipart uploads with live progress β€” a dropped connection does not restart the transfer.
πŸ”₯ Hot & cold One bucket. Cold is a class your provider's lifecycle rule assigns; CrabS3 just tracks it β€” no copying, no second bucket.
πŸ—οΈ Secrets Share a password, a token, a note. Password-protected, time-limited, gone after reading.
πŸ—‘οΈ Self-destruct Set a max download count; the file is deleted from storage automatically when it is reached.
πŸ›‘οΈ Malware scan Every upload goes through ClamAV; infected files are flagged and blocked from download.
πŸ”’ Real accounts Invite-only signup, sessions, 2FA (TOTP), per-user storage quotas.
πŸ“§ Notifications Email on upload, download and share; webhooks for your own integrations.
πŸ“Š Dashboard Per-user file list and download stats; admin view for storage, users and audit logs.
πŸ”Œ Services Scoped API keys for other apps and scripts β€” their own folder, quota and status, issued directly or self-served via an invite code. No user account needed.

Quick start

Requires Docker and Docker Compose.

git clone https://github.com/DoctorPok42/CrabS3.git
cd CrabS3
cp .env.example .env      # then edit it β€” see Configuration
docker compose up -d

The interface is on http://localhost:3000. Health check: GET /api/health.

Prefer the published image? docker pull doctorpok/crabs3:latest, then point compose.yml at it instead of build: ..

See the full doc by DeepWiki: https://deepwiki.com/DoctorPok42/CrabS3

Proxmox VE

A community-scripts LXC install is also available β€” bare-metal (no Docker inside the container), Node.js/PostgreSQL/ClamAV set up automatically:

COMMUNITY_SCRIPTS_URL="https://raw.githubusercontent.com/DoctorPok42/CrabS3/main" \
  bash -c "$(curl -fsSL https://raw.githubusercontent.com/DoctorPok42/CrabS3/main/ct/crabs3.sh)"

You'll be prompted for your S3 endpoint and keys during install (or export var_s3_endpoint, var_s3_access_key, var_s3_secret_key, var_s3_bucket, var_admin_email beforehand for an unattended run). Everything else lives in /opt/crabs3/.env β€” edit and systemctl restart crabs3 to apply. There's no TLS in front by default; see COOKIE_SECURE below before exposing it past your LAN.

Running from source (development)
npm install
npx prisma migrate deploy
npm run dev               # http://localhost:3000

You still need a reachable Postgres instance and an S3 endpoint.

Configuration

Everything is environment variables β€” put them in .env, or manage them in Doppler (a doppler.yaml is included and picked up automatically).

Storage β€” One bucket. "Cold" is a storage class your provider's lifecycle rule assigns to a file, not a separate bucket β€” CrabS3 only records which class a file is in.

Variable Example
S3_ENDPOINT http://192.168.1.100:9000
S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY your keys
S3_BUCKET_NAME crabs3
S3_REGION us-east-1
EXPIRED_FILE_POLICY cold (move) or delete

App

Variable Example
DATABASE_URL postgresql://user:password@db:5432/crabs3
NEXT_PUBLIC_BASE_URL https://files.example.com β€” used in share links and emails
JWT_SECRET a long random string
COOKIE_SECURE true/false β€” defaults to NODE_ENV === "production" if unset. Set false when serving plain HTTP with no reverse proxy in front, or browsers silently drop the session cookie after login
LOG_MIN_LEVEL DEBUG Β· INFO Β· WARN Β· ERROR

Email & scanning

Variable Example
SMTP_HOST / SMTP_USER / SMTP_PASS your mail relay
SMTP_FROM CrabS3 Notifications <bot@example.com>
CLAMAV_HOST / CLAMAV_PORT clamav / 3310
CRON_SECRET shared secret for the expiry job container

First user: signup is invite-only, so nothing lets you in until one admin exists. Seed it once β€” safe to re-run, it does nothing if an admin is already there:

docker compose exec -T web npx tsx install/seed-admin.mjs

Prints the email and generated password once; they are not stored anywhere else. Issue further invites from the admin panel.

How it works

  1. The browser hashes the file and asks the server if it already has this content; if so, a new share is created without sending a single byte. Otherwise it opens a multipart session and uploads parts in parallel β€” the server relays each part straight through to your bucket. A dropped connection reattaches to the same session and only sends the parts that didn't land yet.
  2. Metadata (owner, size, hash, expiry, download quota, password hash) lives in Postgres; file bytes only ever live in your bucket.
  3. ClamAV scans the object; a hit marks the file infected and download is refused for infected files.
  4. The cron container calls the expiry endpoint on a schedule. Files past their deadline or download quota are marked cold or deleted, per EXPIRED_FILE_POLICY.
  5. Duplicate uploads are detected by hash, so the same file is not stored twice.

The hot β†’ cold transition is a storage-class change your bucket's own lifecycle rule performs β€” see RustFS lifecycle rules for an example. CrabS3 never copies bytes anywhere; it only records which class a file ended up in.

API

Everything the UI does is available over HTTP. Public endpoints need no session; the rest use the session cookie, and admin endpoints additionally require an admin account.

Health Β· GET /api/health

Upload

POST /api/upload/dedupe-check           already have this content? link instead of upload
POST /api/upload/multipart/start        open a session
POST /api/upload/multipart/part         upload one part
POST /api/upload/multipart/resume       reattach after a dropped connection
POST /api/upload/multipart/set-hash     attach a content hash to a session
POST /api/upload/multipart/complete     finish + attach metadata
POST /api/upload/multipart/finish       once per batch, after every file is complete β€” sends notifications, fires webhooks
POST /api/upload/multipart/abort        cancel

Files

GET    /api/checkfile                   is this share link still valid?
POST   /api/download/:id                metadata for a share link
GET    /api/download/:id/stream         stream the bytes
DELETE /api/delete                      remove a file (409 + mode if it shares content with others)

Secrets

POST /api/secret/upload                 store a secret, get a link
POST /api/secret/check                  exists? password required?
POST /api/secret/get                    read it

Services β€” scoped API keys for third-party apps and scripts. Two ways to get a token: create a service directly and get its token back immediately, or issue an invite code that a third party redeems for their own token via join β€” no account needed either way. create/create/invite/update/delete/list need an admin session; upload/download use the service's own bearer token (Authorization: Bearer <token>), not the session cookie.

POST   /api/services/create             path 1 β€” create a service + folder, get its token back (admin)
POST   /api/services/create/invite      path 2 β€” issue a redeemable invite code (admin)
POST   /api/services/join               path 2 β€” redeem an invite code for a token (public)
GET    /api/services/:uuid              public info about a service
GET    /api/services/list               list every service (admin)
PUT    /api/services/update             change status or image (admin)
DELETE /api/services/delete/:id         delete a service and its folder (admin)
POST   /api/services/upload             single presigned PUT β€” not the multipart flow
GET    /api/services/download           a share link, or a presigned URL per file

Access Tokens β€” personal bearer tokens that act as you, without the session cookie (scripts, cron, CI). Each carries one or more scopes (READ Β· WRITE Β· DELETE Β· ADMIN) and an expiry of 7/30/90/180/365 days, and works as Authorization: Bearer <token> on any endpoint on this page. READ allows GET only, WRITE allows anything but DELETE, DELETE allows DELETE only, ADMIN allows everything. Managed from /me; the token value is shown once, at creation.

GET    /api/accessToken                 list your tokens β€” name, scopes, expiry, never the value again
POST   /api/accessToken                 create one: name, scopes[], expires_at (days)
DELETE /api/accessToken?id=:id          revoke a token

Auth

POST   /api/auth/login Β· logout Β· signup
GET    /api/auth/me Β· check-invite
DELETE /api/auth/me                     delete your own account and everything it owns
POST   /api/auth/invite                 (admin)
POST   /api/2fa/create                  enable TOTP, returns secret + QR uri
GET    /api/2fa/disable                 disable TOTP for the current account

Dashboard & communication

GET    /api/dashboard/files
GET    /api/dashboard/folders           folders you own or have files in, with a file count each
PATCH  /api/dashboard/folders/:id       rename
DELETE /api/dashboard/folders/:id       delete the folder and every file in it
POST   /api/dashboard/coldtohot         restore a folder from cold to hot storage
PATCH  /api/dashboard/me
GET    /api/communication               webhook settings
POST   /api/communication
GET    /api/fingerprint/:id             download history for a file or folder (?type=file|folder)
GET    /api/settings                    read specific instance settings (?keys=a,b)

Admin

GET    /api/admin/stats                  storage, files, users
GET    /api/admin/users
GET    /api/admin/users/:id
DELETE /api/admin/users/:id
PUT    /api/admin/users/:id/edit-quota
POST   /api/admin/users/:id/reset-password
GET    /api/admin/logs                   filter by level, action, date
PATCH  /api/admin/logs                   set minimum log level
GET    /api/admin/settings               every instance setting and its current value
PATCH  /api/admin/settings               update one: { key, value }
DELETE /api/admin/settings               reset one to default (?key=:key)
POST   /api/admin/settings               sync the settings catalog β€” creates missing rows with defaults

OpenAPI-ish request collections live in doc/api.

Stack

Next.js (App Router) Β· React Β· Tailwind CSS Β· Prisma + PostgreSQL Β· AWS SDK for S3 Β· ClamAV Β· Nodemailer Β· Docker

Contributing

Issues and pull requests are welcome. Keep changes focused, run npm run lint before opening a PR, and describe what you tested.

License

Apache 2.0 β€” see LICENSE and NOTICE.


No cloud. No bill. Just S3 buckets full of crabs. πŸ¦€

CrabS3

About

Send big files and secrets from your own S3 bucket. No cloud vendor, no monthly bill, no upload limit but the one you set.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages