# Site Verification System

## Overview

MaplePress implements **DNS-based domain ownership verification** to ensure users actually own the domains they register. Sites start in "pending" status and remain there until verified through DNS TXT record validation.

## Verification Method: DNS TXT Records

MaplePress uses **DNS TXT record verification** - the industry standard used by Google, Cloudflare, and other major services. This proves domain ownership, not just dashboard access.

### Why DNS Verification?

- **Proves domain ownership**: Only someone with DNS access can add TXT records
- **Industry standard**: Same method used by Google Search Console, Cloudflare, etc.
- **Secure**: Cannot be spoofed or bypassed without actual domain control
- **Automatic**: Backend performs verification via DNS lookup

## Site Status Lifecycle

### Status Constants

**File**: `internal/domain/site/site.go:61-67`

```go
const (
    StatusPending   = "pending"   // Site created, awaiting DNS verification
    StatusActive    = "active"    // Site verified via DNS and operational
    StatusInactive  = "inactive"  // User temporarily disabled
    StatusSuspended = "suspended" // Suspended due to violation or non-payment
    StatusArchived  = "archived"  // Soft deleted
)
```

## 1. Site Creation (Pending State)

**File**: `internal/usecase/site/create.go`

When a site is created via **POST /api/v1/sites**:

### What Gets Generated

1. **API Key** (test or live mode)
   - Test mode: `test_sk_...` (skips DNS verification)
   - Live mode: `live_sk_...` (requires DNS verification)

2. **Verification Token** (lines 88-92)
   - Format: `mvp_` + 128-bit random token (base64-encoded)
   - Example: `mvp_xyz789abc123`
   - Used in DNS TXT record: `maplepress-verify={token}`

3. **DNS Verification Instructions**
   - Provides step-by-step DNS setup guide
   - Includes domain registrar examples (GoDaddy, Namecheap, Cloudflare, etc.)
   - Explains DNS propagation timing (5-10 minutes typical)

4. **Site Entity** (lines 104-113)
   - Initial status: `StatusPending`
   - `IsVerified`: `false`
   - `VerificationToken`: Set to generated token

### Response Example

```json
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "domain": "example.com",
  "site_url": "https://example.com",
  "api_key": "live_sk_a1b2...",           // ⚠️ SHOWN ONLY ONCE
  "verification_token": "mvp_xyz789abc123",
  "status": "pending",
  "search_index_name": "site_...",
  "verification_instructions": "To verify ownership of example.com, add this DNS TXT record:\n\nHost/Name: example.com\nType: TXT\nValue: maplepress-verify=mvp_xyz789abc123\n\nInstructions:\n1. Log in to your domain registrar...\n2. Find DNS settings...\n3. Add a new TXT record...\n4. Wait 5-10 minutes for DNS propagation\n5. Click 'Verify Domain' in MaplePress"
}
```

**Documentation**: `docs/API/create-site.md`

## 2. Test Mode Bypass

**File**: `internal/domain/site/site.go:115-125`

### Test Mode Detection

```go
func (s *Site) IsTestMode() bool {
    return len(s.APIKeyPrefix) >= 7 && s.APIKeyPrefix[:7] == "test_sk"
}
```

### Verification Requirement Check

```go
func (s *Site) RequiresVerification() bool {
    return !s.IsTestMode()  // Test mode sites skip verification
}
```

**Key Points:**
- Sites with `test_sk_` API keys **skip verification** entirely
- Useful for development and testing
- Test mode sites can sync pages immediately

## 3. API Access Control

**File**: `internal/domain/site/site.go:127-140`

### CanAccessAPI() Method

```go
func (s *Site) CanAccessAPI() bool {
    // Allow active sites (fully verified)
    if s.Status == StatusActive {
        return true
    }
    // Allow pending sites (waiting for verification) for initial setup
    if s.Status == StatusPending {
        return true
    }
    // Block inactive, suspended, or archived sites
    return false
}
```

**Important**: Pending sites **CAN access the API** for:
- Status checks (`GET /api/v1/plugin/status`)
- Initial plugin setup
- Retrieving site information

## 4. Verification Enforcement

### Where Verification is Required

**File**: `internal/usecase/page/sync.go:85-89`

When syncing pages (**POST /api/v1/plugin/sync**):

