diff --git a/docs/secret-system-spec.md b/docs/secret-system-spec.md index df93b2a..109220c 100644 --- a/docs/secret-system-spec.md +++ b/docs/secret-system-spec.md @@ -1,76 +1,72 @@ -# Edut Secret System - Two-Factor Designation Spec +# Edut Secret System - Two-Factor Designation Spec (Hardened) ## Overview The Edut designation flow is a two-factor protocol: 1. Phone verification by SMS (Twilio). -2. Email verification by classified mailto request (Mailgun). +2. Email verification by protocol email (Mailgun). -Both factors bind to the same designation code and auth token. This is not a throwaway waitlist. It is the pre-launch identity envelope that can transition into launch activation workflows. +Both factors are bound to one server-generated designation record. +This is a pre-launch identity bootstrap used for launch activation continuity. -## End-to-End Flow +## User Experience Sequence (Continue Flow) + +1. Initial page state: orb, identity text, footer links. +2. First click anywhere: globe spin intensifies, `continue` appears. +3. Click `continue`: minimal phone input appears. +4. User submits phone: page calls `POST /secret/initiate`. +5. API returns status ticket + expiry. UI enters ambient waiting state. +6. User receives SMS and replies `CONFIRM`. +7. Page polls `GET /secret/status` using the status ticket. +8. On `phone_verified`, page attempts `mailto` open. +9. If browser blocks `mailto`, show explicit fallback action: `continue to email`. +10. User sends email request. +11. Mailgun webhook verifies and marks email complete. +12. UI shows `acknowledged · {token}` and stores local marker. + +Privacy and Terms links bypass flow and navigate normally. + +## Architecture ``` -Visitor clicks orb on edut.ai - -> phone input appears - -> user submits phone -POST /secret/initiate - -> code issued - -> row created/updated in SQLite - -> SMS sent via Twilio -User receives SMS and replies: CONFIRM -Twilio POST /twilio/inbound - -> signature verified - -> pending designation matched - -> phone_verified_at set -Landing page polls /secret/status/{code} - -> sees phone_verified=true - -> opens mailto:{code}@secret.edut.ai with protocol body -User sends email -Mailgun POST /mailgun/inbound - -> signature verified - -> code parsed from recipient local-part - -> email_verified_at set - -> classified confirmation reply sent -Landing page stores acknowledged token +Landing page -> POST /secret/initiate + -> SMS outbound via Twilio +User reply CONFIRM -> POST /twilio/inbound +Landing page poll (ticket-bound) -> GET /secret/status +phone verified -> mailto compose (auto attempt + fallback button) +User sends email -> POST /mailgun/inbound +Mailgun reply -> classified confirmation email ``` ## Infrastructure -| Service | Domain / System | Purpose | -|---------|------------------|---------| -| Landing UI | `edut.ai`, `edut.dev` | Orb, phone capture, mailto initiation | -| Twilio | Toll-free SMS number | Outbound designation SMS + inbound CONFIRM replies | -| Mailgun | `secret.edut.ai` | Inbound email processing + confirmation replies | -| API host | `api.edut.ai` | `/secret/*`, `/twilio/inbound`, `/mailgun/inbound` | -| Database | SQLite (`/var/lib/edut/secrets.db`) | Durable designation state | +| Service | Domain / Endpoint | Purpose | +|---------|-------------------|---------| +| Landing UI | `edut.ai`, `edut.dev` | Continue flow + phone capture + mailto bridge | +| API | `api.edut.ai/secret/*` | Initiate + status polling | +| Twilio | `api.edut.ai/twilio/inbound` | SMS outbound/inbound verification | +| Mailgun | `api.edut.ai/mailgun/inbound` + `secret.edut.ai` | Inbound designation email + confirmation reply | +| Database | `/var/lib/edut/secrets.db` | Durable designation state | -## Twilio Channel Design +## State Machine (Deterministic) -### Number strategy +`pending_phone` -> `phone_verified` -> `pending_email` -> `acknowledged` -- Use a toll-free SMS number. -- Voice is disabled or rejected. -- Complete toll-free verification before production traffic. +Additional terminal/side states: -### Outbound SMS template +- `expired` (ticket or flow timed out) +- `abandoned` (no progress within retention window) +- `opted_out` (STOP/UNSUBSCRIBE received) +- `rate_limited` (temporary) -``` -━━━━━━━━━━━━━━━━━ -EDUT GOVERNANCE PROTOCOL +Rules: -Designation: {token} -Reply CONFIRM to proceed. -━━━━━━━━━━━━━━━━━ -``` +1. No backwards transition except administrative recovery event with audit entry. +2. Email verification is blocked unless state is `pending_email`. +3. `acknowledged` is terminal for this flow. -Where `{token}` is formatted from the code: - -- code: `0217073045482` -- token: `0217-0730-4548-2` - -## API Surface +## API Contracts ## 1) Initiate @@ -81,100 +77,128 @@ Request JSON: ```json { "phone": "+12065550123", - "origin": "https://edut.ai" + "origin": "https://edut.ai", + "locale": "en" } ``` Behavior: 1. Normalize phone to E.164. -2. Rate-limit by IP, phone, and rolling time window. -3. Generate designation code (`MMDDHHmmssmmm`) and auth token. -4. Insert or upsert designation row with `phone` and `created_at`. -5. Send SMS via Twilio. -6. Return `200` with minimal response: +2. Enforce rate limits (IP + phone + rolling windows). +3. Server generates `code` and `auth_token`. +4. Ignore/reject any client-supplied code/token fields. +5. Create designation row in `pending_phone` state. +6. Issue short-lived polling ticket. +7. Send SMS via Twilio. + +Response: ```json { - "code": "0217073045482", - "token": "0217-0730-4548-2", - "status": "sms_sent" + "status": "sms_sent", + "status_ticket": "st_...", + "ticket_expires_at": "2026-02-17T07:40:45Z", + "display_token": "0217-0730-4548-2" } ``` Notes: -- Do not expose internal row id. -- Never return auth token in API responses. +- Do not return `auth_token`. +- Do not return internal row id. +- `display_token` is presentation-only. ## 2) Status -### `GET /secret/status/{code}` +### `GET /secret/status` -Response JSON: +Auth: + +- `Authorization: Bearer {status_ticket}` + +Behavior: + +1. Validate ticket existence and expiry. +2. Enforce ticket + IP rate limits. +3. Return minimal status payload. +4. Ticket is reusable during TTL for polling. +5. Ticket is invalidated at terminal state (`acknowledged`, `expired`, `opted_out`) or TTL expiry. + +Response example: ```json { - "code": "0217073045482", + "status": "phone_verified", "phone_verified": true, "email_verified": false, - "status": "phone_verified" + "mailto": { + "enabled": true, + "recipient": "0217073045482@secret.edut.ai", + "subject": "EDUT-0217073045482", + "body": "..." + } } ``` -Rules: +Security notes: -- Polling endpoint is read-only. -- Response is minimal and does not expose phone/email values. -- Apply rate limits to prevent enumeration. +- No code in URL path. +- No phone/email values in response. -## 3) Twilio inbound +## Twilio Webhook ### `POST /twilio/inbound` -Expected Twilio fields include: +Required checks: -- `From` (sender phone) -- `To` (Edut toll-free number) -- `Body` (message body) -- `MessageSid` +1. Verify `X-Twilio-Signature` with `TWILIO_AUTH_TOKEN`. +2. Idempotency on `MessageSid`. +3. Normalize `From` number. -Verification: +Accepted commands (case-insensitive): -- Validate `X-Twilio-Signature` against Twilio auth token. -- Reject unsigned or invalid payloads. +- `CONFIRM` -> verify phone and transition to `pending_email`. +- `STOP`, `UNSUBSCRIBE`, `CANCEL`, `END`, `QUIT` -> transition to `opted_out`. +- `HELP` -> send support/help response. -Processing: +Processing rules: -1. Normalize sender phone. -2. Accept `CONFIRM` (case-insensitive, trim whitespace). -3. Find latest pending designation for that phone within validity window. -4. Idempotency key: `MessageSid`. -5. Mark `phone_verified_at` and set state `phone_verified`. -6. Return `2xx` only after persistence. +- `CONFIRM` applies to latest active designation for that phone within validity window. +- Return `2xx` only after persistence or deterministic dedupe. -## 4) Mailgun inbound +## Mailgun Webhook ### `POST /mailgun/inbound` -Expected Mailgun fields include: +Required checks: -- `recipient` (`{code}@secret.edut.ai`) -- `sender` -- `Message-Id` -- `subject` +1. Verify Mailgun signature with `MAILGUN_SIGNING_KEY`. +2. Idempotency on `Message-Id` (or provider event id fallback). +3. Parse code from recipient local-part first (`{code}@secret.edut.ai`). -Verification: +Processing rules: -- Verify Mailgun webhook signature using Mailgun signing key. +1. Match designation by code where state is `pending_email`. +2. Record sender and verification timestamp. +3. Set state to `acknowledged`. +4. Send confirmation reply email. -Processing: +## SMS Content (Compliance-Safe) -1. Parse `code` from recipient local-part. -2. Require existing designation row with `phone_verified_at IS NOT NULL`. -3. Record `email`, `message_id`, and `email_verified_at`. -4. Idempotency key: `Message-Id` (or Mailgun event id fallback). -5. Send confirmation reply through Mailgun. +Use plain-text SMS (no heavy unicode framing): + +``` +EDUT GOVERNANCE PROTOCOL +Designation: {display_token} +Reply CONFIRM to proceed. +Reply STOP to opt out. HELP for support. +``` + +Rationale: + +- Better carrier compatibility. +- Clear STOP/HELP semantics. ## Confirmation Email Template @@ -225,6 +249,8 @@ CREATE TABLE IF NOT EXISTS designations ( message_id TEXT, auth_token TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending_phone', + status_ticket TEXT, + ticket_expires_at DATETIME, created_at DATETIME DEFAULT (datetime('now')), replied_at DATETIME, reply_status TEXT DEFAULT 'pending' @@ -234,10 +260,12 @@ CREATE INDEX idx_designations_code ON designations(code); CREATE INDEX idx_designations_phone ON designations(phone); CREATE INDEX idx_designations_email ON designations(email); CREATE INDEX idx_designations_status ON designations(status); +CREATE INDEX idx_designations_ticket ON designations(status_ticket); CREATE INDEX idx_designations_created ON designations(created_at); CREATE TABLE IF NOT EXISTS twilio_inbound_events ( message_sid TEXT PRIMARY KEY, + designation_code TEXT, from_phone TEXT NOT NULL, body TEXT, received_at DATETIME DEFAULT (datetime('now')) @@ -245,6 +273,7 @@ CREATE TABLE IF NOT EXISTS twilio_inbound_events ( CREATE TABLE IF NOT EXISTS mailgun_inbound_events ( message_id TEXT PRIMARY KEY, + designation_code TEXT, sender TEXT, recipient TEXT, received_at DATETIME DEFAULT (datetime('now')) @@ -303,30 +332,33 @@ MAILGUN_DOMAIN=secret.edut.ai MAILGUN_SIGNING_KEY= ``` -## Error Handling and Idempotency +## Error Handling + Retry Rules -- Twilio signature failure: return `403`. -- Mailgun signature failure: return `403`. -- Duplicate inbound event id: treat as idempotent success. -- Transient DB/send failure: return `5xx` so provider retries. -- Unknown or expired confirmation attempts: return `200` and no state mutation. +- Invalid Twilio signature: `403`. +- Invalid Mailgun signature: `403`. +- Duplicate inbound event id: idempotent `2xx`. +- Transient DB/send failure: `5xx` so provider retries. +- Unknown/expired status ticket: `401` or `410`. +- Unknown confirmation attempt: `200` with no state mutation. ## Security Controls -- Signature verification on both inbound providers. -- Per-IP and per-phone initiate throttles. -- Per-code polling throttles. -- Do not expose auth token except in confirmation email. -- Back up SQLite and protect file permissions. +- Signature verification for Twilio and Mailgun. +- Strict rate limits on initiate and status endpoints. +- Ticket-bound polling only; no direct code enumeration. +- Auth token never exposed via status/initiate API responses. +- SMS STOP/HELP compliance enabled. +- SQLite file encryption-at-rest where host supports it, plus regular backups. ## Launch Evolution -At launch, each fully verified designation (`phone_verified_at` + `email_verified_at`) is eligible for activation messaging: +At launch, only fully verified designations (`phone_verified_at` + `email_verified_at`) are eligible for access-level change messaging. -- Classification can transition from `OBSERVER` to `OPERATOR`. +- Classification may transition `OBSERVER -> OPERATOR`. - Auth token remains stable. -- Message can include deployment handoff link or activation instructions. +- Activation handoff can attach to this identity envelope. ## Important Boundary -This two-factor designation flow is a pre-launch identity bootstrap. It does not replace runtime device trust anchors, workspace isolation controls, or offline license enforcement in the product runtime. +This two-factor designation flow is a pre-launch identity bootstrap. +It does not replace runtime trust anchors, workspace isolation, or offline license/runtime enforcement in the product runtime. diff --git a/docs/vision.md b/docs/vision.md index bbb6236..f233d1d 100644 --- a/docs/vision.md +++ b/docs/vision.md @@ -10,20 +10,20 @@ The site should feel precise and established while avoiding disclosure of private implementation IP. -## Primary Experience (Two-Factor Designation) +## Primary Experience (Two-Factor, Continue Flow) 1. A visitor lands on `edut.ai`. -2. They see only the globe, identity line, and meaning line. -3. They click anywhere. -4. A minimal phone field appears inline (same aesthetic language). -5. They enter their number and submit. -6. They receive SMS from Edut protocol channel with designation token and `CONFIRM` instruction. -7. They reply `CONFIRM`. -8. Once phone verification is detected, the page opens the classified mailto request for the same designation code. -9. They send the email. -10. They receive confirmation showing designation + auth token with both channels verified. +2. They see the orb, identity line, and footer links. +3. First click anywhere increases orb spin and reveals `continue`. +4. Clicking `continue` reveals minimal phone input. +5. On submit, server initiates SMS verification and returns a short-lived status ticket. +6. User replies `CONFIRM` to protocol SMS. +7. Page polls status using ticket-bound auth. +8. On phone verification, page attempts to open protocol email compose. +9. If compose is blocked by browser gesture policy, page shows explicit fallback action. +10. After email verification completes, final UI state shows `acknowledged · {token}`. -The interaction is intended to feel like protocol registration, not a marketing funnel. +The flow should feel controlled and ambient, not like a conventional signup form. ## Three-Layer Web Model @@ -74,6 +74,9 @@ Localized: - Meaning line (`testimony · witness · evidence`) - Descriptor +- Continue label +- Phone placeholder +- Waiting/confirmation state text - `acknowledged` label - Footer labels - Full AI/accessibility context abstract @@ -87,9 +90,9 @@ English-governing at launch: The two-factor designation is the pre-launch identity envelope: -- factor 1: phone verified via SMS reply -- factor 2: email verified via protocol message -- shared binding: designation code + auth token +- Factor 1: phone verified by SMS reply. +- Factor 2: email verified via protocol message. +- Shared binding: one server-generated designation record (`code` + `auth_token`). When launch activation opens, this record becomes the continuity bridge for deployment onboarding and activation messaging. @@ -99,4 +102,4 @@ When launch activation opens, this record becomes the continuity bridge for depl 2. AI systems in supported locales can classify EDUT accurately from on-page context. 3. Screen-reader users receive equivalent conceptual context. 4. Public framing remains accurate without overexposure of architecture. -5. Two-factor designation can complete with clear state transitions and auditable evidence. +5. Two-factor designation completes through deterministic state transitions with auditable evidence. diff --git a/public/privacy/index.html b/public/privacy/index.html index 85f27fd..11188cb 100644 --- a/public/privacy/index.html +++ b/public/privacy/index.html @@ -157,7 +157,7 @@

