No description
  • Rust 96.4%
  • Shell 2%
  • Dockerfile 1.6%
Find a file
Denis Strizhkin 2ea7272659 proxy: add ProxyForwarder trait (async_trait) with ReqwestForwarder impl
AGENTS.md requires forwarding to be abstracted behind a trait so handlers
never call a raw HTTP client. The trait lives in src/proxy/mod.rs and
ReqwestForwarder implements it; proxy_handler now calls forward().
2026-09-09 17:11:02 +03:00
goals feat: new goal2.md 2026-09-09 16:36:53 +03:00
scripts Goal 1: scaffolding, config parser, TimeProvider trait, and peak-hour gatekeeper 2026-09-09 16:17:25 +03:00
src proxy: add ProxyForwarder trait (async_trait) with ReqwestForwarder impl 2026-09-09 17:11:02 +03:00
tests Goal 2: MVP prototype — streaming proxy, HTTP server, e2e tests, Docker 2026-09-09 17:08:20 +03:00
.dockerignore Goal 2: MVP prototype — streaming proxy, HTTP server, e2e tests, Docker 2026-09-09 17:08:20 +03:00
.gitignore init 2026-09-09 16:02:11 +03:00
AGENTS.md docs: add README with project info & usage guide; require README upkeep in AGENTS.md 2026-09-09 16:32:47 +03:00
Cargo.lock Goal 2: MVP prototype — streaming proxy, HTTP server, e2e tests, Docker 2026-09-09 17:08:20 +03:00
Cargo.toml Goal 2: MVP prototype — streaming proxy, HTTP server, e2e tests, Docker 2026-09-09 17:08:20 +03:00
docker-compose.yml Goal 2: MVP prototype — streaming proxy, HTTP server, e2e tests, Docker 2026-09-09 17:08:20 +03:00
Dockerfile Goal 2: MVP prototype — streaming proxy, HTTP server, e2e tests, Docker 2026-09-09 17:08:20 +03:00
README.md Goal 2: MVP prototype — streaming proxy, HTTP server, e2e tests, Docker 2026-09-09 17:08:20 +03:00

deepseek-peak-proxy

A high-performance, time-gated reverse proxy for the DeepSeek API. It sits in front of the upstream endpoint, evaluates the current time against configured peak-hour windows, and rejects/throttles requests during those windows — without ever opening an outbound connection. Outside peak hours, requests are forwarded to upstream with streaming support.

Current status: MVP complete (Goals 12). The proxy is fully runnable and deployable via Docker. Subsequent milestones are reserved for hardening and optional features.

Features

  • Peak-hour gating engine — 24-hour range parser (HH:MM-HH:MM) supporting multiple comma-separated windows and overnight ranges spanning midnight (e.g. 23:00-02:00).
  • HTTP server — Axum-based router with a peak-gate middleware, a /healthz liveness endpoint, and a catch-all streaming proxy route.
  • Streaming upstream forwarderreqwest client pipes request/response bodies chunk by chunk (minimal buffering, SSE-friendly) and strips RFC 7230 hop-by-hop headers (Connection, Keep-Alive, Proxy-Authenticate, …) while preserving Authorization: Bearer ….
  • Configurable rejection status — blocked requests get a structured JSON error with 429 (default) or 503, honoring BLOCKED_STATUS_CODE.
  • Deterministic time abstractionTimeProvider trait with a wall-clock SystemTimeProvider and a thread-safe MockTimeProvider (zero-flake tests, no network I/O).
  • Strict configuration validation — environment-driven config with descriptive startup errors.
  • Quality gatesrustfmt, Clippy (-D warnings), and a full unit/integration/e2e test suite wired into a pre-commit hook.
  • Containerization — multi-stage, non-root Docker image with a /healthz health check; docker compose for local deployment.

Architecture

Hexagonal / clean architecture. Domain logic is fully decoupled from infrastructure behind traits:

src/
├── config/
│   ├── mod.rs           # Env config loading & validation
│   └── peak_range.rs    # HH:MM-HH:MM parsing & interval math
├── domain/
│   ├── mod.rs
│   ├── time_provider.rs # TimeProvider trait + System/Mock providers
│   └── gatekeeper.rs    # Block/allow decision engine
├── proxy/
│   ├── mod.rs
│   └── client.rs        # ReqwestForwarder (streaming, header sanitization)
├── server/
│   ├── mod.rs           # Router assembly + AppState
│   ├── handlers.rs      # /healthz + catch-all proxy route
│   └── middleware.rs    # Peak-hour gate middleware
├── error.rs             # thiserror AppError enum
├── lib.rs               # Library facade
└── main.rs              # Entry point (telemetry, config, server lifecycle)

The HTTP layer plugs into the Goal 1 domain core without touching domain logic.

Configuration

All configuration is read from environment variables at boot:

Variable Required Default Description
API_URL Yes Base upstream URL (e.g. https://api.deepseek.com). Must be a valid http(s) URL.
PEEK_HOURS Yes Comma-separated peak windows, e.g. 10:00-14:00,18:00-19:00. Ranges are start-inclusive, end-exclusive.
PEAK_HOURS No Alias for PEEK_HOURS (used only when PEEK_HOURS is unset).
TIMEZONE No UTC IANA timezone for window evaluation (e.g. America/New_York).
LISTEN_ADDR No 0.0.0.0:8080 Socket address the server binds to.
BLOCKED_STATUS_CODE No 429 Status code returned for blocked requests (429 or 503).

Usage

Build & run locally

export API_URL="https://api.deepseek.com"
export PEEK_HOURS="10:00-14:00,18:00-19:00"
# optional: TIMEZONE, LISTEN_ADDR, BLOCKED_STATUS_CODE
cargo run

Verify the proxy is up:

curl http://127.0.0.1:8080/healthz        # -> {"status":"healthy"}

During peak hours, proxied requests are rejected:

curl -i -X POST http://127.0.0.1:8080/v1/chat/completions -d '{}'
# HTTP/1.1 429 Too Many Requests
# {"error":{"code":429,"message":"Peak hours active. ...","type":"peak_hour_restriction"}}

Off-peak, requests (and streaming SSE bodies) pass straight through.

Docker

docker compose up -d --build
curl http://127.0.0.1:8080/healthz        # -> HTTP 200 {"status":"healthy"}

The image is a two-stage build (rust:1.97-alpine builder → alpine:3.20 runtime), runs as non-root UID 10001, and embeds a /healthz health check.

Test

cargo test --all-targets

Covers unit tests (parsing, gatekeeper, config), plus an end-to-end test (tests/proxy_e2e_test.rs) that drives the real router through a loopback HTTP server against a wiremock upstream, asserting peak blocking (429) vs. off-peak forwarding (200) and /healthz availability.

Quality gates (before committing)

cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-targets

Or install them as a mandatory pre-commit hook:

./scripts/install-hooks.sh

Installs .git/hooks/pre-commit and runs the full gate suite.

Roadmap

  • Goal 1 — Core domain: config parser, PeakHourSchedule/TimeRange, TimeProvider trait, Gatekeeper, test suite, git hooks.
  • Goal 2 — MVP: Axum server bootstrap, peak-gate middleware, /healthz, streaming Reqwest forwarder with hop-by-hop stripping, wiremock e2e test, Docker image + compose.
  • Hardening / optional: metric exporters, dynamic config reload, graceful-shutdown polish, performance tuning.