```html

Building a Self-Contained Tenant Portal with Automated Payment Logging: Separating dangerouscentaur.com from queenofsandiego.com

What Was Done

We deployed a complete tenant management portal for a rental property at 3028 51st Street (92105) with a critical architectural requirement: complete domain isolation from the main business domain. The system needed to handle tenant credential delivery, secure hub access, and—most importantly—a frictionless payment logging mechanism that accepts forwarded bank transfer notifications.

The Core Problem

The initial implementation had a critical flaw: tenant credentials were being sent from queenofsandiego.com, creating potential security and brand confusion issues. We needed to:

  • Establish dangerouscentaur.com as a completely independent, property-facing domain
  • Configure proper email infrastructure within that domain for transactional communications
  • Build a payment logging system that accepts Zelle email forwards without requiring manual data entry
  • Ensure the tenant portal could scale to multiple properties without cross-contamination

Technical Architecture

1. Tenant Portal Infrastructure

The tenant hub lives at https://3028fiftyfirststreet.92105.dangerouscentaur.com/ with the following deployment structure:

/Users/cb/Documents/repos/sites/dangerouscentaur/demos/
└── 3028fiftyfirststreet.92105.dangerouscentaur.com/
    ├── index.html (tenant dashboard, 28+ edits during dev)
    └── scripts/
        ├── lambda-receipt-action/
        │   └── lambda_function.py (payment logging handler)
        └── lambda-email-parser/
            └── lambda_function.py (Zelle email ingestion)

The portal is deployed to S3 with CloudFront distribution caching for performance. The key infrastructure files track tenant credentials securely—never exposed in git history, injected at deployment time.

2. Email Infrastructure Separation

AWS SES was configured with dangerouscentaur.com as the verified sending domain. Rather than using a generic noreply@ address, we established a functional inbox alias that supports both sending and receiving:

# ImprovMX alias setup for dangerouscentaur.com
# Forwards to monitored inbox; SES uses it as reply-to/from address

This required:

  • SES domain verification with DKIM token configuration at the domain registrar
  • ImprovMX alias creation to establish a real mailbox identity
  • Careful validation to avoid "scary error messages" about SPF/DKIM alignment

3. Payment Logging Pipeline

The Zelle payment workflow is the architectural centerpiece. When a tenant makes a Zelle transfer, they receive a notification email from their bank. Instead of logging into the portal to record payment, they simply forward that email:

# Zelle email forward flow:
User Bank Email → dangerouscentaur.com inbox alias → 
Google Apps Script trigger → Lambda function → receipts.json (S3)

This required three components:

Lambda Email Parser (lambda-email-parser/lambda_function.py):

  • Receives forwarded emails via SES
  • Parses bank transaction details (amount, timestamp, sender)
  • Validates against known tenant phone/email patterns
  • Extracts Zelle confirmation numbers for audit trails

Lambda Receipt Action (lambda-receipt-action/lambda_function.py):

  • Receives parsed payment data from the email parser
  • Validates admin token for security
  • Appends payment record to receipts.json in S3
  • Triggers CloudFront cache invalidation for the portal
  • Logs to CloudWatch for audit compliance

Google Apps Script Handler (WarmLeadResponder.gs):

  • Monitors the dangerouscentaur inbox for Zelle forwards
  • Detects payment emails using keyword matching ("Zelle", "money sent", etc.)
  • Calls the receipt-action Lambda with admin credentials
  • Marks emails as processed to avoid duplicate logging

Key Implementation Details

Credential Generation and Distribution

Fresh temporary passwords were generated for both tenants. These were:

  • Hashed using bcrypt and stored in the portal's user credentials table
  • Sent via SES from the dangerouscentaur.com domain only
  • Never committed to git; injected at S3 deployment time
# Deployment command pattern (credentials injected from repos.env)
aws s3 cp index.html s3://[bucket-name]/index.html
aws cloudfront create-invalidation --distribution-id [ID] --paths "/*"

Payment Data Persistence

All receipts are stored in receipts.json on S3 in a standardized format:

{
  "payments": [
    {
      "tenant_id": "3028_51st_tenant_1",
      "amount": 2500.00,
      "method": "zelle",
      "timestamp": "2024-01-15T14:32:00Z",
      "confirmation_id": "ZELLE_CONF_12345",
      "parsed_from_email": true
    }
  ]
}

This file is:

  • Version-controlled within the property-specific project folder
  • Loaded by the portal dashboard to show payment history
  • Immutable once logged (payments are appended, never edited)

Admin Security

The Lambda functions require an admin token (stored as an environment variable on the receipt-action Lambda). This token is:

  • Generated once and stored in repos.env
  • Never exposed in CloudWatch logs
  • Required for all payment logging calls
  • Rotatable via Lambda environment variable updates

Infrastructure Decisions and Rationale

Why Complete Domain Separation?

Mixing operational real estate domain (queenofsandiego.com) with property-specific infrastructure (dangerouscentaur.com) creates:

  • Credential/reputation risk (if one property's email gets blacklisted, the main domain suffers)
  • Scaling confusion (each property needs its own SES sender identity)
  • Audit trail complexity (payment records from multiple sources comingle)

Complete separation means each property can be managed, scaled, or decommissioned independently.

Why Email Forwarding Over Portal UI?

Requiring tenants to log into a portal to log payments creates friction and errors. By accepting email forwards: