Skip to main content

zeph_gateway/
lib.rs

1// SPDX-FileCopyrightText: 2026 Andrei G <bug-ops>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! HTTP gateway for webhook ingestion with bearer-token auth and health endpoint.
5//!
6//! `zeph-gateway` exposes two HTTP endpoints over a single TCP listener:
7//!
8//! | Endpoint | Method | Auth required | Purpose |
9//! |---|---|---|---|
10//! | `/health` | GET | No | Liveness check; returns uptime in seconds |
11//! | `/webhook` | POST | Yes (if token set) | Ingest external events into the agent |
12//!
13//! # Security model
14//!
15//! - Bearer token comparison is performed in constant time via BLAKE3 + `subtle::ConstantTimeEq`
16//!   to prevent timing-oracle attacks (see [`GatewayServer::with_auth`]).
17//! - When no token is configured the server logs a warning. Callers are expected to enforce
18//!   access control at the network layer (firewall, upstream reverse proxy) in that case.
19//! - Payload fields are sanitised with [`zeph_common::sanitize`] before forwarding.
20//!
21//! # Rate limiting
22//!
23//! Requests to `/webhook` are rate-limited per remote IP using a fixed-window counter with a
24//! 60-second window. The default ceiling is 120 requests per window and is configurable via
25//! [`GatewayServer::with_rate_limit`]. Setting the limit to `0` disables rate limiting.
26//!
27//! # Quick start
28//!
29//! ```no_run
30//! use tokio::sync::{mpsc, watch};
31//! use zeph_gateway::GatewayServer;
32//!
33//! #[tokio::main]
34//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
35//!     let (webhook_tx, mut webhook_rx) = mpsc::channel::<String>(64);
36//!     let (_shutdown_tx, shutdown_rx) = watch::channel(false);
37//!
38//!     // Spawn a consumer that processes incoming webhook messages.
39//!     tokio::spawn(async move {
40//!         while let Some(msg) = webhook_rx.recv().await {
41//!             println!("received: {msg}");
42//!         }
43//!     });
44//!
45//!     GatewayServer::new("127.0.0.1", 8080, webhook_tx, shutdown_rx)
46//!         .with_auth(Some("my-secret-token".into()))
47//!         .with_rate_limit(60)
48//!         .serve()
49//!         .await?;
50//!
51//!     Ok(())
52//! }
53//! ```
54
55mod error;
56mod handlers;
57mod router;
58mod server;
59
60pub use error::GatewayError;
61pub use server::GatewayServer;