Relay Extension + Python Receiver — Documentație Completă
1. Arhitectura generală
┌─────────────────────────────────────────────────────────────────┐
│ BROWSER │
│ │
│ Tab Amazon Relay (relay.amazon.com / mock relay-api.myvio.eu) │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────┐ │
│ │ PAGE world │ │ ISOLATED world │ │ SERVICE │ │
│ │ │ │ │ │ WORKER │ │
│ │ injected.js │←→│ content_script │←→│ background │ │
│ │ (fetch/XHR │ │ (bridge IPC) │ │ .js (WS conn │ │
│ │ monkey-patch, │ │ │ │ + polling) │ │
│ │ UI inject, │ │ │ │ │ │
│ │ book click) │ │ │ │ │ │
│ └──────────────────┘ └──────────────────┘ └──────┬───────┘ │
└──────────────────────────────────────────────────────┼──────────┘
│ WebSocket
↓
┌─────────────────────────────────────────────────────────────────┐
│ PYTHON RECEIVER (FastAPI) │
│ HOST: 192.168.0.31:9001 (typical) │
│ │
│ ws://...:9001/ws/stream │
│ ├─ EXT_CLIENTS (extensii care PUSH-uiesc capturile) │
│ └─ DISPATCHERS (pagini browser care RECV broadcasts) │
│ │
│ HTTP /admin/* (stats, insights, raw-ring, reports, ...) │
│ │
│ Memorie: │
│ POOL{} — tururi active (deduplicated) │
│ LAST_SEEN{} — TTL per tour (grace period cleanup) │
│ POLL_HISTORY — last 200 captures metadata │
│ LATENCIES — last 100 ext→recv ms │
│ LATENCIES_OUT — last 100 recv→UI ms (browser reports) │
│ RAW_RING — last N raw JSON bodies (configurable) │
│ reports_db (SQLite) — book history, snapshots, events │
└──────────────────────┬──────────────────────────────────────────┘
│ WS broadcast
↓
┌─────────────────────────────────────┐
│ BOOKER-ADMIN UI (relay_tours, │
│ relay_ws_sources, mock_tuning) │
│ Conexiuni multiple dispatchers │
└─────────────────────────────────────┘
2. Extensie Chrome MV3
2.1 Structura fișierelor
| Fișier | World | Rol |
manifest.json | — | Declarații extensiei (perms, content_scripts, SW) |
background.js | Service Worker | WS connection persistent, polling scheduler, message router |
content_script.js | ISOLATED | Bridge între page world și service worker (acces chrome.* API) |
injected.js | PAGE | Monkey-patch fetch/XHR, capture body+headers, UI inject, book click |
popup.html + popup.js | popup | Setări utilizator (rate, jitter, target host, pull mode, etc) |
book_unlock.json | filesystem | Safety gate: dacă lipsește/false → toate book-urile blocate |
assets/audio/*.mp3 | — | Sunete pentru new/price/booked alerts |
2.2 Flow capture (passive)
- Pagina Amazon JS face
fetch('/api/loadboard/search', {...})
- injected.js window.fetch override prinde apelul:
- Pre-cache: clone request → salvează body + headers (incluzând
x-csrf-token)
- Apoi
await origFetch.apply(this, args) → cererea reală merge la Amazon
- Măsoară
fetch_ms = performance.now() - t0 (local, fără telemetrie)
- Clone response →
send()
- send() trimite
window.postMessage({__relay_capture: true, body, ts, fetch_ms, ...}, "*")
- content_script.js ascultă
window.message → forward la background via chrome.runtime.sendMessage
- background.js trimite payload prin WebSocket la receiver (sau cozează dacă WS offline)
2.3 Flow polling (active)
- background.js timer (rate configurabil din popup) trimite
trigger_active_fetch la fiecare tab eligibil
- content_script.js forward via
postMessage({__relay_active: true, pull_via_ui_click, ...})
- injected.js handler:
- DEFAULT
pull_via_ui_click=true (Rocket Relay style): găsește butonul Refresh prin #utility-bar .refresh-and-chat-box button + SVG path filter M20.128 2, apoi btn.click() SIMPLU (NU MouseEvent dispatchEvent) — React Amazon răspunde corect. NU există fallback la fetch direct.
pull_via_ui_click=false: doFetch direct la URL-ul cached
- Răspunsul revine via flow passive (mock/Amazon face fetch ca răspuns la click → capture obișnuit)
2.4 Flow book (DOM click style)
Toate cele 10 gates trebuie să treacă, altfel book-ul e blocat:
book_unlock.json exists + enabled: true
- Popup toggle "Book ENABLED" = ON
BOOK_HARDCODED_CONFIG.enabled = true (în code)
- Tour ID în lock (
__relay_book_in_progress)
- Card există în DOM (verificat la click time)
- URL fetch verify (URL conține EXACT tour_id-ul din lock)
dom_click_mode = true
fetch_url_verify = true
- LEVEL 4 fetch global block: orice
/api/loadboard/{tid}/{v}/option/{oid}/majorVersion/{mv} URL care nu trece gate-urile → fake response cu errorCode: work_opportunity_not_available (cod nativ Amazon, fără string custom suspect)
- LEVEL 4 XHR global block: idem pentru XMLHttpRequest
2.5 Configurare popup
| Setting | Default | Effect |
enabled | true | Master toggle — OFF = nimic nu se trimite la receiver |
active_mode | false | ON = polling automat la poll_rate |
poll_rate | 2.0s | Interval bază între polls |
poll_jitter | 0 | ± random pentru anti-bot rate signature |
pull_via_ui_click | true | Click pe butonul Refresh (telemetrie nativă) — NU există fallback fetch |
poll_only_when_active | false | Polluiază doar tab activ + fereastra focusată |
target_host | auto | Filtru tab-uri (relay.amazon.es/de/it/fr/uk/com, relay-api.myvio.eu) |
override_result_size | 0 | Force resultSize în body (0 = lasă natural) |
auto_paginate | true | Aggregate pages prin nextItemToken până la 20 pagini |
alerts_enabled | false | Move-to-top + highlight CSS pe tururi noi |
alerts_audio_on | true | Sunete (new.mp3, price.mp3, successbook.mp3) |
simulation_mode | false | Dry-run book: highlight 15s + sunet, NU click real |
fast_book_button_enabled | false | Injectează buton "🚀 Fast Book" pe fiecare card |
book_user_enabled | false | Toggle în popup pentru book autorizat |
3. Python Receiver
3.1 Structura fișierelor
| Fișier | Rol |
receiver.py | App FastAPI principală — WS + HTTP endpoints |
config.py | Env vars + .env loader (no external deps) |
book_authorization.py | Gate file pentru book live (lazy import) |
book_unlock.json | Status unlock per receiver (separate de extension) |
reports_db.py | SQLite local pentru book history, snapshots, event log |
captures/*.json | Optional disk dump (dacă SAVE_RAW_ENABLED=true) |
3.2 Endpoints HTTP
| Endpoint | Returns | Folosit pentru |
GET / | HTML dashboard | Quick view în browser |
GET /admin/stats | tenant, target, pool_size, ws_clients, ext_clients, uptime, counters, memory (RSS, ring bytes) | Stats overview |
GET /admin/insights | recent_polls (last 30), avg_latency_ms, avg_latency_out_ms, recent_latency_out_ms, polls_per_minute, consecutive_fails, fail_auto_stop_threshold, latest_pool | UI charts |
GET /admin/raw-ring/list | Metadata pentru toate item-urile din ring (fara body-uri) | Lista UI tab Raw |
GET /admin/raw-ring/zip?limit=N | ZIP cu ultimele N JSON-uri + _manifest.json | Download bulk export |
GET /admin/raw-ring/{idx} | Body complet pt un item | Preview UI |
GET /admin/last-response | Ultimul response captat | Quick inspection |
GET /admin/reports/book-history | DB SQLite — istoricul book-urilor | |
GET /admin/reports/snapshots | DB SQLite — snapshots orare | |
GET /admin/reports/events | DB SQLite — event log (info/warn/error) | |
3.3 Endpoints WebSocket
Path: WS /ws/stream — Multiplex: ext (push captures) sau dispatcher (recv broadcasts) după primul mesaj identify
Mesaje ext → receiver
loadboard_response — capture complet (url, status, body, ts, fetch_ms, source)
ext_status — status raportat de extensie (poll rate, active flag, errors)
book_result — rezultat book pentru audit
Mesaje receiver → dispatcher
add / update — tour nou sau version bump
gone — tour expirat (TTL grace period)
ext_status — broadcast la dispatcheri (UI)
Mesaje dispatcher → receiver
identify — declarare rol
latency_report — UI raportează latency Date.now() - msg.__server_ts
ext_command — control remote (pause/resume polling, change rate)
3.4 Variabile de mediu (config.py)
| Env Var | Default | Effect |
RECEIVER_PORT | 9001 | Port HTTP+WS |
RECEIVER_HOST | 0.0.0.0 | Bind addr |
GRACE_PERIOD_S | 1.5 | Cât timp un tur nevăzut rămâne în POOL înainte de TTL cleanup |
POOL_MAX_SIZE | 0 | LRU cap pe POOL (0 = nelimitat) |
EXT_MAX_MSG_BYTES | 10MB | Limită mesaj WS de la extensie |
BOOK_UNLOCK_FILE | ./book_unlock.json | Path la gate file |
BOOK_AUDIT_ENABLED | true | Log book attempts în SQLite |
SAVE_RAW_ENABLED | false | Disk dump capturi în ./captures/ |
SAVE_RAW_DIR | ./captures | Folder pentru dump |
SAVE_RAW_MAX_FILES | 5000 | FIFO rotation |
RAW_RING_SIZE | 50 | Ring buffer JSON-uri raw în RAM (1000 = ~140MB) |
FAIL_AUTO_STOP_THRESHOLD | 0 | Auto-stop receiver după N fails consecutive (0 = OFF) |
3.5 Safety: book_unlock.json
{
"enabled": false,
"token": "",
"note": "Schimba enabled: true + reload pentru a permite book-uri"
}
Dacă fișierul lipsește SAU enabled !== true → toate book paths blocate (gate redundant cu cel din extension). Token-ul opțional e dovadă suplimentară.
4. Ce poate detecta Amazon — analiză honestă
4.1 Ce VEDE Amazon (telemetria lor)
| Detectabil | Cum | Mitigare |
Cererile HTTP la /api/loadboard/search | Standard logging server-side | Inevitabil — userul oricum face cererea via UI. Rate matters: 0.5s × 5 tabs = ~10 req/sec validat empiric pe B_PLUS |
| Headers normale (cookie, csrf, UA) | Server log | Pass-through, neschimbate de extensie |
| Click events native pe butoane (Reservar, Confirm, Refresh) | Pendo, csa.ContentInteraction, optimus | TRUSTED click events emitate de browser când userul/extensia face btn.click() — telemetria primește event-ul. Asta e BUN pentru noi: arată activitate de user normal. |
window.fetch wrapped | Teoretic detectabil prin fetch.toString() | În practică, anti-bot prod nu face asta. Rocket Relay rulează ani de zile fără probleme. |
| Pagina rămâne deschisă mult timp | Time-on-page metrics | Polluare doar pe tab activ + focusat (poll_only_when_active) reduce signature |
4.2 Ce NU VEDE Amazon
| Invizibil | De ce |
Mesajele postMessage interne (__relay_capture: true) | Doar dacă au window.addEventListener('message', ...) cu filtrare exact pe flag — nu fac asta în prod |
performance.now() măsurători locale | Funcție stdlib, nu generează rețea |
| Body-ul captat de extensie | Rămâne pe device + WebSocket spre LAN-ul tău |
WebSocket-ul către receiver (ws://192.168.0.31:9001/) | LAN privat, neaccesibil din amazon.com origin |
| Modificările DOM (move-to-top, CSS classes adăugate) | Doar dacă au MutationObserver activ — în prod nu observă tot DOM-ul |
Click-uri programatice btn.click() vs trusted clicks | Native event listeners se trigerează identic. Doar dacă React onClick verifică event.isTrusted (rar) ar fi diferit. Rocket Relay confirmă că btn.click() merge pe Amazon real. |
| Fast Book button injectat | Element extra în DOM, dar fără chemări la API care să raporteze "extension detected" |
4.3 Comparație cu Rocket Relay (audit reverse-engineering)
Audit-ul din /var/www/booker-admin/audit/kcnegnbacaohnhpmeckfakhlnahliklf/8.91_0/ confirmă:
- RR folosește același pattern:
document.querySelector('.css-q7ppch')?.click() pentru refresh
- RR nu emite niciun event suplimentar către Amazon
- RR rulează din 2021 pe mii de useri fără ban-uri publice
Concluzie: profilul de risc al extensiei noastre = identic cu Rocket Relay. Singurul "tell" semnificativ ar fi RATE de polling agresiv (peste 2-3 req/sec sustained per account).
4.4 Bot signatures pe care le evităm
- ❌ Click cu coordonate
(0, 0) → folosim realisticClick() cu coords reale din getBoundingClientRect ± random jitter (pt book buttons)
- ❌ Polling robotic constant →
poll_jitter configurabil pentru a randomiza intervalul
- ❌ Polluare pe tab inactiv →
poll_only_when_active
- ❌ Cereri cu headers stripped → preservăm TOATE headers din cached request (cookie, csrf, accept-language, etc)
5. Configurare end-to-end
5.1 Setup extensie
- Load unpacked din
chrome://extensions → "Load unpacked" → selectează folder chrome-extension
book_unlock.json lângă manifest:
{"enabled": true, "token": "ceva-secret"}
Restartează extensia (chrome://extensions → 🔄) după modificare.
- Popup: setează
Receiver URL = ws://192.168.0.31:9001/ws/stream
- Bifează
Mod activ + Pull via UI click (default ON)
- Save
5.2 Setup receiver
cd python-receiver/app
python3 -m venv venv && source venv/bin/activate
pip install fastapi uvicorn # + opțional: psutil
# .env (lângă receiver.py)
RECEIVER_PORT=9001
RAW_RING_SIZE=1000
FAIL_AUTO_STOP_THRESHOLD=20
GRACE_PERIOD_S=1.5
# book_unlock.json
{"enabled": true, "token": ""}
# Start
python3 receiver.py
Pe Windows:
- Cross-platform RSS lookup via ctypes nu necesită psutil
- Folosește
pythonw.exe receiver.py pentru a rula în background fără console
5.3 Setup booker-admin UI
INSERT INTO relay_ws_sources (user_id, name, url, active)
VALUES (1, 'LocalReceiver', 'ws://192.168.0.31:9001/ws/stream', 1);
Apoi pe /relay_ws_sources → click pe stats icon → modal cu:
- 6 charts live (status pie, latency ext→recv, body size, polls/min, fetch ms, recv→UI ms)
- Tab "Last captures" + Tab "Raw JSONs in RAM"
- Buton "Download ZIP" pentru ultimele 50/100/500/1000
6. Pro / Contra de design
✅ Pro
| Aspect | Detaliu |
| Decuplare PAGE/ISOLATED/SW | Respectă strict modelul MV3, nu accesează chrome.* din PAGE world |
| Cross-tab orchestration | Background SW are vedere globală — alegere tab pentru book bazat pe cine are turul în DOM |
| CSRF auto-capture | Cached din fetch real, niciodată hardcoded |
| Multi-host support | target_host: auto acoperă es/de/it/fr/uk/com + mock myvio.eu |
| Pull via UI click default | Telemetrie naturală, indistinguibilă de user real |
| Safety chain 10-gates pentru book | Defense in depth — book_unlock.json + popup + hardcoded + DOM verify + URL verify + LEVEL 4 fetch/XHR block |
| Receiver = single source of truth | Mai mulți dispatcheri pot consuma simultan (UI, agent, etc) |
| Reports DB | SQLite local pentru audit retroactiv (book history, snapshots, events) |
| Raw ring în RAM + ZIP export | Debug live fără disk overhead, descărcabil oricând |
| Auto-stop pe fails consecutive | Circuit breaker când Amazon ne blochează |
| Memory monitoring multi-platform | Linux /proc, Windows ctypes, macOS resource — fără dependinte |
⚠️ Contra / Compromisuri
| Aspect | De ce e un trade-off | Mitigare |
| MV3 service worker poate adormi | Chrome oprește SW după 30s idle | chrome.alarms keepalive la 20s + rescheduleActiveFetch() la fiecare wake |
Fetch wrapper detectabil prin fetch.toString() | Anti-bot teoretic poate verifica | În practică, nu se întâmplă (Rocket Relay confirmă) |
| Niciun fallback la fetch direct când pull_via_ui_click ON | Dacă click eșuează, refresh nu se face | Fail loud — log în console, user vede imediat. Trade-off: zero risk de bot signature din fetch fără telemetrie |
| Receiver e single point of failure | Dacă cade → toți dispatcherii orbi | Queue în background.js (200 mesaje) + reconnect cu backoff |
| RAW_RING e volatil | Pierdut la restart | Activează SAVE_RAW_ENABLED pentru persistență disc + rotation FIFO |
| CSRF expiră la restart container mock | VALID_SESSIONS în memorie | F5 pagina pentru sesiune nouă; pentru prod persistență, ar trebui SQLite/Redis |
| Polling rate fix per receiver | Toți tabs partajează aceeași rată | Nu suportăm rate per-tab momentan |
| Mixed content blocking | Browser HTTPS nu poate fetch HTTP receiver | Proxy PHP server-side (deja implementat în relay_ws_sources_api.php) |
| WebSocket scaling | Un receiver = un proces Python | Pentru multi-tenant scaling trebuie sticky session / sharding |
| No backpressure | Dacă receiver e overloaded, ext continuă să trimită | Acceptabil pentru 1-5 ext clients; pentru 100+ ar trebui rate limiting |
🔍 Comparativ cu alternative
| Strategie | Pro | Contra |
| Direct fetch din extensie (no UI click) | Mai rapid (~50ms) | Bot signature: no telemetry pe Pendo, lipsa csa.ContentInteraction events |
| Pull via UI click (CURRENT) | Telemetrie naturală, indistinguibil de user | ~100-200ms overhead per refresh (extra round-trip prin React lifecycle) |
| Headless puppeteer extern | Cross-browser, scriptable | Detectabil prin navigator.webdriver, headless flag |
| MITM proxy (mitmproxy / Burp) | Acces total request/response | Manual configurat, nu portabil |
7. Operațional
7.1 Deploy
Extension (Chrome dev mode)
- Modifică fișiere local
chrome://extensions → 🔄 Reload la "Relay Tour Catcher"
- F5 pe tab-urile deschise (content scripts nu re-injectează la reload extensie!)
Receiver (Windows 192.168.0.31)
# Stop curent
# (taskkill /F /IM python.exe sau Ctrl+C în consolă)
# Copy fișiere noi
scp receiver.py user@192.168.0.31:/path/to/app/
# Restart
python receiver.py
Booker-admin UI
- Edit
templates/relay_ws_sources/*.html și plib/relay/relay_ws_sources_api/*.php local
- Clear Smarty cache:
rm /var/www/booker-admin/templates_c/*relay_ws_sources*
- F5 pagină
7.2 Debug
| Problemă | Verificare |
| Extensia nu trimite la receiver | Service worker logs (chrome://extensions → Relay → "service worker") |
| Polling nu pornește | În popup: Mod activ ON? Save apăsat? pull_via_ui_click ON? |
| "Receiver process —" | Deploy ultima versiune receiver.py (ctypes Windows fallback) |
| "stale" în health | LAST_POLL_AT se update-ează doar la status=200; verifică consecutive_fails |
| Mixed content errors | Browser HTTPS nu poate http://192.168.0.31. Folosește PHP proxy (q=stats&id=X) |
| ZIP corupt | Verifică PHP ob_end_clean() + headers Content-Encoding: identity |
422 pe /admin/raw-ring/zip | Ordine rute FastAPI — /zip trebuie ÎNAINTE de /{idx} |
7.3 Monitoring
/admin/stats — pool size, ext_clients, uptime, RAM RSS
/admin/insights — recent polls, latency avg + time series, consecutive_fails
- Reports DB —
/admin/reports/events?level=error pentru issues istorice
- UI charts în modal stats — vizualizare live 1s refresh
8. Limitări cunoscute
- Auto-paginate max 20 pagini — dacă userul cere
resultSize=50 și sunt 2000 tururi, se opresc la 1000 (20 × 50)
- POLL_HISTORY in-memory — pierdut la restart receiver
- RAW_RING in-memory — idem
- Reports DB SQLite — single-writer; pentru concurrent receivers trebuie PostgreSQL
- Book click fallback unic — DOM click; fetch direct e disabled by design (riscant)
- CSRF generat dynamic la mock — token expiră la restart container
9. Glosar
- Tour / WorkOpportunity (WO) — un job de transport Amazon Relay
- Pool — set de tururi active în memoria receiver-ului
- Grace period — TTL după care un tur nevăzut e șters din pool
- Dispatcher — orice client WS care consumă broadcasts (UI, agent, etc)
- Capture — un response
/api/loadboard/search interceptat
- Passive — capture-ul vine de la fetch-ul propriu al paginii Amazon
- Active — capture-ul vine de la trigger-ul programat al extensiei
- Round complete — răspuns cu
nextItemToken=0 (ultima pagină dintr-o secvență)
- Fast Book — buton injectat de extensie pe fiecare card de tur pentru book one-click
- Simulation mode — dry-run: highlight + sunet, fără click real pe Reservar
10. Backup-uri
Toate modificările majore au backup în /var/www/booker-admin/backups/:
relay_stats_20260525_020909/ — înainte de adăugarea charts + raw ring + ZIP export
Pentru rollback complet: copy din backup în loc.