Information We Collect

-

Information you provide directly. When you interact with our sites — including sending an email, submitting a request, contacting support, or completing a transaction — we may collect your name, email address, contact details, billing details, and any other information you choose to provide.

+

Information you provide directly. When you interact with our sites — including sending an email, submitting a request, contacting support, entering a phone number for verification, replying to verification messages, or completing a transaction — we may collect your name, email address, phone number, contact details, billing details, message content, and any other information you choose to provide.

Information collected automatically. When you visit our sites, we may collect technical and usage information such as IP address, browser type, operating system, referring URLs, pages viewed, timestamps, and interaction events. This information is collected through server logs, local storage, cookies, and similar technologies.

@@ -169,9 +169,13 @@

We may use information we collect to operate, maintain, and improve our websites, products, and services; process transactions and send related confirmations, invoices, and receipts; communicate with you regarding security, operations, product updates, and support; analyze reliability and usage trends; detect and prevent fraud, abuse, and unauthorized access; comply with legal obligations; and enforce our agreements.

+

Verified-channel purpose. Where verification workflows are used, we process phone and email channel data to confirm identity continuity, prevent automated abuse, bind protocol records to a real user, and deliver operational notices related to account activation and deployment readiness.

+

Sharing and Disclosure

-

Service providers. We may share your information with third-party providers that support hosting, infrastructure, email delivery, support tooling, analytics, and payment processing. These providers are contractually obligated to process information only for authorized service purposes.

