Repositories openvibers openvibe.domains official

openvibers/openvibe.domains

Open source domain search and comparison: every ending and registrar for a name, with what you'll really pay.

Get it

Get this repository

Clone over HTTPS

A Git token is made for this copy and shown once. It can read and write the repositories you own, and expires in an hour.

git clone https://git.openvibe.codes/openvibers/openvibe.domains.git
Clone to my computer

OpenVibe Link asks you on your computer. The folder is inside the root you chose in Link, and the request ends after two minutes.

Clone to my computer
Open in Codes

A Codes session clones from our disk: no network, no GitHub quota. When this repository has a GitHub twin, Codes pushes there too.

Open in Codes
Files at main
.env.exampleDomains: the registrar keys are owner settings (domains.registrars) (#90)6 days ago1.5 KB
.githubopenvibe-shared v4.223.0 (was v4.214.0) (#156)22 h ago
.gitignoreOpenVibe.Domains v1: name it, price it, run it. One box prices a name across every ending with 1/3/5-year totals and renewal traps flagged, a page per ending with every registrar, a page per domain with its public record; open JSON API; stateless over OpenVibe.Host's price catalog and OpenVibe.Tools' domain history16 days ago107 B
AGENTS.mdAGENTS.md: the context an agent starts from (#155)1 days ago9.4 KB
CHANGELOG.mdThe control panel: connect Cloudflare (token kept as the person's Network project secret), zones and live records, every change a proposal approved here or in the Action Center, applied with a stale check and rolled back in one step; ov_domains; contracts v0.233.0 (#4)16 days ago3.1 KB
CITATION.cffREADME first screen and CITATION.cff (#131)3 days ago526 B
CLAUDE.mdAGENTS.md: the context an agent starts from (#155)1 days ago11 B
LICENSEOpenVibe.Domains v1: name it, price it, run it. One box prices a name across every ending with 1/3/5-year totals and renewal traps flagged, a page per ending with every registrar, a page per domain with its public record; open JSON API; stateless over OpenVibe.Host's price catalog and OpenVibe.Tools' domain history16 days ago33.7 KB
README.mdopenvibe-shared v4.237.0 (was v4.235.0) (#172)1 h ago8.9 KB
SECURITY.mdOpenVibe.Domains v1: name it, price it, run it. One box prices a name across every ending with 1/3/5-year totals and renewal traps flagged, a page per ending with every registrar, a page per domain with its public record; open JSON API; stateless over OpenVibe.Host's price catalog and OpenVibe.Tools' domain history16 days ago821 B
STATUS.jsonopenvibe-shared v4.237.0 (was v4.235.0) (#172)1 h ago5.1 KB
deploynginx: the vhost includes snippets/security-txt.conf, so /.well-known/security.txt survives the next deploy (#7)15 days ago
migrationsDomains: the registrar keys are owner settings (domains.registrars) (#90)6 days ago
package-lock.jsonopenvibe-shared v4.237.0 (was v4.235.0) (#172)1 h ago61.0 KB
package.jsonopenvibe-shared v4.237.0 (was v4.235.0) (#172)1 h ago1023 B
publicFix the home page folds' summary names (#137)3 days ago
serverFix the home page folds' summary names (#137)3 days ago
testFix the home page folds' summary names (#137)3 days ago

README.md

View file

OpenVibe.Domains

Name it, price it, run it.

Live site: https://openvibe.domains · API docs: https://openvibe.services · For AI: https://openvibe.domains/llms.txt
Run it: npm ci && npm run dev (Node 22 or newer) · Tests: npm test

Status: v1 is the service skeleton (see [STATUS.json](STATUS.json)); the pages are plain placeholders.
Domain: openvibe.domains · Port: 5070 · Service id: domains · Env prefix: DOMAINS
License: AGPL-3.0-only (same as every OpenVibe service).

OpenVibe.Domains is a stateless read-only front over two OpenVibe services. For a name it shows the registry's
availability and every registrar's prices for that ending, with one-, three- and five-year totals; every ending has
its own page; every domain has a page with its public registration history.

There is no database, no sign-in, no session and no event stream. Nothing is stored and nothing is written.

Pages (v1)

| Route | What it shows |
|---|---|
| / | the first page of OpenVibe.Host's price catalog (popular endings), plus OpenVibe.Tools' public "most looked up" and "expiring soon" lists when it answers |
| /search?q= | registry status for the name, and every registrar's first-year, three-year and five-year totals, sorted by the three-year total; noindex because every query is a page |
| /tld/:tld | one ending (.dev, co.uk) with every registrar's prices, the five-year total and the renewal-trap mark |
| /.<ending> | 301 to /tld/<ending> (for example /.dev → /tld/dev) |
| /d/:domain | a full domain: its public history from OpenVibe.Tools and its ending's prices from OpenVibe.Host |
| anything else | the 404 page |

All of it is server-rendered and readable without JavaScript. The shell is the OpenVibe Frame (openvibe-shared/shell):
theme loader, navbar mounted into #navbar-mount, the shared server-rendered footer, service id domains.

The control panel (v2)

/panel is a signed-in person's own (sign-in with OpenVibe.Network, client domains).

  • Connect: paste a Cloudflare API token (Edit zone DNS + Zone Read) or a Porkbun API key pair (create keys at
    porkbun.com/account/api, then switch on API access for each domain). Domains verifies it, then stores it as a
    secret in your own Network project (OpenVibe.Domains, ADR-056), granted to domains for calls to that provider
    only (https://api.cloudflare.com or https://api.porkbun.com). It is revealed per call (network.secret.use)
    and never kept here. Porkbun's pair is one JSON secret, parsed only inside its client.
  • Zones and records: zones are synced into domains_zones (Porkbun's domains are its zones); records are read
    live.
  • Proposals: every change is a domains.proposal@1: create, update or delete records, why, what could break, and
    7 days to decide. A registrar change is a nameserver list update (NS) or a DNSSEC DS create/delete (DS), applied
    through the registrar API. Approve on /panel/p/:id or in the Action Center. Changes apply in order; a record or
    nameserver list that moved since the proposal stops the run (stale). An applied (or part-applied) proposal rolls
    back in one step.
  • API: GET /api/v1/panel/connections, /api/v1/panel/zones, /api/v1/panel/zones/:conn/:zone/records,
    GET|POST /api/v1/proposals, POST /api/v1/proposals/:id/decide|rollback with your Network token; Network's Action
    Center reads GET /api/v1/actions?subject= (domains.actions.read) and decides (domains.proposal.decide).

API

Everything under /api/v1, read-only, CORS-open (Access-Control-Allow-Origin: *), cached publicly for five
minutes. A bad name is RFC 9457 application/problem+json; an ending the catalog does not know is 404.

| Route | Answer |
|---|---|
| GET /api/v1/search?q= | { name, status, rows } — name is { label, tld, ascii, display }, status is the registry's answer, rows are the priced registrar rows |
| GET /api/v1/tlds?q=&kind=&sort=&page= | OpenVibe.Host's list as it answers it: { tlds, next_cursor }. Sorts: popular, tld, register, renew, five_year |
| GET /api/v1/tlds/:tld | { tld } with its prices priced like the page |
| GET /api/v1/domains/:domain | { name, history, prices } — history from OpenVibe.Tools, prices for that domain's ending only |

/api/v1 and /search share a per-IP limit of 120 requests a minute (express-rate-limit).

What "priced" means

OpenVibe.Host's check rows carry register, renew, five_year, registrars, currency, checked_at, tld,
display and kind. Each row gains:

  • y1 — first year: register;
  • y3 — three years: register + 2 × renew;
  • y5 — five years: the catalog's five_year when it has one, else register + 4 × renew;
  • trap — renew ≥ 2 × register and renew − register ≥ 5: the cheap-first-year renewal trap.

A total that needs a missing price is null, never NaN. Money is rounded to cents. Only USD rows are compared and
sorted (by y3, rows without a y3 last); other currencies are kept after them and flagged currency_other: true.

The data it reads

| Source | Endpoint | Used for | Cache |
|---|---|---|---|
| OpenVibe.Host | GET /api/v1/catalog/domains?sort=…&cursor=… | the catalog's ending list and the sitemap | 10 minutes |
| OpenVibe.Host | GET /api/v1/catalog/domains/:tld | one ending's registrar prices and history | 10 minutes |
| OpenVibe.Host | GET /api/v1/catalog/domains/check?name= | a name's registry status and priced endings | 2 minutes |
| OpenVibe.Tools | GET /api/v1/domains/ | the public most-looked-up and expiring-soon lists | 10 minutes |
| OpenVibe.Tools | GET /api/v1/domains/:domain | a domain's public snapshots and changes | 10 minutes |

Both clients take an injectable fetchImpl, time out after 8 seconds, and keep a small in-process Map cache with a
2,000-entry cap (oldest insertion out). A failure is null and one short log line — never an exception in a page.
The upstream URLs come from OV_HOST_INTERNAL_URL / OV_TOOLS_INTERNAL_URL, or the internal origins in the pinned
OpenVibe.Contracts manifests, with https://openvibe.tools as the Tools fallback.

OpenVibe.Tools keeps RDAP's public registration facts only (registrar, dates, status codes, name servers, DNSSEC) and
publishes a domain on its lists only after at least five lookups. This service adds no data of its own.

Configuration

Copy [.env.example](.env.example) to the environment file (/etc/openvibe/domains.env in production):

| Variable | Default | What |
|---|---|---|
| NODE_ENV | development | production selects the production base URL |
| PORT | 5070 | listen port |
| HOST | 127.0.0.1 | listen address |
| BASE_URL | https://openvibe.domains in production | public origin used for canonical URLs, the sitemap and llms.txt |
| TRUST_PROXY | 2 | how many proxies to trust (nginx and the CDN) |
| OV_HOST_INTERNAL_URL | the Host manifest's internalOrigin | OpenVibe.Host's internal API |
| OV_TOOLS_INTERNAL_URL | the Tools manifest's internalOrigin | OpenVibe.Tools' internal API |
| DOMAINS_RATE_LIMIT_PER_MIN | 120 | the shared /api/v1 and /search per-IP budget |
| DOMAINS_UPSTREAM_TIMEOUT_MS | 8000 | the two data clients' timeout |

Run it

npm install
npm test                 # every test/*.test.js, each in its own process, with stub upstreams
npm run dev              # http://localhost:5070
npm start                # production

The tests need no database, no network and no running OpenVibe site. node --check every .js file is the other
gate.

Machine endpoints

| Route | What |
|---|---|
| /api/health | { status: "ok", service: "openvibe-domains", version } |
| /api/ready | truthful readiness (openvibe-shared/ready): ready while the process serves; a Host or Tools failure makes status: "degraded" and names it, never 503 |
| /release.json, /release-metrics | the release manifest and the tab collector (openvibe-shared/release) |
| /metrics | Prometheus text format, loopback only (openvibe-shared/metrics) |
| /shared/* | this service's own pinned OpenVibe Frame files |
| /robots.txt | allow everything, name the sitemap |
| /sitemap.xml | / and one /tld/<ending> per ending in every page of the Host catalog; lastmod from the release |
| /llms.txt | what the site is, its pages and its API |

Deploy

  • deploy/systemd/openvibe-domains.service — user ubuntu, WorkingDirectory=/opt/openvibe.domains,
    EnvironmentFile=/etc/openvibe/domains.env, port 5070.
  • deploy/nginx/openvibe.domains.conf — TLS, www → apex 301, proxy to 127.0.0.1:5070, security headers, GET-only
    /api/v1, loopback-only metrics.
  • CI: .github/workflows/ci.yml runs the shared syntax/contracts/security jobs; there is no database service.

License

AGPL-3.0-only. See [LICENSE](LICENSE).

<!-- versions:start -->

  • openvibe-contracts: v0.419.0
  • openvibe-sdk: v0.161.0
  • openvibe-shared: v4.237.0

<!-- versions:end -->