Align web repo to app-channel member communication

This commit is contained in:
Joshua 2026-02-17 12:15:41 -08:00
parent d58c92ad55
commit 2e8348826f
23 changed files with 217 additions and 68 deletions

View File

@ -7,6 +7,9 @@ Public-facing EDUT web surfaces and deployment specs.
```text ```text
public/ public/
index.html index.html
downloads/desktop/index.html
downloads/ios/index.html
downloads/android/index.html
store/index.html store/index.html
store/offers.json store/offers.json
trust/index.html trust/index.html
@ -45,6 +48,7 @@ docs/
migration-policy-v1-to-v2.md migration-policy-v1-to-v2.md
issuer-onboarding-pack.md issuer-onboarding-pack.md
release-gate.md release-gate.md
app-channel-spec.md
review-notes.md review-notes.md
platform-spec-alignment-review.md platform-spec-alignment-review.md
contracts/ contracts/

43
docs/app-channel-spec.md Normal file
View File

@ -0,0 +1,43 @@
# App Channel Communication Spec
## Principle
Member communication is delivered through EDUT app channels after wallet sign-in.
## Why
1. Avoids email deliverability and spam filtering constraints.
2. Avoids SMS compliance overhead.
3. Keeps communication tied to wallet-authenticated entitlements.
## Channel Model
1. Membership mint unlocks app download.
2. App sign-in with wallet activates member channel.
3. Push/in-app notifications are scoped to member ownership state.
4. Web never becomes a long-term broadcast channel once membership is active.
## Notification Classes
1. Offer availability relevant to owned memberships/entitlements.
2. License activation and entitlement state updates.
3. Platform updates and required migration actions.
4. Publisher updates for offers a member owns.
5. Membership policy changes that affect purchase or activation behavior.
## Gate Rule
No active membership -> no app access channel.
## Deterministic Delivery Rules
1. Notification eligibility is computed from wallet-authenticated entitlement state.
2. Delivery payloads must include deterministic event ids for dedupe and audit.
3. Membership revocation/suspension immediately suppresses member-channel delivery.
4. No marketing list fan-out disconnected from entitlement state.
## Non-Goals
1. Marketing blast lists detached from wallet state.
2. SMS-first member notification flows.
3. Email-first member notification dependency.

View File

@ -8,7 +8,7 @@
## Backend Responsibilities ## Backend Responsibilities
1. Intent/verify/quote/confirm/notify endpoints. 1. Intent/verify/quote/confirm/status endpoints.
2. Deterministic state transitions and persistence. 2. Deterministic state transitions and persistence.
3. Chain verification and policy hash enforcement. 3. Chain verification and policy hash enforcement.

View File

@ -7,7 +7,7 @@ This matrix prevents drift between public surfaces and legal posture.
| Landing (`public/index.html`) | Wallet signature + paid membership unlocks access | Investment, yield, appreciation claims | | Landing (`public/index.html`) | Wallet signature + paid membership unlocks access | Investment, yield, appreciation claims |
| Store (`public/store/index.html`) | Membership required for purchasing offers | "Membership includes all products forever" | | Store (`public/store/index.html`) | Membership required for purchasing offers | "Membership includes all products forever" |
| Terms (`public/terms/index.html`) | Membership is utility access; licenses separate | Equity/ownership implications | | Terms (`public/terms/index.html`) | Membership is utility access; licenses separate | Equity/ownership implications |
| Privacy (`public/privacy/index.html`) | Wallet/signature processing and optional notify email | Hidden collection claims inconsistent with implementation | | Privacy (`public/privacy/index.html`) | Wallet/signature processing and app-channel member communication model | Hidden collection claims inconsistent with implementation |
| Vision/spec docs | Deterministic governance and fail-closed controls | Speculative financial framing | | Vision/spec docs | Deterministic governance and fail-closed controls | Speculative financial framing |
## Hard Rules ## Hard Rules

View File

