11 KiB
Edut Secret System — Deployment Spec
Overview
The Edut secret system is an organic waitlist that runs on edut.ai. Visitors click the landing page, their email client opens with a pre-filled classified-looking message, they send it, and an auto-reply confirms their designation. No forms, no signups — they email you, you reply.
Architecture
Visitor clicks orb on edut.ai
↓
Email client opens: mailto:0217073045482@secret.edut.ai
↓
Person sends email
↓
Mailgun receives on catch-all (*@secret.edut.ai)
↓
Mailgun POSTs to https://api.edut.ai/mailgun/inbound
↓
Webhook parses email, stores in SQLite, sends classified reply
↓
Reply threads under original email in sender's inbox
↓
Copy of inbound + reply also forwarded to j@edut.ai (Gmail archive)
Infrastructure (Already Configured)
| Service | Domain | Purpose |
|---|---|---|
| Google Workspace | edut.ai | Business email (j@edut.ai), OAuth |
| Mailgun (Edut account) | secret.edut.ai | Catch-all inbound, API sending |
| Cloudflare | api.edut.ai → 89.167.8.148 | Webhook endpoint |
| Hetzner | 89.167.8.148 | Server running webhook + nginx |
Mailgun Configuration
- Domain:
secret.edut.ai(verified, green) - Wildcard: On
- Tracking (click/open/unsubscribe): All off
- Sending API key: stored securely (ask Joshua)
- Inbound route:
match_recipient(".*@secret.edut.ai")→https://api.edut.ai/mailgun/inbound(Forward + Stop, priority 0) - Route also forwards to
j@edut.aifor Gmail archive
Component 1: Nginx Configuration
File: /etc/nginx/sites-available/api.edut.ai
server {
listen 80;
server_name api.edut.ai;
location /mailgun/inbound {
proxy_pass http://127.0.0.1:3847;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
SSL is handled by Cloudflare (proxied). Nginx listens on 80, Cloudflare terminates TLS.
Port 3847 is arbitrary — the webhook service listens here.
Component 2: Webhook Service
Language
Go or Python. Single file, minimal dependencies. Must be production-ready from first deploy.
Endpoint: POST /mailgun/inbound
Mailgun sends a multipart form POST with these fields (among others):
| Field | Description |
|---|---|
sender |
Email address of the person who sent the email |
recipient |
The timestamp address (e.g., 0217073045482@secret.edut.ai) |
subject |
EDUT-0217073045482 |
body-plain |
Plain text body (the classified access request) |
Message-Id |
Original message ID (needed for threading reply) |
Date |
Email date header |
Processing Logic
1. Parse the recipient local-part to extract the code
- Recipient format: "{code}@secret.edut.ai" where code is MMDDHHmmssmmm (13 digits)
- Extract code from `recipient` first; use subject parsing only as fallback validation
2. Parse sender email address
3. Store in SQLite:
- id (auto-increment — this is their sequential designation number)
- code (the timestamp code from subject)
- email (sender address)
- recipient (the full recipient address)
- message_id (Message-Id header for threading)
- created_at (server timestamp)
4. Format the token for display:
- code "0217073045482" → token "0217-0730-4548-2"
- Slice: [0:4]-[4:8]-[8:12]-[12:]
5. Generate auth token:
- Hash the code with a secret salt → first 16 chars of hex digest
- Format as: xxxx-xxxx-xxxx-xxxx
6. Get the sequential designation number (the SQLite auto-increment id)
- Pad to 4 digits: id 47 → "0047"
7. Build the classified reply (see template below)
8. Send reply via Mailgun API:
- From: "EDUT <protocol@secret.edut.ai>"
- To: sender's email
- Subject: "Re: EDUT-{code}"
- In-Reply-To: original Message-Id header
- References: original Message-Id header
- Body: plain text classified reply
- Use the sending API key for secret.edut.ai domain
Mailgun Signature Verification
Every inbound POST from Mailgun includes a signature. Verify it to prevent spoofing:
timestamp— Unix timestamptoken— Random stringsignature— HMAC-SHA256 oftimestamp + tokenusing your Mailgun webhook signing key
Reject any request that fails verification.
Error Handling
- If recipient/subject does not match expected format, return
200and drop as noise. - Use
Message-Id(or Mailgun event id) as idempotency key for inbound processing. - If SQLite write fails due to transient failure, return
5xxso Mailgun retries. - If Mailgun send fails, retry with bounded backoff; if still failing, return
5xx. - Return
2xxonly after persistence succeeds and outbound reply is accepted or deterministically deduped.
Component 3: Auto-Reply Email Template
Plain text body:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
EDUT GOVERNANCE PROTOCOL
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ACCESS REGISTRATION CONFIRMED
Designation: #{designation_number}
Auth Token: {auth_token}
Classification: OBSERVER
Status: ACKNOWLEDGED
Timestamp: {iso_timestamp}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
This designation is permanent and
non-transferable. You will be notified
when your access level changes.
Do not reply to this message.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Edut LLC. All rights reserved.
Development and licensing of
deterministic governance systems.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Variable Substitutions
| Variable | Source | Example |
|---|---|---|
{designation_number} |
SQLite auto-increment id, zero-padded to 4 | 0047 |
{auth_token} |
HMAC-SHA256 hash of code, formatted xxxx-xxxx-xxxx-xxxx | e7d2-4f1a-9bc3-a210 |
{iso_timestamp} |
Server UTC time in ISO 8601 | 2026-02-17T07:30:45Z |
Email Headers
From: EDUT <protocol@secret.edut.ai>
To: {sender_email}
Subject: Re: EDUT-{code}
In-Reply-To: {original_message_id}
References: {original_message_id}
Content-Type: text/plain; charset=utf-8
The In-Reply-To and References headers ensure the reply threads under the original email in Gmail/Outlook/Apple Mail.
Component 4: Landing Page Deployment
Files
public/index.html— the Three.js globe landing pagepublic/privacy/index.html— privacy policypublic/terms/index.html— terms of usetranslations/*.json— locale bundles for localized landing content
Deploy to both domains:
edut.ai— primary landing pageedut.dev— same page for now
Nginx for landing pages:
server {
listen 80;
server_name edut.ai www.edut.ai;
root /var/www/edut.ai;
index index.html;
}
server {
listen 80;
server_name edut.dev www.edut.dev;
root /var/www/edut.dev;
index index.html;
}
Copy public/index.html to /var/www/edut.ai/index.html and /var/www/edut.dev/index.html.
Serve translations/ at /translations so locale files can be loaded by the landing page.
Component 5: SQLite Database
File: /var/lib/edut/secrets.db
Schema:
CREATE TABLE IF NOT EXISTS designations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT NOT NULL UNIQUE,
email TEXT NOT NULL,
recipient TEXT NOT NULL,
message_id TEXT,
auth_token TEXT NOT NULL,
created_at DATETIME DEFAULT (datetime('now')),
replied_at DATETIME,
reply_status TEXT DEFAULT 'pending'
);
CREATE INDEX idx_designations_email ON designations(email);
CREATE INDEX idx_designations_code ON designations(code);
CREATE INDEX idx_designations_created ON designations(created_at);
Component 6: Launch Day Email
When the platform launches, query all designations and send the access level change email through Mailgun:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
EDUT GOVERNANCE PROTOCOL
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ACCESS LEVEL CHANGE
Designation: #{designation_number}
Auth Token: {auth_token}
Classification: OPERATOR
Status: ACTIVE
Your system is ready for deployment.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Edut LLC. All rights reserved.
Development and licensing of
deterministic governance systems.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Same format as the access confirmation. Same auth token. Classification changes from OBSERVER to OPERATOR. Status changes from ACKNOWLEDGED to ACTIVE.
Environment Variables
MAILGUN_API_KEY=<sending API key for secret.edut.ai>
MAILGUN_DOMAIN=secret.edut.ai
MAILGUN_SIGNING_KEY=<from Mailgun account settings, for webhook verification>
SECRETS_DB_PATH=/var/lib/edut/secrets.db
AUTH_SALT=<random 32+ character string for hashing auth tokens>
Systemd Service
File: /etc/systemd/system/edut-secret.service
[Unit]
Description=Edut Secret Webhook
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/edut/secret
ExecStart=/opt/edut/secret/secret-server
EnvironmentFile=/opt/edut/secret/.env
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Testing
- Send an email to
test0217120000000@secret.edut.ai - Verify webhook receives the POST
- Verify SQLite entry created with designation #1
- Verify classified auto-reply received and threads correctly
- Verify copy arrives in
j@edut.aiGmail - Send a second email to verify designation number increments to #2
- Clear test data before going live
Security Notes
- Verify Mailgun webhook signatures on every request
- Rate limit the endpoint (prevent abuse if someone discovers the URL)
- The auth token is a one-way hash — it cannot be reversed to reveal the code or email
- SQLite file should be backed up regularly (it's the designation list)
- The sending API key must never be exposed in code or logs
Domain Separation
| Domain | Purpose |
|---|---|
| edut.ai | Brand, landing page, business email, public identity |
| secret.edut.ai | Catch-all email for secret system (Mailgun) |
| api.edut.ai | Webhook endpoint (Hetzner via Cloudflare) |
| edut.dev | Product, developer docs, technical infrastructure |