Każda zmiana w domenie — rejestracja, odnowienie, transfer, delegacja nowych serwerów nazw — kończy się ostatecznie u rejestru. W naszym przypadku rozmowę z rejestrem .PL prowadzi silnik EPP napisany w Scali, ten sam, który obsługuje naszą produkcję. Jego kod udostępniliśmy na GitHubie pod licencją Apache 2.0.

To nie jest ogłoszenie produktowe. Traktujcie to jak notatkę inżynierską: co nas zaskoczyło w protokole, jak rozłożyliśmy system na aktorów i czemu do testów całości nie potrzebujemy kontaktu z rejestrem.

Najpierw argument, który nas do publikacji przekonał. Portfel domen, którym można zarządzać wyłącznie przez klikany panel, to portfel, którym nie władasz do końca: eksport, audyt, migracja stają się ćwiczeniami z klikania. Zmienia to interfejs programistyczny, a u największych polskich rejestratorów pełne API to wciąż rzadkość, najczęściej zastrzeżona dla wybranych partnerów. Na podejście konkurencji nie mamy wpływu. Możemy natomiast nie dokładać się do problemu: publikujemy kod, który obsługuje naszą własną rejestrację, żeby każdy, kto prowadzi domeny .PL, mógł go obejrzeć, wykorzystać i rozliczać nas za każdy jego błąd.

EPP — tylko nie ten z RFC

EPP, Extensible Provisioning Protocol, opisują dokumenty RFC 5730–5734. Kto je czytał, ma w głowie konkretny obraz: XML w dedykowanej sesji TCP/TLS (tak stanowi RFC 5734), stały zestaw komend, przewidywalne kształty komunikatów.

Rejestr .PL pracuje inaczej. EPP jedzie po HTTPS, sesję trzyma ciasteczko (SESSIONID), a ładunki muszą pasować do schematu NASK, „EPP Schema 2.1”, zamiast do kształtów normatywnych. Na to nakładają się lokalne rozszerzenia, których w RFC nie znajdziecie: extdom:book to rezerwacja nazwy domeny na 14 dni — relikt dawnego cyklu życia .PL (NASK wycofał rezerwacje w 2020 r., komenda przetrwała w dialekcie), extdom:reactivate pozwala wyciągnąć domenę z kwarantanny, a utworzenie kontaktu niesie ze sobą polskie identyfikatory — PESEL, NIP, REGON.

Walidacja tych identyfikatorów to osobna lekcja pokory. Sprawdza ją rejestr, nie klient: od cyfry kontrolnej PESEL-u po sumy kontrolne NIP-u i REGON-u. Gdy dane nie przechodzą, wraca kod 2306 z powodem odrzucenia, a silnik mapuje go na czytelny błąd w naszym API. Generyczny klient napisany „pod RFC” z .PL nie pogada w ogóle. Potrzebny jest dialekt. Właśnie ten dialekt opublikowaliśmy.

Transport dokłada swoje warunki. W trybie produkcyjnym połączenie korzysta z wzajemnego TLS: klient przedstawia certyfikat, keystore i truststore trzymamy w JKS, a sesja po wygaśnięciu potrafi się sama odnowić — transport loguje ponownie, bez angażowania kodu wyżej.

Zostaje jeszcze kolejka poll. Rejestr nie powiadamia nas, gdy coś się dzieje; zostawia wiadomości w skrzynce, którą odbiera się komendą poll:req i kwituje poll:ack. Potwierdzenia rejestracji, zmiany statusów, reszta życia obiektu — to wszystko czeka właśnie tam.

Aktorzy zamiast wątków

Dlaczego Scala i Akka? Bo na tym stosie stoi cała nasza platforma, a model aktorowy zaskakująco dobrze pasuje do tej dziedziny: długowieczne sesje wymagające nadzoru i ponownego logowania, kolejka poll, niezależne od siebie potoki komend. Przepisanie tego w modniejszym języku dałoby inny projekt i zgubiłoby sens publikacji. Udostępniamy to, co naprawdę działa w produkcji.

System rozpada się na kilku aktorów: jeden rozmawia z endpointem rejestru, jeden robi walidację wejścia, jeden obsługuje kolejkę poll, jeden zajmuje się DNS-em (w kodzie: user/registry.pl, user/validator.pl, user/PollProcessing, user/dns). Awaria jest lokalna i ma lokalną procedurę naprawczą — to sama filozofia nadzoru aktorów, tylko osadzona w warstwie protokołu.

Pod spodem trzy moduły SBT: biblioteka protokołu z drzewem AST komend, moduł dialektu NASK (generatory i parsery XML plus dwa transporty: produkcyjny i symulator) oraz aplikacja Play z kontrolerami REST, Slickiem i obsługą polla. Na wierzchu zostaje REST API, więc sprawdzenie dostępności domeny to jedno wywołanie:

