## File: README.md

cobalt is a media downloader that doesn't piss you off. it's friendly, efficient, and doesn't have ads, trackers, paywalls or other nonsense.

paste the link, get the file, move on. that simple, just how it should be.

### cobalt monorepo
this monorepo includes source code for api, frontend, and related packages:
- [api tree & readme](/api/)
- [web tree & readme](/web/)
- [packages tree](/packages/)

it also includes documentation in the [docs tree](/docs/):
- [how to run a cobalt instance](/docs/run-an-instance.md)
- [how to protect a cobalt instance](/docs/protect-an-instance.md)
- [cobalt api instance environment variables](/docs/api-env-variables.md)
- [cobalt api documentation](/docs/api.md)

### ethics
cobalt is a tool that makes downloading public content easier. it takes **zero liability**.
the end user is responsible for what they download, how they use and distribute that content.
cobalt never caches any content, it [works like a fancy proxy](/api/src/stream/).

cobalt is in no way a piracy tool and cannot be used as such.
it can only download free & publicly accessible content.
same content can be downloaded via dev tools of any modern web browser.

### contributing
if you're considering contributing to cobalt, first of all, thank you! check the [contribution guidelines here](/CONTRIBUTING.md) before getting started, they'll help you do your best right away.

### thank you
cobalt is sponsored by [royalehosting.net](https://royalehosting.net/?partner=cobalt). a part of our infrastructure is hosted on their network. we really appreciate their kindness and support!

### licenses
for relevant licensing information, see the [api](api/README.md) and [web](web/README.md) READMEs.
unless specified otherwise, the remainder of this repository is licensed under [AGPL-3.0](LICENSE).

---

## File: api/README.md

# cobalt api
this directory includes the source code for cobalt api. it's made with [express.js](https://www.npmjs.com/package/express) and love!

## running your own instance
if you want to run your own instance for whatever purpose, [follow this guide](/docs/run-an-instance.md).
we recommend to use docker compose unless you intend to run cobalt for developing/debugging purposes.

## accessing the api
there is currently no publicly available pre-hosted api.
we recommend [deploying your own instance](/docs/run-an-instance.md) if you wish to use the cobalt api.

you can read [the api documentation here](/docs/api.md).

## supported services
this list is not final and keeps expanding over time!
if the desired service isn't supported yet, feel free to create an appropriate issue (or a pull request 👀).

| service           | video + audio | only audio | only video | metadata | rich file names |
| :--------         | :-----------: | :--------: | :--------: | :------: | :-------------: |
| bilibili          | ✅            | ✅         | ✅         | ➖         | ➖              |
| bluesky           | ✅            | ✅         | ✅         | ➖         | ➖              |
| dailymotion       | ✅            | ✅         | ✅         | ✅         | ✅              |
| instagram         | ✅            | ✅         | ✅         | ➖         | ➖              |
| facebook          | ✅            | ❌         | ✅         | ➖         | ➖              |
| loom              | ✅            | ❌         | ✅         | ✅         | ➖              |
| newgrounds        | ✅            | ✅         | ✅         | ✅         | ✅              |
| ok.ru             | ✅            | ❌         | ✅         | ✅         | ✅              |
| pinterest         | ✅            | ✅         | ✅         | ➖         | ➖              |
| reddit            | ✅            | ✅         | ✅         | ❌         | ❌              |
| rutube            | ✅            | ✅         | ✅         | ✅         | ✅              |
| snapchat          | ✅            | ✅         | ✅         | ➖         | ➖              |
| soundcloud        | ➖            | ✅         | ➖         | ✅         | ✅              |
| streamable        | ✅            | ✅         | ✅         | ➖         | ➖              |
| tiktok            | ✅            | ✅         | ✅         | ❌         | ❌              |
| tumblr            | ✅            | ✅         | ✅         | ➖         | ➖              |
| twitch clips      | ✅            | ✅         | ✅         | ✅         | ✅              |
| twitter/x         | ✅            | ✅         | ✅         | ➖         | ➖              |
| vimeo             | ✅            | ✅         | ✅         | ✅         | ✅              |
| vk videos & clips | ✅            | ❌         | ✅         | ✅         | ✅              |
| youtube           | ✅            | ✅         | ✅         | ✅         | ✅              |

| emoji   | meaning                 |
| :-----: | :---------------------- |
| ✅      | supported               |
| ➖      | unreasonable/impossible |
| ❌      | not supported           |

### additional notes or features (per service)
| service    | notes or features                                                                                                    |
| :--------  | :-----                                                                                                               |
| instagram  | supports reels, photos, and videos. lets you pick what to save from multi-media posts.                               |
| facebook   | supports public accessible videos content only.                                                                      |
| pinterest  | supports photos, gifs, videos and stories.                                                                           |
| reddit     | supports gifs and videos.                                                                                            |
| snapchat   | supports spotlights and stories. lets you pick what to save from stories.                                            |
| rutube     | supports yappy & private links.                                                                                      |
| soundcloud | supports private links.                                                                                              |
| tiktok     | supports videos with or without watermark, images from slideshow without watermark, and full (original) audios.      |
| twitter/x  | lets you pick what to save from multi-media posts. may not be 100% reliable due to current management.               |
| vimeo      | audio downloads are only available for dash.                                                                         |
| youtube    | supports videos, music, and shorts. 8K, 4K, HDR, VR, and high FPS videos. rich metadata & dubs. h264/av1/vp9 codecs. |

## license
cobalt api code is licensed under [AGPL-3.0](LICENSE).

this license allows you to modify, distribute and use the code for any purpose
as long as you:
- give appropriate credit to the original repo when using or modifying any parts of the code,
- provide a link to the license and indicate if changes to the code were made, and
- release the code under the **same license**

## open source acknowledgements
### ffmpeg
cobalt relies on ffmpeg for muxing and encoding media files. ffmpeg is absolutely spectacular and we're privileged to have the ability to use it for free, just like anyone else. we believe it should be way more recognized.

you can [support ffmpeg here](https://ffmpeg.org/donations.html)!

### youtube.js
cobalt relies on **[youtube.js](https://github.com/LuanRT/YouTube.js)** for interacting with youtube's innertube api, it wouldn't have been possible without this package.

you can support the developer via various methods listed on their github page!
(linked above)

### many others
cobalt-api also depends on:

- **[content-disposition-header](https://www.npmjs.com/package/content-disposition-header)** to simplify the provision of `content-disposition` headers.
- **[cors](https://www.npmjs.com/package/cors)** to manage cross-origin resource sharing within expressjs.
- **[dotenv](https://www.npmjs.com/package/dotenv)** to load environment variables from the `.env` file.
- **[express](https://www.npmjs.com/package/express)** as the backbone of cobalt servers.
- **[express-rate-limit](https://www.npmjs.com/package/express-rate-limit)** to rate limit api endpoints.
- **[ffmpeg-static](https://www.npmjs.com/package/ffmpeg-static)** to get binaries for ffmpeg depending on the platform.
- **[hls-parser](https://www.npmjs.com/package/hls-parser)** to parse HLS playlists according to spec (very impressive stuff).
- **[ipaddr.js](https://www.npmjs.com/package/ipaddr.js)** to parse ip addresses (used for rate limiting).
- **[nanoid](https://www.npmjs.com/package/nanoid)** to generate unique identifiers for each requested tunnel.
- **[set-cookie-parser](https://www.npmjs.com/package/set-cookie-parser)** to parse cookies that cobalt receives from certain services.
- **[undici](https://www.npmjs.com/package/undici)** for making http requests.
- **[url-pattern](https://www.npmjs.com/package/url-pattern)** to match provided links with supported patterns.
- **[zod](https://www.npmjs.com/package/zod)** to lock down the api request schema.
- **[@datastructures-js/priority-queue](https://www.npmjs.com/package/@datastructures-js/priority-queue)** for sorting stream caches for future clean up (without redis).
- **[@imput/psl](https://www.npmjs.com/package/@imput/psl)** as the domain name parser, our fork of [psl](https://www.npmjs.com/package/psl).

...and many other packages that these packages rely on.

---

## File: docs/api.md

# cobalt api documentation
methods, acceptable values, headers, responses and everything else related to making and parsing requests from a cobalt api instance.

> [!IMPORTANT]
> hosted api instances (such as `api.cobalt.tools`) use bot protection and are **not** intended to be used in other projects without explicit permission. if you want to use the cobalt api, you should [host your own instance](/docs/run-an-instance.md) or ask an instance owner for access.

- [POST /](#post)
- [POST /session](#post-session)
- [GET /](#get)
- [GET /tunnel](#get-tunnel)

all endpoints (except for `GET /`) are rate limited and return current rate limiting status in `RateLimit-*` headers, according to the ["RateLimit Header Fields for HTTP" spec](https://www.ietf.org/archive/id/draft-polli-ratelimit-headers-02.html#name-header-specifications).

## authentication
an api instance may be configured to require you to authenticate yourself.
if this is the case, you will typically receive an [error response](#error-response)
with a **`api.auth.<method>.missing`** code, which tells you that a particular method
of authentication is required.

authentication is done by passing the `Authorization` header, containing
the authentication scheme and the token:
```
Authorization: <scheme> <token>
```

currently, cobalt supports two ways of authentication. an instance can
choose to configure both, or neither:
- [`Api-Key`](#api-key-authentication)
- [`Bearer`](#bearer-authentication)

### api-key authentication
the api key authentication is the most straightforward. the instance owner
will assign you an api key which you can then use to authenticate like so:
```
Authorization: Api-Key aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
```

if you are an instance owner and wish to configure api key authentication,
see the [instance](run-an-instance.md#api-key-file-format) documentation!

### bearer authentication
the cobalt server may be configured to issue JWT bearers, which are short-lived
tokens intended for use by regular users (e.g. after passing a challenge).
currently, cobalt can issue tokens for successfully solved [turnstile](run-an-instance.md#list-of-all-environment-variables)
challenge, if the instance has turnstile configured. the resulting token is passed like so:
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```

## POST `/`
cobalt's main processing endpoint.

> [!IMPORTANT]
> you must include correct `Accept` and `Content-Type` headers with every `POST /` request.

```
Accept: application/json
Content-Type: application/json
```

### request body
body type: `application/json`

not a fan of reading tables of text?
you can read [the api schema](/api/src/processing/schema.js) directly from code instead!

### api schema
all keys except for `url` are optional. value options are separated by `/`.

#### general
| key               | type      | description/value                                               | default    |
|:------------------|:----------|:----------------------------------------------------------------|:-----------|
| `url`             | `string`  | source URL                                                      | *required* |
| `audioBitrate`    | `string`  | `320 / 256 / 128 / 96 / 64 / 8` (kbps)                          | `128`      |
| `audioFormat`     | `string`  | `best / mp3 / ogg / wav / opus`                                 | `mp3`      |
| `downloadMode`    | `string`  | `auto / audio / mute`                                           | `auto`     |
| `filenameStyle`   | `string`  | `classic / pretty / basic / nerdy`                              | `basic`    |
| `videoQuality`    | `string`  | `max / 4320 / 2160 / 1440 / 1080 / 720 / 480 / 360 / 240 / 144` | `1080`     |
| `disableMetadata` | `boolean` | title, artist, and other info will not be added to the file     | `false`    |
| `alwaysProxy`     | `boolean` | always tunnel all files, even when not necessary                | `false`    |
| `localProcessing` | `string`  | `disabled / preferred / forced`                                 | `disabled` |
| `subtitleLang`    | `string`  | any valid ISO 639-1 language code                               | *none*     |

#### service-specific options
| key                     | type      | description/value                                 | default |
|:------------------------|:----------|:--------------------------------------------------|:--------|
| `youtubeVideoCodec`     | `string`  | `h264 / av1 / vp9`                                | `h264`  |
| `youtubeVideoContainer` | `string`  | `auto / mp4 / webm / mkv`                         | `auto`  |
| `youtubeDubLang`        | `string`  | any valid ISO 639-1 language code                 | *none*  |
| `convertGif`            | `boolean` | convert twitter gifs to the actual GIF format     | `true`  |
| `allowH265`             | `boolean` | allow H265/HEVC videos from tiktok                | `false` |
| `tiktokFullAudio`       | `boolean` | download the original sound used in a video       | `false` |
| `youtubeBetterAudio`    | `boolean` | prefer higher quality youtube audio if possible   | `false` |
| `youtubeHLS`            | `boolean` | use HLS formats when downloading from youtube     | `false` |

### response
body type: `application/json`

the response will always be a JSON object containing the `status` key, which is one of:
- `tunnel`: cobalt is proxying and/or remuxing/transcoding the file for you.
- `local-processing`: cobalt is proxying the files for you, but you have to remux/transcode them locally.
- `redirect`: cobalt will redirect you to the direct service URL.
- `picker`: there are multiple items to choose from, a picker should be shown.
- `error`: something went wrong, here's an error code.

### tunnel/redirect response
| key          | type     | value                                                      |
|:-------------|:---------|:-----------------------------------------------------------|
| `status`     | `string` | `tunnel / redirect`                                        |
| `url`        | `string` | url for the cobalt tunnel, or redirect to an external link |
| `filename`   | `string` | cobalt-generated filename for the file being downloaded    |

### local processing response
| key          | type       | value                                                         |
|:-------------|:-----------|:--------------------------------------------------------------|
| `status`     | `string`   | `local-processing`                                            |
| `type`       | `string`   | `merge`, `mute`, `audio`, `gif`, or `remux`                   |
| `service`    | `string`   | origin service (`youtube`, `twitter`, `instagram`, etc)       |
| `tunnel`     | `string[]` | array of tunnel URLs                                          |
| `output`     | `object`   | details about the output file ([see below](#output-object))   |
| `audio`      | `object`   | audio-specific details (optional, [see below](#audio-object)) |
| `isHLS`      | `boolean`  | whether the output is in HLS format (optional)                |

#### output object
| key         | type      | value                                                                             |
|:------------|:----------|:----------------------------------------------------------------------------------|
| `type`      | `string`  | mime type of the output file                                                      |
| `filename`  | `string`  | filename of the output file                                                       |
| `metadata`  | `object`  | metadata associated with the file (optional, [see below](#outputmetadata-object)) |
| `subtitles` | `boolean` | whether tunnels include a subtitle file                                           |

#### output.metadata object
all keys in this table are optional.

| key            | type     | description                                |
|:---------------|:---------|:-------------------------------------------|
| `album`        | `string` | album name or collection title             |
| `composer`     | `string` | composer of the track                      |
| `genre`        | `string` | track's genre(s)                           |
| `copyright`    | `string` | copyright information or ownership details |
| `title`        | `string` | title of the track or media file           |
| `artist`       | `string` | artist or creator name                     |
| `album_artist` | `string` | album's artist or creator name             |
| `track`        | `string` | track number or position in album          |
| `date`         | `string` | release date or creation date              |
| `sublanguage`  | `string` | subtitle language code (ISO 639-2)         |

#### audio object
| key         | type      | value                                                      |
|:------------|:----------|:-----------------------------------------------------------|
| `copy`      | `boolean` | defines whether audio codec data is copied                 |
| `format`    | `string`  | output audio format                                        |
| `bitrate`   | `string`  | preferred bitrate of audio format                          |
| `cover`     | `boolean` | whether tunnels include a cover art file (optional)        |
| `cropCover` | `boolean` | whether cover art should be cropped to a square (optional) |

### picker response
| key             | type     | value                                                                                          |
|:----------------|:---------|:-----------------------------------------------------------------------------------------------|
| `status`        | `string` | `picker`                                                                                       |
| `audio`         | `string` | returned when an image slideshow (such as on tiktok) has a general background audio (optional) |
| `audioFilename` | `string` | cobalt-generated filename, returned if `audio` exists (optional)                               |
| `picker`        | `array`  | array of objects containing the individual media                                               |

#### picker object
| key          | type      | value                     |
|:-------------|:----------|:--------------------------|
| `type`       | `string`  | `photo` / `video` / `gif` |
| `url`        | `string`  |                           |
| `thumb`      | `string`  | thumbnail url (optional)  |

### error response
| key          | type     | value                         |
|:-------------|:---------|:------------------------------|
| `status`     | `string` | `error`                       |
| `error`      | `object` | error code & optional context |

#### error object
| key          | type     | value                                                     |
|:-------------|:---------|:----------------------------------------------------------|
| `code`       | `string` | machine-readable error code explaining the failure reason |
| `context`    | `object` | additional error context (optional)                       |

#### error.context object
| key          | type     | value                                                                       |
|:-------------|:---------|:----------------------------------------------------------------------------|
| `service`    | `string` | origin service (optional)                                                   |
| `limit`      | `number` | the maximum downloadable video duration or the rate limit window (optional) |

## POST `/session`
used for generating JWT tokens, if enabled. currently, cobalt only supports
generating tokens when a [turnstile](run-an-instance.md#list-of-all-environment-variables) challenge solution
is submitted by the client.

the turnstile challenge response is submitted via the `cf-turnstile-response` header.

### response body
| key             | type       | description                                            |
|:----------------|:-----------|:-------------------------------------------------------|
| `token`         | `string`   | a `Bearer` token used for later request authentication |
| `exp`           | `number`   | number in seconds indicating the token lifetime        |

on failure, an [error response](#error-response) is returned.

## GET `/`
provides basic instance info.

### response
body type: `application/json`

| key         | type     | description                                              |
|:------------|:---------|:---------------------------------------------------------|
| `cobalt`    | `object` | information about the cobalt instance                    |
| `git`       | `object` | information about the codebase that is currently running |

#### cobalt object
| key                | type       | description                                    |
|:-------------------|:-----------|:-----------------------------------------------|
| `version`          | `string`   | cobalt version                                 |
| `url`              | `string`   | instance url                                   |
| `startTime`        | `string`   | instance start time in unix milliseconds       |
| `turnstileSitekey` | `string`   | site key for a turnstile widget (optional)     |
| `services`         | `string[]` | array of services which this instance supports |

#### git object
| key         | type     | description |
|:------------|:---------|:------------|
| `commit`    | `string` | commit hash |
| `branch`    | `string` | git branch  |
| `remote`    | `string` | git remote  |

## GET `/tunnel`
endpoint for file tunnels (proxy/remux/transcode). the response is a file stream. all errors are reported via
[HTTP status codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status).

### returned headers
- `Content-Length`: file size, in bytes. returned when exact final file size is known.
- `Estimated-Content-Length`: estimated file size, in bytes. returned when real `Content-Length` is not known.
a rough estimate which should NOT be used for strict size verification.
can be used to show approximate download progress in UI.

### possible HTTP status codes
- 200: OK
- 401: Unauthorized
- 403: Bad Request
- 404: Not Found
- 429: Too Many Requests (rate limit exceeded, check [RateLimit-* headers](https://www.ietf.org/archive/id/draft-polli-ratelimit-headers-02.html#name-header-specifications))
- 500: Internal Server Error.

---

## File: docs/api-env-variables.md

# cobalt api instance environment variables
you can customize your processing instance's behavior using these environment variables. all of them but `API_URL` are optional.
this document is not final and will expand over time. feel free to improve it!

### general vars
| name                   | default | value example                         |
|:-----------------------|:--------|:--------------------------------------|
| API_URL                |         | `https://api.url.example/`            |
| API_PORT               | `9000`  | `1337`                                |
| COOKIE_PATH            |         | `/cookies.json`                       |
| PROCESSING_PRIORITY    |         | `10`                                  |
| API_INSTANCE_COUNT     |         | `6`                                   |
| API_REDIS_URL          |         | `redis://localhost:6379`              |
| DISABLED_SERVICES      |         | `bilibili,youtube`                    |
| FORCE_LOCAL_PROCESSING | `never` | `always`                              |
| API_ENV_FILE           |         | `/.env`                               |

[*view details*](#general)

### networking vars
| name                | default   | value example                         |
|:--------------------|:----------|:--------------------------------------|
| API_LISTEN_ADDRESS  | `0.0.0.0` | `127.0.0.1`                           |
| FREEBIND_CIDR       |           | `2001:db8::/32`                       |

#### undici proxy vars
| name        | value example                         |
|:------------|:--------------------------------------|
| HTTP_PROXY  | `http://user:password@10.0.0.1:1337/` |
| HTTPS_PROXY | `https://10.0.0.2:1337/`              |
| NO_PROXY    | `localhost`                           |

[*view details*](#networking)

### limit vars
| name                     | default | value example |
|:-------------------------|:--------|:--------------|
| DURATION_LIMIT           | `10800` | `18000`       |
| TUNNEL_LIFESPAN          | `90`    | `120`         |
| RATELIMIT_WINDOW         | `60`    | `120`         |
| RATELIMIT_MAX            | `20`    | `30`          |
| SESSION_RATELIMIT_WINDOW | `60`    | `60`          |
| SESSION_RATELIMIT_MAX    | `10`    | `10`          |
| TUNNEL_RATELIMIT_WINDOW  | `60`    | `60`          |
| TUNNEL_RATELIMIT_MAX     | `40`    | `10`          |

[*view details*](#limits)

### security vars
| name              | default | value example                         |
|:------------------|:--------|:--------------------------------------|
| CORS_WILDCARD     | `1`     | `0`                                   |
| CORS_URL          |         | `https://web.url.example`             |
| TURNSTILE_SITEKEY |         | `1x00000000000000000000BB`            |
| TURNSTILE_SECRET  |         | `1x0000000000000000000000000000000AA` |
| JWT_SECRET        |         | see [details](#security)              |
| JWT_EXPIRY        | `120`   | `240`                                 |
| API_KEY_URL       |         | `file://keys.json`                    |
| API_AUTH_REQUIRED |         | `1`                                   |

[*view details*](#security)

### service-specific vars
| name                             | value example            |
|:---------------------------------|:-------------------------|
| CUSTOM_INNERTUBE_CLIENT          | `IOS`                    |
| YOUTUBE_SESSION_SERVER           | `http://localhost:8080/` |
| YOUTUBE_SESSION_INNERTUBE_CLIENT | `WEB_EMBEDDED`           |
| YOUTUBE_ALLOW_BETTER_AUDIO       | `1`                      |
| ENABLE_DEPRECATED_YOUTUBE_HLS    | `key`                    |
| YOUTUBE_PLAYER_ID                | `abcdefff`               |

[*view details*](#service-specific)

## general
[*jump to the table*](#general-vars)

### API_URL
> [!NOTE]
> API_URL is required to run the API instance.

the URL from which your instance will be accessible. can be external or internal, but it must be a valid URL or else tunnels will not work.

the value is a URL.

### API_PORT
port from which the API server will be accessible.

the value is a number from 1024 to 65535.

### COOKIE_PATH
path to the `cookies.json` file relative to the current working directory of your cobalt instance (usually the main (src/api) folder).

### PROCESSING_PRIORITY
`nice` value for ffmpeg subprocesses. available only on unix systems.

note: the higher the nice value, the lower the priority. you can [read more about nice here](https://en.wikipedia.org/wiki/Nice_(Unix)).

the value is a number.

### API_INSTANCE_COUNT
supported only on linux and node.js `>=23.1.0`. when configured, cobalt will spawn multiple sub-instances amongst which requests will be balanced. `API_REDIS_URL` is required to use this option.

the value is a number.

### API_REDIS_URL
when configured, cobalt will use this redis instance for tunnel cache. required when `API_INSTANCE_COUNT` is more than 1, because else sub-instance wouldn't be able to share cache.

the value is a URL.

### DISABLED_SERVICES
comma-separated list which disables certain services from being used.

the value is a string of cobalt-supported services.

### FORCE_LOCAL_PROCESSING
the value is a string: `never` (default), `session`, or `always`:
- when the var is not defined or set to `never`, all requests will be able to set a preference via `localProcessing` in POST requests.
- when set to `session`, only requests from session (Bearer token) clients will be forced to use on-device processing.
- when set to `always`, all requests will be forced to use on-device processing, no matter the preference.

### API_ENV_FILE
the URL or local path to a `key=value`-style environment variable file. this is used for dynamically reloading environment variables. **not all environment variables are able to be updated by this.** (e.g. the ratelimiters are instantiated when starting cobalt, and cannot be changed)

## networking
[*jump to the table*](#networking-vars)

### API_LISTEN_ADDRESS
defines the local address for the api instance. if you are using a docker container, you usually don't need to configure this.

the value is a local IP address.

### HTTP_PROXY, HTTPS_PROXY, NO_PROXY
URL of the proxy that will be passed to [`EnvHttpProxyAgent`](https://undici.nodejs.org/#/docs/api/EnvHttpProxyAgent) for proxying external requests. if some cobalt functionality breaks when using a proxy, please [make a new issue](https://github.com/imputnet/cobalt/issues) about it!

quoted from [undici docs](https://undici.nodejs.org/#/docs/api/EnvHttpProxyAgent):
> When `HTTP_PROXY` and `HTTPS_PROXY` are set, `HTTP_PROXY` is used for HTTP requests and `HTTPS_PROXY` is used for HTTPS requests. If only `HTTP_PROXY` is set, `HTTP_PROXY` is used for both HTTP and HTTPS requests. If only `HTTPS_PROXY` is set, it is only used for HTTPS requests.

> `NO_PROXY` is a comma or space-separated list of hostnames that should not be proxied. The list may contain leading wildcard characters (`*`). If `NO_PROXY` is set, the EnvHttpProxyAgent() will bypass the proxy for requests to hosts that match the list. If `NO_PROXY` is set to `"*"`, the EnvHttpProxyAgent() will bypass the proxy for all requests.

the value is a string:
- `HTTP_PROXY`/`HTTPS_PROXY`: URL or hostname.
- `NO_PROXY`: comma or space-separated list of hostnames.

### API_EXTERNAL_PROXY (deprecated)
> [!WARNING]
> this env variable is deprecated and will be removed in a future release. please update your configuration to use `HTTP_PROXY` or `HTTPS_PROXY`, as mentioned above.

URL of the proxy that will be passed to [`EnvHttpProxyAgent`](https://undici.nodejs.org/#/docs/api/EnvHttpProxyAgent) and used for proxying external requests. HTTP(S) only.

the value is a URL.

### FREEBIND_CIDR
IPv6 prefix used for randomly assigning addresses to cobalt requests. available only on linux systems.

setting a `FREEBIND_CIDR` allows cobalt to pick a random IP for every download and use it for all requests it makes for that particular download.

to use freebind in cobalt, you need to follow its [setup instructions](https://github.com/imputnet/freebind.js?tab=readme-ov-file#setup) first.

if you want to use this option and run cobalt in a docker container, you also need to set the `API_LISTEN_ADDRESS` env variable to `127.0.0.1` and set `network_mode` for the container to `host`.

the value is an IPv6 range.

## limits
[*jump to the table*](#limit-vars)

### DURATION_LIMIT
media duration limit, in **seconds**

the value is a number.

### TUNNEL_LIFESPAN
the duration for which tunnel info is stored in ram, **in seconds**.

it's recommended to keep this value either default or as low as possible to preserve efficiency and user privacy.

the value is a number.

### RATELIMIT_WINDOW
rate limit time window for api requests, but not session requests, in **seconds**.

the value is a number.

### RATELIMIT_MAX
amount of api requests to be allowed within the time window of `RATELIMIT_WINDOW`.

the value is a number.

### SESSION_RATELIMIT_WINDOW
rate limit time window for session creation requests, in **seconds**.

the value is a number.

### SESSION_RATELIMIT_MAX
amount of session requests to be allowed within the time window of `SESSION_RATELIMIT_WINDOW`.

the value is a number.

### TUNNEL_RATELIMIT_WINDOW
rate limit time window for tunnel (proxy/stream) requests, in **seconds**.

the value is a number.

### TUNNEL_RATELIMIT_MAX
amount of tunnel requests to be allowed within the time window of `TUNNEL_RATELIMIT_WINDOW`.

the value is a number.

## security
[*jump to the table*](#security-vars)

> [!NOTE]
> in order to enable turnstile bot protection, `TURNSTILE_SITEKEY`, `TURNSTILE_SECRET`, and `JWT_SECRET` must be set. all three at once.

### CORS_WILDCARD
defines whether cross-origin resource sharing is enabled. when enabled, your instance will be accessible from foreign web pages.

the value is a number, either `0` or `1`.

### CORS_URL
configures the [cross-origin resource sharing origin](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Allow-Origin). your instance will be available only from this URL if `CORS_WILDCARD` is set to `0`.

the value is a URL.

### TURNSTILE_SITEKEY
[cloudflare turnstile](https://www.cloudflare.com/products/turnstile/) sitekey used by the web client to request & solve a challenge to prove that the user is not a bot.

the value is a specific key.

### TURNSTILE_SECRET
[cloudflare turnstile](https://www.cloudflare.com/products/turnstile/) secret used by the processing instance to verify that the client solved the challenge successfully.

the value is a specific key.

### JWT_SECRET
the secret used for issuing JWT tokens for request authentication. the value must be a random, secure, and long string (over 16 characters).

the value is a specific key.

### JWT_EXPIRY
the duration of how long a cobalt-issued JWT token will remain valid, in seconds.

the value is a number.

### API_KEY_URL
the URL to the the external or local key database. for local files you have to specify a local path using the `file://` protocol.

see [the api key section](/docs/protect-an-instance.md#api-key-file-format) in the "how to protect your cobalt instance" document for more details.

the value is a URL.

### API_AUTH_REQUIRED
when set to `1`, the user always needs to be authenticated in some way before they can access the API (either via an api key or via turnstile, if enabled).

the value is a number, either `0` or `1`.

## service-specific
[*jump to the table*](#service-specific-vars)

### CUSTOM_INNERTUBE_CLIENT
innertube client that will be used instead of the default one.

the value is a string.

### YOUTUBE_SESSION_SERVER
URL to an instance of [yt-session-generator](https://github.com/imputnet/yt-session-generator). used for automatically pulling `poToken` & `visitor_data` for youtube. can be local or remote.

the value is a URL.

### YOUTUBE_SESSION_INNERTUBE_CLIENT
innertube client that's compatible with botguard's (web) `poToken` and `visitor_data`.

the value is a string.

### YOUTUBE_PLAYER_ID
a comma-separated-list of player IDs to use for youtube fetching.
if defined, cobalt chooses one of them at each client initialization, otherwise
defaults to the current latest player ID.

the value is a string.

### YOUTUBE_ALLOW_BETTER_AUDIO
when set to `1`, cobalt will try to use higher quality audio if user requests it via `youtubeBetterAudio`. will negatively impact the rate limit of a secondary youtube client with a session.

the value is a number, either `0` or `1`.

### ENABLE_DEPRECATED_YOUTUBE_HLS
the value is a string: `never` (default), `key`, or `always`:
- when the var is not defined or set to `never`, `youtubeHLS` in POST requests will be ignored.
- when set to `key`, only requests from api-key clients will be able to use `youtubeHLS` in POST requests.
- when set to `always`, all requests will be able to use `youtubeHLS` in POST requests.

---

## File: docs/protect-an-instance.md

# how to protect your cobalt instance
if you keep getting a ton of unknown traffic that hurts the performance of your instance, then it might be a good idea to enable bot protection.

> [!NOTE]
> this tutorial will work reliably on the latest official version of cobalt 10.
we can't promise full compatibility with anything else.

## configure cloudflare turnstile
turnstile is a free, safe, and privacy-respecting alternative to captcha.
cobalt uses it automatically to weed out bots and automated scripts.
your instance doesn't have to be proxied by cloudflare to use turnstile.
all you need is a free cloudflare account to get started.

cloudflare dashboard interface might change over time, but basics should stay the same.

> [!WARNING]
> never share the turnstile secret key, always keep it private. if accidentally exposed, rotate it in widget settings.

1. open [the cloudflare dashboard](https://dash.cloudflare.com/) and log into your account

2. once logged in, select `Turnstile` in the sidebar

    
        
    


3. press `Add widget`

    
        
    


4. enter the widget name (can be anything, such as "cobalt")

    
        
    


5. add cobalt frontend domains you want the widget to work with, you can change this list later at any time
    - if you want to use your processing instance with [cobalt.tools](https://cobalt.tools/) frontend, then add `cobalt.tools` to the list

    
        
    


6. select `invisible` widget mode

    
        
    


7. press `create`

8. keep the page with sitekey and secret key open, you'll need them later.
if you closed it, no worries!
just open the same turnstile page and press "settings" on your freshly made turnstile widget.


    
        
    


you've successfully created a turnstile widget!
time to add it to your processing instance.

### enable turnstile on your processing instance
this tutorial assumes that you only have `API_URL` in your `environment` variables list.
if you have other variables there, just add new ones after existing ones.

> [!CAUTION]
> never use any values from the tutorial, especially `JWT_SECRET`!

1. open your `docker-compose.yml` config file in any text editor of choice.
2. copy the turnstile sitekey & secret key and paste them to their respective variables.
`TURNSTILE_SITEKEY` for the sitekey and `TURNSTILE_SECRET` for the secret key:
```yml
environment:
    API_URL: "https://your.instance.url.here.local/"
    TURNSTILE_SITEKEY: "2x00000000000000000000BB" # use your key
    TURNSTILE_SECRET: "2x0000000000000000000000000000000AA" # use your key
```
3. generate a `JWT_SECRET`. we recommend using an alphanumeric collection with a length of at least 64 characters.
this string will be used as salt for all JWT keys.

    you can generate a random secret with `pnpm -r token:jwt` or use any other that you like.

```yml
environment:
    API_URL: "https://your.instance.url.here.local/"
    TURNSTILE_SITEKEY: "2x00000000000000000000BB" # use your key
    TURNSTILE_SECRET: "2x0000000000000000000000000000000AA" # use your key
    JWT_SECRET: "bgBmF4efNCKPirD" # create a new secret, NEVER use this one
```
4. restart the docker container.

## configure api keys
if you want to use your instance outside of web interface, you'll need an api key!

> [!NOTE]
> this tutorial assumes that you'll keep your keys file locally, on the instance server.
> if you wish to upload your file to a remote location,
> replace the value for `API_KEYS_URL` with a direct url to the file
> and skip the second step.

> [!WARNING]
> when storing keys file remotely, make sure that it's not publicly accessible
> and that link to it is either authenticated (via query) or impossible to guess.
>
> if api keys leak, you'll have to update/remove all UUIDs to revoke them.

1. create a `keys.json` file following [the schema and example down below](#api-key-file-format).

2. expose the `keys.json` to the docker container:
```yml
volumes:
    - ./keys.json:/keys.json:ro # ro - read-only
```

3. add a path to the keys file to container environment:
```yml
environment:
    # ... other variables here ...
    API_KEY_URL: "file:///keys.json"
```

4. restart the docker container.

## limit access to an instance with api keys but no turnstile
by default, api keys are additional, meaning that they're not *required*,
but work alongside with turnstile or no auth (regular ip hash rate limiting).

to always require auth (via keys or turnstile, if configured), set `API_AUTH_REQUIRED` to 1:
```yml
environment:
    # ... other variables here ...
    API_AUTH_REQUIRED: 1
```

- if both keys and turnstile are enabled, then nothing will change.
- if only keys are configured, then all requests without a valid api key will be refused.

### why not make keys exclusive by default?
keys may be useful for going around rate limiting,
while keeping the rest of api rate limited, with no turnstile in place.

## api key file format
the file is a JSON-serialized object with the following structure:
```typescript

type KeyFileContents = Record<
    UUIDv4String,
    {
        name?: string,
        limit?: number | "unlimited",
        ips?: (CIDRString | IPString)[],
        userAgents?: string[],
        allowedServices?: "all" | string[],
    }
>;
```

where *`UUIDv4String`* is a stringified version of a UUIDv4 identifier.
- **name** is a field for your own reference, it is not used by cobalt anywhere.

- **`limit`** specifies how many requests the API key can make during the window specified in the `RATELIMIT_WINDOW` env.
    - when omitted, the limit specified in `RATELIMIT_MAX` will be used.
    - it can be also set to `"unlimited"`, in which case the API key bypasses all rate limits.

- **`ips`** contains an array of allowlisted IP ranges, which can be specified both as individual ips or CIDR ranges (e.g. *`["192.168.42.69", "2001:db8::48", "10.0.0.0/8", "fe80::/10"]`*).
    - when specified, only requests from these ip ranges can use the specified api key.
    - when omitted, any IP can be used to make requests with that API key.

- **`userAgents`** contains an array of allowed user agents, with support for wildcards (e.g. *`["cobaltbot/1.0", "Mozilla/5.0 * Chrome/*"]`*).
    - when specified, requests with a `user-agent` that does not appear in this array will be rejected.
    - when omitted, any user agent can be specified to make requests with that API key.

- **`allowedServices`** is an array of allowed services or `"all"`.
    - when `"all"` is specified, the key will be able to access all supported services, even if they're globally disabled via `DISABLED_SERVICES`.
    - when an array of services is specified, the key will be able to access only the services included in the array.
    - when omitted, the key will use the global list of supported services.

- if both `ips` and `userAgents` are set, the tokens will be limited by both parameters.
- if cobalt detects any problem with your key file, it will be ignored and a warning will be printed to the console.

an example key file could look like this:
```json
{
    "b5c7160a-b655-4c7a-b500-de839f094550": {
        "limit": 10,
        "ips": ["10.0.0.0/8", "192.168.42.42"],
        "userAgents": ["*Chrome*"]
    },
    "b00b1234-a3e5-99b1-c6d1-dba4512ae190": {
        "limit": "unlimited",
        "ips": ["192.168.1.2"],
        "userAgents": ["cobaltbot/1.0"]
    }
}
```

if you are configuring a key file, **do not use the UUID from the example** but instead generate your own. you can do this by running the following command if you have node.js installed:
`node -e "console.log(crypto.randomUUID())"`

---

## File: docs/run-an-instance.md

# how to run a cobalt instance
this tutorial will help you run your own cobalt processing instance. if your instance is public-facing, we highly recommend that you also [protect it from abuse](/docs/protect-an-instance.md) using turnstile or api keys or both.

## using docker compose and package from github (recommended)
to run the cobalt docker package, you need to have `docker` and `docker-compose` installed and configured.

if you need help with installing docker, you can find more information here:
- [how to install docker](https://docs.docker.com/engine/install/)
- [how to install docker compose](https://docs.docker.com/compose/install/)

## how to run a cobalt docker package:
1. create a folder for cobalt config file, something like this:
    ```sh
    mkdir cobalt
    ```

2. go to cobalt folder, and create a docker compose config file:
    ```sh
    cd cobalt && nano docker-compose.yml
    ```
    i'm using `nano` in this example, it may not be available in your distro. you can use any other text editor.

3. copy and paste the [sample config from here](examples/docker-compose.example.yml) and edit it to your needs.
    make sure to replace default URLs with your own or cobalt won't work correctly.

4. finally, start the cobalt container (from cobalt directory):
    ```sh
    docker compose up -d
    ```

if you want your instance to support services that require authentication to view public content, create `cookies.json` file in the same directory as `docker-compose.yml`. example cookies file [can be found here](examples/cookies.example.json).

cobalt package will update automatically thanks to watchtower.

it's highly recommended to use a reverse proxy (such as nginx) if you want your instance to face the public internet. look up tutorials online.

## run cobalt api outside of docker (useful for local development)
requirements:
- node.js >= 18
- git
- pnpm

1. clone the repo: `git clone https://github.com/imputnet/cobalt`.
2. go to api directory: `cd cobalt/api`.
3. install dependencies: `pnpm install`.
4. create `.env` file in the same directory.
5. add needed environment variables to `.env` file. only `API_URL` is required to run cobalt.
    - if you don't know what api url to use for local development, use `http://localhost:9000/`.
6. run cobalt: `pnpm start`.

### ubuntu 22.04 workaround
`nscd` needs to be installed and running so that the `ffmpeg-static` binary can resolve DNS ([#101](https://github.com/imputnet/cobalt/issues/101#issuecomment-1494822258)):

```bash
sudo apt install nscd
sudo service nscd start
```

## list of environment variables
[this section has moved](/docs/api-env-variables.md) to a dedicated document that is way easier to understand and maintain. go check it out!

---

## File: web/README.md

# cobalt web
the cobalt frontend is a static web app built with
[sveltekit](https://kit.svelte.dev/) + [vite](https://vitejs.dev/).

## configuring
- to run the dev environment, run `pnpm run dev`.
- to make the release build of the frontend, run `pnpm run build`.

## environment variables
the frontend has several build-time environment variables for configuring various features. to use
them, you must specify them when building the frontend (or running a vite server for development).

`WEB_DEFAULT_API` is **required** to run cobalt frontend.

| name                            | example                     | description                                                                                             |
|:--------------------------------|:----------------------------|:--------------------------------------------------------------------------------------------------------|
| `WEB_HOST`                      | `cobalt.tools`              | domain on which the frontend will be running. used for meta tags and configuring plausible.             |
| `WEB_PLAUSIBLE_HOST`            | `plausible.io`*             | enables plausible analytics with provided hostname as receiver backend.                                 |
| `WEB_DEFAULT_API`               | `https://api.cobalt.tools/` | changes url which is used for api requests by frontend clients.                                         |
| `ENABLE_DEPRECATED_YOUTUBE_HLS` | `true`                      | enables the youtube HLS settings entry; allows sending the related variable to the processing instance. |

\* don't use plausible.io as receiver backend unless you paid for their cloud service.
   use your own domain when hosting community edition of plausible. refer to their [docs](https://plausible.io/docs) when needed.

## link prefill
to prefill the link into the input box & start the download automatically, you can pass the URL in the `#` parameter, like this:
```
https://cobalt.tools/#https://www.youtube.com/watch?v=dQw4w9WgXcQ
```

the link can also be URI-encoded, like this:
```
https://cobalt.tools/#https%3A//www.youtube.com/watch%3Fv=dQw4w9WgXcQ
```

## license
cobalt web code is licensed under [CC-BY-NC-SA-4.0](LICENSE).

this license allows you to:
- copy and redistribute the code in any medium or format, and
- remix, transform, use and build upon the code

as long as you:
- give appropriate credit to the original repo,
- provide a link to the license and indicate if changes to the code were made,
- release the code under the **same license**, and
- **don't use the code for any commercial purposes**.

cobalt branding, mascots, and other related assets included in the repo are ***copyrighted*** and not covered by the license. you ***cannot*** use them under same terms.

you are allowed to host an ***unmodified*** instance of cobalt with branding for **non-commercial purposes**, but this ***does not*** give you permission to use the branding anywhere else, or make derivatives of it in any way.

when making an alternative version of the project, please replace or remove all branding (including the name).

## open source acknowledgments
### svelte + sveltekit
the cobalt frontend is built using [svelte](https://svelte.dev) and [sveltekit](https://svelte.dev/docs/kit/introduction), a really efficient and badass framework, we love it a lot.

### libav.js
our remux and encode workers rely on [libav.js](https://github.com/imputnet/libav.js), which is an optimized build of ffmpeg for the browser. the ffmpeg builds are made up of many components, whose licenses can be found here: [encode](https://github.com/imputnet/libav.js/blob/main/configs/configs/encode/license.js), [remux](https://github.com/imputnet/libav.js/blob/main/configs/configs/remux/license.js).

you can [support ffmpeg here](https://ffmpeg.org/donations.html)!

### fonts, icons and assets
the cobalt frontend uses several different fonts and icon sets.
- [Tabler Icons](https://tabler.io/icons), released under the [MIT](https://github.com/tabler/tabler-icons?tab=MIT-1-ov-file) license.
- [Fluent Emoji by Microsoft](https://github.com/microsoft/fluentui-emoji), released under the [MIT](https://github.com/microsoft/fluentui-emoji/blob/main/LICENSE) license.
- [Noto Sans Mono](https://fonts.google.com/noto/specimen/Noto+Sans+Mono/) used for the download button, is licensed under the [OFL](https://fonts.google.com/noto/specimen/Noto+Sans+Mono/about) license.
- [IBM Plex Mono](https://fonts.google.com/specimen/IBM+Plex+Mono/) used for all other text, is licensed under the [OFL](https://fonts.google.com/specimen/IBM+Plex+Mono/license) license.
- and the [Redaction](https://redaction.us/) font, which is licensed under the [OFL](https://github.com/fontsource/font-files/blob/main/fonts/other/redaction-10/LICENSE) license (as well as LGPL-2.1).
- many update banners were taken from [tenor.com](https://tenor.com/).

### other packages
- [mdsvex](https://github.com/pngwn/MDsveX) to convert the changelogs into svelte components.
- [compare-versions](https://github.com/omichelsen/compare-versions) for sorting the changelogs.
- [svelte-sitemap](https://github.com/bartholomej/svelte-sitemap) for generating a sitemap for the frontend.
- [sveltekit-i18n](https://github.com/sveltekit-i18n/lib) for displaying cobalt in many different languages.
- [vite](https://github.com/vitejs/vite) for building the frontend.

...and many other packages that these packages rely on.