Skip to content
Draft
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
18 changes: 9 additions & 9 deletions .secrets.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"files": "Cargo\\.lock$|\\.lock$|target/|^.secrets.baseline$",
"lines": null
},
"generated_at": "2026-08-11T12:22:16Z",
"generated_at": "2026-08-11T17:44:49Z",
"plugins_used": [
{
"name": "AWSKeyDetector"
Expand Down Expand Up @@ -142,23 +142,23 @@
"hashed_secret": "4a4645604f0b9e29503be96a87f6f47a6e4a7890",
"is_secret": false,
"is_verified": false,
"line_number": 154,
"line_number": 131,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "427f5e1b530d4a544883308d876a11d724060c86",
"is_secret": false,
"is_verified": false,
"line_number": 157,
"line_number": 134,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "bfc6000db1195a9522813fc405c666dd4ce669ad",
"is_secret": false,
"is_verified": false,
"line_number": 250,
"line_number": 227,
"type": "Secret Keyword",
"verified_result": null
}
Expand All @@ -168,23 +168,23 @@
"hashed_secret": "3e4e260fceb19ea97eb49fdcc922cc84f052b0f6",
"is_secret": false,
"is_verified": false,
"line_number": 235,
"line_number": 267,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "2f3b648abbad8976facf96604c2f272845b2aa23",
"is_secret": false,
"is_verified": false,
"line_number": 241,
"line_number": 273,
"type": "Secret Keyword",
"verified_result": null
},
{
"hashed_secret": "af70e9cc80b554e84e780440cd8fe46092a7e6e5",
"is_secret": false,
"is_verified": false,
"line_number": 255,
"line_number": 287,
"type": "Secret Keyword",
"verified_result": null
}
Expand All @@ -194,7 +194,7 @@
"hashed_secret": "0a24796d4c71ce722a92f450f69dc36c60b21de4",
"is_secret": false,
"is_verified": false,
"line_number": 87,
"line_number": 222,
"type": "Hex High Entropy String",
"verified_result": null
}
Expand Down Expand Up @@ -224,7 +224,7 @@
"hashed_secret": "58e7dc38ba3a7d4a720006d2f3cc4cda774d89dc",
"is_secret": false,
"is_verified": false,
"line_number": 19,
"line_number": 20,
"type": "Hex High Entropy String",
"verified_result": null
}
Expand Down
50 changes: 39 additions & 11 deletions Cargo.lock

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

