One Go binary. Zero dependencies.

The gateway that sees every request

Our gateway software is called SkyEye. It is a reverse proxy, a web application firewall and a load balancer combined in a single binary written in Go. It sits in front of your application servers, terminates HTTPS, decodes and inspects every request, and forwards only what is clean to your backends.

This page explains what SkyEye is, how it processes traffic, what it defends against and how it balances load. It is written for anyone who wants to understand how the software works.

What SkyEye is

SkyEye replaces the usual stack of a reverse proxy, a separate firewall product and a load balancer with one process and one small configuration file. There is no database, no plugin system and no external service. All state lives in memory with hard capacity caps.

It provides a full web application firewall with detection for SQL, NoSQL, XSS, command, template, LDAP and XPath injection, plus XXE, SSRF and prototype pollution, using two independent engines per attack family. It routes by URL prefix like nginx location blocks, balances with round robin or IP hash, fails over automatically and supports WebSockets. HTTPS is automatic. Certificates come from Let's Encrypt, issued and renewed on their own, and every plain HTTP request is redirected safely to HTTPS.

It is also designed to be private. Abuse counters store hashes, never your visitors' URLs, and inspection runs in linear time so a hostile payload cannot slow the gateway down.

A firewall that reads requests the way attackers write them

Attackers hide payloads behind layers of encoding. SkyEye unwraps up to eight layers of percent encoding, folds HTML entities and JSON escapes, strips SQL comment tricks and collapses spacing before it looks for attacks. A payload like UNION/**/SeLeCt is seen for exactly what it is.

Every signature match respects word boundaries, so ordinary words that merely contain a pattern never trigger. On top of the signature lists, structural engines parse the actual shape of SQL expressions, shell commands, HTML markup, template expressions and LDAP filters.

The attack families it detects:

Deep body inspection without ever running a parser you can exploit

SkyEye inspects JSON, XML, form and multipart bodies up to a 4 MiB window. Every JSON key and string is scanned, duplicates included, so a payload cannot hide behind a backend that picks first or last. Depth bombs past 128 levels are rejected before any parser sees them.

XML is analyzed lexically. External entities, DTDs, XInclude and stylesheets are detected and banned but never resolved, so the inspector itself can never be the victim. Multipart uploads are capped at 256 parts with every field scanned and file bytes forwarded untouched.

Capped uploads are spooled to an unlinked temp file, so a crash never leaves request bodies on disk. Streaming routes are inspected on a bounded prefix and then streamed live.

It spots scanners by how they behave, not just what they send

A single 404 means nothing. Thirty distinct probes for dotfiles and scanner-only paths inside a minute means someone is mapping your server. SkyEye watches behavior across a short burst window and a one hour slow-scan window and bans path scanners on both.

Abuse counters store SHA-256 hashes of paths, never the URLs themselves. Your traffic cannot be mined as a log.

Honest bans that escalate for repeat offenders

A confirmed attack bans the address for 7 days. A second offense within 90 days raises it to 14 days and a third to 30. IPv6 attackers are banned by their whole /64 block, so rotating addresses inside one allocation changes nothing.

Only unambiguous attacks ban. Weaker signals get a refusal page that explicitly does not claim a ban. And traffic volume alone never bans anyone, a busy office IP is not an attacker, it just gets a polite 429 with Retry-After.

The block page tells the truth. It states the reason, that the event is logged, and that continued activity extends the restriction.

Bans live in memory by design. A restart is a built-in escape hatch for a bad ban, and the allowlist in skyeye.yaml survives forever.

A protocol firewall in front of everything

Before routing, before inspection, every request must survive strict HTTP validation. Ambiguous framing that enables request smuggling is rejected outright, chunked encoding next to a Content-Length, doubled singleton headers, underscore aliases of security headers, unknown Connection options and method-override headers all die here.

Your backend's Server header is masked. Every response leaves stamped as SkyEye.

Load balancing with automatic failover

Give a route several backends and SkyEye spreads traffic across them, round robin by default or IP hash when a visitor should stick to one server. Routes work like nginx location blocks, longest prefix wins and a prefix can be stripped or remapped onto a backend base path.

When a backend refuses connections it is benched for 10 seconds and traffic flows to the healthy ones. An application error never benches a server, only a real connect failure does, so one bad response cannot take a backend out of rotation.

WebSockets pass through with a strictly validated handshake, streaming and server-sent events are flushed immediately with zero buffering, and HTTP/2 is used to backends when they support it.

How a request travels through SkyEye

Every request passes through the same pipeline, in the same order, before it can reach a backend.

1

Terminate TLS

SkyEye owns ports 80 and 443. Certificates come from Let's Encrypt automatically and plain HTTP is redirected to HTTPS with a 308 that preserves method and body.

2

Validate the protocol

Framing, headers, host, method, query and cookies are checked against strict HTTP rules. Smuggling attempts and malformed requests stop here with an honest status code.

3

Identify the client

The real client IP is resolved safely, even behind Cloudflare or nginx, and checked against the allowlist and the active ban table.

4

Inspect everything

Path, query, cookies, user agent and body are decoded, normalized and run through the signature and structural engines of every attack family.

5

Apply limits

A sliding window rate limiter with soft and hard thresholds throttles floods with a 429. Static assets are exempt from the soft limit so real pages stay fast.

6

Balance and forward

The matched route picks a healthy backend, headers are cleaned and stamped, and the response streams back with SkyEye's signature on it.

Run it, watch it, control it from one command

SkyEye is administered over a private unix socket that never touches the network. The same binary is the server and the client.

One small YAML file. Everything else is built in.

On first run SkyEye writes a commented starter config next to the binary. You set your domains, your routes and your rate limits. The security is not a menu of toggles, every defense is always on with durations fixed in software, so a config mistake can never switch your protection off.

Edit the file, run skyeye reload, and the new config is published atomically while requests in flight keep a consistent snapshot.

tls: true
domains: [api.example.com]

trust_proxy_headers: false

rate:
  window_seconds: 60
  limit_max: 120
  hard_max: 400

allowlist: [203.0.113.10]

routes:
  - prefix: /
    target: http://127.0.0.1:8080
  - prefix: /media/
    targets:
      - http://127.0.0.1:8081
      - http://127.0.0.1:8082
    lb: ip_hash