docs/adr/0070-afternic-listing-provider.md
Rama master · réplica de lectura del repositorio original.
# ADR-0070: Afternic Listing Provider and Trademark-Gated Publishing
## Metadata
| Field | Value |
|-------|-------|
| **Status** | Superseded by [ADR-0082](0082-removal-of-unimplemented-features.md) |
| **Date** | 2026-09-06 |
| **Authors** | Alessio Brillo |
| **Deciders** | Alessio Brillo |
| **Supersedes** | N/A |
| **Relates to** | ADR-0004, ADR-0006, ADR-0012 |
| **Project** | DOMINUS |
## Context
The sell side supports five marketplaces in the type system
(`MarketplaceName`: `dan | afternic | sedo | godaddy | manual`) and the
`0024_create_listings` CHECK constraint, but only `manual` and `dan`
have `ListingProvider` implementations. `LISTING_PROVIDER` rejects
`afternic` at config validation, so the largest distribution network
(GoDaddy/Afternic Fast Transfer) is unreachable.
Separately, `ListingManager` received the `TrademarkGate` as an unused
`_trademarkGate` parameter and published without any trademark check:
a TM-blocked domain could reach `listed` on an external marketplace,
violating Principle 6 (ADR-0006). Any marketplace expansion before
closing this gap multiplies legal exposure.
## Decision Drivers
1. **Trademark gate is non-negotiable (ADR-0006)** — publishing is a
legal-exposure point equal to the buy recommendation.
2. **Provider abstraction (ADR-0004)** — a new marketplace is one
adapter file plus factory/config wiring; zero core-logic changes.
3. **Conservatism (ADR-0002)** — an `Unverified` gate must not publish
externally, but destroying the local draft would lose tracking data.
4. **Zero-cost discipline** — no new dependencies; `fetch` + existing
retry/error types only.
## Considered Options
### Option A: Trademark-gated Afternic adapter, single active provider (Chosen)
- `ListingManager.listDomain()` checks the gate before inserting:
`Blocked` throws `TrademarkGateError` (no draft created);
`Unverified` creates a local `draft` with a warning (local-only,
no legal exposure).
- `ListingManager.listOnMarketplace()` re-checks at publish time
(covers matches appearing after draft creation and pre-gate drafts):
anything but `Clear` throws. Publish is the fail-closed point.
- New `AfternicListingProvider` mirrors `DanListingProvider` (Bearer
auth, paginated sync, status maps). `LISTING_PROVIDER` gains
`afternic`; `AFTERNIC_API_KEY` / `AFTERNIC_API_URL` added. Without a
key `isAvailable=false` and the manager degrades to local tracking,
identical to Dan semantics.
- Single active provider retained (`LISTING_PROVIDER` switch, no
fan-out): one concern per branch, no registry/multi-publish semantics.
**Disadvantages:** no parallel Dan+Afternic listing; Afternic remote
schema is adapter-shaped (refined against live credentials later).
### Option B: Multi-marketplace fan-out registry
Publish one domain to Dan and Afternic in parallel with per-marketplace
status reconciliation.
**Rejected:** requires registry, conflict resolution (price/status
divergence), and offer deduplication — an order of magnitude more
surface for unmeasured sell-through. Revisit when Afternic measures
incremental distribution.
### Option C: Afternic without the gate fix
Ship the adapter first, gate later.
**Rejected:** ships the exact legal hazard identified in the context;
a blocked domain listable on two marketplaces instead of one.
## Decision Outcome
**Chosen: Option A.** Implementation:
- `src/listing/listing-manager.ts` — gate checks in `listDomain()`
(pre-insert) and `listOnMarketplace()` (pre-publish).
- `src/providers/listing/afternic-listing-provider.ts` — new adapter.
- `src/providers/listing/index.ts`, `src/config.ts`,
`src/app/composition-root.ts`, `.env.example` — wiring + docs.
- `AutoListingService` needs no change: gate throws are non-transient,
surfaced as `skipped: error` with the trademark message.
## Consequences
### Positive
- No TM-blocked domain can be created or published through any path
(manual, auto-list, pre-gate drafts).
- Afternic distribution unlocked behind one env switch with graceful
manual-mode degrade.
- Dan `ProviderError` provider/code args corrected (`'dan'` provider,
`DAN_API_*` codes) — error telemetry now groups by provider.
### Negative
- `Unverified` blocks external publish: during a USPTO/EUIPO outage,
sell-side publishing stalls (drafts still tracked; pipeline-buy path
already behaves this way per ADR-0012).
- Single-provider switch: operators wanting Dan+Afternic in parallel
wait for the fan-out follow-up.
### Compliance and Security Implications
- Gate verdicts are fail-closed at both sell-side chokepoints; no
secrets beyond the existing `*_API_KEY` env pattern.
### Migration and Monitoring Plan
- Rollout: `LISTING_PROVIDER=afternic` + `AFTERNIC_API_KEY`; rollback
to `manual` is env-only.
- Watch: `TrademarkGateError` rate in auto-list skips; Afternic
`sync()` error strings.
### Validation
- Unit: gate `Blocked`/`Unverified`/late-match tests in
`listing-manager.test.ts`; full adapter tests in
`afternic-listing-provider.test.ts` (mocked `fetch` boundary).
- Gate: `npm run typecheck`, `npm run lint`, listing test suites.
---
*This ADR was created following the MADR 4.0.0 standard. All DOMINUS ADRs should be consistent with the ADR series starting at `docs/adr/0001-project-architecture.md`.*