Relay Extension + Python Receiver — Documentație Completă

RO Real-time capture + dispatch system for Amazon Relay loadboard.
Data: 2026-05-25 · Components: Chrome MV3 extension + Python receiver (FastAPI + WebSocket)

📑 Table of contents

  1. Overall architecture
  2. Chrome MV3 Extension — files, capture/polling/book flow, popup
  3. Python Receiver — endpoints, env vars, safety
  4. What Amazon can detect — honest analysis, comparison
  5. End-to-end configuration
  6. Pros / Cons by design
  7. Operations — deploy, debug, monitoring
  8. Known limitations
  9. Glossary
  10. Backups

1. Overall architecture

┌─────────────────────────────────────────────────────────────────┐ │ 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. Chrome MV3 Extension

2.1 File structure

FileWorldRole
manifest.jsonExtension declarations (perms, content_scripts, SW)
background.jsService WorkerPersistent WS connection, polling scheduler, message router
content_script.jsISOLATEDBridge between page world and service worker (chrome.* API access)
injected.jsPAGEMonkey-patch fetch/XHR, capture body+headers, UI inject, book click
popup.html + popup.jspopupUser settings (rate, jitter, target host, pull mode, etc)
book_unlock.jsonfilesystemSafety gate: if missing/false → all book attempts blocked
assets/audio/*.mp3Sounds for new/price/booked alerts

2.2 Capture flow (passive)

  1. Amazon page JS calls fetch('/api/loadboard/search', {...})
  2. injected.js window.fetch override intercepts the call:
    • Pre-cache: clone request → saves body + headers (including x-csrf-token)
    • Then await origFetch.apply(this, args) → real request goes to Amazon
    • Measures fetch_ms = performance.now() - t0 (locally, no telemetry)
    • Clone response → send()
  3. send() dispatches window.postMessage({__relay_capture: true, body, ts, fetch_ms, ...}, "*")
  4. content_script.js listens to window.message → forwards to background via chrome.runtime.sendMessage
  5. background.js sends payload via WebSocket to receiver (or queues if WS offline)

2.3 Polling flow (active)

  1. background.js timer (rate configurable from popup) sends trigger_active_fetch to each eligible tab
  2. content_script.js forwards via postMessage({__relay_active: true, pull_via_ui_click, ...})
  3. injected.js handler:
    • DEFAULT pull_via_ui_click=true: finds the Refresh button via #utility-bar .refresh-and-chat-box button + SVG path filter M20.128 2, then SIMPLE btn.click() (NOT MouseEvent dispatchEvent) — Amazon React responds correctly. NO fallback to direct fetch.
    • pull_via_ui_click=false: direct doFetch to the cached URL
  4. Response arrives via passive flow (mock/Amazon makes fetch in response to click → normal capture)

2.4 Book flow (DOM click style)

All 10 gates must pass, otherwise book is blocked:

  1. book_unlock.json exists + enabled: true
  2. Popup toggle "Book ENABLED" = ON
  3. BOOK_HARDCODED_CONFIG.enabled = true (în code)
  4. Tour ID în lock (__relay_book_in_progress)
  5. Card exists in DOM (verified at click time)
  6. URL fetch verify (URL contains EXACT tour_id from lock)
  7. dom_click_mode = true
  8. fetch_url_verify = true
  9. LEVEL 4 global fetch block: any /api/loadboard/{tid}/{v}/option/{oid}/majorVersion/{mv} URL not passing gates → fake response with errorCode: work_opportunity_not_available (native Amazon code, no suspicious custom string)
  10. LEVEL 4 global XHR block: same for XMLHttpRequest

2.5 Popup configuration

SettingDefaultEffect
enabledtrueMaster toggle — OFF = nothing sent to receiver
active_modefalseON = automatic polling at poll_rate
poll_rate2.0sBase interval between polls
poll_jitter0± random for anti-bot rate signature
pull_via_ui_clicktrueClick on Refresh button (native telemetry) — NO fetch fallback
poll_only_when_activefalsePolls only active tab + focused window
target_hostautoTab filter (relay.amazon.es/de/it/fr/uk/com, relay-api.myvio.eu)
override_result_size0Force resultSize in body (0 = leave natural)
auto_paginatetrueAggregate pages via nextItemToken up to 20 pages
alerts_enabledfalseMove-to-top + CSS highlight on new tours
alerts_audio_ontrueSounds (new.mp3, price.mp3, successbook.mp3)
simulation_modefalseDry-run book: highlight 15s + sound, NO real click
fast_book_button_enabledfalseInjects "🚀 Fast Book" on each card
book_user_enabledfalseToggle in popup for authorized book

3. Python Receiver

3.1 File structure

FileRole
receiver.pyMain FastAPI app — WS + HTTP endpoints
config.pyEnv vars + .env loader (no external deps)
book_authorization.pyGate file for live book (lazy import)
book_unlock.jsonUnlock status per receiver (separate from extension)
reports_db.pyLocal SQLite for book history, snapshots, event log
captures/*.jsonOptional disk dump (if SAVE_RAW_ENABLED=true)

3.2 HTTP endpoints

EndpointReturnsUsed for
GET /HTML dashboardQuick view in browser
GET /admin/statstenant, target, pool_size, ws_clients, ext_clients, uptime, counters, memory (RSS, ring bytes)Stats overview
GET /admin/insightsrecent_polls (last 30), avg_latency_ms, avg_latency_out_ms, recent_latency_out_ms, polls_per_minute, consecutive_fails, fail_auto_stop_threshold, latest_poolUI charts
GET /admin/raw-ring/listMetadata for all ring items (no bodies)UI Raw tab list
GET /admin/raw-ring/zip?limit=NZIP cu ultimele N JSON-uri + _manifest.jsonBulk export download
GET /admin/raw-ring/{idx}Full body for one itemPreview UI
GET /admin/last-responseLast captured responseQuick inspection
GET /admin/reports/book-historyDB SQLite — book history
GET /admin/reports/snapshotsDB SQLite — hourly snapshots
GET /admin/reports/eventsDB SQLite — event log (info/warn/error)

3.3 WebSocket endpoints

Path: WS /ws/stream — Multiplex: ext (push captures) sau dispatcher (recv broadcasts) după primul mesaj identify

Messages ext → receiver

Messages receiver → dispatcher

Messages dispatcher → receiver

3.4 Environment variables (config.py)

Env VarDefaultEffect
RECEIVER_PORT9001HTTP+WS port
RECEIVER_HOST0.0.0.0Bind address
GRACE_PERIOD_S1.5How long an unseen tour stays in POOL before TTL cleanup
POOL_MAX_SIZE0LRU cap on POOL (0 = unlimited)
EXT_MAX_MSG_BYTES10MBWS message limit from extension
BOOK_UNLOCK_FILE./book_unlock.jsonPath to gate file
BOOK_AUDIT_ENABLEDtrueLog book attempts in SQLite
SAVE_RAW_ENABLEDfalseDisk dump captures in ./captures/
SAVE_RAW_DIR./capturesFolder for dumps
SAVE_RAW_MAX_FILES5000FIFO rotation
RAW_RING_SIZE50Ring buffer of raw JSONs in RAM (1000 = ~140MB)
FAIL_AUTO_STOP_THRESHOLD0Auto-stop receiver after N consecutive fails (0 = OFF)

3.5 Safety: book_unlock.json

{
  "enabled": false,
  "token": "",
  "note": "Set enabled: true + reload to allow book attempts"
}

If the file is missing OR enabled !== true → all book paths blocked (gate redundant with extension one). Optional token is extra proof.


4. What Amazon can detect — analiză honestă

4.1 What Amazon SEES (their telemetry)

DetectableHowMitigation
Cererile HTTP la /api/loadboard/searchStandard server-side loggingUnavoidable — user makes the request via UI anyway. Rate matters: 0.5s × 5 tabs = ~10 req/sec empirically validated on B_PLUS
Headers normale (cookie, csrf, UA)Server logPass-through, unchanged by extension
Click events native pe butoane (Reservar, Confirm, Refresh)Pendo, csa.ContentInteraction, optimusTRUSTED click events emitted by browser when user/extension does btn.click() — telemetry receives the event. This is GOOD for us: shows normal user activity.
window.fetch wrappedTheoretically detectable via fetch.toString()In practice, production anti-bot does not. Real-world deployments confirm this is safe.
Pagina rămâne deschisă mult timpTime-on-page metricsPolling only on active + focused tab (poll_only_when_active) reduces signature

4.2 What Amazon DOES NOT SEE

InvisibleWhy
Mesajele postMessage interne (__relay_capture: true)Only if they have window.addEventListener('message', ...) with exact filtering on flag — they do not in production
performance.now() măsurători locallyeStdlib function, generates no network
Body-ul captat de extensieStays on device + WebSocket to your LAN
WebSocket-ul către receiver (ws://192.168.0.31:9001/)Private LAN, inaccessible from amazon.com origin
Modificările DOM (move-to-top, CSS classes adăugate)Only if they have active MutationObserver — in production they do not observe the entire DOM
Click-uri programatice btn.click() vs trusted clicksNative event listeners fire identically. Only if React onClick checks event.isTrusted (rare) would it differ. Empirically verified that btn.click() works on real Amazon.
Fast Book button injectatExtra element in DOM, but no API calls reporting "extension detected"

4.3 Comparison with empirical real-world testing

The audit at /var/www/booker-admin/audit/kcnegnbacaohnhpmeckfakhlnahliklf/8.91_0/ confirms:

Conclusion: our extension's risk profile = same as a real user clicking the UI. The only significant "tell" would be aggressive polling RATE (over 2-3 req/sec sustained per account).

4.4 Bot signatures we avoid


5. End-to-end configuration

5.1 Extension setup

  1. Load unpacked from chrome://extensions → "Load unpacked" → select folder chrome-extension
  2. book_unlock.json next to manifest:
    {"enabled": true, "token": "ceva-secret"}
    Restart the extension (chrome://extensions → 🔄) after modification.
  3. Popup: set Receiver URL = ws://192.168.0.31:9001/ws/stream
  4. Check Mod activ + Pull via UI click (default ON)
  5. Save

5.2 Receiver setup

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

On Windows:

5.3 Booker-admin UI setup

INSERT INTO relay_ws_sources (user_id, name, url, active)
VALUES (1, 'LocalReceiver', 'ws://192.168.0.31:9001/ws/stream', 1);

Then on /relay_ws_sources → click stats icon → modal with:


6. Pros / Cons by design

✅ Pros

AspectDetail
PAGE/ISOLATED/SW decouplingStrictly follows the MV3 model, does not access chrome.* from PAGE world
Cross-tab orchestrationBackground SW has global view — book tab selection based on who has the tour in DOM
CSRF auto-captureCached from real fetch, never hardcoded
Multi-host supporttarget_host: auto covers es/de/it/fr/uk/com + myvio.eu mock
Pull via UI click defaultNatural telemetry, indistinguishable from real user
10-gate safety chain for bookDefense in depth — book_unlock.json + popup + hardcoded + DOM verify + URL verify + LEVEL 4 fetch/XHR block
Receiver = single source of truthMultiple dispatchers can consume simultaneously (UI, agent, etc)
Reports DBLocal SQLite for retroactive audit (book history, snapshots, events)
Raw ring in RAM + ZIP exportLive debug without disk overhead, downloadable anytime
Auto-stop on consecutive failsCircuit breaker when Amazon blocks us
Multi-platform memory monitoringLinux /proc, Windows ctypes, macOS resource — no dependencies

⚠️ Cons / Trade-offs

AspectWhy it's a trade-offMitigation
MV3 service worker can sleepChrome stops SW after 30s idlechrome.alarms keepalive at 20s + rescheduleActiveFetch() on each wake
Fetch wrapper detectable via fetch.toString()Anti-bot can theoretically checkIn practice does not happen (empirically confirmed)
No fallback to direct fetch when pull_via_ui_click ONIf click fails, refresh does not happenFail loud — log to console, user sees immediately. Trade-off: zero risk of bot signature from fetch without telemetry
Receiver is single point of failureIf it crashes → all dispatchers blindQueue in background.js (200 messages) + reconnect with backoff
RAW_RING is volatileLost on restartEnable SAVE_RAW_ENABLED for disk persistence + FIFO rotation
CSRF expires on mock container restartVALID_SESSIONS in memoryF5 page for new session; for production persistence, would need SQLite/Redis
Fixed polling rate per receiverAll tabs share the same ratePer-tab rate not currently supported
Mixed content blockingHTTPS browser cannot fetch HTTP receiverServer-side PHP proxy (already implemented in relay_ws_sources_api.php)
WebSocket scalingOne receiver = one Python processFor multi-tenant scaling requires sticky session / sharding
No backpressureIf receiver is overloaded, ext keeps sendingAcceptable for 1-5 ext clients; for 100+ would need rate limiting

🔍 Compared to alternatives

StrategyProCon
Direct fetch from extension (no UI click)Faster (~50ms)Bot signature: no Pendo telemetry, missing csa.ContentInteraction events
Pull via UI click (CURRENT)Natural telemetry, indistinguishable from user~100-200ms overhead per refresh (extra round-trip through React lifecycle)
External headless puppeteerCross-browser, scriptableDetectable via navigator.webdriver, headless flag
MITM proxy (mitmproxy / Burp)Full request/response accessManually configured, not portable

7. Operations

7.1 Deploy

Extension (Chrome dev mode)

  1. Modify files locallyly
  2. chrome://extensions → 🔄 Reload "Relay Tour Catcher"
  3. F5 on open tabs (content scripts do not re-inject on extension reload!)

Receiver (Windows 192.168.0.31)

# Stop current
# (taskkill /F /IM python.exe or Ctrl+C in console)
# Copy new files
scp receiver.py user@192.168.0.31:/path/to/app/
# Restart
python receiver.py

Booker-admin UI

7.2 Debug

IssueCheck
Extension not sending to receiverService worker logs (chrome://extensions → Relay → "service worker")
Polling does not startIn popup: Mod activ ON? Save pressed? pull_via_ui_click ON?
"Receiver process —"Deploy latest receiver.py (ctypes Windows fallback)
"stale" in healthLAST_POLL_AT updates only on status=200; check consecutive_fails
Mixed content errorsHTTPS browser cannot http://192.168.0.31. Use PHP proxy (q=stats&id=X)
Corrupt ZIPCheck PHP ob_end_clean() + headers Content-Encoding: identity
422 pe /admin/raw-ring/zipFastAPI route order — /zip must be BEFORE /{idx}

7.3 Monitoring


8. Known limitations

  1. Auto-paginate max 20 pages — if user requests resultSize=50 and sunt 2000 tururi, se opresc la 1000 (20 × 50)
  2. POLL_HISTORY in-memory — lost on receiver restart
  3. RAW_RING in-memory — same
  4. SQLite Reports DB — single-writer; for concurrent receivers requires PostgreSQL
  5. Single book click fallback — DOM click; direct fetch is disabled by design (risky)
  6. CSRF generated dynamically at mock — token expires on container restart

9. Glossary


10. Backups

All major changes have backups in /var/www/booker-admin/backups/:

For full rollback: copy from backup in place.


Document auto-generated based on code in /var/www/booker-admin/relay-extension/.
Vezi and varianta Markdown: DOCS.md