```go
// Verify site is verified (skip for test mode)
if site.RequiresVerification() && !site.IsVerified {
    uc.logger.Warn("site not verified", zap.String("site_id", siteID.String()))
    return nil, domainsite.ErrSiteNotVerified
}
```

**Error**: `internal/domain/site/errors.go:22`
```go
ErrSiteNotVerified = errors.New("site is not verified")
```

### HTTP Response

```json
{
  "type": "about:blank",
  "title": "Forbidden",
  "status": 403,
  "detail": "site is not verified"
}
```

## 5. DNS Verification Implementation

### DNS Verifier Package

**File**: `pkg/dns/verifier.go`

```go
type Verifier struct {
    resolver *net.Resolver
    logger   *zap.Logger
}

func (v *Verifier) VerifyDomainOwnership(ctx context.Context, domain string, expectedToken string) (bool, error) {
    // Create context with 10-second timeout
    lookupCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
    defer cancel()

    // Look up TXT records for the domain
    txtRecords, err := v.resolver.LookupTXT(lookupCtx, domain)
    if err != nil {
        return false, fmt.Errorf("DNS lookup failed: %w", err)
    }

    // Expected format: "maplepress-verify=TOKEN"
    expectedRecord := fmt.Sprintf("maplepress-verify=%s", expectedToken)

    for _, record := range txtRecords {
        if strings.TrimSpace(record) == expectedRecord {
            return true, nil // Domain ownership verified!
        }
    }

    return false, nil // TXT record not found
}
```

### DNS Verification Use Case

**File**: `internal/usecase/site/verify.go`

The verification use case performs DNS lookup:

```go
func (uc *VerifySiteUseCase) Execute(ctx context.Context, tenantID gocql.UUID, siteID gocql.UUID, input *VerifySiteInput) (*VerifySiteOutput, error) {
    // Get site from repository
    site, err := uc.repo.GetByID(ctx, tenantID, siteID)
    if err != nil {
        return nil, domainsite.ErrSiteNotFound
    }

    // Check if already verified
    if site.IsVerified {
        return &VerifySiteOutput{Success: true, Status: site.Status, Message: "Site is already verified"}, nil
    }

    // Test mode sites skip DNS verification
    if site.IsTestMode() {
        site.Verify()
        if err := uc.repo.Update(ctx, site); err != nil {
            return nil, fmt.Errorf("failed to update site: %w", err)
        }
        return &VerifySiteOutput{Success: true, Status: site.Status, Message: "Test mode site verified successfully"}, nil
    }

    // Perform DNS TXT record verification
    verified, err := uc.dnsVerifier.VerifyDomainOwnership(ctx, site.Domain, site.VerificationToken)
    if err != nil {
        return nil, fmt.Errorf("DNS verification failed: %w", err)
    }

    if !verified {
        return nil, fmt.Errorf("DNS TXT record not found. Please add the verification record to your domain's DNS settings")
    }

    // DNS verification successful - mark site as verified
    site.Verify()
    if err := uc.repo.Update(ctx, site); err != nil {
        return nil, fmt.Errorf("failed to update site: %w", err)
    }

    return &VerifySiteOutput{Success: true, Status: site.Status, Message: "Domain ownership verified successfully via DNS TXT record"}, nil
}
```

### Verify Method

**File**: `internal/domain/site/site.go:169-175`

```go
// Verify marks the site as verified
func (s *Site) Verify() {
    s.IsVerified = true
    s.Status = StatusActive
    s.VerificationToken = "" // Clear token after verification
    s.UpdatedAt = time.Now()
}
```

## 6. What Pending Sites Can Do

**File**: `internal/interface/http/handler/plugin/status_handler.go`

### Allowed Operations

✅ **GET /api/v1/plugin/status** - Check site status and quotas
- Returns full site details
- Shows `is_verified: false`
- Shows `status: "pending"`

### Blocked Operations

❌ **POST /api/v1/plugin/sync** - Sync pages to search index
- Returns 403 Forbidden
- Error: "site is not verified"

❌ **POST /api/v1/plugin/search** - Perform searches
- Blocked for unverified sites

❌ **DELETE /api/v1/plugin/pages** - Delete pages
- Blocked for unverified sites

## 7. Verification Token Details

**File**: `internal/usecase/site/generate_verification_token.go`

### Token Generation

