Skip to content

Architecture

bytetourist is four small Go services around a handful of stateful dependencies. The design rule: keep the data plane thin and direct, push everything else off the hot path.

Service Responsibility Not responsible for
core Auth, plan limits, rate limiting, matching, maintaining gRPC streams to nodes, returning responses Analytics storage, scaling
edge Execute the outbound HTTP call, return response + metadata Caching, retries, routing
scaler Ingest events, write ClickHouse, evaluate rules, provision/recycle nodes Request routing
server Dashboard API, Auth0, Postgres tenancy CRUD, billing rollups Request serving
  • etcd — node membership. Nodes self-register with a lease; core/scaler watch the membership prefix.
  • Redis — the broker: pub/sub and atomic in-flight rate-limit counters.
  • Kafka — the immutable request-events log (analytics/billing only — never on the routing path).
  • ClickHouse — request-event storage and aggregation.
  • Postgres — orgs, users, API keys, plans, edges.
Client ──HTTP proxy──▶ core
├─ authenticate (API key → org)
├─ clamp to plan (regions, ip_type, concurrency)
├─ rate-limit check (Redis INCR)
├─ match: filter fleet → strategy → one node
└─ send ProxyRequest over the node's gRPC stream
edge node
├─ execute HTTP to target
└─ stream ProxyResponse back (+ latency, bytes)
core ── deliver to the waiting request ── publish event to Kafka (async)
└─ decrement in-flight (Redis DECR) ── return response to client

The hot path is client → core → node → target, a direct gRPC bidi stream with no broker in the middle. Analytics is strictly off to the side.

Most nodes are reached over a persistent gRPC stream. Nodes that can’t be dialled into — sandboxes, mobile devices behind NAT — instead dial the core and register over a reverse-connect gateway, then receive requests back down the same stream. Either way they appear as matchable nodes.

  • core is a stateless deployment; every core connects to every node, so any core can serve any request.
  • scaler runs separately and owns metrics + scaling decisions.
  • edge nodes scale autonomously — the scaler provisions them and they self-register in etcd.

See Autoscaling for the scaling rules and Self-hosting to run it all locally.