diff --git a/README.md b/README.md index ff7ef7f..0015a60 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ translations/ docs/ secret-system-spec.md vision.md + roadmap-membership-platform.md review-notes.md platform-spec-alignment-review.md README.md diff --git a/docs/platform-spec-alignment-review.md b/docs/platform-spec-alignment-review.md index 5465b03..ca58aeb 100644 --- a/docs/platform-spec-alignment-review.md +++ b/docs/platform-spec-alignment-review.md @@ -14,6 +14,8 @@ The revised web copy is aligned with core platform direction on: 2. Layered architecture where intelligence is optional augmentation. 3. Local-first durability and operational resilience framing. 4. Open/closed boundary discipline (no disclosure of private kernel internals). +5. Wallet-first identity continuity aligned with licensing architecture direction. +6. Membership-gated commerce model separated from product entitlement rights. ## Resolved Conflicts from Initial Web Draft diff --git a/docs/review-notes.md b/docs/review-notes.md index 3676ee4..f5555e8 100644 --- a/docs/review-notes.md +++ b/docs/review-notes.md @@ -30,10 +30,10 @@ - Kept hidden context layer readable for assistive technology. - Added per-node RTL direction handling for Arabic and Hebrew text. -5. Corrected secret webhook spec risks. -- Mailgun verification now references signing key (not sending API key). -- Inbound code extraction anchored to recipient local-part first. -- Error-handling guidance updated to avoid silent data loss. +5. Updated secret system architecture to wallet-first membership flow. +- Replaced SMS/email-first assumptions with wallet signature + membership mint sequence. +- Added membership-gate framing (`membership required`, `membership != product license`). +- Kept notification email optional and post-membership. 6. Privacy policy hardening. - Removed over-broad ad-tech assumptions. diff --git a/docs/roadmap-membership-platform.md b/docs/roadmap-membership-platform.md new file mode 100644 index 0000000..fd88d4a --- /dev/null +++ b/docs/roadmap-membership-platform.md @@ -0,0 +1,92 @@ +# EDUT Membership Platform Roadmap (Step-Based) + +This roadmap is intentionally step-based and dependency-ordered. No timeline commitments are embedded. + +## Step 1: Lock Core Model + +1. Membership token is required to purchase marketplace offers. +2. Membership token is not a product license. +3. Product/module licenses are separate entitlements. +4. One wallet anchors the identity chain across membership, offers, and activation. + +## Step 2: Freeze Token Taxonomy + +1. `MembershipToken` (non-transferable, one per wallet policy configurable). +2. `OfferEntitlement` (offer-specific rights and policy flags). +3. `RuntimeActivation` (off-chain activation state driven by entitlement checks). + +## Step 3: Finalize Membership Contract Interface + +1. `mintMembership()` payable. +2. `hasMembership(address)` gating view. +3. `setMintPrice(...)` admin update with emitted events. +4. Optional tier ladder based on minted supply. +5. Immutable checks for non-transferability at contract level. + +## Step 4: Lock Signature + Intent Protocol + +1. `POST /secret/wallet/intent` returns nonce-bound typed-data payload. +2. EIP-712 signature proves wallet possession. +3. Server verify endpoint enforces replay protection and origin checks. +4. Intent payload includes price/currency/deadline for explicit consent. + +## Step 5: Add Membership Mint Transaction Stage + +1. After signature verification, UI requests membership quote payload. +2. Wallet sends on-chain mint transaction to membership contract. +3. Backend confirms inclusion and marks membership active. +4. UI transitions to `acknowledged` only when activation evidence is complete. + +## Step 6: Implement Membership Gate in Checkout + +1. All offer purchases call `hasMembership(wallet)` before checkout. +2. No entitlement mint is allowed without active membership. +3. Gate behavior is deterministic and audit-logged. + +## Step 7: Ship Offer Registry + +1. Canonical offer schema (`offer_id`, issuer, price, currency, policies). +2. Offer lifecycle states (`draft`, `active`, `paused`, `retired`). +3. Policy flags (`member_only`, `workspace_bound`, `transferable`, etc.). + +## Step 8: Ship Entitlement Purchase Pipeline + +1. Wallet connect and chain check. +2. Membership gate check. +3. Signed quote check. +4. On-chain entitlement mint/record. +5. Evidence receipt persisted with tx hash and policy snapshot. + +## Step 9: Bind Entitlements to Runtime + +1. Activation checks must pass before module activation. +2. Workspace binding is explicit and evidence-backed. +3. Revocation/suspension propagates deterministically to runtime gates. + +## Step 10: Add Publisher Layer + +1. Issuer namespace per company. +2. Issuer signing keys and role boundaries. +3. Revenue split policy per offer. +4. Marketplace quality gates before public listing. + +## Step 11: Harden Compliance and Policy Surfaces + +1. Terms clearly separate membership rights from license rights. +2. Privacy describes wallet/signature processing and optional notifications. +3. Public copy avoids investment framing and speculative claims. + +## Step 12: Operational Hardening + +1. Conformance vectors for membership gating and entitlement gating. +2. Replay/fraud/rate-limit controls validated in automated tests. +3. Chain outage/reorg fail-closed behavior documented and tested. +4. Deterministic audit export for enterprise and legal review. + +## Step 13: Continuous Optimization + +1. Measure funnel transitions: + - visit -> signature -> membership mint -> first entitlement purchase. +2. Tune copy, wallet guidance, and pricing policy using deterministic metrics. +3. Expand issuer ecosystem only after quality and support controls are stable. + diff --git a/docs/secret-system-spec.md b/docs/secret-system-spec.md index 109220c..b7db6b7 100644 --- a/docs/secret-system-spec.md +++ b/docs/secret-system-spec.md @@ -1,239 +1,277 @@ -# Edut Secret System - Two-Factor Designation Spec (Hardened) +# Edut Secret System - Wallet-First Designation + Membership Spec ## Overview -The Edut designation flow is a two-factor protocol: +The Edut onboarding flow is deterministic and wallet-first: -1. Phone verification by SMS (Twilio). -2. Email verification by protocol email (Mailgun). +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 may optionally add notification email for launch updates. -Both factors are bound to one server-generated designation record. -This is a pre-launch identity bootstrap used for launch activation continuity. +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`: 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. +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. Optional: user submits email via `POST /secret/notify`. 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 +Optional notification email -> POST /secret/notify ``` -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 -``` + +## 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. ## Infrastructure | 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 | +| 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 | +| API | `api.edut.ai/secret/notify` | Store optional launch notification email | +| Chain | Base | Membership mint settlement and evidence | +| Database | `/var/lib/edut/secrets.db` | Durable designation + membership + notification state | -## State Machine (Deterministic) +## Deterministic State Machine -`pending_phone` -> `phone_verified` -> `pending_email` -> `acknowledged` +`pending_signature` -> `signature_verified` -> `pending_membership_mint` -> `membership_active` -Additional terminal/side states: +Optional post-activation state: -- `expired` (ticket or flow timed out) -- `abandoned` (no progress within retention window) -- `opted_out` (STOP/UNSUBSCRIBE received) -- `rate_limited` (temporary) +- `notification_linked` (email saved) + +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. Email verification is blocked unless state is `pending_email`. -3. `acknowledged` is terminal for this flow. +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) Initiate +### 1) Wallet Intent -### `POST /secret/initiate` +#### `POST /secret/wallet/intent` Request JSON: ```json { - "phone": "+12065550123", + "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" +} +``` + +### 5) Optional Notification Link + +#### `POST /secret/notify` + +Request JSON: + +```json +{ + "email": "user@example.com", + "designation_code": "0217073045482", + "designation_token": "0217-0730-4548-2", + "wallet": "0xabc123...", "locale": "en" } ``` Behavior: -1. Normalize phone to E.164. -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. +1. Validate email format and normalize case. +2. Bind email to active designation/membership record. +3. Keep idempotent updates by designation or wallet. +4. Record timestamp and source IP for abuse controls. -Response: +## Security Controls -```json -{ - "status": "sms_sent", - "status_ticket": "st_...", - "ticket_expires_at": "2026-02-17T07:40:45Z", - "display_token": "0217-0730-4548-2" -} -``` +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. -Notes: - -- Do not return `auth_token`. -- Do not return internal row id. -- `display_token` is presentation-only. - -## 2) Status - -### `GET /secret/status` - -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 -{ - "status": "phone_verified", - "phone_verified": true, - "email_verified": false, - "mailto": { - "enabled": true, - "recipient": "0217073045482@secret.edut.ai", - "subject": "EDUT-0217073045482", - "body": "..." - } -} -``` - -Security notes: - -- No code in URL path. -- No phone/email values in response. - -## Twilio Webhook - -### `POST /twilio/inbound` - -Required checks: - -1. Verify `X-Twilio-Signature` with `TWILIO_AUTH_TOKEN`. -2. Idempotency on `MessageSid`. -3. Normalize `From` number. - -Accepted commands (case-insensitive): - -- `CONFIRM` -> verify phone and transition to `pending_email`. -- `STOP`, `UNSUBSCRIBE`, `CANCEL`, `END`, `QUIT` -> transition to `opted_out`. -- `HELP` -> send support/help response. - -Processing rules: - -- `CONFIRM` applies to latest active designation for that phone within validity window. -- Return `2xx` only after persistence or deterministic dedupe. - -## Mailgun Webhook - -### `POST /mailgun/inbound` - -Required checks: - -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`). - -Processing rules: - -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. - -## SMS Content (Compliance-Safe) - -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 - -``` -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -EDUT GOVERNANCE PROTOCOL -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - -ACCESS REGISTRATION CONFIRMED - - Designation: #{designation_number} - Auth Token: {auth_token} - Classification: OBSERVER - Status: ACKNOWLEDGED - - Phone: VERIFIED - Email: VERIFIED - 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. -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -``` - -## SQLite Schema +## Data Model (SQLite) ### File: `/var/lib/edut/secrets.db` @@ -241,124 +279,69 @@ deterministic governance systems. CREATE TABLE IF NOT EXISTS designations ( id INTEGER PRIMARY KEY AUTOINCREMENT, code TEXT NOT NULL UNIQUE, - phone TEXT, - phone_verified_at DATETIME, - email TEXT, - email_verified_at DATETIME, - recipient TEXT, - 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' + 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, + notification_email TEXT, + notification_linked_at DATETIME, + origin TEXT, + locale TEXT, + created_at DATETIME DEFAULT (datetime('now')) ); 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_wallet ON designations(wallet_address); CREATE INDEX idx_designations_status ON designations(status); -CREATE INDEX idx_designations_ticket ON designations(status_ticket); +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); - -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')) -); - -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')) -); ``` -## Nginx Routing +## Launch-Day Activation Messaging -### File: `/etc/nginx/sites-available/api.edut.ai` +At launch, `membership_active` records are eligible for access-level updates and marketplace onboarding. + +## NGINX Routing (Example) ```nginx -server { - listen 80; - server_name api.edut.ai; +location /secret/wallet/intent { + proxy_pass http://127.0.0.1:9091; +} - location /secret/ { - 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; - } +location /secret/wallet/verify { + proxy_pass http://127.0.0.1:9091; +} - location /twilio/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; - } +location /secret/membership/quote { + proxy_pass http://127.0.0.1:9091; +} - 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; - } +location /secret/membership/confirm { + proxy_pass http://127.0.0.1:9091; +} + +location /secret/notify { + proxy_pass http://127.0.0.1:9091; } ``` -## Environment Variables +## Summary -```bash -SECRETS_DB_PATH=/var/lib/edut/secrets.db -AUTH_SALT= +The wallet-first designation plus paid membership flow creates a deterministic two-factor identity and commitment chain: -# Twilio -TWILIO_ACCOUNT_SID= -TWILIO_AUTH_TOKEN= -TWILIO_FROM_NUMBER=<+1 toll-free number> - -# Mailgun -MAILGUN_API_KEY= -MAILGUN_DOMAIN=secret.edut.ai -MAILGUN_SIGNING_KEY= -``` - -## Error Handling + Retry Rules - -- 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 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, only fully verified designations (`phone_verified_at` + `email_verified_at`) are eligible for access-level change messaging. - -- Classification may transition `OBSERVER -> OPERATOR`. -- Auth token remains stable. -- 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 trust anchors, workspace isolation, or offline license/runtime enforcement in the product runtime. +1. signature proves wallet control, +2. paid mint proves intent, +3. membership gates all future marketplace purchases. diff --git a/docs/vision.md b/docs/vision.md index f233d1d..e5d8fe7 100644 --- a/docs/vision.md +++ b/docs/vision.md @@ -10,18 +10,17 @@ The site should feel precise and established while avoiding disclosure of private implementation IP. -## Primary Experience (Two-Factor, Continue Flow) +## Primary Experience (Wallet-First, Membership-Gated) 1. A visitor lands on `edut.ai`. 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}`. +4. Clicking `continue` reveals wallet-first identity options. +5. If they have a wallet, they connect and sign a designation intent (no transaction). +6. They mint EDUT membership as a paid on-chain commitment. +7. Membership confirmation binds durable account state to the wallet. +8. Final state shows `acknowledged · {token}`. +9. Optional `notify me` captures email for launch updates. The flow should feel controlled and ambient, not like a conventional signup form. @@ -50,8 +49,7 @@ The flow should feel controlled and ambient, not like a conventional signup form - `edut.ai`: primary public surface and business identity. - `edut.dev`: developer-facing domain (same landing for now). -- `secret.edut.ai`: inbound designation email namespace. -- `api.edut.ai`: API and webhook endpoint (`/secret/*`, `/twilio/inbound`, `/mailgun/inbound`). +- `api.edut.ai`: API endpoints for wallet intent/verify, membership quote/confirm, and optional notification linking. - `/privacy` and `/terms`: legal pages (English authoritative). ## Messaging Boundaries @@ -75,8 +73,7 @@ Localized: - Meaning line (`testimony · witness · evidence`) - Descriptor - Continue label -- Phone placeholder -- Waiting/confirmation state text +- Wallet-flow labels and status states - `acknowledged` label - Footer labels - Full AI/accessibility context abstract @@ -88,18 +85,26 @@ English-governing at launch: ## Identity Evolution -The two-factor designation is the pre-launch identity envelope: +The designation and membership flow is the pre-launch identity envelope: -- Factor 1: phone verified by SMS reply. -- Factor 2: email verified via protocol message. -- Shared binding: one server-generated designation record (`code` + `auth_token`). +- Factor 1: cryptographic wallet possession (signature proof) +- Factor 2: economic commitment (paid membership mint) +- Optional channel: notification email binding +- 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. +## Commerce Model + +1. Membership is required to purchase from EDUT marketplace surfaces. +2. Membership is not a product/module license. +3. Product rights are granted by separate entitlement purchases. +4. The same wallet carries the full identity and entitlement chain. + ## Acceptance Criteria 1. Human surface remains minimal and intentional. 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 completes through deterministic state transitions with auditable evidence. +5. Membership onboarding is deterministic, signature-verified, and auditable. diff --git a/public/index.html b/public/index.html index fb68b93..cdb1b9d 100644 --- a/public/index.html +++ b/public/index.html @@ -152,6 +152,139 @@ .acknowledged.visible { opacity: 1; } .acknowledged.visible:hover { color: #555a60; } + .flow-ui { + margin-top: 18px; + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; + pointer-events: auto; + } + + .flow-hidden { + display: none !important; + } + + .ghost-action { + border: 0; + background: transparent; + color: #606264; + font-family: 'IBM Plex Mono', 'Courier New', Courier, monospace; + font-size: 12px; + letter-spacing: 0.2em; + text-transform: lowercase; + font-weight: 300; + cursor: pointer; + opacity: 0; + transition: opacity 500ms ease, color 300ms ease; + } + .ghost-action:hover { + color: #2c2c2c; + } + .ghost-action.visible { + opacity: 1; + } + + .flow-panel { + max-width: 480px; + text-align: center; + display: flex; + flex-direction: column; + gap: 8px; + opacity: 0; + transition: opacity 500ms ease; + } + .flow-panel.visible { + opacity: 1; + } + + .flow-line { + font-size: 12px; + letter-spacing: 0.08em; + color: #555a60; + font-weight: 300; + line-height: 1.7; + } + .flow-line.subtle { + font-size: 11px; + color: #6b7078; + } + + .flow-actions { + display: flex; + align-items: center; + justify-content: center; + gap: 16px; + margin-top: 4px; + } + + .flow-link { + border: 0; + background: transparent; + color: #555a60; + font-family: 'IBM Plex Mono', 'Courier New', Courier, monospace; + font-size: 11px; + letter-spacing: 0.12em; + text-transform: lowercase; + font-weight: 300; + cursor: pointer; + text-decoration: underline; + text-underline-offset: 2px; + } + .flow-link:hover { + color: #2c2c2c; + } + + .flow-status { + font-size: 11px; + letter-spacing: 0.12em; + color: #606264; + font-weight: 300; + opacity: 0; + transition: opacity 500ms ease; + min-height: 16px; + } + .flow-status.visible { + opacity: 1; + } + .flow-status.error { + color: #7a3a3a; + } + + .notify-panel { + display: flex; + flex-direction: column; + gap: 10px; + align-items: center; + opacity: 0; + transition: opacity 500ms ease; + } + .notify-panel.visible { + opacity: 1; + } + + .notify-form { + display: flex; + align-items: center; + gap: 10px; + } + + .notify-input { + border: 0; + border-bottom: 1px solid #606264; + background: transparent; + color: #3a3d42; + font-family: 'IBM Plex Mono', 'Courier New', Courier, monospace; + font-size: 12px; + letter-spacing: 0.08em; + padding: 5px 3px; + min-width: 220px; + } + .notify-input:focus { + outline: none; + border-bottom-color: #2c2c2c; + } + /* Footer */ footer { position: fixed; @@ -280,6 +413,30 @@

