domainhawk

Docs

Operations

Configuration, OIDC providers, availability semantics, production and Docker.

Configuration and deployment notes for running domainhawk.

Configuration

All settings are environment variables. Copy .env.example and adjust.

Application

Variable Default Description
DH_APP_URL http://127.0.0.1:8090 Public base URL. Required for OIDC redirects, cookies and email links.
DH_VERSION dev Shown in the footer and health response.
DH_LOG_LEVEL info Minimum log level: debug, info, warn or error (case-insensitive). Applied to the structured logger and PocketBase's logger at startup.
DH_LOG_FORMAT json Log output encoding: json for structured logs or text for logfmt.
DH_PASSWORD_AUTH true Enable email/password login. Set false for SSO-only.
DH_SESSION_SECRET random per boot HMAC key for the OIDC state cookie. Set a stable random value (32+ bytes) for multiple replicas.
DH_REGISTRATION_ENABLED false Allow self-service account creation. Requires DH_PASSWORD_AUTH=true.
DH_ADMIN_EMAIL empty Support inbox for Help tickets. Empty disables the support form.
DH_REGISTRAR_URL_TEMPLATE https://porkbun.com/checkout/search?q={domain} "Buy" link. Must contain {domain}.
DH_SEED_EMAIL / DH_SEED_PASSWORD empty Optional local bootstrap account. Development only; both must be set.

OIDC

Set all three of DH_OIDC_ISSUER, DH_OIDC_CLIENT_ID and DH_OIDC_CLIENT_SECRET to enable OIDC. Leave them empty for password-only.

Variable Default Description
DH_OIDC_ISSUER empty Issuer URL; discovery uses ${issuer}/.well-known/openid-configuration.
DH_OIDC_CLIENT_ID empty Confidential client ID.
DH_OIDC_CLIENT_SECRET empty Client secret.
DH_OIDC_DISPLAY_NAME SSO Button label: "Continue with …".
DH_OIDC_SCOPES openid email profile Space-separated; must include openid, and email is required to create/link users.

Register these redirect URIs:

  • ${DH_APP_URL}/auth/callback — the HTMX UI.
  • ${DH_APP_URL}/api/oauth2-redirect — optional, for PocketBase API clients.

The provider must return a verified email (email_verified: true). Users are provisioned on first login by OIDC subject, with fallback linking by verified email.

Scheduler, RDAP and WHOIS

Variable Default Description
DH_CHECK_INTERVAL 4h Minimum time between checks of one entry.
DH_CHECK_TIMEOUT 20s Per-domain check timeout.
DH_CHECK_CONCURRENCY 3 Parallel checks during a sweep.
DH_MAX_DOMAINS_PER_USER 100 Maximum watched domains per user.
DH_MAX_ADDS_PER_DAY 100 Maximum adds per rolling 24 hours, from an internal log (deleting does not reset it).
DH_API_RATE_LIMIT 120 Requests per minute per API token.
DH_CRON_SCHEDULE */15 * * * * Scheduler wake-up; checks entries older than the interval.
DH_CRON_ENABLED true Disable the scheduler entirely.
DH_STARTUP_SWEEP true Run one due sweep shortly after startup.
DH_RDAP_BOOTSTRAP_URL IANA dns.json Override for testing or a mirror.
DH_RDAP_TIMEOUT 10s HTTP timeout for bootstrap and registry requests.
DH_RDAP_CACHE_TTL 1h Per-domain cache; manual force checks bypass it.
DH_RDAP_USER_AGENT domainhawk/<version> User agent sent to RDAP servers.
DH_WHOIS_ENABLED true Fallback for TLDs without RDAP.
DH_WHOIS_TIMEOUT 10s WHOIS connect/read timeout.
DH_WHOIS_IANA_ADDRESS whois.iana.org:43 IANA referral server.

The cron wakes every 15 minutes, selects entries with no check or a last_checked older than DH_CHECK_INTERVAL, and checks them with bounded concurrency. A domain shared by several users is fetched once per cache TTL. Check now bypasses the cache but is limited to once per day per entry.

Email notifications

Notifications are enabled by default but only send when SMTP is configured, either via the environment below or the PocketBase dashboard (Settings → Mail settings). Environment values overwrite dashboard settings at startup.

Variable Default Description
DH_NOTIFICATIONS_ENABLED true Global switch.
DH_SMTP_HOST empty SMTP host; setting this configures the SMTP client.
DH_SMTP_PORT 587 SMTP port.
DH_SMTP_USERNAME empty Leave empty for an unauthenticated relay.
DH_SMTP_PASSWORD empty SMTP password.
DH_SMTP_AUTH_METHOD PLAIN PLAIN or LOGIN.
DH_SMTP_TLS false true = implicit TLS (usually 465); false = plaintext with opportunistic STARTTLS (usually 587).
DH_SMTP_LOCAL_NAME empty EHLO/HELO name for relays that require it.
DH_MAIL_FROM domainhawk@localhost From address.
DH_MAIL_FROM_NAME domainhawk From display name.

