Date: 2026-01-08
Status: Approved
Version: 2.0 (Simplified - No Scraping Required)
Hermes Bot is a subscription service that notifies customers when specific Hermes products become available in their chosen country. The system leverages shengsho.com's email notifications which already contain complete product data (name, price, SKU, direct Hermes URL, images) - eliminating the need for web scraping entirely.
Key Insight: Shengsho emails include all necessary data pre-parsed. We simply parse the HTML email, match to customer preferences, and forward notifications immediately.
Key Technologies: Hono/JSX, HTMX, Postgres, Resend.com, Stalwart Mail Server, Cheerio (HTML parsing), Rootless Podman on Hetzner
Infrastructure: Fully automated with Terraform (Hetzner provisioning) and Ansible (server configuration)
Hono Server (src/index.ts)
- HTTP routes: landing page, auth (Apple/Google OAuth), subscription management
- HTMX-powered UI (server-rendered JSX, no client-side JS except Stripe checkout)
- Webhook endpoint:
/webhooks/stalwartreceives emails from Stalwart - Synchronous processing: Parse email HTML → Match customers → Send notifications (all in webhook handler)
- Session-based auth using cookies (required for pure HTMX forms)
- No background worker needed - everything happens in webhook request
Infrastructure as Code:
- Terraform: Provisions Hetzner VPS (CPX21/CPX31), firewall rules, DNS records for all domains
- Ansible: Configures server (rootless podman, systemd units, SSL certs via certbot)
- Idempotency: All scripts run multiple times safely, start-from-zero capable
Containerized Services:
- Container 1: Stalwart mail server (receives emails on port 25/587/993)
- Container 2: Postgres database
- Container 3: Hono web app (connects to Postgres, exposes port 3000)
- Container 4: Nginx reverse proxy (SSL termination, routes to Hono)
- All managed via
podman-composeor systemd quadlet units - Rootless setup (runs as non-root user)
- Domain MX records → Hetzner server IP
Data Flow:
Shengsho Email → Stalwart → Webhook → Parse HTML → Match Customers → Send Notifications (Resend)
Processing Time: Sub-second from email arrival to customer notification (no queue delays)
-- Customer accounts (OAuth only)
CREATE TABLE IF NOT EXISTS customers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT NOT NULL UNIQUE,
oauth_provider TEXT NOT NULL, -- 'apple' or 'google'
oauth_id TEXT NOT NULL, -- provider's user ID
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(oauth_provider, oauth_id)
);
-- Active subscriptions (one row per country per customer)
CREATE TABLE IF NOT EXISTS subscriptions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
customer_id UUID NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
country_code TEXT NOT NULL, -- 'FR', 'US', 'UK', etc.
bag_collections TEXT[] NOT NULL, -- ['Birkin', 'Kelly', 'Evelyne']
status TEXT NOT NULL DEFAULT 'active', -- 'active', 'cancelled', 'payment_failed'
stripe_subscription_id TEXT UNIQUE,
stripe_customer_id TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(customer_id, country_code)
);
-- Track which countries we've subscribed to on shengsho
CREATE TABLE IF NOT EXISTS shengsho_subscriptions (
country_code TEXT PRIMARY KEY,
dummy_domain TEXT NOT NULL, -- 'albert07.com'
dummy_email TEXT NOT NULL, -- '[email protected]'
subscribed_at TIMESTAMPTZ DEFAULT NOW(),
last_notification_at TIMESTAMPTZ -- track activity
);
-- Log notifications sent to customers
CREATE TABLE IF NOT EXISTS notifications_sent (
id BIGSERIAL PRIMARY KEY,
customer_id UUID NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
product_name TEXT NOT NULL,
product_sku TEXT,
hermes_url TEXT NOT NULL,
country_code TEXT NOT NULL,
bag_collection TEXT, -- which collection matched (Birkin, Kelly, etc.)
shengsho_email_data JSONB, -- raw email for debugging
sent_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_notifications_customer ON notifications_sent(customer_id, sent_at);
CREATE INDEX IF NOT EXISTS idx_notifications_country ON notifications_sent(country_code, sent_at);
-- Server-side sessions for HTMX auth
CREATE TABLE IF NOT EXISTS sessions (
id TEXT PRIMARY KEY, -- random session ID
customer_id UUID NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
expires_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_sessions_expires ON sessions(expires_at);
-- Track applied migrations
CREATE TABLE IF NOT EXISTS schema_migrations (
version INT PRIMARY KEY,
applied_at TIMESTAMPTZ DEFAULT NOW()
);subscriptions.bag_collectionsis TEXT array for simple substring matchingnotifications_sent.shengsho_email_datastores raw email as JSONB (debugging, audit trail)UNIQUE(customer_id, country_code)prevents duplicate country subscriptions- No job queue needed - processing is synchronous and fast (<1s typically)
notifications_sentprovides full audit trail for analytics and debugging
- Pure SQL files in
migrations/directory (numbered:001_initial.sql,002_add_index.sql) - Applied via simple script that tracks applied migrations in
schema_migrationstable - Idempotent: Each migration uses
CREATE TABLE IF NOT EXISTS,CREATE INDEX IF NOT EXISTS - Start-from-zero:
./scripts/reset-db.shdrops/recreates DB, runs all migrations
1. Stalwart receives email from shengsho (e.g., to [email protected])
2. Webhook fires: POST /webhooks/stalwart with payload:
{
"from": "[email protected]",
"to": "[email protected]",
"subject": "🇪🇸 Evelyne Restocking",
"html": "<full email HTML with product details>",
"text": "fallback text"
}3. Hono handler processes synchronously:
app.post('/webhooks/stalwart', async (c) => {
const { from, to, html, subject } = await c.req.json();
// A. Validate & lookup country
const country = await db.query(
'SELECT country_code FROM shengsho_subscriptions WHERE dummy_email = $1',
[to]
);
if (!country) return c.text('Unknown recipient', 200);
// B. Parse product data from HTML
const productData = parseShengshoeEmail(html);
if (!productData.hermesUrl) return c.text('No product found', 200);
// C. Match to active customers
const matches = await matchCustomers(country.country_code, productData);
// D. Send notifications immediately
for (const customer of matches) {
await sendNotification(customer, productData);
await logNotification(customer, productData);
}
return c.text('OK', 200);
});4. Return 200 OK (entire flow completes in <1 second)
Shengsho emails contain structured HTML with all product details. Parse using Cheerio:
function parseShengshoeEmail(html) {
const $ = cheerio.load(html);
// Product link (direct Hermes URL)
const hermesUrl = $('a[href*="hermes.com/"]').attr('href');
// Product details (from structured divs)
const productName = $('div[style*="font-size:16px"]').first().text().trim();
const price = $('div[style*="font-size:12px"]').eq(0).text().trim();
const sku = $('div[style*="font-size:12px"]').eq(1).text().trim();
const imageUrl = $('img[src*="assets.shengsho.com"]').attr('src');
// Extract country code from Hermes URL
// Example: https://www.hermes.com/es/es/product/... → ES
const countryMatch = hermesUrl?.match(/hermes\.com\/([a-z]{2})\//);
const countryCode = countryMatch ? countryMatch[1].toUpperCase() : null;
return {
hermesUrl,
productName,
price,
sku,
imageUrl,
countryCode
};
}Example parsed data from real email:
- URL:
https://www.hermes.com/es/es/product/bolso-evelyne16-amazone-H069426CCFN/ - Product: "Bolso Évelyne 16 Amazone"
- Price: "1,750 €"
- SKU: "H069426CCFN"
- Image:
https://assets.shengsho.com/H069426CCFN.jpg - Country: "ES" (Spain)
Error handling:
- No Hermes URL found → Log warning, return 200 OK (might be spam/test email)
- Malformed HTML → Store raw email in JSONB, alert admin for manual review
- Missing fields (price/SKU) → Continue processing (not critical for notification)
- Catch-all for all domains: Accepts email to any address at registered domains
- Simple routing: One webhook endpoint for all domains
- Authentication: Shared secret in header (
X-Webhook-Secret) - Error handling:
- Unknown domain → log warning, return 200 OK (might be spam/test)
- Malformed HTML → store raw email, mark job 'failed', alert admin
1. Query matching subscriptions:
async function matchCustomers(countryCode, productData) {
// Get all active subscriptions for this country
const subscriptions = await db.query(`
SELECT c.id, c.email, s.bag_collections
FROM subscriptions s
JOIN customers c ON c.id = s.customer_id
WHERE s.country_code = $1
AND s.status = 'active'
`, [countryCode]);
// Filter by bag collection match (substring matching)
return subscriptions.filter(sub => {
return sub.bag_collections.some(collection =>
productData.productName.toLowerCase().includes(collection.toLowerCase())
);
}).map(sub => ({
...sub,
matchedCollection: sub.bag_collections.find(c =>
productData.productName.toLowerCase().includes(c.toLowerCase())
)
}));
}2. Send notifications via Resend.com:
async function sendNotification(customer, productData) {
await resend.emails.send({
from: '[email protected]',
to: customer.email,
subject: `🛍️ New ${customer.matchedCollection} - ${productData.countryCode}`,
text: `
New Hermes ${customer.matchedCollection} available!
${productData.productName}
${productData.price || 'Price not listed'}
SKU: ${productData.sku}
View now: ${productData.hermesUrl}
---
Manage your subscription: https://hermes-alerts.com/dashboard
Unsubscribe: https://hermes-alerts.com/unsubscribe
`
});
}3. Log notification:
async function logNotification(customer, productData) {
await db.query(`
INSERT INTO notifications_sent (
customer_id, product_name, product_sku,
hermes_url, country_code, bag_collection,
shengsho_email_data
) VALUES ($1, $2, $3, $4, $5, $6, $7)
`, [
customer.id,
productData.productName,
productData.sku,
productData.hermesUrl,
productData.countryCode,
customer.matchedCollection,
productData.rawHtml // full email for audit
]);
}Minimal text + link format:
New Hermes [Collection] available in [Country]!
[Product Title]
[Price]
Click to view: [Hermes URL]
---
Manage your subscription: [Dashboard URL]
Unsubscribe: [Unsubscribe URL]
Key characteristics:
- Plain text (no HTML for MVP)
- Fast to generate
- Loads anywhere (email clients, mobile)
- Professional but minimal
- No matching customers: Log for analytics, return 200 OK (email processed successfully)
- Resend API fails: Retry 3 times with exponential backoff, then log error and alert admin
- Customer email bounces: Resend webhook handles (disable their subscription automatically)
- Multiple collections match: Send one email mentioning primary match (first found)
- Duplicate products: Log prevents duplicate notifications (check recent sends by SKU/customer)
1. Email Parsing Failures:
- No Hermes URL found: Log warning, return 200 OK (might be spam/test)
- Malformed HTML: Store raw email in JSONB, alert admin for review
- Missing country lookup: Unknown recipient domain, log and return 200 OK
- Invalid product data: Log error with raw HTML, alert admin if pattern changes
2. Email Sending Failures (Resend API):
- Rate limit hit: Exponential backoff (1s, 2s, 4s), retry up to 3 times
- API error: Retry 3 times with backoff
- After 3 fails: Log error, alert admin, continue processing (don't block webhook)
3. Webhook Issues:
- Stalwart can't reach Hono: Stalwart queues email, retries automatically (handles downtime)
- Invalid email format: Log error, alert admin, return 200 OK (don't crash webhook)
- Unknown domain: Log warning, return 200 OK (might be spam)
- Processing timeout: Webhook has 30s timeout, log if parsing takes >5s (investigate)
4. Database Issues:
- Connection failure: App crashes, systemd restarts automatically
- Query errors: Log full context, alert admin, return 500 (Stalwart will retry)
- Transaction rollback: Notification sends are idempotent (check recent sends first)
Location: /admin route (password protected via env var or OAuth whitelist)
System Health (auto-refresh every 30s):
- Last webhook received: Timestamp per country (red if >24h ago)
- Webhook processing: Average response time, recent errors
- Email sending: Success rate (last 24h), Resend API status
- Database stats: Connection status, recent query latency
Business Metrics:
- Active subscriptions by country (table: FR: 12, US: 8, UK: 5)
- Revenue: Total MRR, subscriptions by status
- Notifications sent (last 24h/7d/30d)
- Top products: Most notified SKUs, trending collections
Operational Logs:
- Recent notifications (last 50): product, country, customer count, sent time
- Recent emails received: From shengsho, parsing status
- Shengsho activity: Last notification received per country
Manual Actions:
- Button: "Test notification" (send test email to admin)
- Form: "Add country subscription" (track new shengsho subscription)
- Button: "Parse test email" (upload HTML, see parsed output)
Alert Conditions:
- Parsing failures > 5 in 1 hour (Shengsho changed email format?)
- No emails received in 24 hours for any active country (shengsho subscription dead?)
- Email send failures > 10 in 1 hour (Resend API issues?)
- Webhook response time > 5s average (database slow?)
- Application crash/restart detected (systemd notification)
Alert Delivery:
- Email to admin
- Subject prefix:
[Hermes-Bot]for easy filtering
Future Monitoring (Post-MVP):
- Add metrics endpoint for Prometheus/Grafana
- Track: webhooks/minute, parse time, email delivery rate, customer growth
- Uptime monitoring via external service (UptimeRobot, Better Uptime)
Domain Selection:
- Use random, personal-looking domains:
albert07.com,myfavoritepuppy17.org,sunnydays2024.net - Avoid: Business terms (alert, notify, bag, hermes, luxury, track, watch)
- Mix TLDs: .com, .org, .net, .io (look like personal hobby sites)
- Register upfront: 15-20 domains, use as needed per country
- Goal: Each looks like unrelated personal site to shengsho
Domain Setup (Manual, One-Time per Country):
-
Pick unused domain from pool (e.g.,
myfavoritepuppy17.orgfor France) -
Terraform adds DNS records:
- MX record → Hetzner server IP
- SPF/DKIM records for email deliverability
- Optional: A record → static HTML page (adds legitimacy)
-
Manually subscribe to shengsho.com:
- Use email:
[email protected]or[email protected](natural-looking) - Select country: France
- Select bags: ALL (subscribe to everything)
- Use email:
-
Record in database:
INSERT INTO shengsho_subscriptions (country_code, dummy_domain, dummy_email) VALUES ('FR', 'myfavoritepuppy17.org', '[email protected]');
Stalwart Catch-All Configuration:
- Accepts email to any address at registered domains
- Webhook includes full recipient email
- Hono looks up domain in
shengsho_subscriptionstable - Maps to country_code for processing
Customer-Facing Domain:
- Single professional brand:
hermes-alerts.com(or similar) - All customer emails FROM:
[email protected] - Customers NEVER see: Dummy domains (albert07.com, etc.)
- Separation: Dummy domains = shengsho-only, look unrelated
Optional Static Pages:
- Add simple HTML page to each dummy domain
- Examples: "My Photography Blog", "Puppy Pictures", "Travel Diary"
- Makes domain look legitimate if shengsho investigates
- Can use same template, just swap title/images
- Host via Nginx on same server (minimal effort)
Workflow:
- Customer wants country not in
shengsho_subscriptionstable - Admin dashboard shows: "Action needed: Subscribe to shengsho for country XX"
- Admin manually:
- Pick unused dummy domain from pool
- Subscribe to shengsho for that country (all bags)
- Add row to
shengsho_subscriptionsvia dashboard form
- Customer immediately starts receiving notifications (automatic from this point)
Domain Pool Management:
- Track in separate table:
domain_pool(domain, status='available'/'in_use', purpose) - Admin dashboard shows available domains
- Alert when <5 domains remain available
1. Landing Page:
- Value proposition: "Never miss new Hermes products in your country"
- CTA: "Get Started"
2. Authentication:
- "Sign in with Apple" / "Sign in with Google" (OAuth only)
- No email/password forms (simplicity, security)
- Creates customer record in database
3. Configuration:
- Select country from dropdown (show only countries where shengsho covers)
- Select bag collections: Checkboxes for ["Birkin", "Kelly", "Evelyne", "Constance", "Picotin", etc.]
- Preview: "You'll receive notifications for: Birkin, Kelly in France"
4. Payment (Stripe Checkout):
- Show price: "$X/month for France notifications"
- Click "Subscribe" → Redirect to Stripe Checkout
- Stripe handles: Card entry, Apple Pay, payment processing
- Webhook confirms payment → activate subscription
5. Success:
- Redirect to dashboard: "Your subscription is active!"
- Show: Selected country, bag collections, manage/cancel options
Customer capabilities:
- View active subscriptions (country + bag collections)
- Add/remove bag collections (within same country subscription)
- Add new country (triggers new Stripe subscription)
- Cancel subscription (cancels at period end)
- Update billing info (redirect to Stripe portal)
- Unsubscribe from emails (soft delete)
No JavaScript required:
- All forms use HTMX:
hx-post,hx-get,hx-swap - Server renders updated HTML fragments
- Smooth UX without client-side complexity
| Component | Technology | Purpose |
|---|---|---|
| Web Framework | Hono | Fast, lightweight, edge-compatible |
| Templating | Hono JSX | Server-side rendering |
| Frontend | HTMX | Dynamic UI without JavaScript |
| Authentication | OAuth (Apple/Google) | Secure, passwordless login |
| Database | Postgres | Reliable, powerful querying |
| HTML Parsing | Cheerio | Parse Shengsho email HTML |
| Email Receiving | Stalwart | Self-hosted mail server with webhooks |
| Email Sending | Resend.com | Reliable transactional email API |
| Payments | Stripe | Subscription billing, Apple Pay |
| Containers | Rootless Podman | Secure, daemonless containers |
| Infrastructure | Terraform + Ansible | Automated provisioning & configuration |
| Hosting | Hetzner VPS | Cost-effective European hosting |
This guide assumes implementation via LLM CLI tools (Factory/Droid, Claude CLI, etc.) that can read files, execute commands, and make edits directly.
Phase 1 (Infrastructure)
├── [PARALLEL] Terraform VPS + DNS ───────────────┐
├── [PARALLEL] Ansible playbook draft ────────────┤
└── [PARALLEL] Domain registration (manual) ──────┘
│
[SEQUENTIAL] Deploy & verify
│
Phase 2 + Phase 3 ← Start in parallel sessions
├── [PARALLEL] Hono routes + HTMX
├── [PARALLEL] OAuth flows
├── [PARALLEL] Stripe integration
├── [PARALLEL] Webhook handler + email parsing + notifications
│
Phase 4 (Admin & Testing) ← Can overlap
├── [PARALLEL] Dashboard UI
├── [PARALLEL] Monitoring alerts
├── [PARALLEL] E2E testing
│
Phase 5 (Launch) ← Sequential validation
Key Parallelization Opportunities:
- Multiple LLM sessions: Generate Terraform + Ansible simultaneously
- Phase 2 & 3 can proceed fully in parallel (no shared dependencies)
- Phase 3 combines parsing + matching + sending (no worker needed)
- Admin dashboard (Phase 4) can start once DB schema exists
Starting a task:
Read docs/plans/2026-01-08-hermes-bot-mvp-design.md,
then implement [component] following Section [N].
Same session when:
- Iterating on errors from LLM's own code
- Adding features to files LLM just created
- Debugging issues with full context
Fresh session when:
- New phase/component
- LLM stuck repeating same mistake
- Switching to different part of codebase
Dependencies: None (starting point)
Parallel Tasks:
| Task | LLM Prompt | Verification |
|---|---|---|
| Terraform VPS | "Read the design doc Section 1 and 11. Create Terraform for Hetzner CPX31 with firewall for ports 22,25,80,443,587,993" | terraform plan shows server + firewall |
| Terraform DNS | "Add DNS module for MX, SPF, DKIM records. Reference Section 7 for domain strategy" | Plan shows DNS resources |
| Ansible draft | "Create Ansible playbook for rootless podman setup per Section 1. Target Debian 12" | ansible-playbook --check passes |
Sequential Tasks:
| Task | LLM Prompt | Verification |
|---|---|---|
| Deploy | "Run terraform apply, then ansible-playbook. Debug any failures" | SSH to server works |
| Stalwart | "Configure Stalwart catch-all per Section 3. Set webhook to /webhooks/stalwart" | Test email arrives in logs |
Handoff Checklist:
- SSH to server works
-
podman psshows containers running - Stalwart receives test email
- Postgres accepts connections
Dependencies: Phase 1 infrastructure running
Parallel Tasks:
| Task | LLM Prompt | Verification |
|---|---|---|
| Hono skeleton | "Create Hono app with JSX per Section 9. Routes: /, /login, /dashboard, /webhooks/stalwart" | curl localhost:3000 returns HTML |
| OAuth | "Implement Apple/Google OAuth per Section 8. Use session table from Section 2" | OAuth redirect works |
| Stripe | "Add Stripe checkout per Section 8. Webhook for subscription.created" | Test checkout completes |
| DB queries | "Create customer/subscription CRUD using schema from Section 2" | Insert/select works |
Handoff Checklist:
- Full login flow works (OAuth → session → dashboard)
- Subscription creates in DB with Stripe webhook
- Dashboard displays customer subscriptions
- HTMX forms submit without page reload
Dependencies: Hono app running, DB schema exists
Parallel Tasks:
| Task | LLM Prompt | Verification |
|---|---|---|
| Webhook handler | "Implement /webhooks/stalwart per Section 3. Synchronous processing: parse → match → send" | POST returns 200 in <1s |
| Email parser | "Parse shengsho emails with cheerio per Section 3. Extract all product data" | Test HTML extracts complete data |
| Customer matching | "Match product to subscriptions per Section 4. Substring matching on collections" | Correct customers matched |
| Notifications | "Send via Resend per Section 4. Log to notifications_sent table" | Test email received |
Handoff Checklist:
- Test email → notification sent to matching customers
- Parsed data includes URL, name, price, SKU, country
- Only customers with matching collections notified
- Notifications logged in database
- Unknown domains log warning but don't crash
- Processing completes in <1 second
Dependencies: Core app + email processing functional
Parallel Tasks:
| Task | LLM Prompt | Verification |
|---|---|---|
| Dashboard | "Create /admin with stats per Section 5. Password protect via env var" | Dashboard loads |
| Health checks | "Show webhook status, email sending, notifications sent per Section 5" | Metrics display correctly |
| Alerts | "Email admin when parsing failures > 5/hour per Section 5" | Test alert triggers |
| Domain mgmt | "UI to track shengsho_subscriptions per Section 7" | Can add/view domains |
Handoff Checklist:
- Admin can view system health (webhook stats, email delivery)
- Recent notifications display with details
- Parse test email button works
- Alerts fire when thresholds exceeded
- Domain management UI functional
Sequential Tasks:
| Task | LLM Prompt | Verification |
|---|---|---|
| E2E test | "Write E2E test: signup → subscribe → receive notification" | Full flow passes |
| Load test | "Simulate 50 concurrent webhook deliveries. Check for race conditions" | No duplicate notifications |
| Security | "Review OAuth, Stripe webhooks, email parsing for vulnerabilities" | No critical issues |
| Runbook | "Document ops procedures: restart app, parse test emails, add country" | Runbook complete |
Handoff Checklist:
- E2E test passes reliably
- Load test shows no race conditions or duplicate sends
- Security review complete (no exposed secrets)
- Operations runbook documented
- Beta customers invited
Start of task:
Read the design doc at docs/plans/2026-01-08-hermes-bot-mvp-design.md.
Implement [component] following Section [N].
Create files in [directory].
When LLM misses requirements:
Check Section [N] again - you missed [specific requirement].
Update the implementation.
When code breaks:
[error message]
Fix this. The code should match Section [N] behavior.
When stuck:
Read Section [N] and Section [M] together.
Explain how [component A] connects to [component B], then implement.
Verify design compliance:
Compare your implementation against Section [N].
List anything that deviates from the design.
| Phase | Quick Check | Full Check |
|---|---|---|
| 1 | terraform plan, ansible --check |
SSH works, podman ps shows containers |
| 2 | curl localhost:3000 |
Complete OAuth + Stripe flow |
| 3 | curl -X POST localhost:3000/webhooks/stalwart |
Job appears in DB with correct data |
| 4 | SELECT * FROM scrape_jobs WHERE status='completed' |
Notification email arrives |
| 5 | curl localhost:3000/admin |
All metrics display, alerts fire |
| 6 | Run E2E script | Beta user completes full journey |
| Situation | Tell the LLM |
|---|---|
| Wrong library | "Use [X] from Section 9, not [Y]. Refactor." |
| Missing feature | "Section [N] requires [feature]. Add it." |
| Broken code | "Debug this error: [message]. Check against Section [N]." |
| Stuck in loop | Start fresh session: "Read Section [N] fresh and reimplement [component]" |
| Partial work | "Continue from [last working file]. Next: [specific task]." |
Don't: Ask LLM to implement everything at once
Do: One task at a time, verify before next
Don't: Accept first output without testing
Do: Run verification command, iterate if broken
Don't: Let LLM invent architecture
Do: Always reference specific design sections
Don't: Debug forever in same session
Do: Fresh session after 3 failed attempts
Don't: Ignore design doc constraints
Do: Quote relevant sections when redirecting LLM
| Risk | Impact | Mitigation |
|---|---|---|
| Shengsho detects/blocks domains | High | Use 15-20 innocent-looking domains, rotate slowly |
| Shengsho changes email format | Medium | Store raw JSONB, update parser, reprocess old emails |
| Shengsho stops service | High | Have backup: direct Hermes scraping (complex, future) |
| Email parsing breaks | Medium | Alert on parse failures, manual review, update selectors |
| Webhook overload (spam/attacks) | Low | Rate limiting, validate sender, monitor patterns |
| Database connection exhaustion | Medium | Connection pooling, monitor query performance |
| Resend API limits hit | Low | Monitor send rate, implement backoff, upgrade plan if needed |
-
Advanced Filtering:
- Size-specific notifications (e.g., "Birkin 25 only")
- Color preferences (e.g., "black or gold only")
- Price range filters
- Parse additional fields from Shengsho emails
-
Mobile App:
- Push notifications (instant, more reliable than email)
- Native iOS/Android apps
- In-app subscription management
-
Analytics Dashboard:
- Customer engagement metrics
- Product popularity tracking (trending SKUs)
- Revenue analytics and forecasting
- Email parsing success rates
-
Direct Hermes Scraping (Backup Plan):
- Eliminate shengsho dependency entirely
- Scrape Hermes directly (requires Puppeteer + proxies)
- More complex, higher cost, but full control
-
Multi-Brand Support:
- Expand to Chanel, Louis Vuitton, etc.
- Same architecture: email-based monitoring
- Subscribe to multiple notification services
-
Auto-Checkout Service:
- Premium tier: Automated purchase attempts
- Requires sophisticated scraping + payment handling
- High value, high complexity, regulatory considerations
This design provides a solid foundation for Hermes Bot MVP. The architecture prioritizes:
- Extreme Simplicity: Single-process synchronous handling, no job queue, no scraping
- Speed: Sub-second notification delivery from Shengsho email → customer inbox
- Reliability: Minimal moving parts, straightforward error handling, idempotent operations
- Low Cost: No proxy/captcha costs, minimal infrastructure (4 containers total)
- Maintainability: All logic in one place, easy to debug, HTML parsing vs browser automation
Key Innovation: Leverage Shengsho's pre-parsed product data instead of scraping - eliminates 70% of original complexity and cost.
The manual shengsho subscription approach validates the business model with minimal technical risk. If Shengsho becomes unreliable, the architecture can evolve to direct Hermes scraping (add Puppeteer + proxies as needed).
Next Steps: Begin Phase 1 implementation (infrastructure setup).