+

Service providers. We may share your information with third-party providers that support hosting, infrastructure, email delivery, SMS delivery, support tooling, analytics, and payment processing. These providers are contractually obligated to process information only for authorized service purposes.

+ +

Communications providers. We may use communications processors such as Twilio (SMS) and Mailgun (email) to deliver and verify protocol messages. These providers process contact information and message metadata as necessary to transmit, receive, and verify communications.

Legal requirements. We may disclose your information if required to do so by law, regulation, legal process, or governmental request, or when we believe disclosure is necessary to protect our rights, your safety, or the safety of others, or to investigate fraud or respond to a government request.

@@ -194,7 +198,7 @@

Canadian residents. Canadian residents may have rights under the Personal Information Protection and Electronic Documents Act (PIPEDA) or applicable provincial legislation. Contact us to exercise these rights.

Data Retention

-

We retain your information for as long as necessary to fulfill the purposes described in this policy, comply with legal obligations, resolve disputes, and enforce our agreements. When information is no longer needed, we will securely delete or anonymize it. Designation records are retained indefinitely unless you request deletion.

+

We retain your information for as long as necessary to fulfill the purposes described in this policy, comply with legal obligations, resolve disputes, and enforce our agreements. Verification and designation records may be retained to preserve protocol integrity and anti-abuse controls. When information is no longer needed, we will securely delete or anonymize it.

Security

We implement reasonable technical and organizational measures to protect your information against unauthorized access, alteration, disclosure, or destruction, including encryption in transit and at rest where appropriate. However, no method of transmission over the internet or electronic storage is completely secure, and we cannot guarantee absolute security.