```go
func (uc *GenerateVerificationTokenUseCase) Execute() (string, error) {
    b := make([]byte, 16) // 16 bytes = 128 bits
    if _, err := rand.Read(b); err != nil {
        uc.logger.Error("failed to generate random bytes", zap.Error(err))
        return "", err
    }

    token := base64.RawURLEncoding.EncodeToString(b)
    verificationToken := "mvp_" + token // mvp = maplepress verify

    uc.logger.Info("verification token generated")
    return verificationToken, nil
}
```

**Token Format:**
- Prefix: `mvp_` (MaplePress Verify)
- Encoding: Base64 URL-safe (no padding)
- Strength: 128-bit cryptographic randomness
- Example: `mvp_dGhpc2lzYXRlc3Q`

**Security:**
- Never exposed in JSON responses (marked with `json:"-"`)
- Stored in database only
- Cleared after verification

## 8. DNS Verification Flow

### Step-by-Step Process

1. **User creates site** via dashboard (POST /api/v1/sites)
   - Backend generates API key and verification token
   - Site status: `pending`
   - Response includes DNS setup instructions
   - User receives: API key (once), verification token, DNS TXT record format

2. **User adds DNS TXT record** to domain registrar
   - Logs in to domain registrar (GoDaddy, Namecheap, Cloudflare, etc.)
   - Navigates to DNS management
   - Adds TXT record: `maplepress-verify={verification_token}`
   - Waits 5-10 minutes for DNS propagation (can take up to 48 hours)

3. **User installs WordPress plugin**
   - Plugin activation screen shows
   - User enters API key
   - Plugin connects to backend

4. **Plugin checks status** (GET /api/v1/plugin/status)
   - Backend returns site status: `pending`
   - Plugin shows "Site not verified" message
   - Plugin displays DNS instructions if not verified

5. **User verifies site** (POST /api/v1/sites/{id}/verify)
   - User clicks "Verify Domain" in plugin or dashboard
   - No request body needed (empty POST)
   - Backend performs DNS TXT lookup for domain
   - Backend checks for record: `maplepress-verify={verification_token}`
   - If found: Site transitions `pending` → `active`, `IsVerified` set to `true`
   - If not found: Returns error with DNS troubleshooting instructions

6. **Plugin can now sync** (POST /api/v1/plugin/sync)
   - Verification check passes
   - Pages are synced and indexed
   - Search functionality enabled

## 9. Architectural Design Decisions

### Why Pending Sites Can Access API

From `site.go:127-140`, the design allows pending sites to:
- Check their status
- View usage statistics
- Prepare for verification

This is a **deliberate UX decision** to allow:
1. Plugin to be activated immediately
2. Admin to see connection status
3. Admin to complete verification steps
4. Smoother onboarding experience

### Why DNS Verification is Required

DNS verification prevents:
- **Domain squatting**: Claiming domains you don't own
- **Abuse**: Indexing content from sites you don't control
- **Impersonation**: Pretending to be another site
- **Unauthorized access**: Using the service without permission

DNS TXT record verification is the industry standard because:
- **Proves domain control**: Only someone with DNS access can add TXT records
- **Widely recognized**: Same method used by Google Search Console, Cloudflare, etc.
- **Cannot be spoofed**: Requires actual access to domain registrar
- **Automatic verification**: Backend can verify ownership without manual review

### Test Mode Rationale

Test mode (`test_sk_` keys) bypasses verification to enable:
- Local development without DNS
- Integration testing in CI/CD
- Staging environments
- Development workflows

## 10. Security Considerations

### Token Security

1. **Generation**:
   - Cryptographically secure random generation
   - 128-bit entropy (sufficient for this use case)
   - Base64 URL-safe encoding

2. **Storage**:
   - Stored in database as plain text (used in DNS TXT record)
   - Cleared after successful verification
   - Only accessible to authenticated tenant

3. **DNS Verification Security**:
   - DNS TXT records are public (as intended)
   - Token is meaningless without backend verification
   - 10-second timeout on DNS lookups prevents DoS
   - Token cleared after verification prevents reuse

### Attack Vectors Mitigated

1. **Domain Squatting**: DNS verification proves domain ownership
2. **Token Guessing**: 128-bit entropy makes brute force infeasible
3. **Token Reuse**: Token cleared after successful verification
4. **Man-in-the-Middle**: HTTPS required for all API calls
5. **DNS Spoofing**: Uses multiple DNS resolvers and validates responses
6. **DNS Cache Poisoning**: 10-second timeout limits attack window