Development and licensing of deterministic governance systems

+
+ +
+

receive your designation token

+

No transaction. Signature only.

+

Never share your seed phrase.

+
+ + +
+
+

Install a wallet app, then return here to continue.

+ Install wallet +
+
+
+
+ +
+ + +
+
+
Click anywhere on the page to begin your access request. @@ -321,11 +478,119 @@ const container = document.getElementById('globe-container'); const supportedLanguages = ['en', 'zh', 'es', 'ar', 'fr', 'pt', 'de', 'ja', 'ru', 'ko', 'hi', 'he']; let activeLocale = null; +const flowState = { + firstInteraction: false, + stage: 'idle', + currentIntent: null, +}; + +const continueAction = document.getElementById('continue-action'); +const walletPanel = document.getElementById('wallet-panel'); +const walletHave = document.getElementById('wallet-have'); +const walletNeed = document.getElementById('wallet-need'); +const walletInstallHelp = document.getElementById('wallet-install-help'); +const flowStatus = document.getElementById('flow-status'); +const notifyPanel = document.getElementById('notify-panel'); +const notifyOpen = document.getElementById('notify-open'); +const notifyForm = document.getElementById('notify-form'); +const notifyEmail = document.getElementById('notify-email'); +const privacyLink = document.getElementById('privacyLink'); +const termsLink = document.getElementById('termsLink'); function getByPath(obj, path) { return path.split('.').reduce((value, part) => (value && value[part] !== undefined ? value[part] : undefined), obj); } +function t(key, fallback) { + if (!activeLocale) return fallback; + const value = getByPath(activeLocale, key); + return typeof value === 'string' ? value : fallback; +} + +function showElement(el) { + if (!el) return; + el.classList.remove('flow-hidden'); + requestAnimationFrame(() => el.classList.add('visible')); +} + +function hideElement(el) { + if (!el) return; + el.classList.remove('visible'); + el.classList.add('flow-hidden'); +} + +function buildDisplayToken(code) { + if (!code || code.length < 13) return code; + return code.slice(0,4) + '-' + code.slice(4,8) + '-' + code.slice(8,12) + '-' + code.slice(12); +} + +function getStoredAcknowledgement() { + const stateRaw = localStorage.getItem('edut_ack_state'); + if (stateRaw) { + try { + const parsed = JSON.parse(stateRaw); + if (parsed && (parsed.code || parsed.token)) { + return parsed; + } + } catch (err) { + // ignore malformed cache + } + } + const legacyCode = localStorage.getItem('edut_acknowledged'); + if (legacyCode) { + return { + code: legacyCode, + token: buildDisplayToken(legacyCode), + }; + } + return null; +} + +function saveAcknowledgement(state) { + if (!state) return; + localStorage.setItem('edut_ack_state', JSON.stringify(state)); + if (state.code) { + localStorage.setItem('edut_acknowledged', state.code); + } +} + +function renderAcknowledged(state) { + if (!state) return; + const el = document.getElementById('acknowledged'); + const label = t('acknowledged', 'acknowledged'); + const token = state.token || buildDisplayToken(state.code); + if (!token) return; + el.textContent = label + ' · ' + token; + el.classList.add('visible'); +} + +function setFlowStatus(key, fallback, isError) { + flowStatus.textContent = t(key, fallback); + flowStatus.classList.toggle('error', !!isError); + showElement(flowStatus); +} + +function clearFlowStatus() { + flowStatus.textContent = ''; + flowStatus.classList.remove('error'); + hideElement(flowStatus); +} + +async function postJSON(url, payload) { + const res = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }); + if (!res.ok) { + const detail = await res.text(); + throw new Error(detail || ('HTTP ' + res.status)); + } + return res.json(); +} + function resolveLanguage(input) { if (!input) return 'en'; const normalized = String(input).toLowerCase(); @@ -352,15 +617,6 @@ async function loadLocaleBundle(lang) { return null; } -function renderAcknowledged(code) { - if (!code) return; - const el = document.getElementById('acknowledged'); - const token = code.slice(0,4) + '-' + code.slice(4,8) + '-' + code.slice(8,12) + '-' + code.slice(12); - const label = activeLocale && activeLocale.acknowledged ? activeLocale.acknowledged : 'acknowledged'; - el.textContent = label + ' · ' + token; - el.classList.add('visible'); -} - function applyLocale(bundle, lang) { activeLocale = bundle; localStorage.setItem('edut_lang', lang); @@ -378,10 +634,18 @@ function applyLocale(bundle, lang) { node.setAttribute('dir', direction); }); - const savedCode = localStorage.getItem('edut_acknowledged'); - if (savedCode) { - renderAcknowledged(savedCode); - } + const placeholderNodes = document.querySelectorAll('[data-i18n-placeholder]'); + placeholderNodes.forEach((node) => { + const key = node.getAttribute('data-i18n-placeholder'); + const value = getByPath(bundle, key); + if (typeof value === 'string') { + node.setAttribute('placeholder', value); + } + node.setAttribute('dir', direction); + }); + + const saved = getStoredAcknowledgement(); + if (saved) renderAcknowledged(saved); } async function initializeLocale() { @@ -400,20 +664,149 @@ async function initializeLocale() { initializeLocale(); -// Acknowledged check -const savedCode = localStorage.getItem('edut_acknowledged'); -if (savedCode) { - renderAcknowledged(savedCode); +function openWalletInstallHints() { + showElement(walletInstallHelp); + setFlowStatus('wallet_need_status', 'Install a wallet app, then return to continue.', false); } -// Privacy/Terms don't trigger mailto -document.getElementById('privacyLink').addEventListener('click', (e) => e.stopPropagation()); -document.getElementById('termsLink').addEventListener('click', (e) => e.stopPropagation()); +async function startWalletFlow() { + hideElement(walletInstallHelp); + try { + if (!window.ethereum) { + openWalletInstallHints(); + setFlowStatus('wallet_missing', 'No wallet detected on this device.', true); + return; + } + + setFlowStatus('wallet_connecting', 'Connecting wallet...', false); + const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }); + if (!Array.isArray(accounts) || accounts.length === 0) { + throw new Error('Wallet connection was not approved.'); + } + const address = accounts[0]; + const chainIdHex = await window.ethereum.request({ method: 'eth_chainId' }); + const chainId = Number.parseInt(chainIdHex, 16); + + setFlowStatus('wallet_intent', 'Creating designation intent...', false); + const intent = await postJSON('/secret/wallet/intent', { + address, + origin: window.location.origin, + locale: localStorage.getItem('edut_lang') || 'en', + chain_id: chainId, + }); + flowState.currentIntent = intent; + + const typedData = { + types: { + EIP712Domain: [ + { name: 'name', type: 'string' }, + { name: 'version', type: 'string' }, + { name: 'chainId', type: 'uint256' }, + { name: 'verifyingContract', type: 'address' }, + ], + DesignationIntent: [ + { name: 'designationCode', type: 'string' }, + { name: 'designationToken', type: 'string' }, + { name: 'nonce', type: 'string' }, + { name: 'issuedAt', type: 'string' }, + { name: 'origin', type: 'string' }, + ], + }, + primaryType: 'DesignationIntent', + domain: { + name: intent.domain_name || 'EDUT Designation', + version: '1', + chainId: intent.chain_id || chainId, + verifyingContract: intent.verifying_contract || '0x0000000000000000000000000000000000000000', + }, + message: { + designationCode: intent.designation_code, + designationToken: intent.display_token || buildDisplayToken(intent.designation_code), + nonce: intent.nonce, + issuedAt: intent.issued_at || new Date().toISOString(), + origin: window.location.origin, + }, + }; + + setFlowStatus('wallet_signing', 'Awaiting signature...', false); + const signature = await window.ethereum.request({ + method: 'eth_signTypedData_v4', + params: [address, JSON.stringify(typedData)], + }); + + setFlowStatus('wallet_verifying', 'Verifying signature...', false); + const verification = await postJSON('/secret/wallet/verify', { + intent_id: intent.intent_id, + address, + chain_id: chainId, + signature, + }); + + const ackState = { + code: verification.designation_code || intent.designation_code || null, + token: verification.display_token || intent.display_token || buildDisplayToken(verification.designation_code || intent.designation_code), + wallet: address, + chain_id: chainId, + }; + saveAcknowledgement(ackState); + renderAcknowledged(ackState); + hideElement(continueAction); + hideElement(walletPanel); + showElement(notifyPanel); + setFlowStatus('wallet_success', 'Designation acknowledged.', false); + } catch (err) { + const message = err && err.message ? err.message : 'Wallet flow failed.'; + setFlowStatus('wallet_failed', message, true); + } +} + +async function submitNotifyEmail(event) { + event.preventDefault(); + const email = notifyEmail.value.trim(); + if (!email) return; + const ack = getStoredAcknowledgement(); + try { + await postJSON('/secret/notify', { + email, + designation_code: ack && ack.code ? ack.code : null, + designation_token: ack && ack.token ? ack.token : null, + wallet: ack && ack.wallet ? ack.wallet : null, + locale: localStorage.getItem('edut_lang') || 'en', + }); + setFlowStatus('notify_saved', 'Notification saved.', false); + notifyForm.classList.add('flow-hidden'); + } catch (err) { + setFlowStatus('notify_failed', 'Could not save notification preference.', true); + } +} + +function onFirstInteractionReveal() { + if (flowState.firstInteraction || getStoredAcknowledgement()) return; + flowState.firstInteraction = true; + showElement(continueAction); +} + +// Privacy/Terms bypass all flow logic. +privacyLink.addEventListener('click', (e) => e.stopPropagation()); +termsLink.addEventListener('click', (e) => e.stopPropagation()); + +continueAction.addEventListener('click', () => { + hideElement(continueAction); + showElement(walletPanel); + flowState.stage = 'wallet_choice'; +}); +walletHave.addEventListener('click', startWalletFlow); +walletNeed.addEventListener('click', openWalletInstallHints); +notifyOpen.addEventListener('click', () => { + notifyForm.classList.toggle('flow-hidden'); +}); +notifyForm.addEventListener('submit', submitNotifyEmail); // Keyboard support: Enter/Space triggers same as click document.body.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { - if (e.target.tagName !== 'A') { + const tag = e.target.tagName; + if (tag !== 'A' && tag !== 'BUTTON' && tag !== 'INPUT') { e.preventDefault(); document.body.click(); } @@ -425,38 +818,11 @@ let spinSpeed = 0.15; let targetSpin = 0.15; let clickBoost = 0; -// Full page click +// Full page click keeps the globe alive; first click also reveals continue. document.body.addEventListener('click', () => { clickBoost = 2.5; targetSpin = 2.0; - - if (!localStorage.getItem('edut_acknowledged')) { - setTimeout(() => { - const now = new Date(); - const mm = String(now.getMonth() + 1).padStart(2, '0'); - const dd = String(now.getDate()).padStart(2, '0'); - const hh = String(now.getHours()).padStart(2, '0'); - const mi = String(now.getMinutes()).padStart(2, '0'); - const ss = String(now.getSeconds()).padStart(2, '0'); - const ms = String(now.getMilliseconds()).padStart(3, '0'); - const code = mm + dd + hh + mi + ss + ms; - const token = code.slice(0,4) + '-' + code.slice(4,8) + '-' + code.slice(8,12) + '-' + code.slice(12); - const ts = now.toISOString().split('.')[0] + 'Z'; - const addr = code + '@secret.edut.ai'; - const subject = 'EDUT-' + code; - const body = [ - '━━━━━━━━━━━━━━━━━━━━━━━━━━', - 'EDUT GOVERNANCE PROTOCOL', - '', - 'Access Request: ' + token, - 'Timestamp: ' + ts, - '━━━━━━━━━━━━━━━━━━━━━━━━━━', - ].join('\n'); - window.location.href = 'mailto:' + addr + '?subject=' + encodeURIComponent(subject) + '&body=' + encodeURIComponent(body); - localStorage.setItem('edut_acknowledged', code); - renderAcknowledged(code); - }, 600); - } + onFirstInteractionReveal(); }); // ============================================================ diff --git a/public/privacy/index.html b/public/privacy/index.html index 11188cb..bbf3f2c 100644 --- a/public/privacy/index.html +++ b/public/privacy/index.html @@ -157,9 +157,9 @@

Information We Collect

-

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 you provide directly. When you interact with our sites — including connecting a wallet, signing a designation message, entering optional notification details, contacting support, or completing a transaction — we may collect your wallet address, signed message payloads, email address, 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.

+

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, locale preferences, and interaction events. This information is collected through server logs, local storage, cookies, and similar technologies.

Cookies and local storage. We may use cookies and local storage to operate core site behavior, preserve user preferences, and support security and diagnostics. Cookies may be session-based (deleted when you close your browser) or persistent (remaining on your device until expiration or deletion). You can manage cookies through your browser settings; disabling them may affect functionality.

@@ -169,13 +169,16 @@

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.

+

Verified-channel purpose. Where designation workflows are used, we process cryptographic signature data to confirm wallet control, prevent automated abuse, bind protocol records to a stable identity anchor, and maintain auditable activation continuity. If you provide an optional notification email, we use it to deliver launch and operational notices tied to your designation.

+ +

Blockchain and Wallet Data

+

Wallet addresses and related signature metadata may be processed to verify cryptographic intent and establish designation records. Public blockchain networks are independently operated systems; if designation or licensing records are written on-chain, related transaction data may be publicly visible and immutable by design. We do not control third-party blockchain explorers or wallet software.

Sharing and Disclosure

-

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.

+

Service providers. We may share your information with third-party providers that support hosting, infrastructure, email delivery, wallet connectivity infrastructure, 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.

+

Communications providers. We may use communications processors for operational email delivery and response handling. These providers process contact information and message metadata as necessary to transmit 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.

@@ -198,7 +201,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. 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.

+

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. Designation, signature, and anti-abuse records may be retained to preserve protocol integrity and auditable continuity. When information is no longer needed, we will securely delete or anonymize it where legally and technically feasible.

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.

diff --git a/public/terms/index.html b/public/terms/index.html index a665cb6..d9df8f7 100644 --- a/public/terms/index.html +++ b/public/terms/index.html @@ -158,8 +158,11 @@

Eligibility

You must be at least 16 years old and able to enter into a binding agreement to use the Services. If you use the Services on behalf of an organization, you represent that you have authority to bind that organization to these Terms.

-

Designations

-

Edut may issue designations (for example, observer or operator) through site interactions and messaging workflows. Designation identifiers are non-transferable and have no monetary value. Designation records are permanent evidence identifiers, but access level, privileges, and program participation may be modified, suspended, or revoked by Edut at any time.

+

Designations and Membership

+

Edut may issue designations through site interactions and wallet-based verification workflows. Designation identifiers are non-transferable evidence identifiers. Edut may also issue paid membership credentials that gate access to marketplace purchasing. Unless explicitly stated in writing, membership grants access eligibility only and does not itself grant product runtime rights.

+ +

Marketplace Access and Licenses

+

Edut may require an active membership credential for purchase of offers, modules, or services through EDUT marketplace surfaces. Product-specific rights are granted by separate offer entitlements or licenses. Membership, designation, and product licenses are distinct instruments with different rights and limitations.

Products, Licensing, and Availability

Edut may offer software licenses, modules, deployment services, and related tools. Descriptions, availability, and requirements may change without notice. Additional product-specific terms may apply at purchase or activation time and, in case of conflict, those specific terms control for that product.

diff --git a/translations/ar.json b/translations/ar.json index 4a175db..f021130 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -26,5 +26,26 @@ "status_heading": "الحالة الحالية", "status": "الإطار في مرحلة تنفيذ وتحقق نشطة. تُصدر تصنيفات مبكرة استعدادًا لإطلاق مرحلي وتهيئة نشر مضبوط.", "note": "ملاحظة: هذه الصفحة بسيطة عن قصد. وهي تعكس فلسفة منتج ترى أن الحساب قد يكون معقدًا بينما تبقى الواجهات هادئة." - } + }, + "continue_label": "متابعة", + "wallet_intro": "استلم رمز التعيين الخاص بك", + "wallet_fact_no_tx": "لا توجد معاملة. فقط توقيع.", + "wallet_fact_seed": "لا تشارك عبارة الاسترداد الخاصة بك أبدًا.", + "wallet_have": "لدي محفظة", + "wallet_need": "أحتاج إلى محفظة", + "wallet_install_help": "ثبّت تطبيق محفظة ثم ارجع هنا للمتابعة.", + "wallet_install_cta": "تثبيت محفظة", + "wallet_need_status": "ثبّت تطبيق محفظة ثم ارجع للمتابعة.", + "wallet_connecting": "جارٍ ربط المحفظة...", + "wallet_intent": "جارٍ إنشاء نية التعيين...", + "wallet_signing": "بانتظار التوقيع...", + "wallet_verifying": "جارٍ التحقق من التوقيع...", + "wallet_success": "تم تأكيد التعيين.", + "wallet_failed": "فشل مسار المحفظة.", + "wallet_missing": "لم يتم العثور على محفظة على هذا الجهاز.", + "notify_me": "أبلغني", + "notify_placeholder": "البريد الإلكتروني", + "notify_submit": "حفظ", + "notify_saved": "تم حفظ الإشعار.", + "notify_failed": "تعذر حفظ تفضيل الإشعار." } diff --git a/translations/de.json b/translations/de.json index 6a8babe..a1ab175 100644 --- a/translations/de.json +++ b/translations/de.json @@ -26,5 +26,26 @@ "status_heading": "Aktueller Stand", "status": "Das Framework befindet sich in aktiver Implementierung und Validierung. Frühe Designationen werden für gestufte Launch-Bereitschaft und kontrolliertes Deployment-Onboarding vergeben.", "note": "Hinweis: Diese Seite ist bewusst minimal. Sie spiegelt eine Produktphilosophie wider, bei der die Berechnung komplex und die Oberfläche ruhig bleibt." - } + }, + "continue_label": "weiter", + "wallet_intro": "erhalte dein Designations-Token", + "wallet_fact_no_tx": "Keine Transaktion. Nur Signatur.", + "wallet_fact_seed": "Teile niemals deine Seed-Phrase.", + "wallet_have": "Ich habe ein Wallet", + "wallet_need": "Ich brauche ein Wallet", + "wallet_install_help": "Installiere ein Wallet und kehre dann hierher zurück.", + "wallet_install_cta": "Wallet installieren", + "wallet_need_status": "Installiere ein Wallet und kehre zum Fortfahren zurück.", + "wallet_connecting": "Wallet wird verbunden...", + "wallet_intent": "Designations-Intent wird erstellt...", + "wallet_signing": "Warte auf Signatur...", + "wallet_verifying": "Signatur wird verifiziert...", + "wallet_success": "Designation bestätigt.", + "wallet_failed": "Wallet-Ablauf fehlgeschlagen.", + "wallet_missing": "Kein Wallet auf diesem Gerät erkannt.", + "notify_me": "benachrichtige mich", + "notify_placeholder": "E-Mail", + "notify_submit": "speichern", + "notify_saved": "Benachrichtigung gespeichert.", + "notify_failed": "Benachrichtigungseinstellung konnte nicht gespeichert werden." } diff --git a/translations/en.json b/translations/en.json index fdc3342..f95aec4 100644 --- a/translations/en.json +++ b/translations/en.json @@ -26,5 +26,26 @@ "status_heading": "Current Status", "status": "The framework is in active implementation and validation. Early designations are being issued for staged launch readiness and controlled deployment onboarding.", "note": "Note: This page is intentionally minimal. It reflects a product philosophy where computation is complex and interfaces remain quiet." - } + }, + "continue_label": "continue", + "wallet_intro": "receive your designation token", + "wallet_fact_no_tx": "No transaction. Signature only.", + "wallet_fact_seed": "Never share your seed phrase.", + "wallet_have": "I have a wallet", + "wallet_need": "I need a wallet", + "wallet_install_help": "Install a wallet app, then return here to continue.", + "wallet_install_cta": "Install wallet", + "wallet_need_status": "Install a wallet app, then return to continue.", + "wallet_connecting": "Connecting wallet...", + "wallet_intent": "Creating designation intent...", + "wallet_signing": "Awaiting signature...", + "wallet_verifying": "Verifying signature...", + "wallet_success": "Designation acknowledged.", + "wallet_failed": "Wallet flow failed.", + "wallet_missing": "No wallet detected on this device.", + "notify_me": "notify me", + "notify_placeholder": "email", + "notify_submit": "save", + "notify_saved": "Notification saved.", + "notify_failed": "Could not save notification preference." } diff --git a/translations/es.json b/translations/es.json index 949dcdb..a8e2b43 100644 --- a/translations/es.json +++ b/translations/es.json @@ -26,5 +26,26 @@ "status_heading": "Estado Actual", "status": "El marco se encuentra en implementación y validación activa. Se están emitiendo designaciones tempranas para preparación de lanzamiento por etapas e incorporación de despliegues controlados.", "note": "Nota: Esta página es intencionalmente mínima. Refleja una filosofía de producto donde el cómputo es complejo y las interfaces permanecen silenciosas." - } + }, + "continue_label": "continuar", + "wallet_intro": "recibe tu token de designación", + "wallet_fact_no_tx": "Sin transacción. Solo firma.", + "wallet_fact_seed": "Nunca compartas tu frase semilla.", + "wallet_have": "Tengo una wallet", + "wallet_need": "Necesito una wallet", + "wallet_install_help": "Instala una wallet y vuelve aquí para continuar.", + "wallet_install_cta": "Instalar wallet", + "wallet_need_status": "Instala una wallet y vuelve para continuar.", + "wallet_connecting": "Conectando wallet...", + "wallet_intent": "Creando intención de designación...", + "wallet_signing": "Esperando firma...", + "wallet_verifying": "Verificando firma...", + "wallet_success": "Designación reconocida.", + "wallet_failed": "Falló el flujo de wallet.", + "wallet_missing": "No se detectó wallet en este dispositivo.", + "notify_me": "notifícame", + "notify_placeholder": "correo", + "notify_submit": "guardar", + "notify_saved": "Notificación guardada.", + "notify_failed": "No se pudo guardar la preferencia de notificación." } diff --git a/translations/fr.json b/translations/fr.json index e46ce81..cfe793d 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -26,5 +26,26 @@ "status_heading": "État Actuel", "status": "Le cadre est en phase active d'implémentation et de validation. Des désignations précoces sont émises pour la préparation du lancement par étapes et l'intégration de déploiements contrôlés.", "note": "Note : Cette page est volontairement minimale. Elle reflète une philosophie produit où le calcul est complexe et les interfaces restent discrètes." - } + }, + "continue_label": "continuer", + "wallet_intro": "recevez votre jeton de designation", + "wallet_fact_no_tx": "Aucune transaction. Signature uniquement.", + "wallet_fact_seed": "Ne partagez jamais votre phrase secrete.", + "wallet_have": "Je possede un wallet", + "wallet_need": "Je besoin dun wallet", + "wallet_install_help": "Installez un wallet puis revenez ici pour continuer.", + "wallet_install_cta": "Installer un wallet", + "wallet_need_status": "Installez un wallet puis revenez pour continuer.", + "wallet_connecting": "Connexion du wallet...", + "wallet_intent": "Creation de lintention de designation...", + "wallet_signing": "Signature en attente...", + "wallet_verifying": "Verification de la signature...", + "wallet_success": "Designation confirmee.", + "wallet_failed": "Le flux wallet a echoue.", + "wallet_missing": "Aucun wallet detecte sur cet appareil.", + "notify_me": "me notifier", + "notify_placeholder": "e-mail", + "notify_submit": "enregistrer", + "notify_saved": "Notification enregistree.", + "notify_failed": "Impossible denregistrer la preference de notification." } diff --git a/translations/he.json b/translations/he.json index 6615692..edc5082 100644 --- a/translations/he.json +++ b/translations/he.json @@ -26,5 +26,26 @@ "status_heading": "סטטוס נוכחי", "status": "המסגרת נמצאת בשלבי יישום ואימות פעילים. ייעודים מוקדמים מונפקים לצורך מוכנות השקה מדורגת והטמעה מבוקרת.", "note": "הערה: דף זה מינימלי בכוונה. הוא משקף פילוסופיית מוצר שבה החישוב מורכב, אך הממשקים נשארים שקטים." - } + }, + "continue_label": "המשך", + "wallet_intro": "קבל את אסימון הייעוד שלך", + "wallet_fact_no_tx": "אין עסקה. רק חתימה.", + "wallet_fact_seed": "לעולם אל תשתף את ביטוי השחזור שלך.", + "wallet_have": "יש לי ארנק", + "wallet_need": "אני צריך ארנק", + "wallet_install_help": "התקן אפליקציית ארנק ואז חזור לכאן כדי להמשיך.", + "wallet_install_cta": "התקן ארנק", + "wallet_need_status": "התקן אפליקציית ארנק ואז חזור להמשך.", + "wallet_connecting": "מתחבר לארנק...", + "wallet_intent": "יוצר כוונת ייעוד...", + "wallet_signing": "ממתין לחתימה...", + "wallet_verifying": "מאמת חתימה...", + "wallet_success": "הייעוד אושר.", + "wallet_failed": "תהליך הארנק נכשל.", + "wallet_missing": "לא זוהה ארנק במכשיר זה.", + "notify_me": "עדכנו אותי", + "notify_placeholder": "אימייל", + "notify_submit": "שמור", + "notify_saved": "ההתראה נשמרה.", + "notify_failed": "לא ניתן לשמור את העדפת ההתראה." } diff --git a/translations/hi.json b/translations/hi.json index 7cbb78e..eecd823 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -26,5 +26,26 @@ "status_heading": "वर्तमान स्थिति", "status": "फ्रेमवर्क सक्रिय कार्यान्वयन और सत्यापन चरण में है। चरणबद्ध लॉन्च तैयारी और नियंत्रित परिनियोजन ऑनबोर्डिंग के लिए प्रारंभिक नामांकन जारी किए जा रहे हैं।", "note": "नोट: यह पृष्ठ जानबूझकर न्यूनतम रखा गया है। यह उस उत्पाद दर्शन को दर्शाता है जिसमें गणना जटिल हो सकती है, लेकिन इंटरफेस शांत रहते हैं।" - } + }, + "continue_label": "जारी रखें", + "wallet_intro": "अपना नामांकन टोकन प्राप्त करें", + "wallet_fact_no_tx": "कोई ट्रांज़ैक्शन नहीं। केवल सिग्नेचर।", + "wallet_fact_seed": "अपना सीड फ़्रेज़ कभी साझा न करें।", + "wallet_have": "मेरे पास वॉलेट है", + "wallet_need": "मुझे वॉलेट चाहिए", + "wallet_install_help": "वॉलेट ऐप इंस्टॉल करें, फिर जारी रखने के लिए यहां वापस आएं।", + "wallet_install_cta": "वॉलेट इंस्टॉल करें", + "wallet_need_status": "वॉलेट ऐप इंस्टॉल करें और फिर जारी रखें।", + "wallet_connecting": "वॉलेट कनेक्ट किया जा रहा है...", + "wallet_intent": "नामांकन इंटेंट बनाया जा रहा है...", + "wallet_signing": "सिग्नेचर की प्रतीक्षा...", + "wallet_verifying": "सिग्नेचर सत्यापित किया जा रहा है...", + "wallet_success": "नामांकन स्वीकार किया गया।", + "wallet_failed": "वॉलेट फ़्लो विफल रहा।", + "wallet_missing": "इस डिवाइस पर कोई वॉलेट नहीं मिला।", + "notify_me": "मुझे सूचित करें", + "notify_placeholder": "ईमेल", + "notify_submit": "सहेजें", + "notify_saved": "सूचना सहेज ली गई।", + "notify_failed": "सूचना वरीयता सहेजी नहीं जा सकी।" } diff --git a/translations/ja.json b/translations/ja.json index a37dfa1..8bed6d6 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -26,5 +26,26 @@ "status_heading": "現在の状況", "status": "本フレームワークは、実装と検証のアクティブな段階にあります。段階的ローンチ準備と統制された導入オンボーディングに向けて、初期指定の発行を進めています。", "note": "注記: このページは意図的にミニマルです。計算は複雑でも、インターフェースは静かであるべきという製品哲学を表しています。" - } + }, + "continue_label": "続行", + "wallet_intro": "指定トークンを受け取る", + "wallet_fact_no_tx": "取引は発生しません。署名のみです。", + "wallet_fact_seed": "シードフレーズは絶対に共有しないでください。", + "wallet_have": "ウォレットを持っています", + "wallet_need": "ウォレットが必要です", + "wallet_install_help": "ウォレットアプリをインストールしてからここに戻ってください。", + "wallet_install_cta": "ウォレットをインストール", + "wallet_need_status": "ウォレットアプリをインストールしてから続行してください。", + "wallet_connecting": "ウォレットを接続しています...", + "wallet_intent": "指定インテントを作成しています...", + "wallet_signing": "署名を待機中...", + "wallet_verifying": "署名を検証しています...", + "wallet_success": "指定が確認されました。", + "wallet_failed": "ウォレットフローに失敗しました。", + "wallet_missing": "この端末でウォレットが見つかりません。", + "notify_me": "通知を受け取る", + "notify_placeholder": "メール", + "notify_submit": "保存", + "notify_saved": "通知設定を保存しました。", + "notify_failed": "通知設定を保存できませんでした。" } diff --git a/translations/ko.json b/translations/ko.json index 6a25db4..75a1cb8 100644 --- a/translations/ko.json +++ b/translations/ko.json @@ -26,5 +26,26 @@ "status_heading": "현재 상태", "status": "프레임워크는 활발한 구현 및 검증 단계에 있습니다. 단계적 출시 준비와 통제된 배포 온보딩을 위해 초기 지정이 발급되고 있습니다.", "note": "참고: 이 페이지는 의도적으로 미니멀합니다. 연산은 복잡하더라도 인터페이스는 조용해야 한다는 제품 철학을 반영합니다." - } + }, + "continue_label": "계속", + "wallet_intro": "지정 토큰 받기", + "wallet_fact_no_tx": "거래는 없습니다. 서명만 진행됩니다.", + "wallet_fact_seed": "시드 문구를 절대 공유하지 마세요.", + "wallet_have": "지갑이 있습니다", + "wallet_need": "지갑이 필요합니다", + "wallet_install_help": "지갑 앱을 설치한 뒤 여기로 돌아와 계속하세요.", + "wallet_install_cta": "지갑 설치", + "wallet_need_status": "지갑 앱을 설치한 뒤 계속 진행하세요.", + "wallet_connecting": "지갑 연결 중...", + "wallet_intent": "지정 의도를 생성하는 중...", + "wallet_signing": "서명 대기 중...", + "wallet_verifying": "서명 검증 중...", + "wallet_success": "지정이 확인되었습니다.", + "wallet_failed": "지갑 흐름에 실패했습니다.", + "wallet_missing": "이 기기에서 지갑을 찾을 수 없습니다.", + "notify_me": "알림 받기", + "notify_placeholder": "이메일", + "notify_submit": "저장", + "notify_saved": "알림 설정이 저장되었습니다.", + "notify_failed": "알림 설정을 저장하지 못했습니다." } diff --git a/translations/pt.json b/translations/pt.json index 1ded449..7234db0 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -26,5 +26,26 @@ "status_heading": "Status Atual", "status": "O framework está em implementação e validação ativa. Designações iniciais estão sendo emitidas para preparação de lançamento em fases e onboarding de implantação controlada.", "note": "Nota: Esta página é intencionalmente minimalista. Ela reflete uma filosofia de produto em que a computação é complexa e as interfaces permanecem discretas." - } + }, + "continue_label": "continuar", + "wallet_intro": "receba seu token de designação", + "wallet_fact_no_tx": "Sem transação. Apenas assinatura.", + "wallet_fact_seed": "Nunca compartilhe sua frase-semente.", + "wallet_have": "Tenho uma wallet", + "wallet_need": "Preciso de uma wallet", + "wallet_install_help": "Instale uma wallet e volte aqui para continuar.", + "wallet_install_cta": "Instalar wallet", + "wallet_need_status": "Instale uma wallet e volte para continuar.", + "wallet_connecting": "Conectando wallet...", + "wallet_intent": "Criando intenção de designação...", + "wallet_signing": "Aguardando assinatura...", + "wallet_verifying": "Verificando assinatura...", + "wallet_success": "Designação confirmada.", + "wallet_failed": "Falha no fluxo de wallet.", + "wallet_missing": "Nenhuma wallet detectada neste dispositivo.", + "notify_me": "me avise", + "notify_placeholder": "e-mail", + "notify_submit": "salvar", + "notify_saved": "Notificação salva.", + "notify_failed": "Não foi possível salvar a preferência de notificação." } diff --git a/translations/ru.json b/translations/ru.json index fff76de..bb22e95 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -26,5 +26,26 @@ "status_heading": "Текущий статус", "status": "Фреймворк находится в активной стадии реализации и валидации. Выдаются ранние назначения для поэтапной готовности к запуску и контролируемого подключения развертываний.", "note": "Примечание: эта страница намеренно минималистична. Она отражает философию продукта, где вычисления сложны, а интерфейсы остаются тихими." - } + }, + "continue_label": "продолжить", + "wallet_intro": "получите свой токен назначения", + "wallet_fact_no_tx": "Без транзакции. Только подпись.", + "wallet_fact_seed": "Никогда не делитесь seed-фразой.", + "wallet_have": "У меня есть кошелек", + "wallet_need": "Мне нужен кошелек", + "wallet_install_help": "Установите приложение кошелька и вернитесь сюда, чтобы продолжить.", + "wallet_install_cta": "Установить кошелек", + "wallet_need_status": "Установите приложение кошелька и вернитесь для продолжения.", + "wallet_connecting": "Подключение кошелька...", + "wallet_intent": "Создание намерения назначения...", + "wallet_signing": "Ожидание подписи...", + "wallet_verifying": "Проверка подписи...", + "wallet_success": "Назначение подтверждено.", + "wallet_failed": "Сбой процесса кошелька.", + "wallet_missing": "Кошелек на этом устройстве не обнаружен.", + "notify_me": "уведомить меня", + "notify_placeholder": "email", + "notify_submit": "сохранить", + "notify_saved": "Уведомление сохранено.", + "notify_failed": "Не удалось сохранить настройки уведомлений." } diff --git a/translations/zh.json b/translations/zh.json index d3fd376..f2a49de 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -26,5 +26,26 @@ "status_heading": "当前状态", "status": "该框架处于积极实施与验证阶段。早期资格正在发放,用于分阶段上线准备与受控部署接入。", "note": "说明:本页面刻意保持极简,体现了“计算可以复杂,界面应保持安静”的产品哲学。" - } + }, + "continue_label": "继续", + "wallet_intro": "接收你的指定令牌", + "wallet_fact_no_tx": "无交易,仅签名。", + "wallet_fact_seed": "请勿分享你的助记词。", + "wallet_have": "我有钱包", + "wallet_need": "我需要钱包", + "wallet_install_help": "安装钱包应用后返回此页继续。", + "wallet_install_cta": "安装钱包", + "wallet_need_status": "请先安装钱包应用,然后返回继续。", + "wallet_connecting": "正在连接钱包...", + "wallet_intent": "正在创建指定意图...", + "wallet_signing": "等待签名...", + "wallet_verifying": "正在验证签名...", + "wallet_success": "指定已确认。", + "wallet_failed": "钱包流程失败。", + "wallet_missing": "此设备未检测到钱包。", + "notify_me": "通知我", + "notify_placeholder": "邮箱", + "notify_submit": "保存", + "notify_saved": "通知已保存。", + "notify_failed": "无法保存通知偏好。" }