8 changes: 5 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ members = [
"crates/contextforge-data-plane-apis",
"crates/contextforge-data-plane-cpex",
"crates/contextforge-data-plane-lib",
"crates/contextforge-data-plane-observability",
"crates/contextforge-load-test",
"crates/plugins/cpex-secrets-detection",
]
Expand All @@ -24,6 +25,7 @@ repository = "https://github.com/contextforge-org/contextforge-data-plane"
# Keep dependencies here only when at least two workspace members inherit them.
contextforge-data-plane-cpex = { path = "./crates/contextforge-data-plane-cpex" }
contextforge-data-plane-apis = { path = "./crates/contextforge-data-plane-apis"}
contextforge-data-plane-observability = { path = "./crates/contextforge-data-plane-observability" }
rmcp = { version = "3.1.1", features = [
"server",
"client",
Expand All @@ -35,11 +37,11 @@ rmcp = { version = "3.1.1", features = [
] }
serde = {version= "1.0"}
serde_json = "1.0"
chrono = "0.4.44"
tracing = "0.1"
tracing-opentelemetry = "0.33.0"
opentelemetry = "0.32"
opentelemetry_sdk = { version = "0.32", features = ["rt-tokio", "metrics"] }
tokio = { version = "1.53.1", features = ["macros", "net", "rt-multi-thread", "signal", "sync", "time"] }
tower = "0.5.3"
tower-http = "0.7.0"
http = "1.4.0"
redis = { version = "1.2.1", features = [
"default",
Expand Down
23 changes: 18 additions & 5 deletions _context/wiki/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ Tower layers execute outside-in. A request reaches MCP handlers with these exten
```text
TCP/TLS listener
-> HttpMetricsLayer
-> TraceLayer
-> correlation_layer → transaction/correlation request scope + response headers
-> TraceLayer → request span + structured status/latency event
-> /contextforge-rs nested router
-> mcp_origin_layer → validates Host then Origin (403 when disallowed)
-> CORS layer
Expand All @@ -30,23 +31,27 @@ MCP handlers read typed extensions — they never parse headers, paths, or Redis

```text
downstream request
-> correlation + W3C trace extraction
-> Host/Origin validation → virtual host extraction → JWT validation → session extraction
-> user config lookup → MCP handler validation
-> request plugin hooks
-> backend MCP call (concurrent via join_all for initialize/list)

upstream response
-> response plugin hooks → merge/namespace/passthrough
-> metrics, tracing, logging → downstream response
-> structured status/latency log → correlation response headers → metrics → downstream response
```

```mermaid
flowchart TD
bin["binary\nCLI · logging · runtime"]
bin["binary\nCLI · observability init · runtime"]
lib["lib\nrouting · middleware\nsessions · transports"]
obs["observability\nJSON logs · correlation\ntracing · performance"]
apis["apis\nUserConfig · VirtualHost\nBackendMCPGateway"]
cpex["cpex\nCPEX hook factories"]
bin --> lib
bin --> obs
lib --> obs
lib --> apis
lib --> cpex
```
Expand Down Expand Up @@ -82,6 +87,11 @@ Order is invariant: auth/config before backend selection; request plugins before
| `transports/` | Downstream TCP and TLS listener setup |
| `tools.rs` | Local bootstrap helpers (`with_tools` feature only) |

Cross-cutting observability lives in the sibling `contextforge-data-plane-observability`
crate. The binary explicitly installs its subscriber and exporters at startup;
the library uses its request middleware, context propagation, user pseudonym,
and latency timers without owning global initialization.

## State Ownership

| State | Owner | Lifetime |
Expand All @@ -90,6 +100,9 @@ Order is invariant: auth/config before backend selection; request plugins before
| JWT decoders | `ContextForgeDataPlaneAppState` | Process |
| User config | `RedisUserConfigStore` (LRU + Redis) | Request-path consumed; control-plane authored |
| Request identity / VirtualHostId | Request extensions | One HTTP request |
| Transaction and correlation IDs | Request extension + task-local scope | One HTTP request; returned in response headers and snapshotted into backend transport during initialize |
| Trace and span IDs | Request context + request span | One HTTP request; parsed or generated even when OTLP export is disabled and propagated to backend initialization |
| Pseudonymous user ID | Request span | One authenticated request; first 12 hex characters of a SHA-256 digest, never the raw JWT subject |
| Downstream session id | RMCP + `SessionId` extension | MCP session |
| Backend RMCP services | `BackendTransports` map | Local process, per principal/backend/session |
| Local user session mapping | `LocalUserSessionStore` | Local LRU, 50k entries, 1 hour |
Expand Down Expand Up @@ -142,7 +155,7 @@ Startup sequence (`main.rs` → `Gateway::run_gateway`):
```text
install rustls crypto provider
-> Config::parse()
-> logging::init_tracing_logging(&config)
-> observability::init_observability(&logging_config)
-> Runtime::from(&config) ← sets executor shape
-> optional CpexRuntimeRegistry
-> Gateway::builder()
Expand All @@ -165,7 +178,7 @@ backend response
-> session_id_layer response side ← on DELETE success: remove session + backend transports
-> claims_layer response side
-> virtual_host_id_layer response side
-> CORS, mcp_origin_layer, TraceLayer, HttpMetricsLayer
-> CORS, mcp_origin_layer, TraceLayer, correlation_layer, HttpMetricsLayer
-> downstream response
```

Expand Down
Loading
Loading