Ctrl K
ComunidadGuías…

README.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# DomBot

[![Latest release](https://img.shields.io/github/v/release/aoxborrow/dombot?label=release)](https://github.com/aoxborrow/dombot/releases/latest)
[![License: AGPL-3.0-or-later](https://img.shields.io/badge/license-AGPL--3.0--or--later-blue)](LICENSE)
[![Sponsor](https://img.shields.io/badge/sponsor-%E2%9D%A4-ea4aaa?logo=githubsponsors&logoColor=white)](https://github.com/sponsors/aoxborrow)

**All your domains, from every registrar, in one app + MCP.**

DomBot pulls your whole domain portfolio — scattered across GoDaddy, Dynadot,
Cloudflare, and more — into one desktop app, and serves the same portfolio to
your AI agents through a built-in MCP server. It's free, open source, and
local-first: your data and API keys stay on your machine.

**Download** for macOS, Windows, or Linux from
[**dombot.ai**](https://dombot.ai) or the
[Releases](https://github.com/aoxborrow/dombot/releases) page, or
**[try the live demo](https://demo.dombot.ai/)**: the full app in your
browser on an invented portfolio, no install and no account.

<a href="site/public/dombot-domains.png"><img src="site/public/dombot-domains.png" alt="DomBot's Domains screen — every domain across all your registrars in one sortable, filterable table" width="720"></a>

## What it does

- **One portfolio, every registrar.** Aggregates all your domains across the
  registrars you configure into a single sortable, filterable table — search by
  name, filter by TLD / registrar / nameserver, and see registrar, creation and
  expiry dates, auto-renew, transfer lock, WHOIS privacy, and nameservers side
  by side. Expiry dates are color-coded by urgency, and at-risk domains
  (expired, grace, redemption, hold) get a status badge.
- **Renewal costs at a glance.** A renewals dashboard forecasts your spend:
  yearly total, amount due in the next 90 days, a month-by-month renewal chart,
  and breakdowns by registrar and TLD. Prices come from registrar quotes where
  available and a base per-TLD price database otherwise, and you can enter a
  price by hand for anything still unpriced.
- **Marketplaces.** Connect Afternic, Sedo and Atom accounts to see each
  name's listing, price, and 12-month views, leads and searches in the table,
  and list or delist names at your DomBot prices from the row menu or in bulk.
  Completed sales are recorded in each name's history. See
  [Connecting marketplaces](#connecting-marketplaces).
- **Sales.** Every sale in one place: what the markets report, and the private
  and imported sales you record, with fees, net, holding period and profit
  where what you paid is known. Filter by date, market, TLD or registrar and
  the totals above follow, in your preferred currency (a sale in another is
  converted at its month's average exchange rate); export to CSV. A
  sale waiting on the auth code unlocks the name and sends the code from the
  registrar in two clicks, and where the registrar holds the buyer's
  transfer for you to answer (Dynadot), one more approves it.
- **Export.** One click exports the current (filtered, sorted) view to a
  spreadsheet-friendly CSV.
- **Instant, offline-friendly launch.** The whole portfolio is cached on disk
  and painted the moment the app opens, with no network calls; you refresh on
  demand (it never auto-refreshes) and a timestamp flags when data is going
  stale.
- **Agent-ready.** An embedded local MCP server lets AI agents (Claude Code,
  Claude Desktop, any MCP client) read and manage the same portfolio — see
  [Connecting an AI agent](#connecting-an-ai-agent).

## Configuring registrars

DomBot currently supports **GoDaddy, Cloudflare, Dynadot, NameSilo, Spaceship,
Namecheap, Porkbun, Gandi, NameBright, Name.com, 101domain, Unstoppable, and
Namefi**, with more on the way.

Add each registrar in **Settings → Registrars** with an API key from that
provider's account — DomBot shows where to find each one. Credentials are stored
encrypted on your device via your OS keychain (Electron `safeStorage`) and are
never sent anywhere but the registrar's own API. See [SECURITY.md](SECURITY.md)
for the full trust model and how to verify it yourself.

After saving a registrar’s first account, use **Add another account** inside its
card to connect more. An account selector appears only for registrars with multiple
accounts; single-account registrars keep the original flow. See
[multiple registrar accounts](docs/multi-account.md) for migration and MCP selection.

## Connecting marketplaces

DomBot supports **Afternic, Sedo, and Atom**. Add an account in
**Settings → Markets**; each market's card says which keys it needs and where to
get them (Sedo's partner ID and sign key come from applying to Sedo, and Atom's
API access is a beta for sellers with 500+ listings). Market keys are stored
encrypted like registrar keys and are sent only to that market's API. How the
markets map into one model, and what each API can and can't do, is in
[docs/markets.md](docs/markets.md).

## Connecting an AI agent

DomBot runs a local [MCP](https://modelcontextprotocol.io) server, bound to
your machine only, so any MCP client can work with your portfolio. To connect
Claude Code:

```bash
claude mcp add dombot --transport http http://127.0.0.1:4123/mcp
```

On first connect a browser page opens and DomBot's own window shows an
Approve/Deny prompt with a matching confirmation code. Approve once and the
client stays paired across restarts; manage or revoke paired clients in
**Settings → MCP Clients**. The URL and a ready-to-paste connect command are
also shown on the Home screen.

**Claude Desktop** (and other clients that can only launch a command) use the
app itself as a stdio server. Copy the ready-made entry from **Settings → MCP**
into `claude_desktop_config.json`; it looks like:

```json
{
  "mcpServers": {
    "dombot": {
      "command": "/Applications/DomBot.app/Contents/MacOS/DomBot",
      "args": ["--mcp-stdio"]
    }
  }
}
```

No approval prompt is needed for this route — it runs as you, on your machine —
and if DomBot isn't open when the client starts, it launches automatically.

## Staying up to date

Once a week DomBot reads the list of releases at
[dombot.ai/releases.json](https://dombot.ai/releases.json). When there's a newer
version, a green notification appears in the bell; dismiss it and it stays
quiet until the next release. Nothing about you or your portfolio is sent, and
you can turn the check off, or check by hand, in **Settings → About**.

On macOS and Windows, **Update to …** in Settings → About downloads the new
version and **Restart to update** switches to it. Nothing downloads until you
click. Linux packages, and copies run from somewhere other than an installed
location, get a download link instead. Self-hosted instances link to
[how to update your setup](docs/self-hosting.md).

## Reporting a problem

The desktop app keeps an error log: its warnings and errors, such as a failed
sync and what the registrar or marketplace said. Open it with **Help → Show
Error Log** (macOS) or **Settings → About → Show in Finder / Explorer**.
It lives at `~/Library/Logs/DomBot/main.log` on macOS and
`%APPDATA%\DomBot\logs\main.log` on Windows, and rolls over to `main.old.log`
at 2 MB. Every line is scrubbed before it's written: saved keys, passwords and
proxy credentials become `[redacted]`, and URLs lose their query strings. Domain
names can appear. Paste the last 100 or so lines into your bug report. Nothing
is sent anywhere unless you send it.

## Self-hosting in the browser

The same app runs as a private web app on your own Cloudflare account — a
Worker plus one D1 database, encrypted under a key only you hold, with the MCP
server reachable at `https://<your-host>/mcp`.

The recommended setup is to fork this repository and connect your fork in the
Cloudflare dashboard; updating is then GitHub's **Sync fork** button. The
Deploy button is faster to start, but updates are trickier, so it isn't
recommended. Both, plus the CLI and putting it behind Cloudflare Access, are in
[docs/self-hosting.md](docs/self-hosting.md).

[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/aoxborrow/dombot)

Moving from the desktop app: **Settings → Import/Export → Export data**, then
restore the file on your instance.

---

# Development

DomBot is a cross-platform desktop app built with Electron Forge, React 19,
TypeScript (strict), and Vite, styled with Tailwind CSS v4 and Zustand for
state. Registrar API support comes from
[`@aoxborrow/registrar-client`](https://github.com/aoxborrow/registrar-client);
agents connect through an embedded MCP server. Contributions welcome — use Node.js 22.13 or newer, clone
the repo, `npm install`, and `npm start` to run the app with hot reload.

## Scripts

| Command              | Description                                        |
| -------------------- | -------------------------------------------------- |
| `npm start`          | Run the app with hot reload (Forge + Vite)         |
| `npm run package`    | Package the app into an unpacked bundle            |
| `npm run make`       | Build distributables (zip/deb/rpm/Squirrel)        |
| `npm run lint`       | Lint `.ts`/`.tsx` files                            |
| `npm run format`     | Format the codebase with Prettier                  |
| `npm run typecheck`  | Type-check without emitting                        |
| `npm run test:live`  | Live tests against real accounts (keys in `.env`)  |
| `npm run site:build` | Minify the landing page (`site/src` → `site/dist`) |
| `npm run web:dev`    | Run the self-hosted web app locally (wrangler dev) |
| `npm run web:deploy` | Build and deploy the web app to Cloudflare         |

## Credentials

Registrar credentials are entered in **Settings → Registrars** and stored
encrypted via Electron `safeStorage` — in development and production alike. There
is **no `.env`/environment-variable fallback**: credentials come only from the
GUI store, so ambient vars from other tools can't silently shadow them (see
[`resolveField`](src/core/services/registrars.ts) and
[`src/core/services/credentials.ts`](src/core/services/credentials.ts)). The same
saved credentials feed both the UI and the MCP server. For the full trust model
and how to verify it, see [SECURITY.md](SECURITY.md).

## Embedded MCP server

The MCP server (see [Connecting an AI agent](#connecting-an-ai-agent)) is a
third _adapter_ over the same `services/` core the UI uses — see
[`src/electron/mcp/`](src/electron/mcp).

- **Transport:** Streamable HTTP, bound to `127.0.0.1` only. Never exposed off
  the machine.
- **Auth:** OAuth 2.1 (dynamic client registration + PKCE), served by the app.
  The approval prompt lives in DomBot's own window; the issued token is
  persisted (`userData/mcp-tokens.json`) so clients stay paired across
  restarts. Env knobs: `DOMBOT_MCP_PORT` (default `4123`),
  `DOMBOT_MCP_ENABLED=0` to disable, `DOMBOT_MCP_AUTOAPPROVE=1` to skip the
  approval prompt (dev/testing), `DOMBOT_MCP_TOKEN` for a static bearer token
  escape hatch (dev/testing). See
  [`src/core/mcp/oauth.ts`](src/core/mcp/oauth.ts).
- **stdio shim.** `DomBot --mcp-stdio` runs the same binary headless as a
  stdin/stdout bridge to the HTTP server, for clients that can't dial a URL
  (Claude Desktop). It authenticates with a per-install token the app writes to
  `userData/mcp-stdio.json`, launches the app if it isn't running, and
  re-initializes its session transparently if the app restarts. stdout is the
  JSON-RPC channel, so all logging in that mode goes to stderr. See
  [`src/electron/mcp/stdio.ts`](src/electron/mcp/stdio.ts). Dev builds aren't
  auto-launched — start the app first.
- **Tools.** Named by scope, so a caller can tell at a glance what a tool acts
  on: `portfolio_*` take no scope params, `registrar_*` require a `registrar`
  id, and `domain_*` take a `domain`. For `domain_*` tools the `registrar` is
  optional — DomBot resolves it from the cached portfolio (you own the domain,
  so it knows who holds it), so an agent can act on a name without first looking
  up its registrar. Pass `registrar` to skip the lookup, or to act on a name not
  yet in the cache; if the cache can't resolve it, the tool says so and points
  at `portfolio_sync`, or says the name is in Archive.
  - _Portfolio:_ `registrar_list`, `portfolio_query`, and `portfolio_sync`.
    `portfolio_query` is the primary way to read the portfolio: list, search,
    filter, sort, and page the cached portfolio and your imported names (by
    registrar, source, TLD, folder, name, nameserver, auto-renew/lock/privacy,
    status, and expiry), returning only the fields an agent needs (including
    what you paid, sold for, noted, and are asking, and the estimated renewal
    price) plus sync health (`total`, `stale`, and
    per-registrar `errors`). Like the Domains page it covers the names you own
    by default; `ownership` switches to Archive (names you no longer have:
    sold, deleted, expired, or removed) or both, and each row says which, and
    whether an owned name is expiring or sold awaiting transfer. With no filters it returns every name you own (paged), so it
    doubles as a plain list. It's a pure cache read (no registrar calls);
    `portfolio_sync` runs the live cross-registrar pass that refreshes the
    cache, returning a per-registrar summary. An agent syncs once (or when
    `portfolio_query` reports `stale`/empty), then reads cheaply.
  - _Folders:_ `folder_list` (with domain counts), `folder_create`, `folder_rename`, `folder_order_set`, `folder_delete` (its
    domains go back to no folder), and `domain_set_folder` (by folder name or
    id, or `null` to clear). They write the same folder store as the app, so
    an open window updates live.
  - _History and review:_ `domain_history` (every event for a name, with
    alert status), `portfolio_alerts` (what the bell shows) and
    `portfolio_alert_dismiss`.
  - _Money, notes and ownership:_ `domain_purchase_set`, `domain_sale_set`,
    `domain_sale_transferred_set`, `domain_note_set`, `domain_asking_price_set`, `domain_renewal_price_set`
    (your own yearly price, overriding the estimate). To let a name go, an
    agent turns its auto-renew off with `domain_set_autorenew`. Each can
    answer an open alert with `resolves` where it fits. DomBot-local, no
    registrar calls; history they write is marked **Agent** in Activity.
  - _Registrar accounts:_ `registrar_set_enabled`.
  - _Registrar reads:_ `registrar_test`, `registrar_domains`, `registrar_sync`
    (targeted single-registrar refresh of the cache),
    `registrar_check_availability`, `registrar_pricing`.
  - _Domain reads:_ `domain_get`, `domain_contacts_get`,
    `domain_nameservers_get`, `domain_dns_get`, `domain_email_forwarding_get`,
    `domain_url_forwarding_get`, `domain_renewal_price` (DomBot's own estimate,
    distinct from `registrar_pricing`). `domain_get` and
    `domain_nameservers_get` serve from the detail cache when fresh, fetch live
    and write through otherwise, and take `refresh` to force a live fetch.
  - _Writes (non-money):_ `domain_nameservers_set`, `domain_dns_set`,
    `domain_contacts_set`, `domain_email_forwarding_set`,
    `domain_url_forwarding_set`, `domain_set_autorenew`, `domain_set_lock`,
    `domain_set_privacy`. Forwarding (email alias and URL redirect) is
    per-registrar — unsupported providers return an error.
  - _Writes (money):_ `registrar_register_domain`, `registrar_transfer_domain`,
    `domain_renew`. Not gated behind extra per-call approval — the
    connection-level OAuth approval is the gate — and annotated non-idempotent.
  - _Cache write-through._ A successful write patches the local cache (the same
    cache the desktop UI reads) and pushes an update to any open window, so the
    Domains table reflects the change live — no manual Sync needed.
  - _Cache freshness._ Reads serve the local cache and report `stale` /
    `fetchedAt`; an agent refreshes explicitly with `portfolio_sync` /
    `registrar_sync`. The app also runs a periodic background sync so the cache
    stays warm for MCP-only use (no window ever opened). The interval is set in
    **Settings → Sync** (Every hour … 7 days, or Off; default 24h, applied
    live); large portfolios may prefer a longer interval or Off.
    `DOMBOT_SYNC_INTERVAL_MINUTES` overrides the setting for dev/testing (`0`
    disables).
- **Credentials.** Resolved the same way as the UI (see
  [Credentials](#credentials)); a registrar is "configured" when all of its
  required fields are present.

## License

DomBot is free and open source software: Copyright (C) 2026 Aaron Oxborrow,
licensed under the [GNU Affero General Public License v3.0 or later](LICENSE)
(AGPL-3.0-or-later). You may use, study, share, and modify it; if you distribute
a modified version, it must also be AGPL and ship its source.

Any paid data feeds are a separate, optional add-on service with their own terms
— the app itself is and stays free. The registrar logic in
[`@aoxborrow/registrar-client`](https://github.com/aoxborrow/registrar-client)
is a separate project under the MIT license.