diff --git a/docs/LAUNCH-PLAN.md b/docs/LAUNCH-PLAN.md new file mode 100644 index 0000000..eb5594c --- /dev/null +++ b/docs/LAUNCH-PLAN.md @@ -0,0 +1,254 @@ +# Launch Plan – dgray.io + +## Release-Phasen + +| Phase | Preis pro Anzeige | Dauer | Zugang | +|-------|-------------------|-------|--------| +| **Closed Alpha** | 0,01 € / 0,01 USD / 0,01 CHF | 4–6 Wochen | Nur mit Invite-Code | +| **Open Beta** | 0,10 € / 0,10 USD / 0,10 CHF | 3 Monate | Öffentlich, Beta-Banner | +| **Launch** | 1 € / 1 USD / 1 CHF | Dauerhaft | Öffentlich | + +**Pricing-Philosophie:** Die Zahl **1** als magische Zahl — einfach, einprägsam, international gleich. +`1 listing = 1 month = 1` + +--- + +## Phase 1: Closed Alpha + +### Ziel +- Feedback von Crypto/Privacy-Enthusiasten +- Payment-Flow testen (1 Cent = echte Transaktion, minimale Kosten) +- Bugs finden, UX validieren + +### Invite-Code System + +#### Directus: Collection `invite_codes` + +| Feld | Typ | Hinweise | +|------|-----|----------| +| `id` | UUID (auto) | Primary Key | +| `code` | String, unique | z.B. `ALPHA-XMR-2026` | +| `max_uses` | Integer | Max. Einlösungen (0 = unbegrenzt) | +| `used_count` | Integer, default 0 | Aktuelle Einlösungen | +| `expires_at` | DateTime, nullable | Optional: Ablaufdatum | +| `status` | String, default `active` | `active` / `disabled` | +| `date_created` | Timestamp (auto) | | + +#### Directus: Permissions für `invite_codes` + +| Rolle | Read | Create | Update | +|-------|------|--------|--------| +| Public | Nein | Nein | Nein | +| Admin | Ja | Ja | Ja | + +Die Validierung passiert **serverseitig** im PoW-Server (PHP), nicht im Frontend. + +#### PHP: Invite-Code Validierung + +Neuer Endpoint: `POST /invite/validate` + +```php +// pow.dgray.io/invite/validate.php + 'Method not allowed']); + exit; +} + +$input = json_decode(file_get_contents('php://input'), true); +$code = trim($input['code'] ?? ''); + +if (!$code) { + http_response_code(400); + echo json_encode(['valid' => false, 'error' => 'Missing invite code']); + exit; +} + +// Query Directus for invite code +$url = DIRECTUS_URL . '/items/invite_codes?filter[code][_eq]=' . urlencode($code) + . '&filter[status][_eq]=active&limit=1'; + +$context = stream_context_create([ + 'http' => [ + 'header' => "Authorization: Bearer " . DIRECTUS_TOKEN . "\r\n", + 'ignore_errors' => true, + ], +]); + +$response = file_get_contents($url, false, $context); +$data = json_decode($response, true); +$invite = $data['data'][0] ?? null; + +if (!$invite) { + echo json_encode(['valid' => false, 'error' => 'Invalid or expired invite code']); + exit; +} + +// Check max uses +if ($invite['max_uses'] > 0 && $invite['used_count'] >= $invite['max_uses']) { + echo json_encode(['valid' => false, 'error' => 'Invite code fully redeemed']); + exit; +} + +// Check expiry +if ($invite['expires_at'] && strtotime($invite['expires_at']) < time()) { + echo json_encode(['valid' => false, 'error' => 'Invite code expired']); + exit; +} + +// Increment used_count +$updateUrl = DIRECTUS_URL . '/items/invite_codes/' . $invite['id']; +$updateContext = stream_context_create([ + 'http' => [ + 'method' => 'PATCH', + 'header' => "Content-Type: application/json\r\nAuthorization: Bearer " . DIRECTUS_TOKEN . "\r\n", + 'content' => json_encode(['used_count' => $invite['used_count'] + 1]), + 'ignore_errors' => true, + ], +]); +file_get_contents($updateUrl, false, $updateContext); + +echo json_encode(['valid' => true]); +``` + +#### Frontend: Invite-Code bei Registrierung + +In `js/components/auth-modal.js` — im Registrierungs-Flow ein Textfeld hinzufügen: + +``` +[Invite Code: _________ ] ← nur in Alpha-Phase sichtbar +[UUID generieren] +``` + +Vor `createAccount()` den Code serverseitig validieren: + +```js +const res = await fetch('https://pow.dgray.io/invite/validate', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ code: inviteCode }) +}) +const { valid, error } = await res.json() +if (!valid) { /* Fehlermeldung anzeigen */ return } +``` + +#### Feature-Flag + +In `config.php` ein Flag, um Alpha-Modus zu steuern: + +```php +define('REQUIRE_INVITE_CODE', true); // false für Open Beta / Launch +``` + +Im Invoice-Endpoint kann optional der Invite-Code mitgesendet werden, +um Alpha-User zu tracken. + +### Pricing-Änderung für Alpha + +In `docs/pow-server/config.php`: + +```php +// Alpha: 1 Cent +define('LISTING_FEE', ['EUR' => 0.01, 'USD' => 0.01, 'CHF' => 0.01, 'GBP' => 0.01, 'JPY' => 2]); +``` + +### Verteilung der Invite-Codes + +- Manuell in Directus erstellen (Admin-Panel) +- Verteilen über: + - r/Monero, r/privacy + - Monero Matrix/IRC Channels + - Persönliche Kontakte +- **10–20 Codes** mit je 5–10 max_uses → 50–200 Alpha-Tester + +--- + +## Phase 2: Open Beta + +### Änderungen + +1. **Pricing**: `config.php` → `LISTING_FEE` auf 0,10 setzen +2. **Invite-Code**: `REQUIRE_INVITE_CODE` → `false` +3. **Beta-Banner**: Hinweis auf der Seite ("Beta — Feedback willkommen") +4. **Feedback-Kanal**: Link zu Matrix-Raum oder einfaches Kontaktformular + +### Marketing + +- **Show HN** Post auf Hacker News +- r/Monero, r/privacy, r/degoogle — organische Posts +- Privacy-Newsletter (PrivacyGuides, Techlore) +- Monero-Community (Matrix, IRC) + +### Metriken tracken + +- Anzahl Registrierungen +- Anzahl veröffentlichter Anzeigen +- Payment Completion Rate +- Chat-Nutzung (nur Anzahl, nicht Inhalt) + +--- + +## Phase 3: Launch + +### Änderungen + +1. **Pricing**: `config.php` → `LISTING_FEE` auf 1 setzen (bereits Standard) +2. **Beta-Banner** entfernen +3. **Invite-Code-System** kann aktiv bleiben (für spätere Promo-Codes nutzbar) + +### Kommunikation + +**"1 listing = 1 month = 1"** — so simpel wie möglich. + +--- + +## Meta-Tags (pro Sprache) + +Die statischen Meta-Tags in `index.html` sind deutsch (Fallback). +Der OG-Proxy (`pow.dgray.io/og-proxy.php`) liefert Listing-spezifische Tags. + +### Umgesetzte Texte + +**Title:** `dgray.io – [Sprache]` + +| Sprache | Title | Description | +|---------|-------|-------------| +| **de** | dgray.io – Anonyme Kleinanzeigen | Kaufen und verkaufen ohne Konto, ohne E-Mail. Bezahlung mit Monero. Ende-zu-Ende verschlüsselter Chat. | +| **en** | dgray.io – Private Classifieds | Buy and sell without an account, without email. Pay with Monero. End-to-end encrypted chat. | +| **fr** | dgray.io – Petites annonces anonymes | Achetez et vendez sans compte, sans e-mail. Paiement en Monero. Chat chiffré de bout en bout. | +| **it** | dgray.io – Annunci anonimi | Compra e vendi senza account, senza email. Pagamento in Monero. Chat crittografata end-to-end. | +| **es** | dgray.io – Clasificados anónimos | Compra y vende sin cuenta, sin email. Pago con Monero. Chat cifrado de extremo a extremo. | +| **pt** | dgray.io – Classificados anônimos | Compre e venda sem conta, sem email. Pagamento com Monero. Chat criptografado ponta a ponta. | +| **ru** | dgray.io – Анонимные объявления | Покупайте и продавайте без аккаунта, без email. Оплата Monero. Сквозное шифрование чата. | + +### Umsetzung + +Die `index.html` enthält die Standard-Meta-Tags (de). +Für sprachspezifische OG-Tags bei Social-Media-Shares: +Der OG-Proxy kann um einen `?lang=` Parameter erweitert werden. + +Die `i18n.js` `updateDOM()` Methode aktualisiert `document.title`, `og:title/description` +und `twitter:title/description` dynamisch bei jedem Sprachwechsel (i18n-Keys `meta.title`, `meta.description`). + +--- + +## Checkliste vor Alpha-Start + +- [ ] Directus: Collection `invite_codes` anlegen (Schema siehe oben) +- [ ] PHP: `invite/validate.php` deployen auf `pow.dgray.io` +- [ ] PHP: `config.php` → `LISTING_FEE` auf 0.01 setzen +- [ ] PHP: `config.php` → `REQUIRE_INVITE_CODE = true` +- [ ] Frontend: Invite-Code-Feld in `auth-modal.js` einbauen +- [x] Frontend: Meta-Description i18n-Keys in alle 7 Sprachen +- [ ] 10–20 Invite-Codes in Directus erstellen +- [ ] Codes verteilen (r/Monero, Matrix, persönlich) +- [ ] Feedback-Kanal einrichten (Matrix-Raum) diff --git a/index.html b/index.html index d923fa6..f281943 100644 --- a/index.html +++ b/index.html @@ -3,16 +3,16 @@
- + -