Minimal private video streaming service — upload once, stream instantly.
Upload a video file (up to 1GB) and get a shareable link. No accounts required. The browser uploads directly to Cloudflare R2; the API server never touches video bytes.
Live demo: https://streamvault-lilac.vercel.app
| Layer | Technology | Hosted on |
|---|---|---|
| Frontend | SvelteKit | Vercel (free) |
| Backend API | Rust + Axum | Railway (free trial) |
| Video Storage | Cloudflare R2 | Cloudflare (free egress) |
| Database | PostgreSQL | Neon (free tier) |
streamvault/
├── backend/ # Rust/Axum API
│ ├── src/
│ │ ├── main.rs # Server entry, router setup
│ │ ├── config.rs # Env var loading
│ │ ├── routes.rs # HTTP handlers
│ │ ├── models.rs # Shared data types
│ │ ├── db.rs # Postgres connection + migrations
│ │ ├── r2.rs # Cloudflare R2 / presigned URLs
│ │ ├── error.rs # Unified error handling
│ │ └── tests.rs # Unit tests
│ ├── Dockerfile
│ ├── fly.toml
│ └── .env.example
│
├── frontend/ # SvelteKit app
│ ├── src/
│ │ ├── app.html
│ │ ├── app.css
│ │ ├── lib/
│ │ │ └── api.ts # API client
│ │ └── routes/
│ │ ├── +layout.svelte
│ │ ├── +page.svelte # Upload page (/)
│ │ └── watch/[id]/
│ │ ├── +page.ts # Load function
│ │ └── +page.svelte # Watch page (/watch/:id)
│ ├── svelte.config.js
│ ├── vite.config.ts
│ ├── package.json
│ └── .env.example
│
├── docker-compose.yml # Local Postgres
├── ARCHITECTURE.md # Design decisions
└── README.md
# macOS / Linux
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/envWindows: download the installer from rustup.rs.
rustc --version && cargo --version # verify# macOS / Linux — via nvm (recommended)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
nvm install 20 && nvm use 20Windows: download the LTS installer from nodejs.org.
node --version && npm --version # verifygit clone https://github.com/your-org/streamvault.git
cd streamvaultdocker compose up -dStarts Postgres 16 at localhost:5432. The backend auto-creates the schema on first boot.
docker compose down # stop
docker compose down -v # stop + wipe data- Create a bucket named
streamvault-videosin the Cloudflare Dashboard. - Generate an API token under R2 → Manage R2 API Tokens (Object Read & Write).
- Set the CORS policy under R2 → your bucket → Settings → CORS Policy:
[
{
"AllowedOrigins": ["http://localhost:5173"],
"AllowedMethods": ["PUT", "GET"],
"AllowedHeaders": ["*"],
"MaxAgeSeconds": 3600
}
]When deploying, add your production frontend URL to AllowedOrigins.
cd backend
cp .env.example .envFill in your R2 credentials:
R2_ACCOUNT_ID=your_cloudflare_account_id
R2_ACCESS_KEY_ID=your_r2_access_key_id
R2_SECRET_ACCESS_KEY=your_r2_secret_access_key
R2_BUCKET=streamvault-videosDATABASE_URL is pre-configured for the Docker Compose Postgres — no changes needed locally.
cargo runFirst run compiles all dependencies (~2–3 minutes). Subsequent runs are fast.
curl http://localhost:8079/api/health # → {"status":"ok"}cd frontend
cp .env.example .env.local
npm install
npm run devOpen http://localhost:5173.
| Service | URL |
|---|---|
| Frontend | http://localhost:5173 |
| API | http://localhost:8079 |
| Postgres | localhost:5432 |
| Video storage | Cloudflare R2 (remote) |
See ARCHITECTURE.md for full deployment instructions (Railway + Vercel + Neon).
cd backend
cargo fmt -- --check # check formatting
cargo clippy # lint
cargo test # run testsExport the project without build artifacts or secrets:
zip -r streamvault.zip . \
--exclude "*/target/*" \
--exclude "*/node_modules/*" \
--exclude "*/.env" \
--exclude "*/.env.local" \
--exclude "*/.svelte-kit/*" \
--exclude "*/build/*"