Skip to main content

Phase 4: Domain Verification

Implement the domain verification workflow that lets users prove ownership of a domain before running scans against it. This is a critical security boundary -- scans must never target domains the user does not control.

Prerequisites

  • Phase 3 -- The admin portal shell must be in place so the domain management UI has a home.
  • Phase 1 -- SpiceDB must be running for ownership relationship storage.

Blocks

IDBlockDescriptionAcceptance Criteria
4.1domain-service scaffolding & protoScaffold the domain-service NestJS application with gRPC transport, define domain.proto (AddDomain, VerifyDomain, ListDomains, RemoveDomain RPCs), and generate TypeScript stubs.Service starts, registers with the API Gateway, and responds to a health-check RPC.
4.2DNS TXT verification logicImplement the server-side logic that generates a unique TXT record value per domain, performs a DNS lookup to check for the record, and marks the domain as verified on success.Given a domain with the correct TXT record, VerifyDomain returns VERIFIED; without the record it returns PENDING.
4.3Auto-polling + manual verifyAdd a background job (NATS-scheduled or cron) that re-checks pending domains every 5 minutes, and expose a "Verify Now" button in the UI that triggers an immediate check.Pending domains are automatically verified within 5 minutes of the TXT record appearing; the manual button triggers an instant check and updates the UI.
4.4SpiceDB domain ownership integrationOn successful verification, write a domain:owner relationship to SpiceDB. All scan and domain RPCs must check this relationship before proceeding.Only the verified owner (or org members with the domain:viewer relation) can list or scan the domain; unauthorized access returns PERMISSION_DENIED.
4.5Domain management UIBuild the admin portal domain page: add domain form, list of domains with status badges (Pending / Verified / Failed), TXT record instructions, verify button, and delete action.Users can add, view, verify, and remove domains entirely through the UI; status badges update in real time via WebSocket.

Estimated Scope

AreaFiles / Resources
Backend serviceservices/domain-service/ (NestJS app, gRPC controllers, DNS resolver module)
Proto fileproto/domain.proto
DatabasePostgreSQL schema: domain (tables: domains, verification_tokens)
SpiceDBRelations: domain:owner, domain:viewer; permissions: domain:can_scan, domain:can_delete
Angular moduleadmin/src/app/domains/ (list, add, detail components)
NATS subjectsevents.domain.verified, events.domain.failed, jobs.domain.poll
Kubernetesk8s/domain-service/