A Coral ecosystem module — built on TanStack Start, Tailwind v4, and the Jellyfin API.
pnpm installcp .env.example .env
# Set your downloads directory, optional auth, and any other Tide settingspnpm devApp runs at http://localhost:3000.
| Tool | Purpose |
|---|---|
| TanStack Start | Full-stack React framework |
| TanStack Router | Type-safe file-based routing |
| TanStack Query | Server state management |
| Tailwind v4 | Styling |
| Biome | Linting & formatting |
| @get-coral/jellyfin | Jellyfin API client |
| Vitest | Testing |
pnpm dev # Start dev server on :3000
pnpm build # Production build
pnpm start # Run production server
pnpm typecheck # TypeScript check
pnpm check # Biome lint + format check
pnpm lint # Biome lint with auto-fix
pnpm test # Run tests# Pull the published image
docker pull getcoral/tide:latest
# Or build it yourself
docker build -t tide .
# Run
docker run -p 3000:3000 \
-e TIDE_DOWNLOADS_DIR=/downloads \
-e TIDE_MEMORY_LIMIT_MB=8192 \
-e TIDE_MEMORY_PAUSE_MB=7168 \
-e TIDE_MEMORY_RESUME_MB=6144 \
-e TIDE_AUTH_USERNAME=admin \
-e TIDE_AUTH_PASSWORD=change-me \
getcoral/tide:latestTide has two independent layers, both optional.
HTTP basic auth — set TIDE_AUTH_USERNAME and TIDE_AUTH_PASSWORD and the production server
challenges every request before it reaches the app. It is a blunt front door with a single shared
credential, and it is not applied by pnpm dev.
Jellyfin sign-in — Tide can require a Jellyfin account instead of, or on top of, basic auth. It is off by default.
- On
/manage, set the Jellyfin server URL under Access (or setTIDE_JELLYFIN_URL). Tide verifies the URL against/System/Info/Publicbefore storing it. - Turn on Require a Jellyfin sign-in. Tide refuses to enable this until a server is reachable, so you cannot lock yourself out of a Tide that has nowhere to authenticate.
Once required:
- Everyone must sign in with their Jellyfin username and password at
/login. - Signed-in users get the board, the live stream, and read-only API access.
/manageand every mutating endpoint are limited to Jellyfin administrators.- Signing out of Tide also revokes the Jellyfin access token it was issued.
Tide stores no Jellyfin API key — only the server URL. Authentication uses Jellyfin's public
AuthenticateByName endpoint, so there is no server-wide credential for Tide to leak.
Sessions live in the same SQLite database as the rest of Tide's state and survive restarts. If you
ever lock yourself out (server moved, URL wrong), start Tide with TIDE_REQUIRE_LOGIN=false — the
env var overrides the stored setting in both directions.
Tide now includes an RSS-based memory guard for torrent activity.
- If Tide can read the container memory cap from cgroups, the guard enables itself automatically.
- You can override or force thresholds with
TIDE_MEMORY_LIMIT_MB,TIDE_MEMORY_PAUSE_MB, andTIDE_MEMORY_RESUME_MB. - When RSS crosses the pause threshold, Tide pauses active torrents and disconnects peers.
- Activity resumes only after RSS falls below the lower resume threshold.
TIDE_MEMORY_CHECK_INTERVAL_MScontrols how often Tide re-checks memory usage. Default is5000.
For an 8 GB container limit on a NAS, a reasonable starting point is:
TIDE_MEMORY_LIMIT_MB=8192
TIDE_MEMORY_PAUSE_MB=7168
TIDE_MEMORY_RESUME_MB=6144Published automatically on every release via GitHub Actions:
getcoral/tideon Docker Hubghcr.io/get-coral/tideon GHCR
| Workflow | Trigger | What it does |
|---|---|---|
ci.yml |
Every PR + push to main | Typecheck, lint, test, build, Docker build check |
docker-publish.yml |
Push to main + version tags | Publishes to GHCR |
release-please.yml |
Push to main | Opens release PR, publishes Docker on merge |
Releases are fully automated via Release Please. Use conventional commits:
| Commit prefix | Version bump |
|---|---|
feat: |
Minor |
fix: |
Patch |
feat!: / fix!: |
Major |
chore:, docs: |
No bump |
This module is part of the Coral ecosystem. See the contributing guide before opening PRs.