Docs
Private registries
The CLI has always installed from any registry: --registry takes a URL or a path on disk. @dowel-ui/registry builds one, so an organisation can publish its own components and have them installed exactly the way these are — with the same dependency resolution, the same content hashes, and the same update that never overwrites an edited file.
This is the producer half of what the CLI has always consumed. Nothing here needs a licence.
Build one
Declare your items against the files that implement them. The build reads the real imports and refuses anything that would fail in a consumer’s project.
import { buildCustomRegistry, defineRegistryConfig } from "@dowel-ui/registry"; const result = await buildCustomRegistry( defineRegistryConfig({ root: "src", generatedFrom: "@acme/ui@1.0.0", // One URL that serves both your components and everything upstream. extends: "https://dowel-eight.vercel.app/r", items: [ { name: "acme-callout", title: "Acme Callout", description: "Acme's house callout, built on the upstream Badge.", category: "display", registryDependencies: ["badge"], files: ["acme-callout.tsx"], }, ], }),);extends is the part that makes it one registry rather than two. Point a project at the result and add acme-callout installs your component, pulling badge from upstream on the way. Your developers learn one URL.
A local item replaces an upstream one of the same name, and the build tells you which. Overriding upstream’s Button is a legitimate thing to want and a catastrophic thing to do by accident, and the difference is whether anyone was told.
What it refuses
- A file it cannot read.
- An import written against the installed path (
@/components/ui/badge) rather than the authored one (@/components/badge). The leading group is rewritten to wherever the project keeps its components, so naming it twice produces a path that resolves nowhere. - A component that imports something it never declared, which would not be installed alongside it.
Host it
The output is a directory of JSON: an index and one file per item. Any static host serves it — an S3 bucket, a Pages site, a folder behind your VPN. Serve /r/*.json with Access-Control-Allow-Origin: * if a browser will ever read it, and cache it as hard as you like: a registry is immutable per release.
Then set it once per project, so nobody has to remember the flag:
{ "registry": "https://ui.acme.internal/r"}Require a key
Put the directory behind anything that checks a bearer token — a reverse proxy, an edge function, your VPN’s gateway — and answer a missing or wrong key with 401. The CLI asks without a key first, so a public registry never receives one, and answers a 401 once, with the key stored for that registry and no other.
Each developer signs in once: npx @dowel-ui/cli login --registry https://registry.acme.example/r. The key is checked against your index before it is stored, and is kept per registry, so a Pro licence and your company’s key live side by side; logout with --registry removes one, without it every one. In CI, set DOWEL_TOKEN and DOWEL_TOKEN_REGISTRY to the registry it is for. The MCP server reads the same two variables.
A key goes only over HTTPS (or to localhost), and only to the registry it was issued for: a repository whose components.json names some other server gets a refusal, not your key.
Owners, versions and retirement
An item can say who maintains it (owner), which version it first shipped in (since), and that it is retired (deprecated, with the version, a reason, and a replacement). A deprecated item still installs — removing one breaks every project that has it — but add and update say what replaced it, list and doctor mark it, the MCP server and the agent docs tell agents not to start on it, and the planners stop suggesting it. The build refuses a replacement, an alternative or a composesWith that names an item the registry does not have.
Items can also carry the guidance that tells an agent when to use them and what they are confused with — the same fields this registry’s components use.
Your agents see it too
dowel agents and the MCP server generate from whichever registry the project points at, so a coding agent in your repository learns your components alongside these, and is told when it types a name that exists in neither.
What Teams adds
Everything on this page is self-hosted and free. Teams is for organisations that would rather not run it: a hosted private registry, Pro licences for every developer in one agreement, and — on the roadmap — SSO, roles, and a view across every product consuming one design system. It is priced by conversation, not by a table; see pricing.