refactor: rename project from dgray.io to kashilo.com

This commit is contained in:
2026-02-10 18:43:49 +01:00
parent 4e77ce92f3
commit 9069404942
60 changed files with 260 additions and 260 deletions

View File

@@ -4,7 +4,7 @@ Dieses Dokument hilft AI-Assistenten (Amp, Copilot, etc.) das Projekt zu versteh
## Projekt-Überblick ## Projekt-Überblick
**dgray.io** ist eine Kleinanzeigen-PWA mit Monero-Bezahlung. **kashilo.com** ist eine Kleinanzeigen-PWA mit Monero-Bezahlung.
- **Status**: Active Development (Frontend + Directus Backend) - **Status**: Active Development (Frontend + Directus Backend)
- **Ziel**: Anonyme, dezentrale Marktplatz-Alternative - **Ziel**: Anonyme, dezentrale Marktplatz-Alternative
@@ -17,10 +17,10 @@ Dieses Dokument hilft AI-Assistenten (Amp, Copilot, etc.) das Projekt zu versteh
| Routing | Hash-basierter Client-Side Router (`js/router.js`) | | Routing | Hash-basierter Client-Side Router (`js/router.js`) |
| i18n | Custom System (`js/i18n.js`), JSON-Dateien in `/locales/` | | i18n | Custom System (`js/i18n.js`), JSON-Dateien in `/locales/` |
| Theming | CSS Variables, Dark/Light Mode | | Theming | CSS Variables, Dark/Light Mode |
| Backend | Directus (`api.dgray.io`) | | Backend | Directus (`api.kashilo.com`) |
| Auth | UUID-basiert, anonym (`js/services/auth.js`) | | Auth | UUID-basiert, anonym (`js/services/auth.js`) |
| E2E Crypto | TweetNaCl (self-hosted in `js/vendor/`), `box.before` + `secretbox` | | E2E Crypto | TweetNaCl (self-hosted in `js/vendor/`), `box.before` + `secretbox` |
| PoW Captcha | PHP-Server (`pow.dgray.io`), Fallback auf lokal | | PoW Captcha | PHP-Server (`pow.kashilo.com`), Fallback auf lokal |
## Häufige Befehle ## Häufige Befehle
@@ -90,8 +90,8 @@ docs/
├── MONETIZATION.md # Monetarisierung & Anti-Abuse ├── MONETIZATION.md # Monetarisierung & Anti-Abuse
├── LAUNCH-PLAN.md # Release-Phasen, Pricing, Checkliste ├── LAUNCH-PLAN.md # Release-Phasen, Pricing, Checkliste
├── REPUTATION.md # Reputation-System (Konzept, Directus-Anleitung) ├── REPUTATION.md # Reputation-System (Konzept, Directus-Anleitung)
├── pow-server/ # PHP PoW-Captcha Server (pow.dgray.io) ├── pow-server/ # PHP PoW-Captcha Server (pow.kashilo.com)
└── og-proxy.php # Open Graph Meta-Tag Proxy (pow.dgray.io) └── og-proxy.php # Open Graph Meta-Tag Proxy (pow.kashilo.com)
css/ css/
├── fonts.css # @font-face Definitionen (Inter, Space Grotesk) ├── fonts.css # @font-face Definitionen (Inter, Space Grotesk)
@@ -163,10 +163,10 @@ locales/
1. ~~Seiten für Profil-Dropdown~~ ✅ Fertig 1. ~~Seiten für Profil-Dropdown~~ ✅ Fertig
2. ~~Suchseite mit Filtern~~ ✅ Merged in `page-home.js` 2. ~~Suchseite mit Filtern~~ ✅ Merged in `page-home.js`
3. ~~Listings bearbeiten~~ ✅ Edit-Modus via `#/edit/:id` 3. ~~Listings bearbeiten~~ ✅ Edit-Modus via `#/edit/:id`
4. ~~PoW-Captcha server-seitig~~ ✅ PHP-Server auf `pow.dgray.io` 4. ~~PoW-Captcha server-seitig~~ ✅ PHP-Server auf `pow.kashilo.com`
5. ~~TweetNaCl self-hosted~~ ✅ In `js/vendor/` 5. ~~TweetNaCl self-hosted~~ ✅ In `js/vendor/`
6. ~~Chat-Crypto fix~~ ✅ Per-listing E2E Keys, TOFU Key-Pinning, kein Pending-Flow mehr 6. ~~Chat-Crypto fix~~ ✅ Per-listing E2E Keys, TOFU Key-Pinning, kein Pending-Flow mehr
7. ~~Payment-Integration mit BTCpay Server~~ ✅ Proxy auf `pow.dgray.io`, Frontend-Service `btcpay.js` 7. ~~Payment-Integration mit BTCpay Server~~ ✅ Proxy auf `pow.kashilo.com`, Frontend-Service `btcpay.js`
8. ~~Favoriten Directus Sync~~ ✅ FavoritesService mit Union-Merge bei Login 8. ~~Favoriten Directus Sync~~ ✅ FavoritesService mit Union-Merge bei Login
9. ~~Expired Listings~~ ✅ Directus Flow (alle 15 Min), Status-Badges auf Cards 9. ~~Expired Listings~~ ✅ Directus Flow (alle 15 Min), Status-Badges auf Cards
10. ~~Reputation-System~~`reputation.js` Service, Deals/Ratings Collections, Chat-Widget Deal-Bestätigung + Sterne-Bewertung, Seller-Card Badges (siehe `docs/REPUTATION.md`) 10. ~~Reputation-System~~`reputation.js` Service, Deals/Ratings Collections, Chat-Widget Deal-Bestätigung + Sterne-Bewertung, Seller-Card Badges (siehe `docs/REPUTATION.md`)

View File

@@ -1,10 +1,10 @@
# dgray.io # kashilo.com
Eine anonyme, dezentrale Kleinanzeigen-Plattform mit Monero-Bezahlung. Eine anonyme, dezentrale Kleinanzeigen-Plattform mit Monero-Bezahlung.
## 🎯 Vision ## 🎯 Vision
dgray.io ermöglicht es Nutzern, Kleinanzeigen zu schalten und Waren/Dienstleistungen sicher über Monero (XMR) zu handeln. Besonderheiten: kashilo.com ermöglicht es Nutzern, Kleinanzeigen zu schalten und Waren/Dienstleistungen sicher über Monero (XMR) zu handeln. Besonderheiten:
- **Anonymität**: Nutzung ohne Account möglich - **Anonymität**: Nutzung ohne Account möglich
- **Direkte Zahlung**: Peer-to-Peer via Monero, keine Zahlungsvermittlung - **Direkte Zahlung**: Peer-to-Peer via Monero, keine Zahlungsvermittlung
@@ -58,8 +58,8 @@ dgray.io ermöglicht es Nutzern, Kleinanzeigen zu schalten und Waren/Dienstleist
- Custom Extensions für XMR-Integration - Custom Extensions für XMR-Integration
### Services ### Services
- **Directus** Backend: `api.dgray.io` (Docker) - **Directus** Backend: `api.kashilo.com` (Docker)
- **PoW Captcha + Payment Proxy**: `pow.dgray.io` (PHP, HMAC-signierte Challenges, BTCPay Proxy + Webhook, OG Meta Proxy) - **PoW Captcha + Payment Proxy**: `pow.kashilo.com` (PHP, HMAC-signierte Challenges, BTCPay Proxy + Webhook, OG Meta Proxy)
- **BTCPay Server**: `pay.xmr.rocks` (Monero-Zahlungen, Trocador-Plugin) - **BTCPay Server**: `pay.xmr.rocks` (Monero-Zahlungen, Trocador-Plugin)
- **TweetNaCl**: Self-hosted in `js/vendor/` (E2E-Verschlüsselung) - **TweetNaCl**: Self-hosted in `js/vendor/` (E2E-Verschlüsselung)
@@ -124,7 +124,7 @@ Das Build-Script minifiziert alle JS- und CSS-Dateien (~111 KiB Ersparnis) und k
```bash ```bash
# Einmalig: SSH-User und Pfad anpassen # Einmalig: SSH-User und Pfad anpassen
./deploy.sh user@dgray.io /home/user/web/dgray.io/public_html ./deploy.sh user@kashilo.com /home/user/web/kashilo.com/public_html
# Oder Defaults im Script setzen und einfach: # Oder Defaults im Script setzen und einfach:
./deploy.sh ./deploy.sh
@@ -152,7 +152,7 @@ Die Tests laufen im Browser und nutzen einen minimalen Test-Runner ohne externe
### Projektstruktur ### Projektstruktur
``` ```
dgray/ kashilo/
├── index.html # Entry Point ├── index.html # Entry Point
├── manifest.json # PWA Manifest ├── manifest.json # PWA Manifest
├── service-worker.js # Offline-Support ├── service-worker.js # Offline-Support
@@ -222,7 +222,7 @@ dgray/
- [ ] Responsive Optimierungen - [ ] Responsive Optimierungen
### Phase 2: Backend-Integration ⬅️ **Aktuell** ### Phase 2: Backend-Integration ⬅️ **Aktuell**
- [x] Directus aufsetzen (`api.dgray.io`) - [x] Directus aufsetzen (`api.kashilo.com`)
- [x] Listings-Collection (CRUD) - [x] Listings-Collection (CRUD)
- [x] Categories mit Übersetzungen - [x] Categories mit Übersetzungen
- [x] User-Auth (UUID + SHA-256 Hash, anonym) - [x] User-Auth (UUID + SHA-256 Hash, anonym)
@@ -237,7 +237,7 @@ dgray/
- [x] Conversations/Messages Services - [x] Conversations/Messages Services
- [x] Merkliste (Favoriten-Seite) - [x] Merkliste (Favoriten-Seite)
- [x] Favoriten Directus-Sync (Union-Merge bei Login, localStorage-Fallback) - [x] Favoriten Directus-Sync (Union-Merge bei Login, localStorage-Fallback)
- [x] PoW Captcha (server-seitig via pow.dgray.io, HMAC-signiert) - [x] PoW Captcha (server-seitig via pow.kashilo.com, HMAC-signiert)
- [x] TweetNaCl self-hosted (kein CDN) - [x] TweetNaCl self-hosted (kein CDN)
- [x] In-App Benachrichtigungen (Notifications-Service, Glocke mit Badge) - [x] In-App Benachrichtigungen (Notifications-Service, Glocke mit Badge)
- [x] Open Graph & X Card Meta-Tags (dynamisch pro Listing) - [x] Open Graph & X Card Meta-Tags (dynamisch pro Listing)
@@ -246,7 +246,7 @@ dgray/
### Phase 4: Payments ### Phase 4: Payments
- [x] XMR-Kursabfrage API (CoinGecko) - [x] XMR-Kursabfrage API (CoinGecko)
- [x] Fiat ↔ XMR Umrechnung (Dual-Preis-Anzeige) - [x] Fiat ↔ XMR Umrechnung (Dual-Preis-Anzeige)
- [x] BTCPay Server Integration (`pay.xmr.rocks`, Proxy auf `pow.dgray.io`) - [x] BTCPay Server Integration (`pay.xmr.rocks`, Proxy auf `pow.kashilo.com`)
- [x] Listing-Gebühr: 1 EUR/USD/CHF/GBP (200 JPY) via Monero - [x] Listing-Gebühr: 1 EUR/USD/CHF/GBP (200 JPY) via Monero
- [x] Webhook für Auto-Publish nach Blockchain-Confirmation - [x] Webhook für Auto-Publish nach Blockchain-Confirmation
- [x] Expired Listings (Directus Flow, Status-Badges auf Cards) - [x] Expired Listings (Directus Flow, Status-Badges auf Cards)

View File

@@ -1,12 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 32"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 140 36">
<style> <style>
.logo-text { .logo-text {
font-family: 'Space Grotesk', system-ui, sans-serif; font-family: 'Space Grotesk', system-ui, sans-serif;
font-weight: 700; font-weight: 700;
font-size: 24px; font-size: 30px;
} }
</style> </style>
<text x="4" y="24" class="logo-text"> <text x="2" y="28" class="logo-text">
<tspan fill="#10B981">d</tspan><tspan fill="#AAAAAA">gray</tspan> <tspan fill="#10B981">k</tspan><tspan fill="#AAAAAA">ashilo</tspan>
</text> </text>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 339 B

After

Width:  |  Height:  |  Size: 341 B

View File

@@ -1,12 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 32"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 140 36">
<style> <style>
.logo-text { .logo-text {
font-family: 'Space Grotesk', system-ui, sans-serif; font-family: 'Space Grotesk', system-ui, sans-serif;
font-weight: 700; font-weight: 700;
font-size: 24px; font-size: 30px;
} }
</style> </style>
<text x="4" y="24" class="logo-text"> <text x="2" y="28" class="logo-text">
<tspan fill="#059669">d</tspan><tspan fill="#555555">gray</tspan> <tspan fill="#059669">k</tspan><tspan fill="#555555">ashilo</tspan>
</text> </text>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 339 B

After

Width:  |  Height:  |  Size: 341 B

View File

@@ -1,4 +1,4 @@
# dgray.io Press Kit # kashilo.com Press Kit
## Brand Assets ## Brand Assets
@@ -35,7 +35,7 @@
## Verwendung ## Verwendung
- Logo nicht verzerren, strecken oder recolorieren - Logo nicht verzerren, strecken oder recolorieren
- Mindestabstand: Höhe des "d" in "dgray" auf allen Seiten - Mindestabstand: Höhe des "k" in "kashilo" auf allen Seiten
- Bevorzugt SVG verwenden - Bevorzugt SVG verwenden
- Bei dunklem Hintergrund: `logo-light` verwenden - Bei dunklem Hintergrund: `logo-light` verwenden
- Bei hellem Hintergrund: `logo-dark` verwenden - Bei hellem Hintergrund: `logo-dark` verwenden
@@ -43,10 +43,10 @@
## Beschreibung (Copy & Paste) ## Beschreibung (Copy & Paste)
**Kurz (1 Satz):** **Kurz (1 Satz):**
dgray.io ist ein anonymer Kleinanzeigen-Marktplatz mit Monero-Bezahlung. kashilo.com ist ein anonymer Kleinanzeigen-Marktplatz mit Monero-Bezahlung.
**Mittel (23 Sätze):** **Mittel (23 Sätze):**
dgray.io ermöglicht es, Waren und Dienstleistungen anonym zu handeln — bezahlt mit Monero (XMR). Kein KYC, keine E-Mail, keine persönlichen Daten. Kommunikation ist Ende-zu-Ende verschlüsselt. kashilo.com ermöglicht es, Waren und Dienstleistungen anonym zu handeln — bezahlt mit Monero (XMR). Kein KYC, keine E-Mail, keine persönlichen Daten. Kommunikation ist Ende-zu-Ende verschlüsselt.
**Lang:** **Lang:**
dgray.io ist eine Privacy-First Kleinanzeigen-Plattform für den DACH-Raum. Nutzer können Anzeigen erstellen und über Ende-zu-Ende verschlüsselte Nachrichten kommunizieren — ohne persönliche Daten preiszugeben. Die Bezahlung erfolgt ausschließlich über Monero (XMR). Die Plattform erfordert keine E-Mail-Adresse, kein KYC und kein Tracking. Verfügbar als Progressive Web App in Deutsch, Englisch und Französisch. kashilo.com ist eine Privacy-First Kleinanzeigen-Plattform für den DACH-Raum. Nutzer können Anzeigen erstellen und über Ende-zu-Ende verschlüsselte Nachrichten kommunizieren — ohne persönliche Daten preiszugeben. Die Bezahlung erfolgt ausschließlich über Monero (XMR). Die Plattform erfordert keine E-Mail-Adresse, kein KYC und kein Tracking. Verfügbar als Progressive Web App in Deutsch, Englisch und Französisch.

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Build script for dgray.io Build script for kashilo.com
Minifies JS and CSS files into dist/ directory. Minifies JS and CSS files into dist/ directory.
Usage: python3 build.py Usage: python3 build.py

