GOtify is a lightweight Go service that distributes time-limited playback URLs for HTTP Live Streaming (HLS) content. Tokens are signed with HMAC and paired with an API key check to keep media files private while remaining easy to integrate with players and automation scripts.
- Features
- Architecture Overview
- Getting Started
- Running the Server
- API Reference
- Security Notes
- Development
- Troubleshooting
- License
- HMAC-signed playback URLs that expire automatically.
- Mandatory API key required on every request (
X-API-Keyheader). - Built-in rate limiting (10 requests per second per client) to protect against abuse.
- Simple file server with path sanitisation to prevent directory traversal.
- Environment-based configuration for secrets and port selection.
The service is split into several internal packages:
internal/security— implements the HMAC signer used to generate and validate tokens.internal/handlers— contains HTTP handlers for issuing tokens and serving media files.internal/server— wires together middleware (logging, protection headers, rate limiting, API key enforcement) and routes.cmd/server— entry point that loads environment variables and starts the Gin HTTP server.
Media assets are read from the directory you pass to server.New, which defaults to assets/audio when using the bundled main.go.
- Go 1.25 or newer.
- A valid API key that clients will send in the
X-API-Keyheader. - Optional:
direnvor similar if you prefer automatically loading the.envfile.
git clone https://github.com/MyBroder-Me/GOtify GOtify
cd GOtify
go mod downloadCreate a .env file (or export the variables another way) with at least:
SECRET=your_shared_api_key
PORT=8080SECRETis used in two places:- As the API key clients must send in the
X-API-Keyheader. - As the HMAC signing key for playback tokens.
- As the API key clients must send in the
PORTdefines the HTTP port (defaults to8080when omitted).
go run ./cmd/serverBy default the server will stream files from assets/audio. You can customise this by changing the argument passed to server.New inside cmd/server/main.go.
To build a binary instead:
go build -o gotify ./cmd/server
./gotifyEvery request (including token generation and playback) must include the API key:
X-API-Key: <SECRET>
Requests that omit the header or provide an incorrect value return 401 Unauthorized. The secret is never accepted through query parameters.
Generates a signed playback URL for the requested file.
| Query Param | Required | Description |
|---|---|---|
ttl |
optional | Token lifetime in minutes (integer > 0). Defaults to 10 minutes. |
Sample request:
curl -H "X-API-Key: $SECRET" \
"http://localhost:8080/token/demo?ttl=5"Sample response:
{
"file": "demo",
"expires": 1733836800,
"url": "/stream/demo?t=6da1...&e=1733836800"
}Serves the requested HLS playlist. The URL returned by /token/:file already contains the required query parameters:
t— HMAC token.e— Unix timestamp (seconds) when the token expires.
Example playback request using the previously issued URL:
curl -H "X-API-Key: $SECRET" \
"http://localhost:8080/stream/demo/master?t=6da1...&e=1733836800"The helper resolveFilename automatically appends .m3u8 when no extension is supplied, so a request for /stream/demo returns master.m3u8, whereas /stream/demo/variant returns variant.m3u8.
- Tokens are validated with constant-time comparisons to mitigate timing attacks.
- Expiration timestamps are checked on both issuance and playback.
- Directory traversal is blocked (
..segments are rejected) to ensure only files under the configured root are accessible. - Secret negotiation via query string is disabled; use headers exclusively to avoid accidental leaks through logs or referrers.
- Rate limiting via
tollboothis enabled globally. Tune the limits ininternal/server/server.goif your deployment requires different thresholds.
- Format code:
gofmt -w <path> - Run tests:
go test ./... - Useful directories:
assets/audio— bundled sample media.internal/...— application source code.
| Symptom | Likely Cause | Fix |
|---|---|---|
401 Unauthorized on every endpoint |
Missing or incorrect X-API-Key header |
Ensure clients send the same value defined in SECRET. |
500 Internal Server Error immediately on boot |
SECRET is empty |
Set the SECRET environment variable. |
go run fails with missing modules |
Dependencies not downloaded | Run go mod tidy or go mod download. |
| Playback URL expires too quickly | ttl too small |
Request a longer TTL when calling /token/:file. |
This project is distributed under the terms of the LICENSE file.