@ -24,7 +24,7 @@ Validate membership-flow strings and legal-critical labels across all locale bun
1. core identity support keys (`definition`, `descriptor`, `acknowledged`, `privacy`, `terms`) 1. core identity support keys (`definition`, `descriptor`, `acknowledged`, `privacy`, `terms`)
2. wallet flow keys (`continue_label`, `wallet_intro`, `wallet_connecting`, `wallet_signing`, `wallet_verifying`, `wallet_failed`) 2. wallet flow keys (`continue_label`, `wallet_intro`, `wallet_connecting`, `wallet_signing`, `wallet_verifying`, `wallet_failed`)
3. membership flow keys (`membership_quoting`, `membership_minting`, `membership_confirming`, `membership_active`) 3. membership flow keys (`membership_quoting`, `membership_minting`, `membership_confirming`, `membership_active`)
4. notify keys (`notify_me`, `notify_placeholder`, `notify_submit`, `notify_saved`, `notify_failed`) 4. delivery keys (`download_heading`, `download_desktop`, `download_ios`, `download_android`, `app_notifications_note`)
## QA Checks ## QA Checks

View File

@ -28,7 +28,7 @@ The revised web copy is aligned with core platform direction on:
- Revised copy avoids hard claims that can conflict with evolving licensing rails. - Revised copy avoids hard claims that can conflict with evolving licensing rails.
3. Secret system spec correctness fixed. 3. Secret system spec correctness fixed.
- Mailgun signing verification now references signing key. - Legacy email/SMS designation path removed from active flow.
- Failure handling now avoids silent acceptance on processing failure. - Failure handling now avoids silent acceptance on processing failure.
## Deliberate Neutrality Choices ## Deliberate Neutrality Choices

View File

@ -19,7 +19,7 @@
## API Controls ## API Controls
1. Rate limits on intent, verify, quote, confirm, notify. 1. Rate limits on intent, verify, quote, confirm, and status.
2. Request size limits. 2. Request size limits.
3. Structured error responses without sensitive internals. 3. Structured error responses without sensitive internals.
4. Correlation ID logging for all transitions. 4. Correlation ID logging for all transitions.