## 11. API Documentation

See individual endpoint documentation:
- [Create Site](./API/create-site.md) - Initial site creation
- [Verify Site](./API/verify-site.md) - Site verification endpoint
- [Plugin Status](./API/plugin-verify-api-key.md) - Check verification status
- [Sync Pages](./API/plugin-sync-pages.md) - Requires verification

## 12. WordPress Plugin Integration

The WordPress plugin should:

1. **On Activation**:
   - Prompt user for API key
   - Connect to backend
   - Check verification status

2. **If Not Verified**:
   - Display DNS TXT record instructions
   - Show the exact TXT record to add: `maplepress-verify={token}`
   - Provide domain registrar examples (GoDaddy, Namecheap, Cloudflare)
   - Explain DNS propagation timing (5-10 minutes)
   - Provide "Verify Domain" button
   - Disable sync/search features

3. **Verification Process**:
   - User clicks "Verify Domain"
   - Plugin calls POST /api/v1/sites/{id}/verify (no body)
   - Backend performs DNS TXT lookup
   - If successful: Enable all features
   - If failed: Show specific DNS error (record not found, timeout, etc.)

4. **After Verification**:
   - Enable all features
   - Allow page synchronization
   - Enable search functionality
   - Hide verification prompts

5. **Error Handling**:
   - Handle 403 "site is not verified" gracefully
   - Guide user to DNS verification process
   - Show DNS troubleshooting tips (check propagation, verify record format)
   - Retry verification status check

## 13. Database Schema

### Site Table Fields

```
sites_by_id:
  - id (UUID, primary key)
  - tenant_id (UUID)
  - status (text: pending|active|inactive|suspended|archived)
  - is_verified (boolean)
  - verification_token (text, sensitive)
  - ...
```

### Indexes Required

No special indexes needed for DNS verification - uses existing site_id and tenant_id lookups.

## 14. Troubleshooting

### Common Issues

1. **DNS TXT record not found**:
   - Check DNS propagation status (use dig or nslookup)
   - Verify record format: `maplepress-verify={exact_token}`
   - Wait 5-10 minutes for DNS propagation
   - Check that TXT record was added to correct domain/subdomain
   - Verify no typos in the verification token

2. **DNS lookup timeout**:
   - Check domain's DNS servers are responding
   - Verify domain is properly registered
   - Check for DNS configuration issues
   - Try again after DNS stabilizes

3. **Site stuck in pending**:
   - Verify DNS TXT record is correctly set
   - Call verification endpoint: POST /api/v1/sites/{id}/verify
   - Check logs for DNS lookup errors
   - Use DNS checking tools (dig, nslookup) to verify record

4. **Test mode not working**:
   - Verify API key starts with `test_sk_`
   - Check `IsTestMode()` logic in site.go:115-125
   - Test mode sites skip DNS verification entirely

5. **DNS verification fails**:
   - Token may have been cleared (already verified)
   - DNS record format incorrect
   - Wrong domain or subdomain
   - Check error logs for specific DNS errors

### Debug Commands

```bash
# Check DNS TXT record manually
dig TXT example.com
nslookup -type=TXT example.com

# Check site status
curl -X GET http://localhost:8000/api/v1/sites/{id} \
  -H "Authorization: JWT {token}"

# Verify site via DNS
curl -X POST http://localhost:8000/api/v1/sites/{id}/verify \
  -H "Authorization: JWT {token}"
```

## 15. Future Enhancements

Potential improvements to the verification system:

1. **Token Expiration**:
   - Add 24-48 hour expiration for verification tokens
   - Allow token regeneration
   - Email token to site admin

2. **Alternative Verification Methods**:
   - Meta tag verification (alternative to DNS)
   - File upload verification (.well-known/maplepress-verify.txt)
   - WordPress plugin automatic verification (callback endpoint)

3. **Automatic Re-verification**:
   - Periodic DNS checks to ensure domain ownership hasn't changed
   - Alert if DNS record is removed
   - Grace period before disabling site

4. **Verification Audit Log**:
   - Track when site was verified
   - Record who performed verification
   - Log IP address and timestamp
   - DNS lookup results and timing
