Building a Multi-Domain Tenant Portal with Automated Payment Logging via Email Forwarding

Overview

We built a complete tenant management system for a rental property, segregated entirely within the dangerouscentaur.com domain, with three key components: a secure tenant hub for credential management, a Lambda-based payment logging system, and an email-forwarding pipeline that automatically captures Zelle payments without manual intervention.

What Was Done

The project involved three interconnected systems:

  • Deploying a tenant hub portal at 3028fiftyfirststreet.92105.dangerouscentaur.com with generated credentials
  • Setting up SES email delivery from dangerouscentaur.com domain aliases to replace messages previously sent from queenofsandiego.com
  • Creating a Lambda-based payment intake system that processes Zelle email forwards and logs them to a receipts registry

Architecture & Infrastructure

Tenant Hub Portal

The hub is deployed to S3 with CloudFront acceleration. The portal lives at:

  • S3 bucket: 3028fiftyfirststreet.92105.dangerouscentaur.com
  • Index file: /index.html (contains embedded credentials table with bcrypt-hashed passwords)
  • CloudFront distribution: Configured to serve from the S3 bucket with cache invalidation on deploys

The hub includes:

  • An embedded user credentials table (username/password pairs with hashed passwords for verification)
  • A receipts dashboard that loads payment records via Lambda function URL
  • Dynamic section loading based on authenticated user context

Lambda Functions

Two Lambda functions handle the backend logic:

  • Receipt Action Lambda: Located at /scripts/lambda-receipt-action/, handles admin actions including log_rent_payment
  • Email Parser Lambda: Located at /scripts/lambda-email-parser/, processes incoming email forwards and triggers payment logging

Both are deployed with function URLs, allowing direct HTTPS invocation without API Gateway overhead. The receipt-action Lambda is protected via environment variable authentication token.

Email System

AWS SES handles all outbound email from the dangerouscentaur.com domain:

  • Domain verified in SES with DKIM tokens configured in DNS
  • Sender address: appropriate inbox alias for the domain (not specified here for security)
  • All tenant credential emails sent from this verified identity

Incoming Zelle notifications are forwarded to an email-parsing endpoint, which triggers Lambda processing.

Technical Implementation Details

Password Generation & Storage

Tenant credentials were generated with bcrypt hashing. The process:

  1. Generate random alphanumeric passwords
  2. Hash passwords using bcrypt with appropriate salt rounds
  3. Embed hashed values in the portal HTML for client-side verification
  4. Distribute plaintext passwords via SES (one-time delivery, not stored in code)

This approach allows the portal to verify passwords without storing them in plaintext or requiring a database backend.

Payment Logging Pipeline

The payment intake system works as follows:

User sends Zelle payment → Bank sends confirmation email 
→ User forwards email to dangerouscentaur domain alias 
→ SES/Lambda triggers email-parser function 
→ Parser extracts payment details (amount, date, payer)
→ Calls receipt-action Lambda with log_rent_payment action
→ Admin token validates the request
→ Payment logged to receipts.json in S3
→ Portal dashboard reflects new payment on next load

The receipt-action Lambda uses an environment variable ADMIN_TOKEN to authenticate internal calls, preventing unauthorized payment entries.

File Structure

The deployment structure in the repo:

/dangerouscentaur/demos/3028fiftyfirststreet.92105.dangerouscentaur.com/
├── index.html                          # Portal UI with embedded credentials
├── scripts/
│   ├── lambda-receipt-action/
│   │   └── lambda_function.py          # Handles payment logging & admin actions
│   └── lambda-email-parser/
│       └── lambda_function.py          # Processes forwarded emails
└── receipts.json                       # Source of truth for payments (in S3)

Deployment Pipeline

Updates to the portal follow this sequence:

  1. Update index.html locally with new content or credentials
  2. Deploy to S3: aws s3 sync . s3://3028fiftyfirststreet.92105.dangerouscentaur.com/
  3. Invalidate CloudFront cache to ensure immediate propagation
  4. For Lambda updates: package function, deploy via AWS CLI, set environment variables

Lambda deployments include setting the ADMIN_TOKEN environment variable after initial deploy, ensuring the receipt-action function can validate internally-triggered actions.

Key Architectural Decisions

Why a static receipts.json instead of DynamoDB? This keeps the system simple and fully contained in the dangerouscentaur domain without external service dependencies. S3 provides sufficient durability and cost-effectiveness for a single-property rental use case.

Why embed passwords in HTML instead of a database? The tenant hub is read-only by design—tenants authenticate to view their section. Bcrypt hashing in HTML provides client-side verification without exposing plaintext credentials to the server.

Why email forwarding instead of manual logging? Email forwarding is a natural workflow for someone already handling bank notifications. Automating the parsing eliminates data-entry friction and ensures consistency in logging.

Why separate the domains completely? The queenofsandiego.com domain is for real estate agent business; dangerouscentaur.com is for property management. Segregation prevents credential leakage, simplifies compliance, and allows independent scaling of each system.

What's Next

Future enhancements could include:

  • Automated rent reminders sent to tenants via SES
  • Dashboard reporting for landlord view (payments, late fees, occupancy)
  • Integration with other payment methods (ACH, credit card) beyond Zelle
  • Encrypted file upload to the hub for lease agreements or maintenance requests
  • Audit logging of all admin actions (payments logged, credentials issued)

The foundation is in place for all of these—the Lambda-based architecture makes adding new actions straightforward, and the SES integration provides the backbone for outbound communications.