web/docs/secret-system-spec.md

319 lines
9.0 KiB
Markdown

# Edut Secret System - Wallet-First Designation + Membership Spec
## Overview
The Edut onboarding flow is deterministic and wallet-first:
1. Visitor requests designation intent from `edut.ai`.
2. Server issues one-time typed-data payload (`nonce`, `deadline`, `price`, `currency`).
3. Visitor signs intent with wallet (identity proof, no value transfer).
4. Server verifies signature and creates/updates designation record.
5. Visitor mints paid EDUT membership on-chain (intent proof).
6. Server confirms transaction and marks membership active.
7. Visitor downloads the EDUT platform app from post-mint success links.
This flow is the pre-launch identity and commerce envelope. It is not a throwaway waitlist.
## 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`: wallet explainer appears.
4. User selects `I have a wallet` or `I need a wallet`.
5. `I need a wallet` shows install guidance and remains in-page.
6. `I have a wallet` triggers wallet connection request.
7. Page calls `POST /secret/wallet/intent`.
8. Wallet signs EIP-712 typed intent.
9. Page calls `POST /secret/wallet/verify`.
10. Page requests membership quote via `POST /secret/membership/quote`.
11. Wallet submits membership mint transaction on Base.
12. Page confirms via `POST /secret/membership/confirm` and/or status poll.
13. UI shows `acknowledged · {token}` when membership is active.
14. Post-mint success state presents `download your platform` links (Desktop/iOS/Android).
15. Download endpoints perform wallet membership status checks before channel authorization messaging.
16. Member opens the app, signs in with the same wallet, and receives platform updates through app notifications.
Privacy and Terms links bypass flow and navigate normally.
## Architecture
```text
Landing page -> POST /secret/wallet/intent
Wallet signs typed intent (EIP-712)
Landing page -> POST /secret/wallet/verify
Landing page -> POST /secret/membership/quote
Wallet sends paid mint tx on Base
Landing page -> POST /secret/membership/confirm
Membership active -> acknowledged state
Post-mint success -> app download links (Desktop/iOS/Android)
```
## Core Commerce Rule
1. Membership is required to purchase marketplace offers.
2. Membership is not a product/module license.
3. Offer-specific licenses/entitlements are purchased separately.
4. Membership purchase delivers initial platform access (download entry point) immediately after activation.
5. Entitlement ownership wallet is the runtime authority; payment wallet may differ when authorized by ownership-wallet proof.
## Infrastructure
| Service | Domain / Endpoint | Purpose |
|---------|-------------------|---------|
| Landing UI | `edut.ai`, `edut.dev` | Continue flow + wallet intent/signature + membership mint UX |
| API | `api.edut.ai/secret/wallet/intent` | Create one-time designation intent |
| API | `api.edut.ai/secret/wallet/verify` | Verify signature and bind wallet identity |
| API | `api.edut.ai/secret/membership/quote` | Return current payable membership quote |
| API | `api.edut.ai/secret/membership/confirm` | Confirm membership tx and activation state |
| Chain | Base | Membership mint settlement and evidence |
| Database | `/var/lib/edut/secrets.db` | Durable designation + membership state |
## Deterministic State Machine
`pending_signature` -> `signature_verified` -> `pending_membership_mint` -> `membership_active`
Additional side states:
- `intent_expired`
- `quote_expired`
- `tx_unconfirmed`
- `rejected` (invalid signature, wrong chain, mismatched wallet)
- `rate_limited`
Rules:
1. No backwards transition except administrative recovery event with audit entry.
2. `membership_active` requires successful on-chain payment confirmation.
3. Signature and quote nonces are single-use; replay attempts are rejected.
4. Acknowledged UI state requires `membership_active`.
## API Contracts
### 1) Wallet Intent
#### `POST /secret/wallet/intent`
Request JSON:
```json
{
"address": "0xabc123...",
"origin": "https://edut.ai",
"locale": "en",
"chain_id": 8453
}
```
Behavior:
1. Normalize and validate wallet address format.
2. Validate `origin` against allowlist.
3. Enforce rate limits (IP + address + rolling windows).
4. Generate designation `code`, `auth_token`, nonce, and intent TTL.
5. Persist `pending_signature` state.
6. Return typed-data envelope fields required for signing.
Response:
```json
{
"intent_id": "wi_...",
"designation_code": "0217073045482",
"display_token": "0217-0730-4548-2",
"nonce": "f2e9...",
"issued_at": "2026-02-17T07:30:45Z",
"expires_at": "2026-02-17T07:40:45Z",
"domain_name": "EDUT Designation",
"chain_id": 8453,
"verifying_contract": "0x0000000000000000000000000000000000000000"
}
```
### 2) Wallet Verify
#### `POST /secret/wallet/verify`
Request JSON:
```json
{
"intent_id": "wi_...",
"address": "0xabc123...",
"chain_id": 8453,
"signature": "0x..."
}
```
Behavior:
1. Load pending intent by `intent_id`.
2. Verify not expired and not consumed.
3. Reconstruct typed payload exactly as issued.
4. Recover signer and compare to declared address.
5. Validate chain allowlist and origin.
6. Transition to `signature_verified` and `pending_membership_mint`.
Response:
```json
{
"status": "signature_verified",
"designation_code": "0217073045482",
"display_token": "0217-0730-4548-2",
"verified_at": "2026-02-17T07:31:12Z"
}
```
### 3) Membership Quote
#### `POST /secret/membership/quote`
Request JSON:
```json
{
"designation_code": "0217073045482",
"address": "0xabc123...",
"chain_id": 8453
}
```
Behavior:
1. Verify `signature_verified` status for designation/address pair.
2. Fetch current policy (`currency`, `amount`, `deadline`, `contract`).
3. Return quote payload for client-side transaction submission.
Response:
```json
{
"quote_id": "mq_...",
"chain_id": 8453,
"currency": "USDC",
"amount": "5.00",
"amount_atomic": "5000000",
"deadline": "2026-02-17T07:36:12Z",
"contract_address": "0x...",
"method": "mintMembership",
"calldata": "0x..."
}
```
### 4) Membership Confirm
#### `POST /secret/membership/confirm`
Request JSON:
```json
{
"designation_code": "0217073045482",
"quote_id": "mq_...",
"tx_hash": "0x...",
"address": "0xabc123...",
"chain_id": 8453
}
```
Behavior:
1. Validate quote ownership and expiry.
2. Verify tx inclusion and success on allowed chain.
3. Validate minted membership recipient and amount policy.
4. Transition to `membership_active`.
5. Emit activation evidence receipt.
Response:
```json
{
"status": "membership_active",
"designation_code": "0217073045482",
"display_token": "0217-0730-4548-2",
"tx_hash": "0x...",
"activated_at": "2026-02-17T07:33:09Z"
}
```
## Security Controls
1. Intent TTL and one-time nonce consumption.
2. Quote TTL with explicit `deadline`.
3. Strict origin allowlist for intent and verify endpoints.
4. Chain allowlist enforcement.
5. Signature and quote replay prevention.
6. IP + address rate limits on all public endpoints.
7. Deterministic audit trail for signature and payment confirmation.
8. No private key handling server-side.
## Data Model (SQLite)
### File: `/var/lib/edut/secrets.db`
```sql
CREATE TABLE IF NOT EXISTS designations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT NOT NULL UNIQUE,
auth_token TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending_signature',
wallet_address TEXT,
chain_id INTEGER,
intent_id TEXT UNIQUE,
intent_nonce TEXT,
intent_issued_at DATETIME,
intent_expires_at DATETIME,
signature TEXT,
signature_verified_at DATETIME,
membership_quote_id TEXT,
membership_currency TEXT,
membership_amount_atomic TEXT,
membership_quote_expires_at DATETIME,
membership_tx_hash TEXT,
membership_activated_at DATETIME,
origin TEXT,
locale TEXT,
created_at DATETIME DEFAULT (datetime('now'))
);
CREATE INDEX idx_designations_code ON designations(code);
CREATE INDEX idx_designations_wallet ON designations(wallet_address);
CREATE INDEX idx_designations_status ON designations(status);
CREATE INDEX idx_designations_intent ON designations(intent_id);
CREATE INDEX idx_designations_quote ON designations(membership_quote_id);
CREATE INDEX idx_designations_created ON designations(created_at);
```
## Launch-Day Activation Messaging
At launch, `membership_active` records are eligible for access-level updates and marketplace onboarding.
## NGINX Routing (Example)
```nginx
location /secret/wallet/intent {
proxy_pass http://127.0.0.1:9091;
}
location /secret/wallet/verify {
proxy_pass http://127.0.0.1:9091;
}
location /secret/membership/quote {
proxy_pass http://127.0.0.1:9091;
}
location /secret/membership/confirm {
proxy_pass http://127.0.0.1:9091;
}
```
## Summary
The wallet-first designation plus paid membership flow creates a deterministic two-factor identity and commitment chain:
1. signature proves wallet control,
2. paid mint proves intent,
3. membership gates all future marketplace purchases.