View File

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EDUT Platform - Android</title>
<meta name="description" content="EDUT Android platform delivery channel.">
<style>
body {
font-family: 'IBM Plex Mono', 'Courier New', Courier, monospace;
background: #f0f4f8;
color: #2c2c2c;
padding: 40px 20px;
margin: 0;
}
.container {
max-width: 760px;
margin: 0 auto;
line-height: 1.7;
letter-spacing: 0.06em;
font-size: 12px;
}
h1 {
font-size: 18px;
font-weight: 300;
letter-spacing: 0.12em;
text-transform: uppercase;
margin: 10px 0 20px;
}
.muted {
color: #5f646b;
}
.card {
margin-top: 18px;
padding: 16px;
border: 1px solid #d6dce4;
background: #f7fafc;
}
a { color: #2c2c2c; text-decoration: underline; text-underline-offset: 2px; }
</style>
</head>
<body>
<div class="container">
<p><a href="/">back</a></p>
<h1>EDUT Platform Android</h1>
<p class="muted">membership channel acknowledged</p>
<div class="card">
<p>This endpoint is bound to wallet-authenticated membership access.</p>
<p>When Android distribution is staged for your designation era, this channel delivers current install instructions.</p>
<p>Member updates and entitlement notices are delivered inside the EDUT app after wallet sign-in.</p>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EDUT Platform - Desktop</title>
<meta name="description" content="EDUT desktop platform delivery channel.">
<style>
body {
font-family: 'IBM Plex Mono', 'Courier New', Courier, monospace;
background: #f0f4f8;
color: #2c2c2c;
padding: 40px 20px;
margin: 0;
}
.container {
max-width: 760px;
margin: 0 auto;
line-height: 1.7;
letter-spacing: 0.06em;
font-size: 12px;
}
h1 {
font-size: 18px;
font-weight: 300;
letter-spacing: 0.12em;
text-transform: uppercase;
margin: 10px 0 20px;
}
.muted {
color: #5f646b;
}
.card {
margin-top: 18px;
padding: 16px;
border: 1px solid #d6dce4;
background: #f7fafc;
}
a { color: #2c2c2c; text-decoration: underline; text-underline-offset: 2px; }
</style>
</head>
<body>
<div class="container">
<p><a href="/">back</a></p>
<h1>EDUT Platform Desktop</h1>
<p class="muted">membership channel acknowledged</p>
<div class="card">
<p>This endpoint is bound to wallet-authenticated membership access.</p>
<p>When desktop installers are staged for your designation era, this channel delivers them directly.</p>
<p>Member updates and entitlement notices are delivered inside the EDUT app after wallet sign-in.</p>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EDUT Platform - iOS</title>
<meta name="description" content="EDUT iOS platform delivery channel.">
<style>
body {
font-family: 'IBM Plex Mono', 'Courier New', Courier, monospace;
background: #f0f4f8;
color: #2c2c2c;
padding: 40px 20px;
margin: 0;
}
.container {
max-width: 760px;
margin: 0 auto;
line-height: 1.7;
letter-spacing: 0.06em;
font-size: 12px;
}
h1 {
font-size: 18px;
font-weight: 300;
letter-spacing: 0.12em;
text-transform: uppercase;
margin: 10px 0 20px;
}
.muted {
color: #5f646b;
}
.card {
margin-top: 18px;
padding: 16px;
border: 1px solid #d6dce4;
background: #f7fafc;
}
a { color: #2c2c2c; text-decoration: underline; text-underline-offset: 2px; }
</style>
</head>
<body>
<div class="container">
<p><a href="/">back</a></p>
<h1>EDUT Platform iOS</h1>
<p class="muted">membership channel acknowledged</p>
<div class="card">
<p>This endpoint is bound to wallet-authenticated membership access.</p>
<p>When iOS distribution is staged for your designation era, this channel delivers current install instructions.</p>
<p>Member updates and entitlement notices are delivered inside the EDUT app after wallet sign-in.</p>
</div>
</div>
</body>
</html>

View File

@ -251,7 +251,7 @@
color: #7a3a3a; color: #7a3a3a;
} }
.notify-panel { .delivery-panel {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 10px; gap: 10px;
@ -259,7 +259,7 @@
opacity: 0; opacity: 0;
transition: opacity 500ms ease; transition: opacity 500ms ease;
} }
.notify-panel.visible { .delivery-panel.visible {
opacity: 1; opacity: 1;
} }
@ -415,7 +415,7 @@
</div> </div>
</div> </div>
<div id="flow-status" class="flow-status flow-hidden" aria-live="polite"></div> <div id="flow-status" class="flow-status flow-hidden" aria-live="polite"></div>
<div id="delivery-panel" class="notify-panel flow-hidden"> <div id="delivery-panel" class="delivery-panel flow-hidden">
<p class="flow-line localizable" data-i18n="download_heading">download your platform</p> <p class="flow-line localizable" data-i18n="download_heading">download your platform</p>
<div class="download-links"> <div class="download-links">
<a id="download-desktop" class="flow-link localizable" data-i18n="download_desktop" href="/downloads/desktop">desktop</a> <a id="download-desktop" class="flow-link localizable" data-i18n="download_desktop" href="/downloads/desktop">desktop</a>

View File

@ -47,11 +47,6 @@
"membership_active": "العضوية نشطة. تم تأكيد التعيين.", "membership_active": "العضوية نشطة. تم تأكيد التعيين.",
"wallet_failed": "فشل مسار المحفظة.", "wallet_failed": "فشل مسار المحفظة.",
"wallet_missing": "لم يتم العثور على محفظة على هذا الجهاز.", "wallet_missing": "لم يتم العثور على محفظة على هذا الجهاز.",
"notify_me": "أبلغني",
"notify_placeholder": "البريد الإلكتروني",
"notify_submit": "حفظ",
"notify_saved": "تم حفظ الإشعار.",
"notify_failed": "تعذر حفظ تفضيل الإشعار.",
"download_heading": "نزّل منصتك", "download_heading": "نزّل منصتك",
"download_desktop": "سطح المكتب", "download_desktop": "سطح المكتب",
"download_ios": "iOS", "download_ios": "iOS",

View File

@ -47,11 +47,6 @@
"membership_active": "Membership aktiv. Designation bestaetigt.", "membership_active": "Membership aktiv. Designation bestaetigt.",
"wallet_failed": "Wallet-Ablauf fehlgeschlagen.", "wallet_failed": "Wallet-Ablauf fehlgeschlagen.",
"wallet_missing": "Kein Wallet auf diesem Gerät erkannt.", "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.",
"download_heading": "lade deine plattform herunter", "download_heading": "lade deine plattform herunter",
"download_desktop": "desktop", "download_desktop": "desktop",
"download_ios": "iOS", "download_ios": "iOS",

View File

@ -47,11 +47,6 @@
"membership_active": "Membership active. Designation acknowledged.", "membership_active": "Membership active. Designation acknowledged.",
"wallet_failed": "Wallet flow failed.", "wallet_failed": "Wallet flow failed.",
"wallet_missing": "No wallet detected on this device.", "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.",
"download_heading": "download your platform", "download_heading": "download your platform",
"download_desktop": "desktop", "download_desktop": "desktop",
"download_ios": "iOS", "download_ios": "iOS",

View File

@ -47,11 +47,6 @@
"membership_active": "Membresía activa. Designación reconocida.", "membership_active": "Membresía activa. Designación reconocida.",
"wallet_failed": "Falló el flujo de wallet.", "wallet_failed": "Falló el flujo de wallet.",
"wallet_missing": "No se detectó wallet en este dispositivo.", "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.",
"download_heading": "descarga tu plataforma", "download_heading": "descarga tu plataforma",
"download_desktop": "escritorio", "download_desktop": "escritorio",
"download_ios": "iOS", "download_ios": "iOS",

View File

@ -47,11 +47,6 @@
"membership_active": "Adhesion active. Designation confirmee.", "membership_active": "Adhesion active. Designation confirmee.",
"wallet_failed": "Le flux wallet a echoue.", "wallet_failed": "Le flux wallet a echoue.",
"wallet_missing": "Aucun wallet detecte sur cet appareil.", "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.",
"download_heading": "telechargez votre plateforme", "download_heading": "telechargez votre plateforme",
"download_desktop": "bureau", "download_desktop": "bureau",
"download_ios": "iOS", "download_ios": "iOS",

View File

@ -47,11 +47,6 @@
"membership_active": "החברות פעילה. הייעוד אושר.", "membership_active": "החברות פעילה. הייעוד אושר.",
"wallet_failed": "תהליך הארנק נכשל.", "wallet_failed": "תהליך הארנק נכשל.",
"wallet_missing": "לא זוהה ארנק במכשיר זה.", "wallet_missing": "לא זוהה ארנק במכשיר זה.",
"notify_me": "עדכנו אותי",
"notify_placeholder": "אימייל",
"notify_submit": "שמור",
"notify_saved": "ההתראה נשמרה.",
"notify_failed": "לא ניתן לשמור את העדפת ההתראה.",
"download_heading": "הורד את הפלטפורמה שלך", "download_heading": "הורד את הפלטפורמה שלך",
"download_desktop": "דסקטופ", "download_desktop": "דסקטופ",
"download_ios": "iOS", "download_ios": "iOS",

View File

@ -47,11 +47,6 @@
"membership_active": "सदस्यता सक्रिय है। नामांकन स्वीकार किया गया।", "membership_active": "सदस्यता सक्रिय है। नामांकन स्वीकार किया गया।",
"wallet_failed": "वॉलेट फ़्लो विफल रहा।", "wallet_failed": "वॉलेट फ़्लो विफल रहा।",
"wallet_missing": "इस डिवाइस पर कोई वॉलेट नहीं मिला।", "wallet_missing": "इस डिवाइस पर कोई वॉलेट नहीं मिला।",
"notify_me": "मुझे सूचित करें",
"notify_placeholder": "ईमेल",
"notify_submit": "सहेजें",
"notify_saved": "सूचना सहेज ली गई।",
"notify_failed": "सूचना वरीयता सहेजी नहीं जा सकी।",
"download_heading": "अपना प्लेटफ़ॉर्म डाउनलोड करें", "download_heading": "अपना प्लेटफ़ॉर्म डाउनलोड करें",
"download_desktop": "डेस्कटॉप", "download_desktop": "डेस्कटॉप",
"download_ios": "iOS", "download_ios": "iOS",

View File

@ -47,11 +47,6 @@
"membership_active": "メンバーシップが有効です。指定が確認されました。", "membership_active": "メンバーシップが有効です。指定が確認されました。",
"wallet_failed": "ウォレットフローに失敗しました。", "wallet_failed": "ウォレットフローに失敗しました。",
"wallet_missing": "この端末でウォレットが見つかりません。", "wallet_missing": "この端末でウォレットが見つかりません。",
"notify_me": "通知を受け取る",
"notify_placeholder": "メール",
"notify_submit": "保存",
"notify_saved": "通知設定を保存しました。",
"notify_failed": "通知設定を保存できませんでした。",
"download_heading": "プラットフォームをダウンロード", "download_heading": "プラットフォームをダウンロード",
"download_desktop": "デスクトップ", "download_desktop": "デスクトップ",
"download_ios": "iOS", "download_ios": "iOS",

View File

@ -47,11 +47,6 @@
"membership_active": "멤버십이 활성화되었습니다. 지정이 확인되었습니다.", "membership_active": "멤버십이 활성화되었습니다. 지정이 확인되었습니다.",
"wallet_failed": "지갑 흐름에 실패했습니다.", "wallet_failed": "지갑 흐름에 실패했습니다.",
"wallet_missing": "이 기기에서 지갑을 찾을 수 없습니다.", "wallet_missing": "이 기기에서 지갑을 찾을 수 없습니다.",
"notify_me": "알림 받기",
"notify_placeholder": "이메일",
"notify_submit": "저장",
"notify_saved": "알림 설정이 저장되었습니다.",
"notify_failed": "알림 설정을 저장하지 못했습니다.",
"download_heading": "플랫폼 다운로드", "download_heading": "플랫폼 다운로드",
"download_desktop": "데스크톱", "download_desktop": "데스크톱",
"download_ios": "iOS", "download_ios": "iOS",

View File

@ -47,11 +47,6 @@
"membership_active": "Associação ativa. Designação confirmada.", "membership_active": "Associação ativa. Designação confirmada.",
"wallet_failed": "Falha no fluxo de wallet.", "wallet_failed": "Falha no fluxo de wallet.",
"wallet_missing": "Nenhuma wallet detectada neste dispositivo.", "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.",
"download_heading": "baixe sua plataforma", "download_heading": "baixe sua plataforma",
"download_desktop": "desktop", "download_desktop": "desktop",
"download_ios": "iOS", "download_ios": "iOS",

View File

@ -47,11 +47,6 @@
"membership_active": "Членство активно. Назначение подтверждено.", "membership_active": "Членство активно. Назначение подтверждено.",
"wallet_failed": "Сбой процесса кошелька.", "wallet_failed": "Сбой процесса кошелька.",
"wallet_missing": "Кошелек на этом устройстве не обнаружен.", "wallet_missing": "Кошелек на этом устройстве не обнаружен.",
"notify_me": "уведомить меня",
"notify_placeholder": "email",
"notify_submit": "сохранить",
"notify_saved": "Уведомление сохранено.",
"notify_failed": "Не удалось сохранить настройки уведомлений.",
"download_heading": "скачайте свою платформу", "download_heading": "скачайте свою платформу",
"download_desktop": "десктоп", "download_desktop": "десктоп",
"download_ios": "iOS", "download_ios": "iOS",

View File

@ -47,11 +47,6 @@
"membership_active": "会员已激活。指定已确认。", "membership_active": "会员已激活。指定已确认。",
"wallet_failed": "钱包流程失败。", "wallet_failed": "钱包流程失败。",
"wallet_missing": "此设备未检测到钱包。", "wallet_missing": "此设备未检测到钱包。",
"notify_me": "通知我",
"notify_placeholder": "邮箱",
"notify_submit": "保存",
"notify_saved": "通知已保存。",
"notify_failed": "无法保存通知偏好。",
"download_heading": "下载你的平台", "download_heading": "下载你的平台",
"download_desktop": "桌面版", "download_desktop": "桌面版",
"download_ios": "iOS", "download_ios": "iOS",