Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion gears/system/oagw/oagw/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ toolkit-security = { workspace = true }
toolkit-macros = { workspace = true }
inventory = { workspace = true }
async-trait = { workspace = true }
axum = { workspace = true }
axum = { workspace = true, features = ["ws"] }
http = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
Expand Down Expand Up @@ -79,6 +79,8 @@ tokio = { workspace = true, features = ["time"] }
tokio-retry = { workspace = true }
hyper = { workspace = true }
hyper-util = { workspace = true }
# WebSocket upstream leg (frozen contract permits this single added dependency)
tokio-tungstenite = "0.29"
# Pingora proxy engine
pingora-proxy = { version = "0.8", features = ["rustls"] }
pingora-core = { version = "0.8", features = ["rustls"] }
Expand Down
52 changes: 52 additions & 0 deletions gears/system/oagw/oagw/IMPLEMENTATION-NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Implementation Notes

Deviations and platform limitations observed while implementing the `oagw` gear
against `gears/system/oagw/docs/`. The documents remain the contract; everything
below is a place where the contract could not be met as written, with the reason
and the fail-safe behaviour that stands in.

## Forced deviations

| Area | Contract | Actual | Reason |
|---|---|---|---|
| WebTransport upstreams | `wt://` scheme in `upstream.v1` | Configured, but every proxied request answers `503 LinkUnavailable` | No WebTransport/QUIC client is in the workspace lockfile and the build is offline; failing closed keeps an unconfigured transport from silently degrading to HTTP |
| WebSocket over TLS (`wss://`) | `wss://` endpoints should upgrade | `wss://` upstreams answer `502 LinkUnavailable` at connect time | The locked `tokio-tungstenite` has no TLS feature enabled; `ws://` upgrades work end to end |
| `ConnectionTimeout` / `IdleTimeout` error types | Distinct `504` types | Both surface as `504 RequestTimeout` | The toolkit transport exposes one per-request timeout and no separate connect/idle budget, so the distinction is not observable |
| `connect_timeout_secs` knob | TCP connect timeout for the upstream leg | Accepted in configuration, not applied | `toolkit-http::HttpClientConfig` has no connect-timeout field; only the request timeout can be set |

## Deliberate deferrals (documented, not missing)

- **Config caching (ADR-0005, DESIGN §4.1)**: explicitly deferred by the design to
a future consideration. The `hot_cache_capacity` knob is accepted and retained
for that layer, but no L1 cache is built — the control plane is already
in-memory at this milestone, so a cache would front nothing.
- **Upstream-health and connection-pool gauges** (DESIGN §4.2): not instrumented;
the toolkit transport does not expose pool counters. Breaker state, transitions,
requests, durations, errors, rate-limit rejections and routing selections are.
- **Fine-grained authorization** (DESIGN "Authentication & Authorization" table):
the platform's gear API exposes only authenticated/anonymous/public gates and no
per-permission registration, so all seventeen oagw operations are registered
`.authenticated()`. The permission strings in the design are not enforceable
from a gear with the current `toolkit-security` surface.

## Security posture worth restating

- Credentials, secrets, request/response bodies, query strings and header values
never reach logs, audit events, metrics labels or problem documents. The audit
event field set is closed (see `src/infra/audit.rs`) and the error table's
`detail` strings are the only user-controlled text echoed back.
- Upstream URLs are HTTPS-only unless `allow_http_upstream` is explicitly `true`;
the SSRF guard refuses private, loopback, link-local and unique-local targets
unless `ssrf_policy.allow_private_addresses` is `true`.
- Rate-limit counters are keyed so that a missing client identity falls back to
the tenant rather than collapsing into one global bucket.
- The apikey plugin's query delivery mode appends the credential *after* the
route's query allowlist filter; caller parameters remain subject to it.

## Testing

Automated tests live in this crate (`tests/`, plus unit tests beside the code)
and cover the management CRUD surface, alias resolution and shadowing, config
merge semantics, the plugin chain order and rejection statuses, rate limiting,
circuit breaking, CORS preflight, SSRF and protocol policy, plain HTTP proxying,
server-sent-event streaming and WebSocket upgrades.
4 changes: 4 additions & 0 deletions gears/system/oagw/oagw/src/api/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Created: 2026-08-29 by Constructor Tech
//! Transport layer.

pub mod rest;
Loading