cobalt self-hosted: the Svelte monorepo that proxies every download
GitHub describes it as best way to save what you love. The repository metadata lists Svelte as its primary language. The metadata lists the AGPL-3.0 license. This article stays within the project description and details documented in the GitHub repository README.
At a glance
- What is it?
- cobalt is a Svelte monorepo whose API streams public media through to the caller without storing anything. The repository ships no releases, its Dockerfile builds the API alone on port 9000, and the frontend is a separate tree you deploy yourself.
- Who is it for?
- cobalt fits self-hosters who want a proxy endpoint for public media and can live inside that boundary, and it does not fit anyone chasing paywalled or DRM protected files. Before deploying, read docs/api-env-variables.md and docs/protect-an-instance.md, because the root README names neither the variables nor the access control, and there is no release tag to roll back to.
- Can I use it commercially?
- Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 177 days ago.
- What is it written in?
- Mainly Svelte, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
cobalt streams each download through your instance instead of storing it
cobalt does one job: you paste the link to a public media page and it hands you the file. The repository description is "best way to save what you love", and the API keeps no copy of what it passes through. The project states that cobalt never caches content and works like a proxy, pointing at /api/src/stream/ as the code that does it. Every download therefore crosses your instance on the way in and again on the way out, so where the instance sits and what it pays for egress decides how the whole thing feels.
That split is the first decision a reader has to make. Use the public site at cobalt.tools and somebody else carries the bandwidth. Run your own and you carry the uptime, the bandwidth bill, and every complaint about what your users requested through it.
cobalt stops where a browser dev tools tab already stops
The project draws its line at content anyone could already save. It says cobalt is in no way a piracy tool, that it can only download free & publicly accessible content, and that the same content can be downloaded via dev tools of any modern web browser. That last sentence is also the honest alternative, and it is worth reading twice: your browser network tab takes the same approach with fewer moving parts and costs nothing to run.
What you give up is everything that is not free and publicly accessible, and no file in the repository describes a flag that widens that boundary. The project takes zero liability and puts the responsibility on the end user, which is a statement about law, not about who may call your API.
The root manifest pins pnpm and installs nothing
The root package.json is a toolchain declaration with no dependencies at all:
{
"name": "cobalt",
"packageManager": "[email protected]",
"engines": {
"pnpm": ">=9"
}
}Nothing gets installed from the repository root, because this is a pnpm workspace. pnpm-workspace.yaml and pnpm-lock.yaml sit beside the manifest, and the real code lives in api/, web/ and packages/. The primary language is Svelte, which is the web tree. The API is a separate package that the Dockerfile names as @imput/cobalt-api, so the two halves of the product do not build together in any way the root file shows. The engines field is the one that bites: pnpm 9 or newer, matching the [email protected] pin that corepack activates during the image build. Contributors are sent to CONTRIBUTING.md first.
The Dockerfile deploys the API alone and exposes a single port
There is no install command in the root README. For self hosting, the project points at docs/run-an-instance.md, and the Dockerfile is the only build recipe in the repository. It starts from a pinned Node image and puts pnpm on the PATH:
FROM node:24-alpine AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
FROM base AS build
WORKDIR /app
COPY . /app
RUN corepack enable
RUN apk add --no-cache python3 alpine-sdkcorepack enable is what makes the [email protected] pin take effect. python3 and alpine-sdk are the native build dependencies. The dependency install is production only and uses a cache mount, then only the API package is deployed into a separate directory:
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --prod --frozen-lockfile
RUN pnpm deploy --filter=@imput/cobalt-api --prod /prod/apiThe filter is the important word. The Svelte frontend in web/ never enters /prod/api, so a self hosted setup means running the API and pointing your own client at it. The last stage copies the deployed output, copies .git alongside it, drops to the unprivileged node user, and opens one port:
FROM base AS api
WORKDIR /app
COPY --from=build --chown=node:node /prod/api /app
COPY --from=build --chown=node:node /app/.git /app
USER node
EXPOSE 9000
CMD [ "node", "src/cobalt" ]The entry point is node src/cobalt, so there is no separate CLI binary and no command line flags in this file. Configuration comes from docs/api-env-variables.md, not from the Dockerfile.
No releases to pin, and the last push was on 2026-04-06
Upgrades have no version to aim at. The repository has no GitHub releases, so there is no tag, no published binary, and no release note to diff against. You build whatever main holds at that moment. The last push to the default branch was on 2026-04-06, and that date is the only maintenance signal the repository gives. No support window is stated anywhere in the files.
The frozen lockfile cuts both ways. --frozen-lockfile means a lockfile that has drifted from the manifests fails the build instead of quietly resolving new versions, which protects you from surprise transitive updates on a service other people call. It also means the only path forward is to pull and rebuild, and any change in the API package that breaks a live instance becomes your upgrade to schedule. If you need a way back, pin your own image digest, because this project gives you no tags to roll back to.
Licence terms are split between the api and web READMEs
Licensing does not live in one file. The root README says that for relevant licensing information you should see the api and web READMEs, and that unless specified otherwise the remainder of the repository is licensed under AGPL-3.0. The phrase unless otherwise is carrying weight: at least one subtree ships terms you have not read yet, and the root file does not say which one or how they differ.
If you plan to modify the API and serve it to other people over a network, read api/README.md before you deploy rather than after the first complaint. That is a compliance step, not a legal opinion. Infrastructure has its own dependency as well: the project credits royalehosting.net as its sponsor and says a part of its infrastructure is hosted on their network, which is one more party to think about before you build an instance of your own on the same pattern.
Everything you need to run cobalt sits in four files the README only links
Deployment knowledge is spread across docs/run-an-instance.md, docs/protect-an-instance.md, docs/api-env-variables.md and docs/api.md. The root README links all four and opens none of them. It names no environment variable, no request format, no authentication header, and no port other than the 9000 the Dockerfile exposes. A reader planning an instance starts with a download page at cobalt.tools and a repository whose real entry point is four markdown files.
Two consequences follow. An instance with no protection in front of it is a live exposure, which is what docs/protect-an-instance.md exists for, and the zero liability sentence covers responsibility for what gets downloaded rather than who may call the API. Request syntax lives only in docs/api.md, so writing a client against cobalt means reading that document rather than guessing at the JSON shape from the frontend.
Editorial conclusion
cobalt fits self-hosters who want a proxy endpoint for public media and can live inside that boundary, and it does not fit anyone chasing paywalled or DRM protected files. Before deploying, read docs/api-env-variables.md and docs/protect-an-instance.md, because the root README names neither the variables nor the access control, and there is no release tag to roll back to.
Frequently asked questions
how to use cobalt tools
You paste a link to a public media page into the interface at cobalt.tools and it returns the file, which the project summarises as paste the link, get the file, move on. If you run your own instance the same behaviour sits behind the API described in docs/api.md, and the Dockerfile serves it on port 9000.
how to use cobalt video downloader
cobalt never caches content. It works like a proxy, with the streaming code in /api/src/stream/, so the file passes through the instance instead of being stored on it. It can only download free and publicly accessible content, the same content a browser dev tools tab can reach.
how to install cobalt
The README points to docs/run-an-instance.md for running your own instance, and the Dockerfile is the only build recipe in the repository. It builds the @imput/cobalt-api package with pnpm and starts it as node src/cobalt on port 9000. There are no GitHub releases, so there is no tagged installer to download.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/imputnet-cobalt)