Files
surfnathanrip/README.md
T
CallMeVerity 3369f22f69 Initial commit
2026-06-03 00:44:48 +01:00

74 lines
2.4 KiB
Markdown

# surf.nathan.rip
Momentum Mod surf replay hosting - video + `.mtv` replay files with automatic map tier badges, stage splits, and PB history.
## Architecture
| Layer | Stack |
|-------|-------|
| Frontend | React + Vite + Tailwind CSS v4 |
| Backend | ElysiaJS (Bun) |
| Storage | RustFS (S3-compatible) |
| Database | SQLite |
## How it works
1. Upload a video + `.mtv` replay file via the auth-gated upload page
2. Backend parses the `.mtv` binary header for metadata (map, player, time, Steam ID, stage splits)
3. Map tier and thumbnail are fetched from the Momentum Mod API
4. Video uploads go directly to RustFS via presigned URLs (no size limit)
5. If a better time is uploaded for the same map+player, the PB is updated and the old run is archived as a previous PB
## Setup
```bash
# Backend
cd backend
cp .env.example .env # edit with your credentials
bun install
bun run dev
# Frontend
cd frontend
bun install
bun run dev
```
The frontend dev server proxies `/api` to `localhost:3001`.
## Environment variables
| Variable | Required | Description |
|----------|----------|-------------|
| `ADMIN_TOKEN` | Yes | Bearer token for upload/delete auth |
| `RUSTFS_ENDPOINT` | No | S3 endpoint (default: `http://localhost:9000`) |
| `RUSTFS_ACCESS_KEY` | No | S3 access key |
| `RUSTFS_SECRET_KEY` | No | S3 secret key |
| `RUSTFS_BUCKET` | No | S3 bucket name (default: `surf`) |
| `RUSTFS_PUBLIC` | No | Public read access (default: `true`) |
| `PORT` | No | Server port (default: `3001`) |
| `DB_PATH` | No | SQLite path (default: `./data/surf.db`) |
| `STATIC_DIR` | No | Frontend static files (default: `./public`) |
## RustFS setup
1. Create a bucket called `surf` in your RustFS instance
2. Set bucket to public read for direct video URLs
3. Configure CORS on the bucket to allow `PUT` from your frontend origin (required for presigned video uploads)
4. Create an IAM policy with `s3:ListBucket`, `s3:GetObject`, `s3:PutObject`, `s3:DeleteObject` on `surf/*`
## .mtv file format
195-byte binary header followed by JSON stats and LZMA-compressed replay data.
| Offset | Size | Field |
|--------|------|-------|
| 0x00 | 4 | Magic (`MMTV` = 0x56544D4D) |
| 0x04 | 4 | Version |
| 0x10 | 64 | Map name (null-terminated) |
| 0x50 | 41 | Map hash |
| 0x7B | 4 | Tick interval (float) |
| 0x7F | 8 | Steam ID (uint64) |
| 0x87 | 32 | Player name (null-terminated) |
| 0xA9 | 8 | Run time (double) |
| 0xB1 | 4 | Total ticks |