ROADMAP.md
Rama master · réplica de lectura del repositorio original.
# Roadmap
DOMINUS v1.0.0 - DOMINUS Cloud MVP (released).
v1.1.0 - Release hardening (released).
v1.2.0 - Post-acquisition automation (released; the marketplace vendor
adapters remain blocked on vendor access).
v1.3.0 - Launch readiness: the legal surface, the tax and consent
compliance, the data-subject rights, the launch gate and the verified
artifact (released).
v1.3.2 - The front door: a decision command that answers, a trademark
gate that fails closed, and a release state that is real (released).
v1.3.3 - The trademark gate answers again on the migrated USPTO endpoint
(ADR-0098, released).
v1.3.4 - The front door is verified: the installers and the published
tarball are exercised on both platforms, and a tag does not publish while
the live trademark canary is red (released).
v1.4.0 - First Light: mode-aware launch gate with evidence, a launch
rehearsal on the production-parity stack, the audit scope and the
first-apply runbook; the community edition is the first soft launch
(released).
v1.5.0 - First Boot: local-first release engine, PostgreSQL migration lock
fixes, pre-migration backup, `dominus doctor`, `selfhost:up` (released).
v1.5.1 - The Gate Leaves GitHub: the merge gate moves from GitHub Actions
(billing-blocked) to Woodpecker (`.woodpecker/ci.yml`, local-first via
`ci-local`); ADR-0101 documents the local-first release engine; front-door
polish (`--help`/`--version`/`score --help` silent on stderr); release and
launch documentation describe the real publishing path (released).
Next: execute the first staging apply (v1.6.0), the third-party audit,
the fiscal position, and only then the paid Cloud tiers.
This roadmap outlines the planned releases and their scope. Timelines are
estimates and subject to change.
## v0.4.0 — SaaS Foundation
> **Status**: Completed
- [x] ADR-0025 through ADR-0028
- [x] AGPL v3 + commercial license, CLA, OpenAPI docs
## v0.5.0 — Production Hardening
> **Status**: Completed
> **Focus**: Security, code quality, retry consolidation, benchmarks
- [x] CSP hardening, AuthProvider DI, rate limiting, retry consolidation
- [x] Circuit breaker, DnsProvider interface, benchmark suite
- [x] API error handler consistency, architecture diagrams, changelog generation
- [x] Codecov integration + coverage badge in README
- [x] SEO-ready README with screenshots (generated by `e2e/screenshots.spec.ts`)
## v0.6.0 — Database Abstraction
> **Status**: Completed
- [x] DatabaseProvider interface, SQLite + PostgreSQL adapters
- [x] All repositories refactored, RLS policies, migration path
## v0.7.0 — Authentication & Multi-Tenancy
> **Status**: Completed (foundation laid)
> **Focus**: User management, tenant isolation, JWT auth
- [x] API key management (DB-backed, scrypt hashed)
- [x] `tenant_id` context resolution middleware + RLS policies
- [x] Community edition retains static `.env` API key (backward compatible)
- [x] Auth0 OIDC single sign-on (ADR-0062). Refresh tokens are intentionally not
implemented: sessions last 8 hours and end at expiry or logout.
## v0.8.0 — Conversion Features & Onboarding Wizard
> **Status**: Completed
- [x] Onboarding wizard, portfolio import, savings callout
- [x] Shareable score pages, analytics events
## v0.9.0 — Professional Frontend Dashboard
> **Status**: Completed
- [x] Recharts, TanStack Table, shadcn/ui, auth flow, themes
- [x] Loading skeletons, React Query, error boundaries
- [x] Accessibility pass (WCAG 2.1 AA): `jsx-a11y` lint plus an axe-core E2E gate on
the main flows, in light and dark themes (v1.1.0)
- [x] Frontend test coverage ≥50%
## v0.10.0 — Operations & Reliability
> **Status**: Completed
> **Focus**: Load testing, security audit, CI/CD maturity, bridge repair
- [x] Fix: POST /portfolio/verdicts route (frontend 404 bug)
- [x] Fix: Cloudflare purchase priceEur: 0 bug
- [x] Fix: frontend login validation (API key verified against backend)
- [x] Tests: dan-listing-provider (0% → 85%+), auto-listing-service (0% → 90%+)
- [x] Tests: listings route (0% → 18 tests covering all CRUD + offers)
- [x] Pagination: Dan sync + Cloudflare listDomains
- [x] Load testing and performance benchmarking (see `npm run bench`; live-network benches against real resolvers/registries: `DNS_LIVE=1 npm run bench`)
- [x] DNS verdict hardening: 2-of-3 consensus on by default with endpoint-level
disjointness enforcement (no shared resolver between primary and
secondary), parking IP probe under rate limit/deadline/abort
- [x] Security: timing-safe API key comparison (`EnvApiKeyProvider`)
- [x] Rate limiting: distributed Redis-backed store for the public router
- [x] Backend test coverage thresholds raised to 70/65/60 (lines/functions/branches)
- [x] CI matrix testing (Node 22 + 24 on ubuntu and windows, native-bindings smoke check)
- [x] Compose topology fix: the documented prod command now starts the full
stack (worker/scheduler by default; PG/Redis/monitoring via the prod
overlay) — guarded by a CI job that asserts the service sets
- [x] Observability: Prometheus text-format endpoint
(`GET /api/v1/metrics/prometheus`), self-hosted Prometheus +
Alertmanager + Grafana in the prod profile, alert rules for API down /
provider error rate / stage errors / stuck queue / dead letters
- [x] Restore drill script (`scripts/restore-drill.mjs`) — backup, restore,
integrity_check and row-count comparison; failure path verified
- [x] CI matrix testing (Node 22 + 24 on ubuntu and windows, native-bindings smoke check)
- [x] Codecov integration + coverage badge in README
- [x] Security audit (dependency scan, CSP review, auth hardening) — `npm audit` reports 0 vulnerabilities in both workspaces; the script runs with `--omit=dev`, and the frontend tree is clean in full
- [x] Frontend coverage thresholds raised to ≥70% (enforced in CI via `npm run test:coverage`; vitest thresholds 70/70/70 statements/lines/functions and 60 branches)
## v0.11.0 — Consensus Hardening (released)
> **Status**: Completed
> **Focus**: Availability-verdict trust, distributed rate limiting, usage enforcement
- [x] Usage enforcement at chokepoints (ADR-0038): metered candidate scoring, API calls, portfolio tracking
- [x] DNS consensus: majority-vote 2-of-3 gate on every resolution path (ADRs 0039, 0040), tertiary leg (0045), private recursor (0042), budget + DoH keep-alive pooling (0044), live-verified DoH legs (0047, 0048)
- [x] RDAP consensus: opt-in 2-of-2 second opinion (0050), WHOIS rescue leg + startup probe (0051), transport parity / undici pool (0049)
- [x] Distributed per-tenant fair share on shared Redis budgets (0041, 0052)
- [x] Configurable per-IP public rate limits (0043), image supply-chain pinning (0046)
## v0.10.1 — Hardening Polish
> **Status**: Completed (scope shipped with the v0.11.0 release)
> **Focus**: DNS consensus strictness, metrics access control, release docs
- [x] DNS verdict hardening: 2-of-3 consensus is now _strict_ — a failed or
unknown secondary resolver downgrades the verdict to Unknown instead
of trusting the primary, plus a startup probe that fails fast when
the consensus provider is misconfigured
- [x] DNS consensus failure policy (ADR-0039): fail-closed with a visible
`consensus-unverified` degraded-run flag, majority + floor knobs
(`DNS_CONSENSUS_DEGRADED_RATIO`/`_MIN`), and `dominus_dns_consensus_*`
Prometheus tallies
- [x] Metrics access control: optional `METRICS_TOKEN` bearer auth on the
`/api/v1/metrics/*` router (401 when set and not presented;
unchanged when unset), with a commented Prometheus scrape
authorization block for operators who enable it
- [x] Documentation: SQLite multi-process concurrency limits in the
deployment guide, `.gitignore` covers benchmark output
- [x] SEO-ready README (FAQ, keyword-rich intro, semantic headings;
screenshot/GIF demo deferred to v1.0.0 launch assets)
## v1.0.0 — DOMINUS Cloud MVP & Programmatic SEO
> **Status**: Released
> **Focus**: Managed hosting, billing, paid tiers, SEO
- [x] DNS consensus strictness and per-endpoint circuit breakers (ADR-0059):
`DNS_CONSENSUS_REQUIRED_AVAILABLE` gate (default 1 — one independent
confirmation per ADR-0002; knob up to 2 for triple agreement), breaker
registry on every resolver leg with `dominus_dns_breaker_*` metrics,
bundled parking-IP reference list, watchlist single-provider labelling
- [x] DOMINUS Cloud infrastructure (VPS + PostgreSQL + reverse proxy)
— Hetzner IaC in `deploy/terraform/` (app node + PITR DB node + Caddy)
- [x] Free tier: rate-limited pipeline runs, single user
- [x] Pro tier (€29/mo): 500 candidates scored/month, 3 team seats, email support
(support channel is ops, deferred to v1.1.0)
- [x] Team tier (€79/mo): 2,500 candidates scored/month, 10 team seats, Slack support
(support channel is ops, deferred to v1.1.0)
- [x] Stripe billing integration (subscriptions + customer portal)
— incl. Team checkout and status-aware enforcement (ADR-0053)
- [x] Usage metering (pipeline runs, API calls per tenant)
- [x] Admin panel (tenant lifecycle + usage metrics — ADR-0057: overview,
per-tenant daily usage drill-down, suspend/unsuspend, plan override)
- [x] Automated daily backups with point-in-time recovery
(ADR-0054: `deploy/postgres/base-backup.sh` + restore drill script)
- [x] Monitoring and alerting (uptime, error rate, latency —
Prometheus/Alertmanager/Grafana stack in the prod compose profile)
- [x] Migration guide: from community edition to DOMINUS Cloud
(`docs/migration/community-to-cloud.md`)
- [x] `AnonScoringService` — scoring engine in no-persist mode for public endpoints
- [x] `GET /public/domain/:name` — public domain valuation page (crawlable, cached)
- [x] `GET /sitemap.xml` — dynamic sitemap with public pages
- [x] `GET /public/compare/:slug` — editorial comparison pages
- [x] JSON-LD structured data for rich snippets
- [x] SSR layer for public pages (server-rendered HTML views — see
[ADR-0030](docs/adr/0030-public-namespace-architecture.md))
- [x] Off-host backups made mandatory by default with a dedicated backup
volume (ADR-0054: `#341`) and the base-backup manifest recorded in the
`pitr_health` table (`#342`)
- [x] RDAP origin-overlap guard fail-closed: a resolver failure on the
authoritative-origin lookup skips the second leg and downgrades the
verdict as unverifiable (ADR-0060, `#344`)
- [x] CI reliability: dead-letter tests pinned to a past `scheduled_at`,
fixing the flaky `getDeadLetter respects limit` (`#343`)
## v1.1.0 — Release Engineering Safety
> **Status**: Released
> **Focus**: Release engineering safety, tenant isolation and PostgreSQL
> parity, accessibility, and removing features that never worked
- [x] Schema migration gate: fail-closed manifest prefix check at boot and
deploy, destructive-DDL scanner in CI, RTO/RPO documentation (#349)
- [x] Migrate-before-roll: explicit migration step with a dedicated timeout
before the roll; the old image keeps serving while migrations apply
(ADR-0061)
- [x] DNS consensus hardening: native resolver leg pinned to the private
recursor, rigorous DNSSEC (SERVFAIL-on-bogus) (#350)
- [x] DNS fail-closed parity: escaped resolver errors are never manufactured
into Available verdicts (ADR-0002 conservatism)
- [x] Billing surface E2E coverage (community edition, fail-closed checkout)
- [x] Enterprise SSO via Auth0 OIDC (ADR-0062, interactive PKCE flow + session cookies)
- [x] Enterprise tier: per-tenant Stripe prices and SSO shipped; SLA published in
`docs/operations/sla.md`; dedicated infrastructure is provided on contract
(Terraform modules in `deploy/terraform/`), not as a self-service product
- [x] Platform operator role (`OPERATOR_SUBJECTS`): tenant admins can no longer reach
the cross-tenant admin surface (ADR-0081)
- [x] Tenant isolation: domain uniqueness per tenant, tenant-scoped run history, no
tenant leak on pooled PostgreSQL connections
- [x] Team invitations by email (single-use links, optional SMTP) and SSO sign-in
resolved from team seats (ADR-0081)
- [x] PostgreSQL parity: portable SQL (checked in CI), repository parity tests, and
a CI job that runs the suite against a real PostgreSQL
- [x] Release pipeline: distinct `vX.Y.Z` / `-worker` / `-scheduler` image tags,
working container image, OpenAPI contract tested against the routers
- [x] Removed features that were declared but never functional: per-query DNSSEC,
DoH fallbacks, Dan/Afternic/Sedo adapters, Kubernetes manifests (ADR-0082)
- [x] First-party security review of the multi-tenant surface (v1.1.0)
- [ ] Third-party security audit (when budget allows)
- [ ] Public launch: Product Hunt, Hacker News, domain investor communities
- [x] Workflow guides in `docs/guides/` (closeouts to a buy decision, portfolio
keep/drop, Cloud team setup with SSO, self-hosting)
- [ ] Customer case studies (need real customers)
- [ ] Community Discord server
## v1.2.0 — Post-Acquisition Automation (ADR-0083)
> **Status**: Released (the M4 vendor adapters stay blocked on vendor access)
> **Focus**: automated lifecycle after the buy decision — site, indexing,
> DNS delegation, parking/monetization, marketplace listing
- [x] Post-acquisition pipeline: journaled steps (`post_acquisition_steps`),
audit trail (`automation_audit`), kill switch `AUTOMATION_ENABLED`
(default off) and dry-run `AUTOMATION_DRY_RUN` (default on)
- [x] `POST_ACQUISITION` job type on the existing queue; purchase and
won-auction paths trigger the pipeline instead of the old fire-and-forget
auto-list
- [x] `listing` step (delegates to the existing AutoListingService)
- [x] Bug fix: `AUTO_LIST` / `POST_ACQUISITION` job handlers are now registered
by the worker (previously enqueued jobs dead-lettered)
- [x] CLI `dominus automate settings|status|run|reset|audit`, API
`/api/v1/automation/*`, OpenAPI coverage, operator runbook
- [x] **M2** — `site` + `indexing` steps: landing page generated from scoring
data and published at `/public/for-sale/<domain>` (Schema.org Offer),
sitemap inclusion, IndexNow submission (`INDEXNOW_KEY`), contact via
`AUTOMATION_CONTACT_EMAIL`
- [x] **Phase 0** — tenant opt-in: `tenant_automation_settings` (per-tenant
kill switch/dry-run, conservative combination with the env brake),
`PUT /automation/settings`, Automation dashboard page (settings, step
journal, audit trail), `dominus automate settings --enabled/--dry-run`
- [x] **M3** — `dns` + `parking` steps: DnsWriteProvider (Cloudflare-first,
manual fallback) behind a fail-closed write gate (ADR-0083 amendment),
MonetizationProvider with a CSV revenue reader (zero paid API
dependencies; provider API adapters land with verified access,
ADR-0082), revenue ingestion idempotent per (domain, date, source)
- [x] **M4 (partial)** — marketplace automation without unverified vendor APIs:
tenant listing policy (`tenant_listing_settings`): fixed base price
for automatic listings, offer auto-decline floor and counter rule
(both conservative by default), `GET/PUT /api/v1/listings/settings`,
listing policy card in the dashboard
- [ ] **M4 (vendor adapters)** — Dan.com + Afternic ListingProvider
adapters and offer sync: blocked on verified API access (ADR-0082);
they return once the vendors grant and verify the credentials
- [x] **M5** — sustainability: scheduled revenue ingestion
(`REVENUE_SYNC` job + `revenue-sync` scheduler job, read-only,
idempotent), carry-cost verdict (`sustainable` / `marginal` /
`loss` / `insufficient_data`, 30-day minimum before judging) exposed
at `GET /api/v1/sustainability` and in the Portfolio dashboard, and
fed into the drop engine as a Reprice signal (never an auto-drop)
## v1.3.0 — Launch readiness (ADR-0090, ADR-0091, ADR-0092, ADR-0093, ADR-0094)
> **Status**: Released — the code is done; what remains is the operator's
> work (identity, fiscal position, infrastructure, audit)
> **Focus**: the public launch of DOMINUS Cloud — the legal surface, the
> tax and consumer-law compliance, the data-subject rights, and a launch
> gate that can be executed
- [x] Legal surface rebuilt for the right jurisdiction (art. 7 D.Lgs.
70/2003, not the German Impressum): legal notice, terms of service
and privacy notice as server-owned structured data, published
unauthenticated, linked from the footer of every public page
- [x] Identity as configuration (`LEGAL_*`), never as a placeholder: the
launch gate refuses a deployment whose identity is missing
- [x] Community edition publishes the documents that apply to it (AGPL,
self-hosted data flow) and never the operator identity
- [x] Stripe Tax completed: billing address required, VAT ID collected for
the EU reverse charge, customer address written back on returning
customers
- [x] EU consumer law: the art. 59(1)(o) consent and its acknowledgement
are collected at checkout, in one constant, asserted by the tests
- [x] Data-subject rights: export (art. 15) and erasure (art. 17) from
the API and the CLI, over one registry of tenant-scoped tables that
cannot drift from the schema
- [x] Data processing agreement (art. 28) template, sub-processor list and
retention table published
- [x] `npm run launch:check`: the launch state is executable, not a
document — exit 0 or exit 1
- [x] The green trunk: the E2E suite is part of the local gate, the
secret-scanning false positives are gone, the master is green
- [x] The published artifact is verified, not assumed (ADR-0091): the
local gate installs and runs the tarball before a tag, the release
workflow installs and runs the published package after the publish,
and the release rehearsal works from a branch again
- [x] The granted plan is derived from the Stripe price id, never from
the session metadata (ADR-0093); an unknown price id grants nothing
- [x] The launch posture is measured, not promised (ADR-0094): one
replica stated in the SLA, an external availability probe outside
the app node, a real PITR rehearsal, and fail-closed SSH ingress
- [x] The community first run does not wait thirty seconds for a
resolver that is usually not there (ADR-0092)
- [ ] Operator identity (name, address, P.IVA, REA, PEC, contacts)
- [ ] Fiscal position and the accountant: OSS registration, the invoice
series, the reverse-charge wording, the review of the legal texts
- [ ] The three Stripe verification scenarios, recorded
- [ ] The infrastructure, provisioned for real: `terraform apply`, DNS,
TLS, a real Alertmanager webhook, an external probe, a restore
drill against the real backup target
- [ ] The third-party security audit, before the paid tiers
- [ ] The npm trusted publisher, so `dominus-oss` is installable
- [ ] Public launch: Product Hunt, Hacker News, domain investor communities
## v1.3.2 — The front door (ADR-0097)
> **Status**: Released
> **Focus**: the first command a stranger runs, and the truth of what the
> repository announces about itself
- [x] The decision profile: `dominus score` builds the scoring engine and
the trademark gate and nothing else — no database, no job worker, no
resolver probe (12.4 s → ~2.5 s, and nothing written to the disk)
- [x] The catalog profile: `--help` and `--version` are answered from the
command line alone (10.4 s → ~1.4 s), with a test that keeps the two
command lines identical
- [x] The diagnostics moved to stderr: stdout carries the decision and
nothing else, so the output can be piped
- [x] `dominus score --json`: the same decision, as an object
- [x] The trademark providers fail closed: a source that cannot answer is
an error, not "no matches". A domain whose trademark was not checked is
reported as unverified, and an unverified domain is never recommended
- [x] The quickstart gate (`npm run check:quickstart`): the advertised
command is executed and its contract is asserted, and the output
documented in the README is compared with the output produced
- [x] The release doctor (`npm run release:doctor`): the version
`package.json` announces must have a tag, a clean tree and — with
`--remote` — a package and an image
- [x] The verified tarball and its checksum are attached to the GitHub
Release: the community edition is installable without the npm registry
- [x] **The USPTO endpoint migration** — closed in v1.3.3 (ADR-0098): the
provider reads the `prod-stage-v1-0-0` endpoint and the gate answers
`clear`/`blocked` again
## v1.3.3 — The trademark gate answers again (ADR-0098)
> **Status**: Released
> **Focus**: the one non-negotiable gate, after the upstream endpoint moved
- [x] USPTO provider migrated to the `prod-stage-v1-0-0` search endpoint
- [x] The gate reports `clear` or `blocked` instead of `unverified` on a
live query, and still fails closed on an unanswerable source
## v1.3.4 — The front door is verified
> **Status**: Released
> **Focus**: the release is verified against what a stranger downloads and
> runs, not against the repository's opinion of itself
- [x] The installers install a working command: `npm install -g` puts the
binary in `<prefix>/bin` on Linux/macOS and in the prefix on Windows,
which is where the scripts say it is
- [x] `npm run test:installers` runs the real installer against a local
HTTP server that serves the real tarball, on Linux and Windows, and
asserts a working `dominus score` and a refused corrupted checksum
- [x] `check:published-install` is fixed and wired: it starts the trademark
fixture before reading its port, measures the time budget, resolves a
release tag URL to the alias tarball, and asserts a `clear` verdict
- [x] The release workflow verifies the GitHub Release channel after the
publish, even when the npm publish fails, and asserts the alias
tarball, its checksum and both installers are attached
- [x] The live trademark canary blocks the release: a tag does not publish
while `npm run check:trademarks` is red (ADR-0098's claim, made true),
and a daily schedule opens an issue when the endpoint drifts
- [x] The npm channel is documented for what it is: best-effort until the
trusted publisher is configured; the GitHub Release is primary and
verified end-to-end
## v1.4.0 — First Light (ADR-0100)
> **Status**: Released
> **Focus**: the launch bar is executable before the infrastructure exists,
> and the community edition ships first
- [x] `launch:check` has `--community` and `--cloud` profiles, auto-detects
the mode with the server's own rule, and adds `--paid` for the Cloud
contracts
- [x] `--evidence` writes `launch-evidence-<version>-<sha>.json` and `.md`
on pass and on fail
- [x] the gate checks the canonical origin (configured and rendered), TLS,
HSTS/CSP, HTTP→HTTPS, HTML/JSON parity, robots → sitemap, public
input validation, data-rights endpoints and the live trademark canary
- [x] `npm run launch:rehearse` builds the production-parity Cloud stack,
runs the gate against it, writes the evidence and tears it down; PITR
is opt-in and a missing prerequisite is a SKIP, never a pass
- [x] the audit scope is a statement of work, and the evidence index maps
every claim to the command that produces it
- [x] the first-apply runbook goes from an empty Hetzner project to a
gate-green staging deployment
- [x] the community soft-launch runbook, with the announcement and the
support posture
## Post-1.0
- **Internationalisation** (after v1.4.0) — the legal documents and the
interface as a translatable catalogue, with machine-assisted
translation that opens a pull request and a check that no user-facing
string can bypass the catalogue. Runtime translation is excluded: it
would send user data to a third party, and it is neither indexable nor
cacheable.
- **Real-time collaboration** — WebSocket-based shared pipeline views
- **Advanced analytics** — Portfolio diversification heatmaps, trend analysis
- **Name generator** — AI-assisted brandable domain generation
- **Marketplace integrations** — Afternic, Sedo, Dan.com, GoDaddy Auctions. Adapters
shipped in 1.0.x were removed in 1.1.0 because they were never verified against the
vendors' APIs (ADR-0082); they return once API access exists
- **Tenant switcher** — follow-up to ADR-0081
- **Row-level security on control-plane tables** — needs a dedicated bypass design
- **Mobile app** — Native notifications for renewal alerts and bid updates
- **Multi-replica rate limiting** — the protected router counts per
replica; the shared store arrives with the multi-replica topology
---
> _This roadmap is a living document. Priorities may shift based on user
> feedback and business needs. See the [ADR series](docs/adr/README.md) for
> the rationale behind major decisions._