bash — POST /api/domain/check
curl -X POST http://localhost:9000/api/domain/check \
  -H "Content-Type: application/json" \
  -d '{
    "user": "admin",
    "password": "twoje_haslo",
    "domain": { "name": "innowacje-2026.pl" }
  }'

Z operacji jest pełny zestaw: check, rejestracja, info, odnowienie, transfer po authInfo, zmiana abonenta, delegacja NS, rezerwacja i reaktywacja, wyłączenie auto-renew, kontakty oraz hosty z glue IPv4/IPv6, saldo rejestratora i asynchroniczne raporty. A jest w tym zestawie jeden aktor, do którego mam słabość: ten od DNS-u.

Delegacja serwerów nazw przechodzi przez EPP, ale jeśli ktoś prowadzi własne serwery BIND9, po EPP sprawa się nie kończy: trzeba dopisać strefę do named.conf.local, wygenerować plik strefy i przeciągnąć konfigurację przez rndc reload. Robi to dedykowany aktor. To w tym miejscu domeny stają się kodem: opis domeny jest danymi wejściowymi, strefy są jego wynikiem, a ręczne poprawki w konfiguracji znikają z obiegu.

Tryb mock, czyli testy bez akredytacji

Integracja z prawdziwym rejestrem zwykle zaczyna się od papierów: akredytacja, konto w testowym OT&E, depozyt. Zanim cokolwiek się uruchomi, mija sporo czasu. Dlatego silnik ma trzy tryby pracy przełączane jedną linijką konfiguracji:

application.conf
app {
  # Opcje: "mock" (Symulator EPP), "ote" (NASK OT&E), "prod" (NASK Live)
  registry_mode = "mock"
  registry_mode = ${?REGISTRY_MODE}
}

Cytat dosłowny z README; komentarz zostaje po polsku, bo tak to wychodzi z naszego repozytorium.

Tryb mock jest wpięty dokładnie tam, gdzie transport produkcyjny. Wbudowany symulator odtwarza w pamięci pełny cykl życia domeny .PL — check, create, renew, info, book, reactivate — razem z błędami w konwencji rejestru, jak 2302 Object exists. Cały silnik da się uruchomić na laptopie albo w pipeline GitHub Actions, bez konta w NASK i bez depozytu. Sami tak przepuszczamy każdą zmianę, zanim trafi na OT&E. Z repozytorium „do przeczytania” robi się repozytorium „do sprawdzenia”, a to różnica między ciekawostką a narzędziem, które da się ocenić.

Notka o dojrzałości, bo i tak ją zobaczycie

Gdy klikniecie w repo, zauważycie trzy rzeczy. Publiczna historia to jeden commit z końca sierpnia. Gwiazdek jest zero. Część dokumentacji jest po polsku.

To nie jest przeoczenie. Nie przepisywaliśmy historii, nie dokładaliśmy „starszych” commitów, nie sadziliśmy gwiazdek. Silnik napędza naszą platformę produkcyjną, ale jako projekt open source jest w dniu zerowym i świadomie nie obiecujemy żadnej roadmapy — złamana obietnica kosztuje więcej niż jej brak. Z README dowiecie się też, że kod jest Apache 2.0, natomiast nazwy Adcelero i ElasticName wraz z logotypami pozostają znakami towarowymi: forkujcie i używajcie, tylko nie przemianowujcie tego na nas.

Jeśli w kodzie coś wygląda podejrzanie, być może faktycznie jest. Zgłoście issue. Recenzje obcych są dokładnie tym, po co to publikujemy.

Repo i reszta

Całość leży tutaj: github.com/adcelero/elasticname-epp-engine

git clone https://github.com/adcelero/elasticname-epp-engine.git

Sklonujcie, ustawcie registry_mode = "mock" i przejdźcie cały cykl .PL — bez konta, bez depozytu. Jeśli coś się przyda, issue powie nam o tym więcej niż gwiazdka.

A jeśli po lekturze potrzebujecie po prostu domeny .PL — prowadzimy rejestrację na tym samym silniku, którego wnętrza przed chwilą przeszliście:

Zarejestruj .PL Sprawdź dostępność
🏛️
Polecana Lektura: Warsztat Architekta

Czego uczy 15 lat budowania systemów rozproszonych

Autorski esej doktrynalny Artura Buczka: dlaczego w inżynierii sieciowej prostota i determinizm zawsze wygrywają z modnym przekombinowaniem oraz Dekalog Architekta na kolejne 15 lat.

Czytaj Pełny Esej Doktrynalny →