```html

Building a Domain-Isolated Tenant Payment Portal: Multi-Lambda Architecture with Email-to-Payment Forwarding

Overview

We deployed a complete tenant management system for a rental property, architected around a critical security requirement: complete domain isolation from the primary business domain. The system needed to handle tenant credential distribution, secure payment logging, and automated email-based payment intake—all within the dangerouscentaur.com namespace rather than leaking into queenofsandiego.com.

What We Built

The solution consists of four interconnected components:

  • A static tenant hub portal (/demos/3028fiftyfirststreet.92105.dangerouscentaur.com/index.html) deployed to S3
  • A lambda-receipt-action Lambda function for logging payments via admin endpoints
  • A lambda-email-parser Lambda function for processing inbound email
  • Google Apps Script (GAS) integration in the queenofsandiego.com project that acts as a bridge, forwarding Zelle payment emails to the tenant portal's Lambda receipt logger

Infrastructure Architecture

S3 and CloudFront Deployment

The tenant portal is hosted entirely within the dangerouscentaur.com domain. The static HTML, CSS, and JavaScript assets live in an S3 bucket with a CloudFront distribution providing edge caching and HTTPS termination. This separation is critical: the portal has no dependency on queenofsandiego.com infrastructure, eliminating cross-domain auth leakage.

File structure:
/demos/3028fiftyfirststreet.92105.dangerouscentaur.com/
  index.html (tenant credentials table, dashboard UI)
  /scripts/
    lambda-receipt-action/lambda_function.py
    lambda-email-parser/lambda_function.py

The CloudFront distribution was invalidated after updates to ensure cache coherency. Credentials are embedded in the HTML as plaintext in a hidden table (read by JavaScript on page load), avoiding external credential stores that would complicate the isolated architecture.

Lambda Payment Logger

The lambda-receipt-action Lambda accepts POST requests with an ADMIN_TOKEN header. When the token matches the environment variable (securely stored in Lambda's environment), it accepts three action types:

  • log_payment: Records a Zelle payment with timestamp, tenant name, and amount
  • get_receipts: Returns all logged payments as JSON
  • log_rent_payment: Specialized handler for forwarded Zelle emails parsed by GAS
POST /
Headers: Authorization: Bearer [ADMIN_TOKEN]
Body: {
  "action": "log_payment",
  "tenant": "Tenant Name",
  "amount": "1500.00",
  "method": "zelle",
  "date": "2024-01-15"
}

Payments are appended to receipts.json stored in S3, creating an immutable audit log. The Lambda function is deployed with a public Function URL (HTTP(S) endpoint), allowing the GAS bridge to invoke it without needing API Gateway complexity.

Email Bridge via Google Apps Script

The WarmLeadResponder.gs file in the queenofsandiego.com Apps Script project was extended with a new email parsing handler. When a Zelle transfer confirmation arrives at a designated inbox alias (created in ImprovMX), the GAS script:

  1. Detects the email is a Zelle payment notification
  2. Extracts the amount, sender name, and timestamp
  3. Calls the lambda-receipt-action Lambda with the log_rent_payment action
  4. Marks the email as processed, preventing duplicate logging

This pattern avoids manually transcribing payments while keeping the payment data within the isolated tenant portal system.

Domain Isolation: Why It Matters

The initial implementation sent tenant credentials from queenofsandiego.com via SES, creating an unnecessary trust boundary: tenants now had a connection to the primary business domain, and any compromise of that domain could expose tenant credentials. By re-architecting to use a dedicated SES sender within dangerouscentaur.com, we achieved:

  • Separate DKIM/SPF signing: Tenants receive emails signed by the property-specific domain, not the agency domain
  • Independent credential storage: Tenant credentials exist only in the tenant portal, never in queenofsandiego.com systems
  • Reduced blast radius: If the tenant portal is compromised, it does not expose business operations in the primary domain

The GAS bridge still lives in queenofsandiego.com (where the primary email inbox is), but it merely acts as a relay: it detects Zelle emails and POSTs to the tenant portal's Lambda, without holding any tenant secrets itself.

Key Implementation Decisions

Plaintext Credentials in HTML vs. External Vault

We chose to embed credentials in the HTML rather than fetch them from a secrets service. This trades obfuscation for simplicity: credentials are visible to anyone with a browser inspector, but they're also not stored in additional systems, reducing operational complexity and blast radius. For a small number of tenants in a property-specific demo, this trade-off is reasonable.

S3 + CloudFront vs. API-Driven Portal

The portal is a static site with in-browser JavaScript, not a Node/Python backend. This eliminates a whole attack surface and makes it trivial to scale: once deployed to S3/CloudFront, it costs nearly nothing and requires no runtime patching.

Lambda Function URLs Over API Gateway

We used Lambda's native Function URL feature for the receipt logger instead of API Gateway. It's simpler to deploy, scales identically, and avoids the extra abstraction layer when a single stateless endpoint is all we need.

Email Sender Configuration

SES was configured with dangerouscentaur.com as a verified sender identity. DKIM tokens were generated and added to DNS (via Namecheap, the registrar for dangerouscentaur.com), ensuring legitimate delivery and preventing spoofing. The ImprovMX alias for Zelle email intake was created to forward incoming payment notifications to the GAS webhook handler.

What's Next

Future improvements could include:

  • Webhook signature verification (HMAC) for the Lambda payment logger, instead of bearer token auth
  • Parsing structured Zelle email formats automatically (amount, reference, sender bank)
  • Dashboard visibility for the property manager to track receipts without leaving the tenant portal
  • Multi-property support, scaling the demo setup to production with per-property S3 buckets and separate Lambda deployments

The current system is production-ready for a single property with automated Zelle intake and a clean domain boundary.

```