One availability email is sent per availability window per entry. Expiry alerts are sent once per threshold — when a name first falls within 30, 7 and 1 days of the stored expiry date — and drop alerts once per distinct pending delete or redemption period status. Failed delivery is retried on the next sweep because notification markers are only written after a successful send. A transient unknown result does not reopen the availability window or erase the last known registry details. Each entry has separate availability and expiry/drop preferences on its Details page.

OIDC provider examples

Keycloak: confidential client, Standard Flow enabled, PKCE required (S256). Redirect URI ${DH_APP_URL}/auth/callback; issuer https://keycloak.example/realms/domainhawk.

Authentik: OAuth2/OpenID provider with the same redirect URI; issuer https://authentik.example/application/o/domainhawk/.

Auth0: Regular Web Application with the same redirect URI; issuer https://TENANT.eu.auth0.com/.

Availability semantics

RDAP is authoritative for whether a registration record exists, but it is not a purchase quote:

  • 404 from the authoritative registry with a valid RDAP error body → available.
  • A domain object response → registered / taken.
  • No RDAP service for the TLD → query the registry's WHOIS service. WHOIS is trusted only for explicit answers such as DENIC's Status: free / Status: connect; ambiguous or rate-limited responses stay unknown.
  • Rate limits, registry errors, malformed responses, non-RDAP 404 HTML or no WHOIS service → unknown. Unknown never becomes available, which avoids false positives.
  • Reserved, premium and registry-locked names may still be unavailable even without a registration record. Confirm with a registrar before buying.

WHOIS is plaintext and registry-specific: a best-effort fallback, not a replacement for RDAP.

Drop Radar metadata

RDAP domain objects may also include registration and expiry events, registrar, status codes and nameservers. domainhawk stores these as best-effort metadata and shows them on the entry's Details page. They do not change availability: only an authoritative 404 registration record makes a domain available, and pending delete or redemption period still means the domain is registered.

Not every registry publishes an expiry date. DENIC's .de WHOIS service, for example, exposes registration state but not the expiry date, so those entries show Expiry not published and do not send expiry alerts. A renewal moves the expiry date; domainhawk resets the reminder cycle when the date moves later by more than a day.

Expiry and drop emails are enabled per entry on the Details page. They are separate from the availability email preference, so an existing watchlist entry does not start receiving new kinds of mail until its owner opts in.

Production notes

  • Reverse proxy: terminate TLS in front of domainhawk and set DH_APP_URL to the public HTTPS URL so cookies become Secure. Configure PocketBase's trusted proxy headers so rate limiting sees real client IPs.
  • Persistence: back up pb_data (SQLite database, settings, uploads); PocketBase also exposes dashboard backups.
  • Scaling: the scheduler and in-memory RDAP cache assume one instance. For several replicas, run the scheduler in one and share the cache or accept duplicate polls. Set DH_SESSION_SECRET identically.
  • Health: GET /healthz returns JSON and checks the database.
  • Security: HttpOnly, SameSite=Lax session cookies; OIDC state, nonce and PKCE S256; origin-checked UI mutations; rate-limited password login; enabled PocketBase API rate limits; CSP/frame/referrer headers. Public signup is disabled unless DH_REGISTRATION_ENABLED=true; signed-in users can manage their own email and password. Other user mutation is disabled. Logout rotates the token key and revokes sessions; password reset, password change and email change also rotate the token key and deactivate every API token owned by the account. Direct PocketBase mutations of watchlist, api_tokens, email_codes and support_tickets are disabled; API tokens may expire or be unlimited.
  • Check all: the watchlist header action respects the once-per-day limit per entry and reports skipped entries.
  • MFA: the HTMX login flow does not implement MFA. Leave MFA disabled on the users collection (the default).
  • User provisioning: OIDC users are created on first login. Email linking requires an existing verified account; an unverified match is rejected with a 403 and must be resolved by an administrator. When DH_REGISTRATION_ENABLED=true visitors can create password accounts; they must verify their email with a one-time code before signing in. Password reset and email changes also use email codes, so configure SMTP.

Docker

make docker-build
docker run --rm -p 8090:8090 \
  -v domainhawk_data:/app/pb_data \
  -e DH_APP_URL=http://localhost:8090 \
  domainhawk:dev

The image runs as UID 10001. Use a named volume (as above) or chown -R 10001:10001 ./pb_data when bind-mounting. Pass DH_* variables or an --env-file for OIDC and SMTP.