View File

@@ -1,6 +1,6 @@
:root { :root {
/* /*
* Monochrome Theme - dgray.io * Monochrome Theme - kashilo.com
* Pure grayscale with neon green accent * Pure grayscale with neon green accent
*/ */

View File

@@ -1,8 +1,8 @@
#!/bin/bash #!/bin/bash
# deploy.sh - Deploy dgray.io to production server via rsync # deploy.sh - Deploy kashilo.com to production server via rsync
# #
# Usage: ./deploy.sh [user@host] [remote-path] # Usage: ./deploy.sh [user@host] [remote-path]
# Example: ./deploy.sh admin@dgray.io /home/admin/web/dgray.io/public_html # Example: ./deploy.sh admin@kashilo.com /home/admin/web/kashilo.com/public_html
# #
# Prerequisites: # Prerequisites:
# - SSH key authentication configured # - SSH key authentication configured
@@ -11,8 +11,8 @@
set -e set -e
# --- Configuration --- # --- Configuration ---
REMOTE_USER_HOST="${1:-admin@dgray.io}" REMOTE_USER_HOST="${1:-admin@kashilo.com}"
REMOTE_PATH="${2:-/home/admin/web/dgray.io/public_html}" REMOTE_PATH="${2:-/home/admin/web/kashilo.com/public_html}"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
DIST_DIR="$SCRIPT_DIR/dist" DIST_DIR="$SCRIPT_DIR/dist"
@@ -22,7 +22,7 @@ python3 "$SCRIPT_DIR/build.py"
echo "" echo ""
# --- Deploy from dist/ --- # --- Deploy from dist/ ---
echo "Deploying dgray.io" echo "Deploying kashilo.com"
echo " From: $DIST_DIR" echo " From: $DIST_DIR"
echo " To: $REMOTE_USER_HOST:$REMOTE_PATH" echo " To: $REMOTE_USER_HOST:$REMOTE_PATH"
echo "" echo ""

View File

@@ -2,7 +2,7 @@
**Directus Version:** 11.14.1 **Directus Version:** 11.14.1
**Database:** PostgreSQL **Database:** PostgreSQL
**API Endpoint:** https://api.dgray.io **API Endpoint:** https://api.kashilo.com
## Collections Overview ## Collections Overview

View File

@@ -1,8 +1,8 @@
# Directus Setup für dgray.io # Directus Setup für kashilo.com
Komplette Anleitung zur Einrichtung von Directus als Backend für die dgray Kleinanzeigen-PWA. Komplette Anleitung zur Einrichtung von Directus als Backend für die kashilo Kleinanzeigen-PWA.
**API URL**: https://api.dgray.io/ **API URL**: https://api.kashilo.com/
--- ---
@@ -622,8 +622,8 @@ module.exports = async function(data, { database }) {
**Settings > Project Settings** **Settings > Project Settings**
``` ```
Project Name: dgray.io Project Name: kashilo.com
Project URL: https://dgray.io Project URL: https://kashilo.com
Project Color: #555555 Project Color: #555555
``` ```
@@ -637,7 +637,7 @@ services:
image: directus/directus:latest image: directus/directus:latest
environment: environment:
CORS_ENABLED: "true" CORS_ENABLED: "true"
CORS_ORIGIN: "https://dgray.io,https://www.dgray.io,http://localhost:8080" CORS_ORIGIN: "https://kashilo.com,https://www.kashilo.com,http://localhost:8080"
CORS_METHODS: "GET,POST,PATCH,DELETE" CORS_METHODS: "GET,POST,PATCH,DELETE"
CORS_ALLOWED_HEADERS: "Content-Type,Authorization" CORS_ALLOWED_HEADERS: "Content-Type,Authorization"
CORS_CREDENTIALS: "true" CORS_CREDENTIALS: "true"
@@ -655,7 +655,7 @@ services:
```env ```env
# .env # .env
CORS_ENABLED=true CORS_ENABLED=true
CORS_ORIGIN=https://dgray.io,https://www.dgray.io,http://localhost:8080 CORS_ORIGIN=https://kashilo.com,https://www.kashilo.com,http://localhost:8080
CORS_METHODS=GET,POST,PATCH,DELETE CORS_METHODS=GET,POST,PATCH,DELETE
CORS_ALLOWED_HEADERS=Content-Type,Authorization CORS_ALLOWED_HEADERS=Content-Type,Authorization
CORS_CREDENTIALS=true CORS_CREDENTIALS=true
@@ -706,7 +706,7 @@ environment:
STORAGE_S3_DRIVER: "s3" STORAGE_S3_DRIVER: "s3"
STORAGE_S3_KEY: "your-access-key" STORAGE_S3_KEY: "your-access-key"
STORAGE_S3_SECRET: "your-secret-key" STORAGE_S3_SECRET: "your-secret-key"
STORAGE_S3_BUCKET: "dgray-files" STORAGE_S3_BUCKET: "kashilo-files"
STORAGE_S3_REGION: "fsn1" # oder nbg1 STORAGE_S3_REGION: "fsn1" # oder nbg1
STORAGE_S3_ENDPOINT: "https://fsn1.your-objectstorage.com" # oder nbg1 STORAGE_S3_ENDPOINT: "https://fsn1.your-objectstorage.com" # oder nbg1
``` ```
@@ -720,7 +720,7 @@ environment:
STORAGE_S3_DRIVER: "s3" STORAGE_S3_DRIVER: "s3"
STORAGE_S3_KEY: "your-access-key" STORAGE_S3_KEY: "your-access-key"
STORAGE_S3_SECRET: "your-secret-key" STORAGE_S3_SECRET: "your-secret-key"
STORAGE_S3_BUCKET: "dgray-files" STORAGE_S3_BUCKET: "kashilo-files"
STORAGE_S3_REGION: "auto" STORAGE_S3_REGION: "auto"
STORAGE_S3_ENDPOINT: "https://xxx.r2.cloudflarestorage.com" STORAGE_S3_ENDPOINT: "https://xxx.r2.cloudflarestorage.com"
``` ```
@@ -746,7 +746,7 @@ RATE_LIMITER_POINTS=100
RATE_LIMITER_DURATION=60 RATE_LIMITER_DURATION=60
``` ```
**Hinweis:** Keine E-Mail-Konfiguration nötig - dgray.io nutzt keine E-Mails (Privacy by Design). **Hinweis:** Keine E-Mail-Konfiguration nötig - kashilo.com nutzt keine E-Mails (Privacy by Design).
### 5.6 Währungsumrechnung & Preismodus ### 5.6 Währungsumrechnung & Preismodus
@@ -901,7 +901,7 @@ if (directus.isAuthenticated()) {
## 9. Anonyme Authentifizierung (UUID-basiert) ## 9. Anonyme Authentifizierung (UUID-basiert)
Für maximale Privatsphäre nutzt dgray.io ein UUID-basiertes Login-System ohne echte E-Mail-Adressen. Für maximale Privatsphäre nutzt kashilo.com ein UUID-basiertes Login-System ohne echte E-Mail-Adressen.
### 9.1 Konzept ### 9.1 Konzept
@@ -913,7 +913,7 @@ Für maximale Privatsphäre nutzt dgray.io ein UUID-basiertes Login-System ohne
│ f47ac10b-58cc-4372-a567-0e02b2c3d479 │ │ f47ac10b-58cc-4372-a567-0e02b2c3d479 │
│ ↓ │ │ ↓ │
│ Directus erhält: │ │ Directus erhält: │
│ • E-Mail: f47ac10b-58cc-4372-a567-0e02b2c3d479@dgray.io │ • E-Mail: f47ac10b-58cc-4372-a567-0e02b2c3d479@kashilo.com
│ • Passwort: f47ac10b-58cc-4372-a567-0e02b2c3d479 │ │ • Passwort: f47ac10b-58cc-4372-a567-0e02b2c3d479 │
│ ↓ │ │ ↓ │
│ User speichert UUID → fertig │ │ User speichert UUID → fertig │
@@ -943,7 +943,7 @@ Für maximale Privatsphäre nutzt dgray.io ein UUID-basiertes Login-System ohne
// Account erstellen // Account erstellen
async function createAnonymousAccount() { async function createAnonymousAccount() {
const uuid = crypto.randomUUID(); const uuid = crypto.randomUUID();
const email = `${uuid}@dgray.io`; const email = `${uuid}@kashilo.com`;
const password = uuid; const password = uuid;
// Bei Directus registrieren // Bei Directus registrieren
@@ -957,7 +957,7 @@ async function createAnonymousAccount() {
// Login // Login
async function login(uuid) { async function login(uuid) {
const email = `${uuid}@dgray.io`; const email = `${uuid}@kashilo.com`;
await directus.login(email, uuid); await directus.login(email, uuid);
} }
``` ```

View File

@@ -1,4 +1,4 @@
# Killer-Features — dgray.io # Killer-Features — kashilo.com
Differenzierung gegenüber eBay Kleinanzeigen, Tutti, XMRBazaar. Differenzierung gegenüber eBay Kleinanzeigen, Tutti, XMRBazaar.
Drei Features, die kein Konkurrent hat. Drei Features, die kein Konkurrent hat.

View File

@@ -1,4 +1,4 @@
# Launch Plan dgray.io # Launch Plan kashilo.com
## Release-Phasen ## Release-Phasen
@@ -48,12 +48,12 @@ Die Validierung passiert **serverseitig** im PoW-Server (PHP), nicht im Frontend
Neuer Endpoint: `POST /invite/validate` Neuer Endpoint: `POST /invite/validate`
```php ```php
// pow.dgray.io/invite/validate.php // pow.kashilo.com/invite/validate.php
<?php <?php
require __DIR__ . '/config.php'; require __DIR__ . '/config.php';
header('Content-Type: application/json'); header('Content-Type: application/json');
header('Access-Control-Allow-Origin: https://dgray.io'); header('Access-Control-Allow-Origin: https://kashilo.com');
header('Access-Control-Allow-Methods: POST, OPTIONS'); header('Access-Control-Allow-Methods: POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type'); header('Access-Control-Allow-Headers: Content-Type');
@@ -132,7 +132,7 @@ In `js/components/auth-modal.js` — im Registrierungs-Flow ein Textfeld hinzuf
Vor `createAccount()` den Code serverseitig validieren: Vor `createAccount()` den Code serverseitig validieren:
```js ```js
const res = await fetch('https://pow.dgray.io/invite/validate', { const res = await fetch('https://pow.kashilo.com/invite/validate', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code: inviteCode }) body: JSON.stringify({ code: inviteCode })
@@ -214,21 +214,21 @@ define('LISTING_FEE', ['EUR' => 0.01, 'USD' => 0.01, 'CHF' => 0.01, 'GBP' => 0.0
## Meta-Tags (pro Sprache) ## Meta-Tags (pro Sprache)
Die statischen Meta-Tags in `index.html` sind deutsch (Fallback). Die statischen Meta-Tags in `index.html` sind deutsch (Fallback).
Der OG-Proxy (`pow.dgray.io/og-proxy.php`) liefert Listing-spezifische Tags. Der OG-Proxy (`pow.kashilo.com/og-proxy.php`) liefert Listing-spezifische Tags.
### Umgesetzte Texte ### Umgesetzte Texte
**Title:** `dgray.io [Sprache]` **Title:** `kashilo.com [Sprache]`
| Sprache | Title | Description | | 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. | | **de** | kashilo.com 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. | | **en** | kashilo.com 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. | | **fr** | kashilo.com 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. | | **it** | kashilo.com 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. | | **es** | kashilo.com 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. | | **pt** | kashilo.com Classificados anônimos | Compre e venda sem conta, sem email. Pagamento com Monero. Chat criptografado ponta a ponta. |
| **ru** | dgray.io Анонимные объявления | Покупайте и продавайте без аккаунта, без email. Оплата Monero. Сквозное шифрование чата. | | **ru** | kashilo.com Анонимные объявления | Покупайте и продавайте без аккаунта, без email. Оплата Monero. Сквозное шифрование чата. |
### Umsetzung ### Umsetzung
@@ -244,7 +244,7 @@ und `twitter:title/description` dynamisch bei jedem Sprachwechsel (i18n-Keys `me
## Checkliste vor Alpha-Start ## Checkliste vor Alpha-Start
- [ ] Directus: Collection `invite_codes` anlegen (Schema siehe oben) - [ ] Directus: Collection `invite_codes` anlegen (Schema siehe oben)
- [ ] PHP: `invite/validate.php` deployen auf `pow.dgray.io` - [ ] PHP: `invite/validate.php` deployen auf `pow.kashilo.com`
- [ ] PHP: `config.php``LISTING_FEE` auf 0.01 setzen - [ ] PHP: `config.php``LISTING_FEE` auf 0.01 setzen
- [ ] PHP: `config.php``REQUIRE_INVITE_CODE = true` - [ ] PHP: `config.php``REQUIRE_INVITE_CODE = true`
- [ ] Frontend: Invite-Code-Feld in `auth-modal.js` einbauen - [ ] Frontend: Invite-Code-Feld in `auth-modal.js` einbauen

View File

@@ -1,4 +1,4 @@
# Marketing-Strategie dgray.io # Marketing-Strategie kashilo.com
## Positionierung ## Positionierung
@@ -43,7 +43,7 @@
### Woche 5: Name Reveal ### Woche 5: Name Reveal
- **Post 7:** Logo + Name + "dgray.io — Anonymous classifieds. Powered by Monero. Beta coming next week." - **Post 7:** Logo + Name + "kashilo.com — Anonymous classifieds. Powered by Monero. Beta coming next week."
- Landing Page live schalten (optional, kann auch direkt die App sein) - Landing Page live schalten (optional, kann auch direkt die App sein)
- Einmalig in relevanten Subreddits posten: r/Monero, r/privacy, r/selfhosted - Einmalig in relevanten Subreddits posten: r/Monero, r/privacy, r/selfhosted
@@ -58,10 +58,10 @@
- **Ziel:** Echte Listings generieren, Feedback sammeln, Bugs finden - **Ziel:** Echte Listings generieren, Feedback sammeln, Bugs finden
**Launch-Post auf X:** **Launch-Post auf X:**
> "dgray.io is live in beta. 🔒 Anonymous classifieds with Monero. > "kashilo.com is live in beta. 🔒 Anonymous classifieds with Monero.
> First 100 listings for just 0.10 EUR. > First 100 listings for just 0.10 EUR.
> No KYC. No email. No tracking. E2E encrypted chat. > No KYC. No email. No tracking. E2E encrypted chat.
> Try it: https://dgray.io" > Try it: https://kashilo.com"
### Weitere Launch-Kanäle ### Weitere Launch-Kanäle
@@ -83,7 +83,7 @@
- **Weekly Update** auf X: Neue Features, Stats (wenn Schwellwert erreicht) - **Weekly Update** auf X: Neue Features, Stats (wenn Schwellwert erreicht)
- **Build in Public:** Entwicklungsfortschritte teilen - **Build in Public:** Entwicklungsfortschritte teilen
- **User Stories:** Erfolgreiche Deals highlighten (anonym natürlich) - **User Stories:** Erfolgreiche Deals highlighten (anonym natürlich)
- **Vergleiche:** "Why we built dgray.io" — Vergleich mit KYC-Plattformen - **Vergleiche:** "Why we built kashilo.com" — Vergleich mit KYC-Plattformen
### Community ### Community
@@ -123,7 +123,7 @@
| XmrBazaar | Etabliert, 7800 User | Spam, altbackenes Design, nur EN | | XmrBazaar | Etabliert, 7800 User | Spam, altbackenes Design, nur EN |
| Bitejo | Dezentral | Wenig Nutzer | | Bitejo | Dezentral | Wenig Nutzer |
| MoneroMarket | Einfach | Kaum Features | | MoneroMarket | Einfach | Kaum Features |
| **dgray.io** | Modern, lokal, E2E, PWA | Neu, noch keine User | | **kashilo.com** | Modern, lokal, E2E, PWA | Neu, noch keine User |
--- ---

View File

@@ -1,4 +1,4 @@
# Monetarisierung - dgray.io # Monetarisierung - kashilo.com
## Preismodell ## Preismodell
@@ -50,7 +50,7 @@
- **Provider**: BTCpay Server (self-hosted) - **Provider**: BTCpay Server (self-hosted)
- **URL**: https://pay.xmr.rocks/ - **URL**: https://pay.xmr.rocks/
- **Proxy**: `pow.dgray.io` — alle API-Aufrufe laufen über den PHP-Proxy (BTCPay API-Key bleibt serverseitig) - **Proxy**: `pow.kashilo.com` — alle API-Aufrufe laufen über den PHP-Proxy (BTCPay API-Key bleibt serverseitig)
- **Primär**: Monero (XMR) - **Primär**: Monero (XMR)
- **Alternativ**: Andere Kryptos via Trocador-Plugin (automatischer Swap zu XMR) - **Alternativ**: Andere Kryptos via Trocador-Plugin (automatischer Swap zu XMR)
- **Preisumrechnung**: Live XMR-Kurs via Kraken API - **Preisumrechnung**: Live XMR-Kurs via Kraken API
@@ -60,14 +60,14 @@
### Flow: Draft → Processing → Published ### Flow: Draft → Processing → Published
1. User erstellt Listing → wird als `draft` mit `payment_status: unpaid` gespeichert 1. User erstellt Listing → wird als `draft` mit `payment_status: unpaid` gespeichert
2. BTCPay Invoice wird über `pow.dgray.io/btcpay/invoice` erstellt 2. BTCPay Invoice wird über `pow.kashilo.com/btcpay/invoice` erstellt
3. BTCPay Checkout-Modal öffnet sich im Frontend (`js/services/btcpay.js`) 3. BTCPay Checkout-Modal öffnet sich im Frontend (`js/services/btcpay.js`)
4. Nach Zahlung: 4. Nach Zahlung:
- **Frontend**: Prüft Status via `pow.dgray.io/btcpay/status` nach Modal-Close - **Frontend**: Prüft Status via `pow.kashilo.com/btcpay/status` nach Modal-Close
- **Webhook**: `pow.dgray.io/btcpay/webhook` empfängt BTCPay Events, setzt `status: published` + `payment_status: paid` nach 1 Confirmation - **Webhook**: `pow.kashilo.com/btcpay/webhook` empfängt BTCPay Events, setzt `status: published` + `payment_status: paid` nach 1 Confirmation
5. Listing wird veröffentlicht (30 Tage Laufzeit, `expires_at` wird gesetzt) 5. Listing wird veröffentlicht (30 Tage Laufzeit, `expires_at` wird gesetzt)
### Endpunkte (pow.dgray.io) ### Endpunkte (pow.kashilo.com)
| Endpoint | Methode | Beschreibung | | Endpoint | Methode | Beschreibung |
|----------|---------|-------------| |----------|---------|-------------|
@@ -81,5 +81,5 @@
- [x] ~~XMR-Kurs API für Umrechnung~~ → Kraken API - [x] ~~XMR-Kurs API für Umrechnung~~ → Kraken API
- [x] ~~Anzahl Deals für Power-User Status~~ → 5/15/50 Stufen - [x] ~~Anzahl Deals für Power-User Status~~ → 5/15/50 Stufen
- [x] ~~Captcha-Lösung~~ → Eigenes PoW-Captcha (keine Lizenzkosten) - [x] ~~Captcha-Lösung~~ → Eigenes PoW-Captcha (keine Lizenzkosten)
- [x] ~~Payment-Proxy~~`pow.dgray.io` (PHP, API-Key serverseitig) - [x] ~~Payment-Proxy~~`pow.kashilo.com` (PHP, API-Key serverseitig)
- [x] ~~Webhook für Auto-Publish~~`btcpay-webhook.php` auf `pow.dgray.io` - [x] ~~Webhook für Auto-Publish~~`btcpay-webhook.php` auf `pow.kashilo.com`

View File

@@ -1,13 +1,13 @@
# Datenschutzerklärung # Datenschutzerklärung
**dgray.io — Anonymer Marktplatz** **kashilo.com — Anonymer Marktplatz**
Stand: Februar 2026 Stand: Februar 2026
--- ---
## 1. Verantwortlicher ## 1. Verantwortlicher
Verantwortlich für die Datenbearbeitung ist der Betreiber der Plattform dgray.io mit Sitz in der Schweiz. Verantwortlich für die Datenbearbeitung ist der Betreiber der Plattform kashilo.com mit Sitz in der Schweiz.
--- ---

View File

@@ -1,4 +1,4 @@
# Reputation-System — dgray.io # Reputation-System — kashilo.com
## Ziel ## Ziel

View File

@@ -1,13 +1,13 @@
# Allgemeine Geschäftsbedingungen (AGB) # Allgemeine Geschäftsbedingungen (AGB)
**dgray.io — Anonymer Marktplatz** **kashilo.com — Anonymer Marktplatz**
Stand: Februar 2026 Stand: Februar 2026
--- ---
## 1. Geltungsbereich ## 1. Geltungsbereich
1.1 Diese Allgemeinen Geschäftsbedingungen (nachfolgend «AGB») regeln die Nutzung der Plattform dgray.io (nachfolgend «Plattform»). 1.1 Diese Allgemeinen Geschäftsbedingungen (nachfolgend «AGB») regeln die Nutzung der Plattform kashilo.com (nachfolgend «Plattform»).
1.2 Die Plattform wird betrieben von einer natürlichen Person mit Sitz in der Schweiz (nachfolgend «Betreiber»). 1.2 Die Plattform wird betrieben von einer natürlichen Person mit Sitz in der Schweiz (nachfolgend «Betreiber»).

View File

@@ -5,7 +5,7 @@
# This script adds it-IT, es-ES, pt-BR, ru-RU translations to all categories. # This script adds it-IT, es-ES, pt-BR, ru-RU translations to all categories.
# It reads existing categories from the API and creates missing translations. # It reads existing categories from the API and creates missing translations.
API="https://api.dgray.io" API="https://api.kashilo.com"
TOKEN="${DIRECTUS_TOKEN:?Set DIRECTUS_TOKEN environment variable}" TOKEN="${DIRECTUS_TOKEN:?Set DIRECTUS_TOKEN environment variable}"
add_translation() { add_translation() {

View File

@@ -1,8 +1,8 @@
#!/bin/bash #!/bin/bash
# Bulk-Import: All categories for dgray.io # Bulk-Import: All categories for kashilo.com
# Usage: DIRECTUS_TOKEN=your_admin_token bash docs/import-categories.sh # Usage: DIRECTUS_TOKEN=your_admin_token bash docs/import-categories.sh
API="https://api.dgray.io" API="https://api.kashilo.com"
TOKEN="${DIRECTUS_TOKEN:?Set DIRECTUS_TOKEN environment variable}" TOKEN="${DIRECTUS_TOKEN:?Set DIRECTUS_TOKEN environment variable}"
create_category() { create_category() {

View File

@@ -1,10 +1,10 @@
# PoW Captcha & Payment Server # PoW Captcha & Payment Server
PHP-basierter Server für dgray.io mit Proof-of-Work Captcha und BTCPay Payment-Proxy. PHP-basierter Server für kashilo.com mit Proof-of-Work Captcha und BTCPay Payment-Proxy.
## Setup ## Setup
1. Subdomain `pow.dgray.io` auf den Server zeigen 1. Subdomain `pow.kashilo.com` auf den Server zeigen
2. Dateien in das Web-Root kopieren 2. Dateien in das Web-Root kopieren
3. Secrets setzen: 3. Secrets setzen:
```bash ```bash
@@ -18,10 +18,10 @@ PHP-basierter Server für dgray.io mit Proof-of-Work Captcha und BTCPay Payment-
4. Testen: 4. Testen:
```bash ```bash
# PoW Challenge # PoW Challenge
curl https://pow.dgray.io/challenge curl https://pow.kashilo.com/challenge
# BTCPay Invoice erstellen # BTCPay Invoice erstellen
curl -X POST https://pow.dgray.io/btcpay/invoice \ curl -X POST https://pow.kashilo.com/btcpay/invoice \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{"listingId": "test-123", "currency": "EUR"}' -d '{"listingId": "test-123", "currency": "EUR"}'
``` ```
@@ -76,7 +76,7 @@ Response:
### POST /btcpay/webhook ### POST /btcpay/webhook
Empfängt BTCPay Server Webhook-Events. Wird in BTCPay unter Store → Settings → Webhooks konfiguriert. Empfängt BTCPay Server Webhook-Events. Wird in BTCPay unter Store → Settings → Webhooks konfiguriert.
- **URL**: `https://pow.dgray.io/btcpay/webhook` - **URL**: `https://pow.kashilo.com/btcpay/webhook`
- **Event**: `InvoiceSettled` (nach 1 Blockchain-Confirmation) - **Event**: `InvoiceSettled` (nach 1 Blockchain-Confirmation)
- **Aktion**: Setzt das zugehörige Listing in Directus auf `status: published`, `payment_status: paid`, setzt `paid_at` und `expires_at` (30 Tage) - **Aktion**: Setzt das zugehörige Listing in Directus auf `status: published`, `payment_status: paid`, setzt `paid_at` und `expires_at` (30 Tage)
- **Sicherheit**: Webhook-Secret wird serverseitig geprüft - **Sicherheit**: Webhook-Secret wird serverseitig geprüft
@@ -95,7 +95,7 @@ Empfängt BTCPay Server Webhook-Events. Wird in BTCPay unter Store → Settings
- HMAC-SHA256 signierte Challenges (nicht fälschbar) - HMAC-SHA256 signierte Challenges (nicht fälschbar)
- TTL: 2 Minuten - TTL: 2 Minuten
- CORS: nur `https://dgray.io` - CORS: nur `https://kashilo.com`
- `hash_equals()` gegen Timing-Attacks - `hash_equals()` gegen Timing-Attacks
- BTCPay API-Key bleibt serverseitig (nie im Frontend) - BTCPay API-Key bleibt serverseitig (nie im Frontend)
- Gebühren serverseitig erzwungen (nicht manipulierbar) - Gebühren serverseitig erzwungen (nicht manipulierbar)

View File

@@ -9,5 +9,5 @@ define('BTCPAY_STORE_ID', getenv('BTCPAY_STORE_ID') ?: 'CHANGE_ME');
define('BTCPAY_WEBHOOK_SECRET', getenv('BTCPAY_WEBHOOK_SECRET') ?: ''); define('BTCPAY_WEBHOOK_SECRET', getenv('BTCPAY_WEBHOOK_SECRET') ?: '');
define('LISTING_FEE', ['EUR' => 1, 'USD' => 1, 'CHF' => 1, 'GBP' => 1, 'JPY' => 200]); define('LISTING_FEE', ['EUR' => 1, 'USD' => 1, 'CHF' => 1, 'GBP' => 1, 'JPY' => 200]);
define('DIRECTUS_URL', getenv('DIRECTUS_URL') ?: 'https://api.dgray.io'); define('DIRECTUS_URL', getenv('DIRECTUS_URL') ?: 'https://api.kashilo.com');
define('DIRECTUS_TOKEN', getenv('DIRECTUS_TOKEN') ?: 'CHANGE_ME'); define('DIRECTUS_TOKEN', getenv('DIRECTUS_TOKEN') ?: 'CHANGE_ME');

View File

@@ -1,12 +1,12 @@
<?php <?php
header('Content-Type: application/json'); header('Content-Type: application/json');
$allowedOrigins = ['https://dgray.io', 'http://localhost:5500', 'http://localhost:8080']; $allowedOrigins = ['https://kashilo.com', 'http://localhost:5500', 'http://localhost:8080'];
$origin = $_SERVER['HTTP_ORIGIN'] ?? ''; $origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if (in_array($origin, $allowedOrigins)) { if (in_array($origin, $allowedOrigins)) {
header('Access-Control-Allow-Origin: ' . $origin); header('Access-Control-Allow-Origin: ' . $origin);
} else { } else {
header('Access-Control-Allow-Origin: https://dgray.io'); header('Access-Control-Allow-Origin: https://kashilo.com');
} }
header('Access-Control-Allow-Methods: GET, POST, OPTIONS'); header('Access-Control-Allow-Methods: GET, POST, OPTIONS');

View File

@@ -2,14 +2,14 @@
/** /**
* OG Meta Tag Proxy for Social Media Crawlers * OG Meta Tag Proxy for Social Media Crawlers
* *
* Setup: Nginx/Apache rewrite rule on dgray.io: * Setup: Nginx/Apache rewrite rule on kashilo.com:
* If User-Agent matches crawler → proxy to this script * If User-Agent matches crawler → proxy to this script
* Else → serve static index.html * Else → serve static index.html
* *
* Example Apache config (in VHost): * Example Apache config (in VHost):
* RewriteEngine On * RewriteEngine On
* RewriteCond %{HTTP_USER_AGENT} Twitterbot|facebookexternalhit|TelegramBot|Discordbot|Slackbot|WhatsApp [NC] * RewriteCond %{HTTP_USER_AGENT} Twitterbot|facebookexternalhit|TelegramBot|Discordbot|Slackbot|WhatsApp [NC]
* RewriteRule ^/listing/(.*)$ https://pow.dgray.io/og-proxy.php?listing=$1 [P,L] * RewriteRule ^/listing/(.*)$ https://pow.kashilo.com/og-proxy.php?listing=$1 [P,L]
* *
* Required modules: sudo a2enmod rewrite proxy proxy_http * Required modules: sudo a2enmod rewrite proxy proxy_http
* *
@@ -17,7 +17,7 @@
* location /listing/ { * location /listing/ {
* if ($http_user_agent ~* "Twitterbot|facebookexternalhit|TelegramBot|Discordbot|Slackbot|WhatsApp") { * if ($http_user_agent ~* "Twitterbot|facebookexternalhit|TelegramBot|Discordbot|Slackbot|WhatsApp") {
* rewrite ^/listing/(.*)$ /og-proxy.php?listing=$1 break; * rewrite ^/listing/(.*)$ /og-proxy.php?listing=$1 break;
* proxy_pass https://pow.dgray.io; * proxy_pass https://pow.kashilo.com;
* } * }
* } * }
* *
@@ -40,8 +40,8 @@ if (!$listingId && isset($_GET['listing'])) {
$listingId = $_GET['listing']; $listingId = $_GET['listing'];
} }
$siteUrl = 'https://dgray.io'; $siteUrl = 'https://kashilo.com';
$defaultTitle = 'dgray.io Anonymous Classifieds with Monero'; $defaultTitle = 'kashilo.com Anonymous Classifieds with Monero';
$defaultDesc = 'Buy and sell anonymously with Monero. No KYC, no email, E2E encrypted chat.'; $defaultDesc = 'Buy and sell anonymously with Monero. No KYC, no email, E2E encrypted chat.';
$defaultImage = $siteUrl . '/assets/press/og-image.png'; $defaultImage = $siteUrl . '/assets/press/og-image.png';
@@ -70,7 +70,7 @@ if ($listingId) {
$listing = $data['data'] ?? null; $listing = $data['data'] ?? null;
if ($listing) { if ($listing) {
$title = htmlspecialchars($listing['title'] ?? '') . ' dgray.io'; $title = htmlspecialchars($listing['title'] ?? '') . ' kashilo.com';
$description = htmlspecialchars(mb_substr($listing['description'] ?? '', 0, 160)); $description = htmlspecialchars(mb_substr($listing['description'] ?? '', 0, 160));
$url = $siteUrl . '/#/listing/' . $listing['id']; $url = $siteUrl . '/#/listing/' . $listing['id'];
$type = 'product'; $type = 'product';
@@ -101,7 +101,7 @@ header('Content-Type: text/html; charset=utf-8');
<!-- Open Graph --> <!-- Open Graph -->
<meta property="og:type" content="<?= $type ?>"> <meta property="og:type" content="<?= $type ?>">
<meta property="og:site_name" content="dgray.io"> <meta property="og:site_name" content="kashilo.com">
<meta property="og:title" content="<?= $title ?>"> <meta property="og:title" content="<?= $title ?>">
<meta property="og:description" content="<?= $description ?>"> <meta property="og:description" content="<?= $description ?>">
<meta property="og:url" content="<?= $url ?>"> <meta property="og:url" content="<?= $url ?>">

View File

@@ -6,15 +6,15 @@
<meta name="description" content="Kaufen und verkaufen ohne Konto, ohne E-Mail. Bezahlung mit Monero. Ende-zu-Ende verschlüsselter Chat."> <meta name="description" content="Kaufen und verkaufen ohne Konto, ohne E-Mail. Bezahlung mit Monero. Ende-zu-Ende verschlüsselter Chat.">
<meta name="theme-color" content="#555555"> <meta name="theme-color" content="#555555">
<title>dgray.io Anonyme Kleinanzeigen</title> <title>kashilo.com Anonyme Kleinanzeigen</title>
<!-- Open Graph --> <!-- Open Graph -->
<meta property="og:type" content="website"> <meta property="og:type" content="website">
<meta property="og:site_name" content="dgray.io"> <meta property="og:site_name" content="kashilo.com">
<meta property="og:title" content="dgray.io Anonyme Kleinanzeigen"> <meta property="og:title" content="kashilo.com Anonyme Kleinanzeigen">
<meta property="og:description" content="Kaufen und verkaufen ohne Konto, ohne E-Mail. Bezahlung mit Monero. Ende-zu-Ende verschlüsselter Chat."> <meta property="og:description" content="Kaufen und verkaufen ohne Konto, ohne E-Mail. Bezahlung mit Monero. Ende-zu-Ende verschlüsselter Chat.">
<meta property="og:url" content="https://dgray.io"> <meta property="og:url" content="https://kashilo.com">
<meta property="og:image" content="https://dgray.io/assets/press/og-image.png"> <meta property="og:image" content="https://kashilo.com/assets/press/og-image.png">
<meta property="og:image:width" content="1200"> <meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630"> <meta property="og:image:height" content="630">
<meta property="og:locale" content="de_DE"> <meta property="og:locale" content="de_DE">
@@ -23,9 +23,9 @@
<!-- X (Twitter) Card --> <!-- X (Twitter) Card -->
<meta name="twitter:card" content="summary_large_image"> <meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="dgray.io Anonyme Kleinanzeigen"> <meta name="twitter:title" content="kashilo.com Anonyme Kleinanzeigen">
<meta name="twitter:description" content="Kaufen und verkaufen ohne Konto, ohne E-Mail. Bezahlung mit Monero. Ende-zu-Ende verschlüsselter Chat."> <meta name="twitter:description" content="Kaufen und verkaufen ohne Konto, ohne E-Mail. Bezahlung mit Monero. Ende-zu-Ende verschlüsselter Chat.">
<meta name="twitter:image" content="https://dgray.io/assets/press/og-image.png"> <meta name="twitter:image" content="https://kashilo.com/assets/press/og-image.png">
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json">
<link rel="icon" type="image/svg+xml" href="assets/icon-light.svg" media="(prefers-color-scheme: light)"> <link rel="icon" type="image/svg+xml" href="assets/icon-light.svg" media="(prefers-color-scheme: light)">
@@ -154,7 +154,7 @@
<app-footer> <app-footer>
<div class="footer-inner container"> <div class="footer-inner container">
<p class="footer-copyright"> <p class="footer-copyright">
&copy; 2026 dgray.io - <span data-i18n="footer.rights">Alle Rechte vorbehalten.</span> &copy; 2026 kashilo.com - <span data-i18n="footer.rights">Alle Rechte vorbehalten.</span>
<span class="footer-swiss">🇨🇭 Made in Switzerland</span> <span class="footer-swiss">🇨🇭 Made in Switzerland</span>
</p> </p>
<span class="xmr-rate" title="CoinGecko">1 XMR ≈ ...</span> <span class="xmr-rate" title="CoinGecko">1 XMR ≈ ...</span>

View File

@@ -26,7 +26,7 @@ class AppFooter extends HTMLElement {
} }
getCurrency() { getCurrency() {
return localStorage.getItem('dgray_currency') || 'USD' return localStorage.getItem('kashilo_currency') || 'USD'
} }
async loadXmrRates() { async loadXmrRates() {
@@ -54,7 +54,7 @@ class AppFooter extends HTMLElement {
this.innerHTML = /* html */` this.innerHTML = /* html */`
<div class="footer-inner container"> <div class="footer-inner container">
<p class="footer-copyright"> <p class="footer-copyright">
&copy; ${year} dgray.io - <span data-i18n="footer.rights">${t('footer.rights')}</span> &copy; ${year} kashilo.com - <span data-i18n="footer.rights">${t('footer.rights')}</span>
<span class="footer-swiss">🇨🇭 Made in Switzerland</span> <span class="footer-swiss">🇨🇭 Made in Switzerland</span>
</p> </p>
<span class="xmr-rate" title="CoinGecko">1 XMR ≈ ...</span> <span class="xmr-rate" title="CoinGecko">1 XMR ≈ ...</span>

View File

@@ -113,9 +113,9 @@ class AppHeader extends HTMLElement {
render() { render() {
this.innerHTML = /* html */` this.innerHTML = /* html */`
<div class="header-inner container"> <div class="header-inner container">
<a href="#/" class="logo" aria-label="dgray.io ${t('common.home')}"> <a href="#/" class="logo" aria-label="kashilo.com ${t('common.home')}">
<img src="assets/logo-light.svg" alt="dgray.io" class="logo-img logo-light" width="100" height="28"> <img src="assets/logo-light.svg" alt="kashilo.com" class="logo-img logo-light" width="100" height="28">
<img src="assets/logo-dark.svg" alt="dgray.io" class="logo-img logo-dark" width="100" height="28"> <img src="assets/logo-dark.svg" alt="kashilo.com" class="logo-img logo-dark" width="100" height="28">
</a> </a>
<div class="header-actions"> <div class="header-actions">

View File

@@ -319,14 +319,14 @@ class AuthModal extends HTMLElement {
} }
downloadBackup() { downloadBackup() {
const content = `dgray.io Account Backup const content = `kashilo.com Account Backup
======================== ========================
Your UUID (keep this secret!): Your UUID (keep this secret!):
${this.generatedUuid} ${this.generatedUuid}
Login URL: Login URL:
https://dgray.io/#/login https://kashilo.com/#/login
Created: ${new Date().toISOString()} Created: ${new Date().toISOString()}
@@ -337,7 +337,7 @@ WARNING: If you lose this UUID, you cannot recover your account!
const url = URL.createObjectURL(blob) const url = URL.createObjectURL(blob)
const a = document.createElement('a') const a = document.createElement('a')
a.href = url a.href = url
a.download = `dgray-backup-${this.generatedUuid.slice(0, 8)}.txt` a.download = `kashilo-backup-${this.generatedUuid.slice(0, 8)}.txt`
a.click() a.click()
URL.revokeObjectURL(url) URL.revokeObjectURL(url)
} }

View File

@@ -144,7 +144,7 @@ class LocationMap extends HTMLElement {
}) })
const response = await fetch(`${NOMINATIM_URL}?${params}`, { const response = await fetch(`${NOMINATIM_URL}?${params}`, {
headers: { 'User-Agent': 'dgray.io/1.0' } headers: { 'User-Agent': 'kashilo.com/1.0' }
}) })
const results = await response.json() const results = await response.json()

View File

@@ -183,7 +183,7 @@ class LocationPicker extends HTMLElement {
const response = await fetch(`${NOMINATIM_URL}?${params}`, { const response = await fetch(`${NOMINATIM_URL}?${params}`, {
headers: { headers: {
'User-Agent': 'dgray.io/1.0' 'User-Agent': 'kashilo.com/1.0'
} }
}) })

View File

@@ -15,8 +15,8 @@ class PageAbout extends HTMLElement {
getContent(lang) { getContent(lang) {
const content = { const content = {
de: /* html */` de: /* html */`
<h1>Über dgray.io</h1> <h1>Über kashilo.com</h1>
<p>dgray.io ist eine Privacy-First Kleinanzeigen-Plattform. Nutzer können Anzeigen erstellen und über Ende-zu-Ende verschlüsselte Nachrichten kommunizieren — ohne persönliche Daten preiszugeben.</p> <p>kashilo.com ist eine Privacy-First Kleinanzeigen-Plattform. Nutzer können Anzeigen erstellen und über Ende-zu-Ende verschlüsselte Nachrichten kommunizieren — ohne persönliche Daten preiszugeben.</p>
<p>Die Bezahlung erfolgt ausschliesslich über Monero (XMR). Kein KYC, keine E-Mail, kein Tracking.</p> <p>Die Bezahlung erfolgt ausschliesslich über Monero (XMR). Kein KYC, keine E-Mail, kein Tracking.</p>
<h2>Prinzipien</h2> <h2>Prinzipien</h2>
@@ -29,8 +29,8 @@ class PageAbout extends HTMLElement {
</ul> </ul>
`, `,
en: /* html */` en: /* html */`
<h1>About dgray.io</h1> <h1>About kashilo.com</h1>
<p>dgray.io is a privacy-first classifieds platform. Users can create listings and communicate via end-to-end encrypted messages — without revealing any personal data.</p> <p>kashilo.com is a privacy-first classifieds platform. Users can create listings and communicate via end-to-end encrypted messages — without revealing any personal data.</p>
<p>Payments are made exclusively in Monero (XMR). No KYC, no email, no tracking.</p> <p>Payments are made exclusively in Monero (XMR). No KYC, no email, no tracking.</p>
<h2>Principles</h2> <h2>Principles</h2>
@@ -43,8 +43,8 @@ class PageAbout extends HTMLElement {
</ul> </ul>
`, `,
fr: /* html */` fr: /* html */`
<h1>À propos de dgray.io</h1> <h1>À propos de kashilo.com</h1>
<p>dgray.io est une plateforme de petites annonces axée sur la confidentialité. Les utilisateurs peuvent créer des annonces et communiquer via des messages chiffrés de bout en bout — sans révéler de données personnelles.</p> <p>kashilo.com est une plateforme de petites annonces axée sur la confidentialité. Les utilisateurs peuvent créer des annonces et communiquer via des messages chiffrés de bout en bout — sans révéler de données personnelles.</p>
<p>Les paiements s'effectuent exclusivement en Monero (XMR). Pas de KYC, pas d'e-mail, pas de tracking.</p> <p>Les paiements s'effectuent exclusivement en Monero (XMR). Pas de KYC, pas d'e-mail, pas de tracking.</p>
<h2>Principes</h2> <h2>Principes</h2>
@@ -57,8 +57,8 @@ class PageAbout extends HTMLElement {
</ul> </ul>
`, `,
it: /* html */` it: /* html */`
<h1>Informazioni su dgray.io</h1> <h1>Informazioni su kashilo.com</h1>
<p>dgray.io è una piattaforma di annunci incentrata sulla privacy. Gli utenti possono creare annunci e comunicare tramite messaggi crittografati end-to-end — senza rivelare dati personali.</p> <p>kashilo.com è una piattaforma di annunci incentrata sulla privacy. Gli utenti possono creare annunci e comunicare tramite messaggi crittografati end-to-end — senza rivelare dati personali.</p>
<p>I pagamenti avvengono esclusivamente in Monero (XMR). Nessun KYC, nessuna email, nessun tracking.</p> <p>I pagamenti avvengono esclusivamente in Monero (XMR). Nessun KYC, nessuna email, nessun tracking.</p>
<h2>Principi</h2> <h2>Principi</h2>
@@ -71,8 +71,8 @@ class PageAbout extends HTMLElement {
</ul> </ul>
`, `,
es: /* html */` es: /* html */`
<h1>Acerca de dgray.io</h1> <h1>Acerca de kashilo.com</h1>
<p>dgray.io es una plataforma de clasificados centrada en la privacidad. Los usuarios pueden crear anuncios y comunicarse mediante mensajes cifrados de extremo a extremo — sin revelar datos personales.</p> <p>kashilo.com es una plataforma de clasificados centrada en la privacidad. Los usuarios pueden crear anuncios y comunicarse mediante mensajes cifrados de extremo a extremo — sin revelar datos personales.</p>
<p>Los pagos se realizan exclusivamente en Monero (XMR). Sin KYC, sin email, sin rastreo.</p> <p>Los pagos se realizan exclusivamente en Monero (XMR). Sin KYC, sin email, sin rastreo.</p>
<h2>Principios</h2> <h2>Principios</h2>
@@ -85,8 +85,8 @@ class PageAbout extends HTMLElement {
</ul> </ul>
`, `,
pt: /* html */` pt: /* html */`
<h1>Sobre o dgray.io</h1> <h1>Sobre o kashilo.com</h1>
<p>dgray.io é uma plataforma de classificados focada em privacidade. Os usuários podem criar anúncios e se comunicar por mensagens criptografadas de ponta a ponta — sem revelar dados pessoais.</p> <p>kashilo.com é uma plataforma de classificados focada em privacidade. Os usuários podem criar anúncios e se comunicar por mensagens criptografadas de ponta a ponta — sem revelar dados pessoais.</p>
<p>Os pagamentos são feitos exclusivamente em Monero (XMR). Sem KYC, sem email, sem rastreamento.</p> <p>Os pagamentos são feitos exclusivamente em Monero (XMR). Sem KYC, sem email, sem rastreamento.</p>
<h2>Princípios</h2> <h2>Princípios</h2>
@@ -99,8 +99,8 @@ class PageAbout extends HTMLElement {
</ul> </ul>
`, `,
ru: /* html */` ru: /* html */`
<h1>О dgray.io</h1> <h1>О kashilo.com</h1>
<p>dgray.io — это платформа объявлений с приоритетом конфиденциальности. Пользователи могут создавать объявления и общаться через сквозное шифрование — без раскрытия личных данных.</p> <p>kashilo.com — это платформа объявлений с приоритетом конфиденциальности. Пользователи могут создавать объявления и общаться через сквозное шифрование — без раскрытия личных данных.</p>
<p>Оплата осуществляется исключительно в Monero (XMR). Без KYC, без email, без отслеживания.</p> <p>Оплата осуществляется исключительно в Monero (XMR). Без KYC, без email, без отслеживания.</p>
<h2>Принципы</h2> <h2>Принципы</h2>

View File

@@ -17,17 +17,17 @@ class PageContact extends HTMLElement {
de: /* html */` de: /* html */`
<h1>Kontakt</h1> <h1>Kontakt</h1>
<p>Bei Fragen oder Problemen erreichst du uns unter:</p> <p>Bei Fragen oder Problemen erreichst du uns unter:</p>
<p><strong>E-Mail:</strong> <a href="mailto:hello@dgray.io">hello@dgray.io</a></p> <p><strong>E-Mail:</strong> <a href="mailto:hello@kashilo.com">hello@kashilo.com</a></p>
`, `,
en: /* html */` en: /* html */`
<h1>Contact</h1> <h1>Contact</h1>
<p>For questions or issues, reach us at:</p> <p>For questions or issues, reach us at:</p>
<p><strong>Email:</strong> <a href="mailto:hello@dgray.io">hello@dgray.io</a></p> <p><strong>Email:</strong> <a href="mailto:hello@kashilo.com">hello@kashilo.com</a></p>
`, `,
fr: /* html */` fr: /* html */`
<h1>Contact</h1> <h1>Contact</h1>
<p>Pour toute question ou problème, contactez-nous à :</p> <p>Pour toute question ou problème, contactez-nous à :</p>
<p><strong>E-mail :</strong> <a href="mailto:hello@dgray.io">hello@dgray.io</a></p> <p><strong>E-mail :</strong> <a href="mailto:hello@kashilo.com">hello@kashilo.com</a></p>
` `
} }
return content[lang] || content.de return content[lang] || content.de

View File

@@ -12,7 +12,7 @@ import '../location-picker.js'
import '../pow-captcha.js' import '../pow-captcha.js'
import '../image-cropper.js' import '../image-cropper.js'
const STORAGE_KEY = 'dgray_create_draft' const STORAGE_KEY = 'kashilo_create_draft'
class PageCreate extends HTMLElement { class PageCreate extends HTMLElement {
constructor() { constructor() {

View File

@@ -17,7 +17,7 @@ class PageImprint extends HTMLElement {
de: /* html */` de: /* html */`
<h1>Impressum</h1> <h1>Impressum</h1>
<div class="legal-draft-notice">ENTWURF — Bitte durch eine Fachperson prüfen lassen.</div> <div class="legal-draft-notice">ENTWURF — Bitte durch eine Fachperson prüfen lassen.</div>
<p class="legal-meta">dgray.io — Anonymer Marktplatz | Stand: Februar 2026</p> <p class="legal-meta">kashilo.com — Anonymer Marktplatz | Stand: Februar 2026</p>
<h2>1. Angaben gemäss Schweizer Recht</h2> <h2>1. Angaben gemäss Schweizer Recht</h2>
<p>Verantwortlich für diese Website:</p> <p>Verantwortlich für diese Website:</p>
@@ -49,7 +49,7 @@ class PageImprint extends HTMLElement {
en: /* html */` en: /* html */`
<h1>Legal Notice</h1> <h1>Legal Notice</h1>
<div class="legal-draft-notice">DRAFT — Please have this reviewed by a legal professional.</div> <div class="legal-draft-notice">DRAFT — Please have this reviewed by a legal professional.</div>
<p class="legal-meta">dgray.io — Anonymous Marketplace | Last updated: February 2026</p> <p class="legal-meta">kashilo.com — Anonymous Marketplace | Last updated: February 2026</p>
<h2>1. Information according to Swiss law</h2> <h2>1. Information according to Swiss law</h2>
<p>Responsible for this website:</p> <p>Responsible for this website:</p>
@@ -81,7 +81,7 @@ class PageImprint extends HTMLElement {
fr: /* html */` fr: /* html */`
<h1>Mentions légales</h1> <h1>Mentions légales</h1>
<div class="legal-draft-notice">ÉBAUCHE — Veuillez faire vérifier ce document par un professionnel du droit.</div> <div class="legal-draft-notice">ÉBAUCHE — Veuillez faire vérifier ce document par un professionnel du droit.</div>
<p class="legal-meta">dgray.io — Marché anonyme | Mise à jour : février 2026</p> <p class="legal-meta">kashilo.com — Marché anonyme | Mise à jour : février 2026</p>
<h2>1. Informations selon le droit suisse</h2> <h2>1. Informations selon le droit suisse</h2>
<p>Responsable de ce site web :</p> <p>Responsable de ce site web :</p>
@@ -113,7 +113,7 @@ class PageImprint extends HTMLElement {
it: /* html */` it: /* html */`
<h1>Avviso legale</h1> <h1>Avviso legale</h1>
<div class="legal-draft-notice">BOZZA — Si prega di far verificare questo documento da un professionista legale.</div> <div class="legal-draft-notice">BOZZA — Si prega di far verificare questo documento da un professionista legale.</div>
<p class="legal-meta">dgray.io — Mercato anonimo | Aggiornamento: febbraio 2026</p> <p class="legal-meta">kashilo.com — Mercato anonimo | Aggiornamento: febbraio 2026</p>
<h2>1. Informazioni secondo il diritto svizzero</h2> <h2>1. Informazioni secondo il diritto svizzero</h2>
<p>Responsabile di questo sito web:</p> <p>Responsabile di questo sito web:</p>
@@ -145,7 +145,7 @@ class PageImprint extends HTMLElement {
es: /* html */` es: /* html */`
<h1>Aviso legal</h1> <h1>Aviso legal</h1>
<div class="legal-draft-notice">BORRADOR — Por favor, haga revisar este documento por un profesional legal.</div> <div class="legal-draft-notice">BORRADOR — Por favor, haga revisar este documento por un profesional legal.</div>
<p class="legal-meta">dgray.io — Mercado anónimo | Actualización: febrero 2026</p> <p class="legal-meta">kashilo.com — Mercado anónimo | Actualización: febrero 2026</p>
<h2>1. Información según la legislación suiza</h2> <h2>1. Información según la legislación suiza</h2>
<p>Responsable de este sitio web:</p> <p>Responsable de este sitio web:</p>
@@ -177,7 +177,7 @@ class PageImprint extends HTMLElement {
pt: /* html */` pt: /* html */`
<h1>Aviso legal</h1> <h1>Aviso legal</h1>
<div class="legal-draft-notice">RASCUNHO — Por favor, solicite a revisão deste documento por um profissional jurídico.</div> <div class="legal-draft-notice">RASCUNHO — Por favor, solicite a revisão deste documento por um profissional jurídico.</div>
<p class="legal-meta">dgray.io — Mercado anônimo | Atualização: fevereiro 2026</p> <p class="legal-meta">kashilo.com — Mercado anônimo | Atualização: fevereiro 2026</p>
<h2>1. Informações conforme a legislação suíça</h2> <h2>1. Informações conforme a legislação suíça</h2>
<p>Responsável por este site:</p> <p>Responsável por este site:</p>
@@ -209,7 +209,7 @@ class PageImprint extends HTMLElement {
ru: /* html */` ru: /* html */`
<h1>Правовая информация</h1> <h1>Правовая информация</h1>
<div class="legal-draft-notice">ЧЕРНОВИК — Пожалуйста, передайте этот документ на проверку юристу.</div> <div class="legal-draft-notice">ЧЕРНОВИК — Пожалуйста, передайте этот документ на проверку юристу.</div>
<p class="legal-meta">dgray.io — Анонимный маркетплейс | Обновлено: февраль 2026</p> <p class="legal-meta">kashilo.com — Анонимный маркетплейс | Обновлено: февраль 2026</p>
<h2>1. Сведения согласно швейцарскому праву</h2> <h2>1. Сведения согласно швейцарскому праву</h2>
<p>Ответственный за данный сайт:</p> <p>Ответственный за данный сайт:</p>

View File

@@ -90,11 +90,11 @@ class PageListing extends HTMLElement {
updateMetaTags() { updateMetaTags() {
if (!this.listing) return if (!this.listing) return
const title = `${this.listing.title} dgray.io` const title = `${this.listing.title} kashilo.com`
const description = (this.listing.description || '').substring(0, 160) const description = (this.listing.description || '').substring(0, 160)
const imageId = this.listing.images?.[0]?.directus_files_id?.id || this.listing.images?.[0]?.directus_files_id const imageId = this.listing.images?.[0]?.directus_files_id?.id || this.listing.images?.[0]?.directus_files_id
const imageUrl = imageId ? directus.getFileUrl(imageId, { width: 1200, height: 630, fit: 'cover' }) : 'https://dgray.io/assets/press/og-image.png' const imageUrl = imageId ? directus.getFileUrl(imageId, { width: 1200, height: 630, fit: 'cover' }) : 'https://kashilo.com/assets/press/og-image.png'
const url = `https://dgray.io/#/listing/${this.listing.id}` const url = `https://kashilo.com/#/listing/${this.listing.id}`
document.title = title document.title = title
this._setMeta('description', description) this._setMeta('description', description)
@@ -109,16 +109,16 @@ class PageListing extends HTMLElement {
} }
resetMetaTags() { resetMetaTags() {
const defaultTitle = 'dgray.io Anonymous Classifieds with Monero' const defaultTitle = 'kashilo.com Anonymous Classifieds with Monero'
const defaultDesc = 'Buy and sell anonymously with Monero. No KYC, no email, E2E encrypted chat.' const defaultDesc = 'Buy and sell anonymously with Monero. No KYC, no email, E2E encrypted chat.'
const defaultImage = 'https://dgray.io/assets/press/og-image.png' const defaultImage = 'https://kashilo.com/assets/press/og-image.png'
document.title = defaultTitle document.title = defaultTitle
this._setMeta('description', defaultDesc) this._setMeta('description', defaultDesc)
this._setMeta('og:title', defaultTitle, true) this._setMeta('og:title', defaultTitle, true)
this._setMeta('og:description', defaultDesc, true) this._setMeta('og:description', defaultDesc, true)
this._setMeta('og:image', defaultImage, true) this._setMeta('og:image', defaultImage, true)
this._setMeta('og:url', 'https://dgray.io', true) this._setMeta('og:url', 'https://kashilo.com', true)
this._setMeta('og:type', 'website', true) this._setMeta('og:type', 'website', true)
this._setMeta('twitter:title', defaultTitle) this._setMeta('twitter:title', defaultTitle)
this._setMeta('twitter:description', defaultDesc) this._setMeta('twitter:description', defaultDesc)

View File

@@ -16,10 +16,10 @@ class PagePrivacy extends HTMLElement {
const content = { const content = {
de: /* html */` de: /* html */`
<h1>Datenschutzerklärung</h1> <h1>Datenschutzerklärung</h1>
<p class="legal-meta">dgray.io — Anonymer Marktplatz | Stand: Februar 2026</p> <p class="legal-meta">kashilo.com — Anonymer Marktplatz | Stand: Februar 2026</p>
<h2>1. Verantwortlicher</h2> <h2>1. Verantwortlicher</h2>
<p>Verantwortlich für die Datenbearbeitung ist der Betreiber der Plattform dgray.io mit Sitz in der Schweiz.</p> <p>Verantwortlich für die Datenbearbeitung ist der Betreiber der Plattform kashilo.com mit Sitz in der Schweiz.</p>
<h2>2. Grundsatz</h2> <h2>2. Grundsatz</h2>
<p>Die Plattform wurde nach dem Prinzip der Datensparsamkeit konzipiert.</p> <p>Die Plattform wurde nach dem Prinzip der Datensparsamkeit konzipiert.</p>
@@ -67,10 +67,10 @@ class PagePrivacy extends HTMLElement {
`, `,
en: /* html */` en: /* html */`
<h1>Privacy Policy</h1> <h1>Privacy Policy</h1>
<p class="legal-meta">dgray.io — Anonymous Marketplace | Last updated: February 2026</p> <p class="legal-meta">kashilo.com — Anonymous Marketplace | Last updated: February 2026</p>
<h2>1. Controller</h2> <h2>1. Controller</h2>
<p>The controller for data processing is the operator of dgray.io, based in Switzerland.</p> <p>The controller for data processing is the operator of kashilo.com, based in Switzerland.</p>
<h2>2. Principle</h2> <h2>2. Principle</h2>
<p>The platform was designed following the principle of data minimization.</p> <p>The platform was designed following the principle of data minimization.</p>
@@ -118,10 +118,10 @@ class PagePrivacy extends HTMLElement {
`, `,
fr: /* html */` fr: /* html */`
<h1>Politique de confidentialité</h1> <h1>Politique de confidentialité</h1>
<p class="legal-meta">dgray.io — Marché anonyme | Mise à jour : février 2026</p> <p class="legal-meta">kashilo.com — Marché anonyme | Mise à jour : février 2026</p>
<h2>1. Responsable</h2> <h2>1. Responsable</h2>
<p>Le responsable du traitement des données est l'exploitant de dgray.io, domicilié en Suisse.</p> <p>Le responsable du traitement des données est l'exploitant de kashilo.com, domicilié en Suisse.</p>
<h2>2. Principe</h2> <h2>2. Principe</h2>
<p>La plateforme a été conçue selon le principe de minimisation des données.</p> <p>La plateforme a été conçue selon le principe de minimisation des données.</p>
@@ -169,10 +169,10 @@ class PagePrivacy extends HTMLElement {
`, `,
it: /* html */` it: /* html */`
<h1>Informativa sulla privacy</h1> <h1>Informativa sulla privacy</h1>
<p class="legal-meta">dgray.io — Mercato anonimo | Aggiornamento: febbraio 2026</p> <p class="legal-meta">kashilo.com — Mercato anonimo | Aggiornamento: febbraio 2026</p>
<h2>1. Titolare del trattamento</h2> <h2>1. Titolare del trattamento</h2>
<p>Il titolare del trattamento dei dati è il gestore della piattaforma dgray.io con sede in Svizzera.</p> <p>Il titolare del trattamento dei dati è il gestore della piattaforma kashilo.com con sede in Svizzera.</p>
<h2>2. Principio</h2> <h2>2. Principio</h2>
<p>La piattaforma è stata progettata secondo il principio della minimizzazione dei dati.</p> <p>La piattaforma è stata progettata secondo il principio della minimizzazione dei dati.</p>
@@ -220,10 +220,10 @@ class PagePrivacy extends HTMLElement {
`, `,
es: /* html */` es: /* html */`
<h1>Política de Privacidad</h1> <h1>Política de Privacidad</h1>
<p class="legal-meta">dgray.io — Mercado anónimo | Actualización: febrero 2026</p> <p class="legal-meta">kashilo.com — Mercado anónimo | Actualización: febrero 2026</p>
<h2>1. Responsable del tratamiento</h2> <h2>1. Responsable del tratamiento</h2>
<p>El responsable del tratamiento de datos es el operador de la plataforma dgray.io con sede en Suiza.</p> <p>El responsable del tratamiento de datos es el operador de la plataforma kashilo.com con sede en Suiza.</p>
<h2>2. Principio</h2> <h2>2. Principio</h2>
<p>La plataforma fue diseñada según el principio de minimización de datos.</p> <p>La plataforma fue diseñada según el principio de minimización de datos.</p>
@@ -271,10 +271,10 @@ class PagePrivacy extends HTMLElement {
`, `,
pt: /* html */` pt: /* html */`
<h1>Política de Privacidade</h1> <h1>Política de Privacidade</h1>
<p class="legal-meta">dgray.io — Mercado anônimo | Atualização: fevereiro 2026</p> <p class="legal-meta">kashilo.com — Mercado anônimo | Atualização: fevereiro 2026</p>
<h2>1. Responsável pelo tratamento</h2> <h2>1. Responsável pelo tratamento</h2>
<p>O responsável pelo tratamento de dados é o operador da plataforma dgray.io com sede na Suíça.</p> <p>O responsável pelo tratamento de dados é o operador da plataforma kashilo.com com sede na Suíça.</p>
<h2>2. Princípio</h2> <h2>2. Princípio</h2>
<p>A plataforma foi projetada segundo o princípio da minimização de dados.</p> <p>A plataforma foi projetada segundo o princípio da minimização de dados.</p>
@@ -322,10 +322,10 @@ class PagePrivacy extends HTMLElement {
`, `,
ru: /* html */` ru: /* html */`
<h1>Политика конфиденциальности</h1> <h1>Политика конфиденциальности</h1>
<p class="legal-meta">dgray.io — Анонимный маркетплейс | Обновлено: февраль 2026</p> <p class="legal-meta">kashilo.com — Анонимный маркетплейс | Обновлено: февраль 2026</p>
<h2>1. Ответственный за обработку данных</h2> <h2>1. Ответственный за обработку данных</h2>
<p>Ответственным за обработку данных является оператор платформы dgray.io с местонахождением в Швейцарии.</p> <p>Ответственным за обработку данных является оператор платформы kashilo.com с местонахождением в Швейцарии.</p>
<h2>2. Принцип</h2> <h2>2. Принцип</h2>
<p>Платформа разработана по принципу минимизации данных.</p> <p>Платформа разработана по принципу минимизации данных.</p>

View File

@@ -146,11 +146,11 @@ class PageSettings extends HTMLElement {
} }
getCurrentCurrency() { getCurrentCurrency() {
return localStorage.getItem('dgray_currency') || 'USD' return localStorage.getItem('kashilo_currency') || 'USD'
} }
async setCurrency(currency) { async setCurrency(currency) {
localStorage.setItem('dgray_currency', currency) localStorage.setItem('kashilo_currency', currency)
window.dispatchEvent(new CustomEvent('currency-changed', { detail: { currency } })) window.dispatchEvent(new CustomEvent('currency-changed', { detail: { currency } }))
// Save to user profile if logged in // Save to user profile if logged in
@@ -323,7 +323,7 @@ class PageSettings extends HTMLElement {
<a href="#/privacy">${t('footer.privacy')}</a> <a href="#/privacy">${t('footer.privacy')}</a>
<a href="#/terms">${t('footer.terms')}</a> <a href="#/terms">${t('footer.terms')}</a>
</div> </div>
<p class="version">dgray.io v1.0.0</p> <p class="version">kashilo.com v1.0.0</p>
</section> </section>
</div> </div>
</div> </div>

View File

@@ -40,10 +40,10 @@ class PageTerms extends HTMLElement {
getContentDE() { getContentDE() {
return /* html */` return /* html */`
<h1>Allgemeine Geschäftsbedingungen (AGB)</h1> <h1>Allgemeine Geschäftsbedingungen (AGB)</h1>
<p class="legal-meta">dgray.io — Anonymer Marktplatz | Stand: Februar 2026</p> <p class="legal-meta">kashilo.com — Anonymer Marktplatz | Stand: Februar 2026</p>
<h2>1. Geltungsbereich</h2> <h2>1. Geltungsbereich</h2>
<p>1.1 Diese AGB regeln die Nutzung der Plattform dgray.io.</p> <p>1.1 Diese AGB regeln die Nutzung der Plattform kashilo.com.</p>
<p>1.2 Die Plattform wird betrieben von einer natürlichen Person mit Sitz in der Schweiz.</p> <p>1.2 Die Plattform wird betrieben von einer natürlichen Person mit Sitz in der Schweiz.</p>
<p>1.3 Mit der Nutzung der Plattform erklärt sich die nutzende Person mit diesen AGB einverstanden.</p> <p>1.3 Mit der Nutzung der Plattform erklärt sich die nutzende Person mit diesen AGB einverstanden.</p>
@@ -114,10 +114,10 @@ class PageTerms extends HTMLElement {
getContentEN() { getContentEN() {
return /* html */` return /* html */`
<h1>Terms of Service</h1> <h1>Terms of Service</h1>
<p class="legal-meta">dgray.io — Anonymous Marketplace | Last updated: February 2026</p> <p class="legal-meta">kashilo.com — Anonymous Marketplace | Last updated: February 2026</p>
<h2>1. Scope</h2> <h2>1. Scope</h2>
<p>1.1 These Terms of Service govern the use of the platform dgray.io.</p> <p>1.1 These Terms of Service govern the use of the platform kashilo.com.</p>
<p>1.2 The platform is operated by a natural person based in Switzerland.</p> <p>1.2 The platform is operated by a natural person based in Switzerland.</p>
<p>1.3 By using the platform, the user agrees to these terms.</p> <p>1.3 By using the platform, the user agrees to these terms.</p>
@@ -188,10 +188,10 @@ class PageTerms extends HTMLElement {
getContentFR() { getContentFR() {
return /* html */` return /* html */`
<h1>Conditions Générales d'Utilisation (CGU)</h1> <h1>Conditions Générales d'Utilisation (CGU)</h1>
<p class="legal-meta">dgray.io — Marché anonyme | Mise à jour : février 2026</p> <p class="legal-meta">kashilo.com — Marché anonyme | Mise à jour : février 2026</p>
<h2>1. Champ d'application</h2> <h2>1. Champ d'application</h2>
<p>1.1 Les présentes CGU régissent l'utilisation de la plateforme dgray.io.</p> <p>1.1 Les présentes CGU régissent l'utilisation de la plateforme kashilo.com.</p>
<p>1.2 La plateforme est exploitée par une personne physique domiciliée en Suisse.</p> <p>1.2 La plateforme est exploitée par une personne physique domiciliée en Suisse.</p>
<p>1.3 En utilisant la plateforme, l'utilisateur accepte les présentes CGU.</p> <p>1.3 En utilisant la plateforme, l'utilisateur accepte les présentes CGU.</p>
@@ -262,10 +262,10 @@ class PageTerms extends HTMLElement {
getContentIT() { getContentIT() {
return /* html */` return /* html */`
<h1>Condizioni generali di utilizzo (CGU)</h1> <h1>Condizioni generali di utilizzo (CGU)</h1>
<p class="legal-meta">dgray.io — Mercato anonimo | Aggiornamento: febbraio 2026</p> <p class="legal-meta">kashilo.com — Mercato anonimo | Aggiornamento: febbraio 2026</p>
<h2>1. Campo di applicazione</h2> <h2>1. Campo di applicazione</h2>
<p>1.1 Le presenti CGU disciplinano l'utilizzo della piattaforma dgray.io.</p> <p>1.1 Le presenti CGU disciplinano l'utilizzo della piattaforma kashilo.com.</p>
<p>1.2 La piattaforma è gestita da una persona fisica con sede in Svizzera.</p> <p>1.2 La piattaforma è gestita da una persona fisica con sede in Svizzera.</p>
<p>1.3 Utilizzando la piattaforma, l'utente accetta le presenti CGU.</p> <p>1.3 Utilizzando la piattaforma, l'utente accetta le presenti CGU.</p>
@@ -336,10 +336,10 @@ class PageTerms extends HTMLElement {
getContentES() { getContentES() {
return /* html */` return /* html */`
<h1>Condiciones generales de uso (CGU)</h1> <h1>Condiciones generales de uso (CGU)</h1>
<p class="legal-meta">dgray.io — Mercado anónimo | Actualización: febrero 2026</p> <p class="legal-meta">kashilo.com — Mercado anónimo | Actualización: febrero 2026</p>
<h2>1. Ámbito de aplicación</h2> <h2>1. Ámbito de aplicación</h2>
<p>1.1 Las presentes CGU regulan el uso de la plataforma dgray.io.</p> <p>1.1 Las presentes CGU regulan el uso de la plataforma kashilo.com.</p>
<p>1.2 La plataforma es operada por una persona física con sede en Suiza.</p> <p>1.2 La plataforma es operada por una persona física con sede en Suiza.</p>
<p>1.3 Al utilizar la plataforma, el usuario acepta las presentes CGU.</p> <p>1.3 Al utilizar la plataforma, el usuario acepta las presentes CGU.</p>
@@ -410,10 +410,10 @@ class PageTerms extends HTMLElement {
getContentPT() { getContentPT() {
return /* html */` return /* html */`
<h1>Termos de Uso</h1> <h1>Termos de Uso</h1>
<p class="legal-meta">dgray.io — Mercado anônimo | Atualização: fevereiro 2026</p> <p class="legal-meta">kashilo.com — Mercado anônimo | Atualização: fevereiro 2026</p>
<h2>1. Âmbito de aplicação</h2> <h2>1. Âmbito de aplicação</h2>
<p>1.1 Estes Termos de Uso regulam a utilização da plataforma dgray.io.</p> <p>1.1 Estes Termos de Uso regulam a utilização da plataforma kashilo.com.</p>
<p>1.2 A plataforma é operada por uma pessoa física com sede na Suíça.</p> <p>1.2 A plataforma é operada por uma pessoa física com sede na Suíça.</p>
<p>1.3 Ao utilizar a plataforma, o usuário concorda com estes Termos de Uso.</p> <p>1.3 Ao utilizar a plataforma, o usuário concorda com estes Termos de Uso.</p>
@@ -484,10 +484,10 @@ class PageTerms extends HTMLElement {
getContentRU() { getContentRU() {
return /* html */` return /* html */`
<h1>Условия использования</h1> <h1>Условия использования</h1>
<p class="legal-meta">dgray.io — Анонимный маркетплейс | Обновлено: февраль 2026</p> <p class="legal-meta">kashilo.com — Анонимный маркетплейс | Обновлено: февраль 2026</p>
<h2>1. Область применения</h2> <h2>1. Область применения</h2>
<p>1.1 Настоящие Условия использования регулируют пользование платформой dgray.io.</p> <p>1.1 Настоящие Условия использования регулируют пользование платформой kashilo.com.</p>
<p>1.2 Платформа управляется физическим лицом с местонахождением в Швейцарии.</p> <p>1.2 Платформа управляется физическим лицом с местонахождением в Швейцарии.</p>
<p>1.3 Используя платформу, пользователь соглашается с настоящими Условиями использования.</p> <p>1.3 Используя платформу, пользователь соглашается с настоящими Условиями использования.</p>

View File

@@ -11,14 +11,14 @@ import { setPersist, getPersist } from './directus/client.js'
import { cryptoService } from './crypto.js' import { cryptoService } from './crypto.js'
import { i18n } from '../i18n.js' import { i18n } from '../i18n.js'
const AUTH_DOMAIN = 'dgray.io' const AUTH_DOMAIN = 'kashilo.com'
class AuthService { class AuthService {
constructor() { constructor() {
this.currentUser = null this.currentUser = null
this.listeners = new Set() this.listeners = new Set()
this.hashCache = new Map() this.hashCache = new Map()
if (localStorage.getItem('dgray_remember') === '1') { if (localStorage.getItem('kashilo_remember') === '1') {
setPersist(true) setPersist(true)
} }
} }
@@ -142,7 +142,7 @@ class AuthService {
this.currentUser = null this.currentUser = null
this.clearStoredUuid() this.clearStoredUuid()
localStorage.removeItem('dgray_remember') localStorage.removeItem('kashilo_remember')
setPersist(false) setPersist(false)
cryptoService.lock() cryptoService.lock()
this.resetPreferencesToDefaults() this.resetPreferencesToDefaults()
@@ -159,7 +159,7 @@ class AuthService {
const keysToRemove = [] const keysToRemove = []
for (let i = 0; i < localStorage.length; i++) { for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i) const key = localStorage.key(i)
if (key && key.startsWith('dgray_')) keysToRemove.push(key) if (key && key.startsWith('kashilo_')) keysToRemove.push(key)
} }
keysToRemove.forEach(k => localStorage.removeItem(k)) keysToRemove.forEach(k => localStorage.removeItem(k))
@@ -169,7 +169,7 @@ class AuthService {
for (let i = sessionStorage.length - 1; i >= 0; i--) { for (let i = sessionStorage.length - 1; i >= 0; i--) {
const key = sessionStorage.key(i) const key = sessionStorage.key(i)
if (key && key.startsWith('dgray_')) sessionStorage.removeItem(key) if (key && key.startsWith('kashilo_')) sessionStorage.removeItem(key)
} }
} }
@@ -180,7 +180,7 @@ class AuthService {
const defaultCurrency = 'USD' const defaultCurrency = 'USD'
const defaultLocale = 'en' const defaultLocale = 'en'
localStorage.setItem('dgray_currency', defaultCurrency) localStorage.setItem('kashilo_currency', defaultCurrency)
localStorage.setItem('locale', defaultLocale) localStorage.setItem('locale', defaultLocale)
window.dispatchEvent(new CustomEvent('currency-changed', { detail: { currency: defaultCurrency } })) window.dispatchEvent(new CustomEvent('currency-changed', { detail: { currency: defaultCurrency } }))
@@ -223,7 +223,7 @@ class AuthService {
if (this.currentUser.preferred_currency) { if (this.currentUser.preferred_currency) {
const currency = this.currentUser.preferred_currency const currency = this.currentUser.preferred_currency
localStorage.setItem('dgray_currency', currency) localStorage.setItem('kashilo_currency', currency)
window.dispatchEvent(new CustomEvent('currency-changed', { detail: { currency } })) window.dispatchEvent(new CustomEvent('currency-changed', { detail: { currency } }))
} }
if (this.currentUser.preferred_locale) { if (this.currentUser.preferred_locale) {
@@ -261,7 +261,7 @@ class AuthService {
*/ */
storeUuid(uuid) { storeUuid(uuid) {
const storage = getPersist() ? localStorage : sessionStorage const storage = getPersist() ? localStorage : sessionStorage
storage.setItem('dgray_uuid', uuid) storage.setItem('kashilo_uuid', uuid)
} }
/** /**
@@ -269,28 +269,28 @@ class AuthService {
* @returns {string|null} * @returns {string|null}
*/ */
getStoredUuid() { getStoredUuid() {
return sessionStorage.getItem('dgray_uuid') || localStorage.getItem('dgray_uuid') return sessionStorage.getItem('kashilo_uuid') || localStorage.getItem('kashilo_uuid')
} }
/** /**
* Clears stored UUID * Clears stored UUID
*/ */
clearStoredUuid() { clearStoredUuid() {
sessionStorage.removeItem('dgray_uuid') sessionStorage.removeItem('kashilo_uuid')
localStorage.removeItem('dgray_uuid') localStorage.removeItem('kashilo_uuid')
} }
setRememberMe(value) { setRememberMe(value) {
setPersist(value) setPersist(value)
if (value) { if (value) {
localStorage.setItem('dgray_remember', '1') localStorage.setItem('kashilo_remember', '1')
} else { } else {
localStorage.removeItem('dgray_remember') localStorage.removeItem('kashilo_remember')
} }
} }
getRememberMe() { getRememberMe() {
return localStorage.getItem('dgray_remember') === '1' return localStorage.getItem('kashilo_remember') === '1'
} }
/** /**

View File

@@ -1,6 +1,6 @@
import { i18n } from '../i18n.js' import { i18n } from '../i18n.js'
const POW_SERVER = 'https://pow.dgray.io' const POW_SERVER = 'https://pow.kashilo.com'
let modalScriptLoaded = false let modalScriptLoaded = false
let modalScriptLoading = null let modalScriptLoading = null
@@ -131,7 +131,7 @@ export async function pollUntilDone(invoiceId, options = {}) {
*/ */
export function getPendingInvoice(listingId) { export function getPendingInvoice(listingId) {
try { try {
const data = localStorage.getItem(`dgray_invoice_${listingId}`) const data = localStorage.getItem(`kashilo_invoice_${listingId}`)
return data ? JSON.parse(data) : null return data ? JSON.parse(data) : null
} catch (e) { } catch (e) {
return null return null
@@ -145,7 +145,7 @@ export function getPendingInvoice(listingId) {
*/ */
export function savePendingInvoice(listingId, invoiceId) { export function savePendingInvoice(listingId, invoiceId) {
try { try {
localStorage.setItem(`dgray_invoice_${listingId}`, JSON.stringify({ localStorage.setItem(`kashilo_invoice_${listingId}`, JSON.stringify({
invoiceId, invoiceId,
createdAt: Date.now() createdAt: Date.now()
})) }))
@@ -159,7 +159,7 @@ export function savePendingInvoice(listingId, invoiceId) {
* @param {string} listingId * @param {string} listingId
*/ */
export function clearPendingInvoice(listingId) { export function clearPendingInvoice(listingId) {
localStorage.removeItem(`dgray_invoice_${listingId}`) localStorage.removeItem(`kashilo_invoice_${listingId}`)
} }
export default { export default {

View File

@@ -10,8 +10,8 @@ class CategoriesService {
this.cache = null this.cache = null
this.cacheTimestamp = 0 this.cacheTimestamp = 0
this.cacheTimeout = 24 * 60 * 60 * 1000 // 24 hours this.cacheTimeout = 24 * 60 * 60 * 1000 // 24 hours
this.storageKey = 'dgray_categories' this.storageKey = 'kashilo_categories'
this.storageTimestampKey = 'dgray_categories_ts' this.storageTimestampKey = 'kashilo_categories_ts'
this._pending = null this._pending = null
this._loadFromStorage() this._loadFromStorage()
} }

View File

@@ -6,9 +6,9 @@
* derived from the user's UUID via PBKDF2. * derived from the user's UUID via PBKDF2.
*/ */
const STORAGE_KEY = 'dgray_keypair' const STORAGE_KEY = 'kashilo_keypair'
const SALT_KEY = 'dgray_keypair_salt' const SALT_KEY = 'kashilo_keypair_salt'
const LISTING_KEYS_STORAGE = 'dgray_listing_keys' const LISTING_KEYS_STORAGE = 'kashilo_listing_keys'
class CryptoService { class CryptoService {
constructor() { constructor() {

View File

@@ -195,7 +195,7 @@ export function convertFiat(amount, fromCurrency, toCurrency, rates) {
* @returns {string} Currency code (default: 'USD') * @returns {string} Currency code (default: 'USD')
*/ */
export function getDisplayCurrency() { export function getDisplayCurrency() {
return localStorage.getItem('dgray_currency') || 'USD' return localStorage.getItem('kashilo_currency') || 'USD'
} }
/** /**

View File

@@ -1,5 +1,5 @@
/** /**
* Directus API Service for dgray.io — Facade * Directus API Service for kashilo.com — Facade
* Re-exports modular sub-services as a single backward-compatible singleton. * Re-exports modular sub-services as a single backward-compatible singleton.
* @module services/directus * @module services/directus
*/ */

View File

@@ -1,4 +1,4 @@
const DIRECTUS_URL = 'https://api.dgray.io' const DIRECTUS_URL = 'https://api.kashilo.com'
let _persist = false let _persist = false
@@ -51,14 +51,14 @@ class DirectusClient {
// ── Token Management ── // ── Token Management ──
loadTokens() { loadTokens() {
const stored = sessionStorage.getItem('dgray_auth') || localStorage.getItem('dgray_auth') const stored = sessionStorage.getItem('kashilo_auth') || localStorage.getItem('kashilo_auth')
if (stored) { if (stored) {
try { try {
const { accessToken, refreshToken, expiry } = JSON.parse(stored) const { accessToken, refreshToken, expiry } = JSON.parse(stored)
this.accessToken = accessToken this.accessToken = accessToken
this.refreshToken = refreshToken this.refreshToken = refreshToken
this.tokenExpiry = expiry this.tokenExpiry = expiry
if (localStorage.getItem('dgray_auth')) { if (localStorage.getItem('kashilo_auth')) {
_persist = true _persist = true
} }
this.scheduleTokenRefresh() this.scheduleTokenRefresh()
@@ -73,7 +73,7 @@ class DirectusClient {
this.refreshToken = refreshToken this.refreshToken = refreshToken
this.tokenExpiry = Date.now() + (expiresIn * 1000) this.tokenExpiry = Date.now() + (expiresIn * 1000)
_storage().setItem('dgray_auth', JSON.stringify({ _storage().setItem('kashilo_auth', JSON.stringify({
accessToken: this.accessToken, accessToken: this.accessToken,
refreshToken: this.refreshToken, refreshToken: this.refreshToken,
expiry: this.tokenExpiry expiry: this.tokenExpiry
@@ -86,8 +86,8 @@ class DirectusClient {
this.accessToken = null this.accessToken = null
this.refreshToken = null this.refreshToken = null
this.tokenExpiry = null this.tokenExpiry = null
sessionStorage.removeItem('dgray_auth') sessionStorage.removeItem('kashilo_auth')
localStorage.removeItem('dgray_auth') localStorage.removeItem('kashilo_auth')
if (this.refreshTimeout) { if (this.refreshTimeout) {
clearTimeout(this.refreshTimeout) clearTimeout(this.refreshTimeout)

View File

@@ -1,7 +1,7 @@
import { directus } from './directus.js' import { directus } from './directus.js'
import { auth } from './auth.js' import { auth } from './auth.js'
const ANON_KEY = 'dgray_favorites' const ANON_KEY = 'kashilo_favorites'
class FavoritesService { class FavoritesService {
constructor() { constructor() {

View File

@@ -3,7 +3,7 @@
* Stores seller contact keys on first use, warns on key changes * Stores seller contact keys on first use, warns on key changes
*/ */
const PINNED_KEYS_STORAGE = 'dgray_pinned_keys' const PINNED_KEYS_STORAGE = 'kashilo_pinned_keys'
class KeyPinningService { class KeyPinningService {
constructor() { constructor() {

View File

@@ -2,11 +2,11 @@
// Client must find nonce where SHA256(challenge + nonce) has N leading zeros // Client must find nonce where SHA256(challenge + nonce) has N leading zeros
// Server-first: tries /pow/challenge endpoint, falls back to local generation // Server-first: tries /pow/challenge endpoint, falls back to local generation
const POW_SERVER = 'https://pow.dgray.io' const POW_SERVER = 'https://pow.kashilo.com'
const DIFFICULTY = 4 const DIFFICULTY = 4
const SERVER_TIMEOUT_MS = 1500 const SERVER_TIMEOUT_MS = 1500
const LOCAL_HMAC_KEY = 'dgray-pow-local-v1' const LOCAL_HMAC_KEY = 'kashilo-pow-local-v1'
async function hmacSign(message) { async function hmacSign(message) {
const enc = new TextEncoder() const enc = new TextEncoder()

View File

@@ -1,6 +1,6 @@
{ {
"meta": { "meta": {
"title": "dgray.io Anonyme Kleinanzeigen", "title": "kashilo.com Anonyme Kleinanzeigen",
"description": "Kaufen und verkaufen ohne Konto, ohne E-Mail. Bezahlung mit Monero. Ende-zu-Ende verschlüsselter Chat." "description": "Kaufen und verkaufen ohne Konto, ohne E-Mail. Bezahlung mit Monero. Ende-zu-Ende verschlüsselter Chat."
}, },
"header": { "header": {
@@ -19,7 +19,7 @@
"imprint": "Impressum" "imprint": "Impressum"
}, },
"home": { "home": {
"title": "Willkommen bei dgray.io", "title": "Willkommen bei kashilo.com",
"subtitle": "Finde tolle Angebote in deiner Nähe oder verkaufe, was du nicht mehr brauchst.", "subtitle": "Finde tolle Angebote in deiner Nähe oder verkaufe, was du nicht mehr brauchst.",
"browseListings": "Anzeigen durchsuchen", "browseListings": "Anzeigen durchsuchen",
"createListing": "Anzeige erstellen", "createListing": "Anzeige erstellen",

View File

@@ -1,6 +1,6 @@
{ {
"meta": { "meta": {
"title": "dgray.io Private Classifieds", "title": "kashilo.com Private Classifieds",
"description": "Buy and sell without an account, without email. Pay with Monero. End-to-end encrypted chat." "description": "Buy and sell without an account, without email. Pay with Monero. End-to-end encrypted chat."
}, },
"header": { "header": {
@@ -19,7 +19,7 @@
"imprint": "Legal Notice" "imprint": "Legal Notice"
}, },
"home": { "home": {
"title": "Welcome to dgray.io", "title": "Welcome to kashilo.com",
"subtitle": "Find great deals near you or sell what you no longer need.", "subtitle": "Find great deals near you or sell what you no longer need.",
"browseListings": "Browse Listings", "browseListings": "Browse Listings",
"createListing": "Create Listing", "createListing": "Create Listing",

View File

@@ -1,6 +1,6 @@
{ {
"meta": { "meta": {
"title": "dgray.io Clasificados anónimos", "title": "kashilo.com Clasificados anónimos",
"description": "Compra y vende sin cuenta, sin email. Pago con Monero. Chat cifrado de extremo a extremo." "description": "Compra y vende sin cuenta, sin email. Pago con Monero. Chat cifrado de extremo a extremo."
}, },
"header": { "header": {
@@ -19,7 +19,7 @@
"imprint": "Aviso legal" "imprint": "Aviso legal"
}, },
"home": { "home": {
"title": "Bienvenido a dgray.io", "title": "Bienvenido a kashilo.com",
"subtitle": "Encuentra grandes ofertas cerca de ti o vende lo que ya no necesitas.", "subtitle": "Encuentra grandes ofertas cerca de ti o vende lo que ya no necesitas.",
"browseListings": "Explorar anuncios", "browseListings": "Explorar anuncios",
"createListing": "Crear anuncio", "createListing": "Crear anuncio",

View File

@@ -1,6 +1,6 @@
{ {
"meta": { "meta": {
"title": "dgray.io Petites annonces anonymes", "title": "kashilo.com Petites annonces anonymes",
"description": "Achetez et vendez sans compte, sans e-mail. Paiement en Monero. Chat chiffré de bout en bout." "description": "Achetez et vendez sans compte, sans e-mail. Paiement en Monero. Chat chiffré de bout en bout."
}, },
"header": { "header": {
@@ -19,7 +19,7 @@
"imprint": "Mentions légales" "imprint": "Mentions légales"
}, },
"home": { "home": {
"title": "Bienvenue sur dgray.io", "title": "Bienvenue sur kashilo.com",
"subtitle": "Trouvez de bonnes affaires près de chez vous ou vendez ce dont vous n'avez plus besoin.", "subtitle": "Trouvez de bonnes affaires près de chez vous ou vendez ce dont vous n'avez plus besoin.",
"browseListings": "Parcourir les annonces", "browseListings": "Parcourir les annonces",
"createListing": "Créer une annonce", "createListing": "Créer une annonce",

View File

@@ -1,6 +1,6 @@
{ {
"meta": { "meta": {
"title": "dgray.io Annunci anonimi", "title": "kashilo.com Annunci anonimi",
"description": "Compra e vendi senza account, senza email. Pagamento in Monero. Chat crittografata end-to-end." "description": "Compra e vendi senza account, senza email. Pagamento in Monero. Chat crittografata end-to-end."
}, },
"header": { "header": {
@@ -19,7 +19,7 @@
"imprint": "Avviso legale" "imprint": "Avviso legale"
}, },
"home": { "home": {
"title": "Benvenuto su dgray.io", "title": "Benvenuto su kashilo.com",
"subtitle": "Trova ottime offerte vicino a te o vendi ciò che non ti serve più.", "subtitle": "Trova ottime offerte vicino a te o vendi ciò che non ti serve più.",
"browseListings": "Sfoglia annunci", "browseListings": "Sfoglia annunci",
"createListing": "Crea annuncio", "createListing": "Crea annuncio",

View File

@@ -1,6 +1,6 @@
{ {
"meta": { "meta": {
"title": "dgray.io Classificados anônimos", "title": "kashilo.com Classificados anônimos",
"description": "Compre e venda sem conta, sem email. Pagamento com Monero. Chat criptografado ponta a ponta." "description": "Compre e venda sem conta, sem email. Pagamento com Monero. Chat criptografado ponta a ponta."
}, },
"header": { "header": {
@@ -19,7 +19,7 @@
"imprint": "Aviso legal" "imprint": "Aviso legal"
}, },
"home": { "home": {
"title": "Bem-vindo ao dgray.io", "title": "Bem-vindo ao kashilo.com",
"subtitle": "Encontre boas ofertas perto de você ou venda o que não precisa mais.", "subtitle": "Encontre boas ofertas perto de você ou venda o que não precisa mais.",
"browseListings": "Ver Anúncios", "browseListings": "Ver Anúncios",
"createListing": "Criar Anúncio", "createListing": "Criar Anúncio",

View File

@@ -1,6 +1,6 @@
{ {
"meta": { "meta": {
"title": "dgray.io Анонимные объявления", "title": "kashilo.com Анонимные объявления",
"description": "Покупайте и продавайте без аккаунта, без email. Оплата Monero. Сквозное шифрование чата." "description": "Покупайте и продавайте без аккаунта, без email. Оплата Monero. Сквозное шифрование чата."
}, },
"header": { "header": {
@@ -19,7 +19,7 @@
"imprint": "Правовая информация" "imprint": "Правовая информация"
}, },
"home": { "home": {
"title": "Добро пожаловать на dgray.io", "title": "Добро пожаловать на kashilo.com",
"subtitle": "Находите выгодные предложения рядом или продавайте ненужное.", "subtitle": "Находите выгодные предложения рядом или продавайте ненужное.",
"browseListings": "Смотреть объявления", "browseListings": "Смотреть объявления",
"createListing": "Создать объявление", "createListing": "Создать объявление",

View File

@@ -1,6 +1,6 @@
{ {
"name": "dgray.io - marketplace", "name": "kashilo.com - marketplace",
"short_name": "dgray.io", "short_name": "kashilo.com",
"description": "Anonymous marketplace with Monero payment", "description": "Anonymous marketplace with Monero payment",
"start_url": "/", "start_url": "/",
"display": "standalone", "display": "standalone",

View File

@@ -1,4 +1,4 @@
const CACHE_NAME = 'dgray-v51'; const CACHE_NAME = 'kashilo-v51';
const STATIC_ASSETS = [ const STATIC_ASSETS = [
'/', '/',
'/index.html', '/index.html',
@@ -108,7 +108,7 @@ self.addEventListener('fetch', (event) => {
if (request.method !== 'GET') return; if (request.method !== 'GET') return;
// API calls: Network First (external API domain) // API calls: Network First (external API domain)
if (url.hostname === 'api.dgray.io') { if (url.hostname === 'api.kashilo.com') {
event.respondWith(networkFirst(request)); event.respondWith(networkFirst(request));
return; return;
} }

View File

@@ -136,8 +136,8 @@ describe('DirectusClient token management', () => {
// Cleanup // Cleanup
client.clearTokens() client.clearTokens()
sessionStorage.removeItem('dgray_auth') sessionStorage.removeItem('kashilo_auth')
localStorage.removeItem('dgray_auth') localStorage.removeItem('kashilo_auth')
}) })
test('isAuthenticated returns true with valid token', () => { test('isAuthenticated returns true with valid token', () => {
@@ -145,8 +145,8 @@ describe('DirectusClient token management', () => {
assertTrue(client.isAuthenticated()) assertTrue(client.isAuthenticated())
client.clearTokens() client.clearTokens()
sessionStorage.removeItem('dgray_auth') sessionStorage.removeItem('kashilo_auth')
localStorage.removeItem('dgray_auth') localStorage.removeItem('kashilo_auth')
}) })
test('isAuthenticated returns false when token expired', () => { test('isAuthenticated returns false when token expired', () => {
@@ -163,22 +163,22 @@ describe('DirectusClient token management', () => {
refreshToken: 'stored-refresh', refreshToken: 'stored-refresh',
expiry: Date.now() + 600000 expiry: Date.now() + 600000
}) })
sessionStorage.setItem('dgray_auth', authData) sessionStorage.setItem('kashilo_auth', authData)
client.loadTokens() client.loadTokens()
assertEquals(client.accessToken, 'stored-access') assertEquals(client.accessToken, 'stored-access')
assertEquals(client.refreshToken, 'stored-refresh') assertEquals(client.refreshToken, 'stored-refresh')
client.clearTokens() client.clearTokens()
sessionStorage.removeItem('dgray_auth') sessionStorage.removeItem('kashilo_auth')
}) })
test('loadTokens clears on invalid JSON', () => { test('loadTokens clears on invalid JSON', () => {
sessionStorage.setItem('dgray_auth', 'not-json') sessionStorage.setItem('kashilo_auth', 'not-json')
client.loadTokens() client.loadTokens()
assertEquals(client.accessToken, null) assertEquals(client.accessToken, null)
sessionStorage.removeItem('dgray_auth') sessionStorage.removeItem('kashilo_auth')
}) })
}) })

View File

@@ -26,8 +26,8 @@ describe('CryptoService', () => {
}) })
asyncTest('unlock generates keypair and stores encrypted', async () => { asyncTest('unlock generates keypair and stores encrypted', async () => {
localStorage.removeItem('dgray_keypair') localStorage.removeItem('kashilo_keypair')
localStorage.removeItem('dgray_keypair_salt') localStorage.removeItem('kashilo_keypair_salt')
await cryptoService.unlock(TEST_UUID) await cryptoService.unlock(TEST_UUID)
@@ -35,7 +35,7 @@ describe('CryptoService', () => {
assertTrue(cryptoService.getPublicKey() !== null) assertTrue(cryptoService.getPublicKey() !== null)
assertTrue(cryptoService.getPublicKey().length > 10) assertTrue(cryptoService.getPublicKey().length > 10)
const stored = JSON.parse(localStorage.getItem('dgray_keypair')) const stored = JSON.parse(localStorage.getItem('kashilo_keypair'))
assertTrue(stored.ct !== undefined, 'stored keypair should be encrypted (has ct)') assertTrue(stored.ct !== undefined, 'stored keypair should be encrypted (has ct)')
assertTrue(stored.iv !== undefined, 'stored keypair should be encrypted (has iv)') assertTrue(stored.iv !== undefined, 'stored keypair should be encrypted (has iv)')
assertFalse(stored.publicKey !== undefined, 'stored keypair should NOT have plaintext publicKey') assertFalse(stored.publicKey !== undefined, 'stored keypair should NOT have plaintext publicKey')
@@ -77,7 +77,7 @@ describe('CryptoService', () => {
cryptoService.lock() cryptoService.lock()
assertEquals(cryptoService.keyPair, null) assertEquals(cryptoService.keyPair, null)
assertTrue(localStorage.getItem('dgray_keypair') !== null) assertTrue(localStorage.getItem('kashilo_keypair') !== null)
}) })
asyncTest('destroyKeyPair clears memory and storage', async () => { asyncTest('destroyKeyPair clears memory and storage', async () => {
@@ -85,8 +85,8 @@ describe('CryptoService', () => {
cryptoService.destroyKeyPair() cryptoService.destroyKeyPair()
assertEquals(cryptoService.keyPair, null) assertEquals(cryptoService.keyPair, null)
assertEquals(localStorage.getItem('dgray_keypair'), null) assertEquals(localStorage.getItem('kashilo_keypair'), null)
assertEquals(localStorage.getItem('dgray_keypair_salt'), null) assertEquals(localStorage.getItem('kashilo_keypair_salt'), null)
}) })
asyncTest('migrates plaintext keypair to encrypted on unlock', async () => { asyncTest('migrates plaintext keypair to encrypted on unlock', async () => {
@@ -96,14 +96,14 @@ describe('CryptoService', () => {
publicKey: cryptoService.naclUtil.encodeBase64(kp.publicKey), publicKey: cryptoService.naclUtil.encodeBase64(kp.publicKey),
secretKey: cryptoService.naclUtil.encodeBase64(kp.secretKey) secretKey: cryptoService.naclUtil.encodeBase64(kp.secretKey)
} }
localStorage.setItem('dgray_keypair', JSON.stringify(plaintext)) localStorage.setItem('kashilo_keypair', JSON.stringify(plaintext))
localStorage.removeItem('dgray_keypair_salt') localStorage.removeItem('kashilo_keypair_salt')
await cryptoService.unlock(TEST_UUID) await cryptoService.unlock(TEST_UUID)
assertEquals(cryptoService.getPublicKey(), plaintext.publicKey) assertEquals(cryptoService.getPublicKey(), plaintext.publicKey)
const stored = JSON.parse(localStorage.getItem('dgray_keypair')) const stored = JSON.parse(localStorage.getItem('kashilo_keypair'))
assertTrue(stored.ct !== undefined, 'should be migrated to encrypted format') assertTrue(stored.ct !== undefined, 'should be migrated to encrypted format')
assertTrue(stored.iv !== undefined) assertTrue(stored.iv !== undefined)
}) })
@@ -255,7 +255,7 @@ describe('ConversationsService.hashPublicKey', () => {
describe('Per-listing keypair management', () => { describe('Per-listing keypair management', () => {
asyncTest('generateListingKeyPair creates and stores keypair', async () => { asyncTest('generateListingKeyPair creates and stores keypair', async () => {
localStorage.removeItem('dgray_listing_keys') localStorage.removeItem('kashilo_listing_keys')
await cryptoService.unlock(TEST_UUID) await cryptoService.unlock(TEST_UUID)
const publicKey = await cryptoService.generateListingKeyPair('test-listing-1') const publicKey = await cryptoService.generateListingKeyPair('test-listing-1')
@@ -326,7 +326,7 @@ describe('Per-listing keypair management', () => {
await cryptoService.generateListingKeyPair('temp-listing') await cryptoService.generateListingKeyPair('temp-listing')
cryptoService.destroyKeyPair() cryptoService.destroyKeyPair()
assertEquals(localStorage.getItem('dgray_listing_keys'), null) assertEquals(localStorage.getItem('kashilo_listing_keys'), null)
}) })
}) })

View File

@@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>dgray.io - Tests</title> <title>kashilo.com - Tests</title>
<style> <style>
:root { :root {
--color-bg: #1a1a1a; --color-bg: #1a1a1a;
@@ -88,7 +88,7 @@
</head> </head>
<body> <body>
<div id="results"> <div id="results">
<h1>🧪 dgray.io Test Suite</h1> <h1>🧪 kashilo.com Test Suite</h1>
<p class="loading">Running tests...</p> <p class="loading">Running tests...</p>
</div> </div>
@@ -118,7 +118,7 @@
renderResults(container) renderResults(container)
} catch (error) { } catch (error) {
container.innerHTML = ` container.innerHTML = `
<h1>🧪 dgray.io Test Suite</h1> <h1>🧪 kashilo.com Test Suite</h1>
<p style="color: var(--color-failed)"> <p style="color: var(--color-failed)">
Error loading tests: ${error.message} Error loading tests: ${error.message}
</p> </p>

View File

@@ -82,7 +82,7 @@ describe('getFileUrl', () => {
test('builds basic URL without options', () => { test('builds basic URL without options', () => {
const url = getFileUrl('abc-123') const url = getFileUrl('abc-123')
assertEquals(url, 'https://api.dgray.io/assets/abc-123') assertEquals(url, 'https://api.kashilo.com/assets/abc-123')
}) })
test('appends width parameter', () => { test('appends width parameter', () => {