19 KiB
Architecture
This document is the canonical Rust workspace architecture for
codex-proxy-rs. The Rust implementation must converge to this directory
shape and naming exactly. A migration is not complete while any extra Rust
source file, extra Rust source directory, or forbidden transitional name remains.
This document intentionally governs Rust workspace source layout. The web/
frontend has its own package layout and is not part of the Rust source
white-list below.
Architectural Style
The application is a modular monolith with hexagonal boundaries.
coreowns domain model, use cases, protocol semantics, policy, and ports.adaptersowns concrete implementations ofcoreports.runtimeowns composition, application state, and background task wiring.serverowns the Axum HTTP boundary.platformowns shared infrastructure primitives.assetsowns compiled frontend asset serving.xtaskowns local automation.
Non-Negotiable Rules
- Rust source directories and files must match this document exactly at completion.
- Do not keep transitional module names after migration.
- Do not hide structure drift behind compatibility re-exports.
coremust not depend on any project crate in the final architecture.coremust not own concrete SQLx, Reqwest, Axum, filesystem path, TLS, or upstream IO implementations.servermust not decide business policy.runtimemust not contain raw SQL, HTTP handlers, or domain rules.adaptersmust not define domain policy.- Architecture tests must enforce directory shape, dependency direction, forbidden names, and forbidden imports.
Root Workspace Layout
codex-proxy-rs/
├── Cargo.toml
├── Cargo.lock
├── README.md
├── AGENTS.md
├── src/
│ └── lib.rs
├── crates/
│ ├── core/
│ ├── adapters/
│ ├── runtime/
│ ├── server/
│ ├── platform/
│ ├── assets/
│ └── xtask/
├── tests/
├── web/
└── docs/
Root src/lib.rs is the only root facade. The root Cargo.toml must use the
default library path or explicitly point to src/lib.rs. Files such as
crates/server/facade.rs are forbidden.
Dependency Direction
Final dependency graph:
root facade -> server
server -> runtime + core + assets + platform
runtime -> core + adapters + platform
adapters -> core + platform
core -> no project crate dependencies
assets -> no project crate dependencies
xtask -> workspace tooling only
platform -> no domain crate dependencies
Forbidden project-crate dependencies:
coremust not depend onplatform,server,runtime,adapters, orassets.platformmust not depend oncore,server,runtime,adapters, orassets.adaptersmust not depend onruntimeorserver.runtimemust not depend onserver.assetsmust not depend on project crates.
runtime maps platform configuration into core configuration DTOs before
constructing core services. This keeps core independent from config loading,
filesystem paths, environment variables, and storage bootstrap concerns.
Crate Responsibilities
crates/core
Owns domain concepts, invariants, use case orchestration, protocol semantics, and ports.
Allowed:
- account pool and scheduling policy
- admin business use cases
- authentication/session domain behavior
- model catalog rules
- usage and event policy
- OpenAI/Codex protocol data structures and pure conversion
- request dispatch, fallback, retry, quota, affinity, recovery, and usage policy
- port traits
- typed domain and application errors
Forbidden:
- Axum extractors, routers, responses, or
IntoResponse - SQLx pools, queries, rows, transactions, or migrations
- concrete
reqwest::Client - concrete WebSocket/TLS connection setup
- concrete filesystem path decisions
- platform config loader/types
- public re-exports of project crates
crates/adapters
Owns concrete implementations of ports defined by core.
Allowed:
- SQLx implementations of storage ports
- encrypted SQLite stores
- Reqwest HTTP clients
- concrete OpenAI OAuth client
- concrete Codex upstream HTTP/SSE/WebSocket clients
- concrete fingerprint update client
- adapter-specific translation from framework errors into
coreerrors
Forbidden:
- business policy
- Axum route handlers
- runtime task orchestration
crates/runtime
Owns application composition.
Allowed:
AppState- service construction
- repository construction
- adapter construction
- dependency injection
- background task startup and shutdown
- startup restore flows
- platform-to-core config mapping
Forbidden:
- HTTP request/response mapping
- raw SQL query strings
- concrete upstream protocol side effects
- account selection, fallback, quota, model, or recovery policy
crates/server
Owns the HTTP boundary.
Allowed:
- Axum routers and route handlers
- extractors and middleware
- request IDs, tracing, CORS, and auth extraction
- admin response envelopes
- OpenAI-compatible HTTP response bodies
- HTTP status and header mapping
- SSE framing for client responses
Forbidden:
- SQL queries
- concrete upstream Codex IO
- account selection, fallback, quota, model, or recovery policy
- background task orchestration
crates/platform
Owns cross-cutting infrastructure primitives.
Allowed:
- config loading and config schema types
- crypto primitives
- identity hashing primitives
- SQLite connection setup and schema files
- filesystem path primitives
- logging primitives
- JSON and pagination helpers
Forbidden:
- Codex/OpenAI business use cases
- admin business use cases
- Axum handlers
- implementations of
coreports
crates/assets
Owns static frontend asset serving.
Allowed:
- root
index.html /assets/*- SPA fallback after API routes
- static cache and security headers
crates/xtask
Owns local automation.
Allowed:
- frontend build orchestration
- architecture checks
- release/package commands
- developer maintenance commands
Exact Rust Source Shape
Root Facade
src/
└── lib.rs
Core
crates/core/
├── Cargo.toml
└── src/
├── lib.rs
├── error.rs
├── admin/
│ ├── mod.rs
│ ├── ports.rs
│ ├── auth.rs
│ ├── client_keys.rs
│ └── settings.rs
├── accounts/
│ ├── mod.rs
│ ├── model.rs
│ ├── ports.rs
│ ├── service.rs
│ ├── pool.rs
│ ├── lifecycle.rs
│ ├── cookies.rs
│ ├── jwt.rs
│ └── usage.rs
├── auth/
│ ├── mod.rs
│ ├── ports.rs
│ ├── oauth.rs
│ └── session.rs
├── models/
│ ├── mod.rs
│ ├── model.rs
│ ├── ports.rs
│ ├── catalog.rs
│ └── service.rs
├── events/
│ ├── mod.rs
│ ├── model.rs
│ ├── ports.rs
│ └── service.rs
├── usage/
│ ├── mod.rs
│ ├── model.rs
│ ├── ports.rs
│ └── service.rs
├── protocol/
│ ├── mod.rs
│ ├── openai/
│ │ ├── mod.rs
│ │ ├── chat.rs
│ │ ├── responses.rs
│ │ ├── models.rs
│ │ ├── errors.rs
│ │ └── schema.rs
│ └── codex/
│ ├── mod.rs
│ ├── chat.rs
│ ├── responses.rs
│ ├── events.rs
│ ├── sse.rs
│ ├── websocket.rs
│ └── schema.rs
├── gateway/
│ ├── mod.rs
│ ├── ports.rs
│ ├── fingerprint.rs
│ ├── conversation.rs
│ └── installation.rs
└── serving/
├── mod.rs
├── chat.rs
├── responses.rs
├── errors.rs
├── routing.rs
├── fallback.rs
├── affinity.rs
├── quota.rs
├── stream.rs
├── recovery.rs
└── usage.rs
protocol contains only protocol data structures and pure conversion.
gateway contains upstream-facing ports and transport-neutral request context.
Concrete HTTP, SSE, WebSocket, TLS, and cookie-jar behavior lives in adapters.
Adapters
crates/adapters/
├── Cargo.toml
└── src/
├── lib.rs
├── sqlite/
│ ├── mod.rs
│ ├── accounts.rs
│ ├── account_tokens.rs
│ ├── account_usage.rs
│ ├── refresh_leases.rs
│ ├── cookies.rs
│ ├── events.rs
│ ├── models.rs
│ ├── session_affinity.rs
│ ├── admin_sessions.rs
│ └── client_keys.rs
├── http/
│ ├── mod.rs
│ ├── reqwest_client.rs
│ └── headers.rs
├── codex/
│ ├── mod.rs
│ ├── client.rs
│ ├── sse.rs
│ ├── models.rs
│ ├── fingerprint.rs
│ └── websocket/
│ ├── mod.rs
│ ├── client.rs
│ ├── pool.rs
│ ├── codec.rs
│ ├── deflate.rs
│ └── opening.rs
└── oauth/
├── mod.rs
└── openai.rs
Runtime
crates/runtime/
├── Cargo.toml
└── src/
├── lib.rs
├── bootstrap.rs
├── state.rs
├── services.rs
├── repositories.rs
├── upstream.rs
├── config.rs
└── tasks/
├── mod.rs
├── coordinator.rs
├── token_refresh.rs
├── quota_refresh.rs
├── model_refresh.rs
├── cookie_cleanup.rs
├── session_cleanup.rs
└── fingerprint_update.rs
runtime/src/config.rs maps platform configuration into core configuration
DTOs.
Server
crates/server/
├── Cargo.toml
└── src/
├── main.rs
├── lib.rs
├── router.rs
├── error/
│ ├── mod.rs
│ ├── admin.rs
│ └── openai.rs
├── middleware/
│ ├── mod.rs
│ ├── request_id.rs
│ ├── trace.rs
│ ├── auth.rs
│ └── cors.rs
├── admin_api/
│ ├── mod.rs
│ ├── router.rs
│ ├── response.rs
│ ├── session.rs
│ ├── settings.rs
│ ├── diagnostics.rs
│ ├── models.rs
│ ├── usage.rs
│ ├── accounts/
│ │ ├── mod.rs
│ │ ├── list.rs
│ │ ├── create.rs
│ │ ├── import.rs
│ │ ├── export.rs
│ │ ├── lifecycle.rs
│ │ ├── quota.rs
│ │ ├── cookies.rs
│ │ ├── oauth.rs
│ │ └── health.rs
│ ├── client_keys/
│ │ ├── mod.rs
│ │ ├── list.rs
│ │ ├── create.rs
│ │ ├── import.rs
│ │ ├── export.rs
│ │ └── lifecycle.rs
│ └── logs/
│ ├── mod.rs
│ ├── query.rs
│ ├── detail.rs
│ └── state.rs
└── openai_api/
├── mod.rs
├── router.rs
├── auth.rs
├── chat.rs
├── responses.rs
├── models.rs
├── diagnostics.rs
├── error.rs
└── sse.rs
server/src/error/openai.rs maps application errors into OpenAI-compatible HTTP
responses. server/src/openai_api/error.rs and server/src/openai_api/sse.rs
own route-local OpenAI API error/event framing.
Platform
crates/platform/
├── Cargo.toml
└── src/
├── lib.rs
├── config/
│ ├── mod.rs
│ ├── loader.rs
│ └── types.rs
├── crypto/
│ ├── mod.rs
│ ├── secret_box.rs
│ └── hash.rs
├── identity/
│ ├── mod.rs
│ ├── admin_password.rs
│ └── client_key.rs
├── storage/
│ ├── mod.rs
│ ├── sqlite.rs
│ ├── schema.sql
│ └── paths.rs
├── logging/
│ ├── mod.rs
│ └── rotation.rs
└── json/
├── mod.rs
└── pagination.rs
Assets
crates/assets/
├── Cargo.toml
└── src/
├── lib.rs
├── router.rs
└── headers.rs
Xtask
crates/xtask/
├── Cargo.toml
└── src/
├── main.rs
├── build_web.rs
├── check_architecture.rs
└── release.rs
Cargo Integration Test Shape
Cargo integration test entry files must remain at tests/*.rs; submodules live
under matching directories.
tests/
├── admin.rs
├── openai_api.rs
├── accounts.rs
├── codex_upstream.rs
├── runtime.rs
├── architecture.rs
├── admin/
├── openai_api/
├── accounts/
├── codex_upstream/
├── runtime/
├── architecture/
│ ├── mod.rs
│ ├── dependency_direction.rs
│ ├── forbidden_imports.rs
│ └── directory_shape.rs
└── support/
├── mod.rs
├── app.rs
├── fixtures.rs
└── wiremock.rs
Additional fixture files are allowed under tests/fixtures/.
Docs Shape
docs/
├── architecture.md
├── adr/
├── api/
└── operations/
Historical migration plans may remain under docs/superpowers/ while active
development is ongoing. They are not part of the final architecture white-list.
Web Shape
The web/ directory is outside this Rust workspace architecture white-list.
It is governed by frontend build tooling and must not be checked by Rust
directory-shape tests. web/node_modules/ and web/dist/ are build artifacts
from the frontend toolchain, not Rust architecture inputs.
Required Names
- Root facade:
src/lib.rs - HTTP admin boundary:
server/src/admin_api - HTTP OpenAI-compatible boundary:
server/src/openai_api - Core port files:
ports.rs - Concrete SQLite adapters:
adapters/src/sqlite - Concrete OAuth adapter:
adapters/src/oauth/openai.rs - Concrete Codex upstream adapters:
adapters/src/codex - Concrete Codex WebSocket adapter:
adapters/src/codex/websocket - Shared middleware:
server/src/middleware - HTTP response errors:
server/src/error - OpenAI API route-local SSE framing:
server/src/openai_api/sse.rs - Business errors:
core/src/error.rsor domain-localerrors.rs
Forbidden Names
crates/admincrates/server/facade.rsserver/src/admin.rsserver/src/codex_httpserver/src/openai_api/stream.rscore/src/**/repository.rscore/src/**/repository/adapters/src/*_repository.rsruntime/src/*_repository.rscore/src/codex/gateway/transportcore/src/codex/gateway/oauth/client.rscore/src/codex/serving/httpplatform/src/platformplatform/src/utils
Port Placement
Ports are traits owned by core.
- account storage ports live in
core/src/accounts/ports.rs - admin session/client-key ports live in
core/src/admin/ports.rs - OAuth and token refresh ports live in
core/src/auth/ports.rs - model snapshot ports live in
core/src/models/ports.rs - event log ports live in
core/src/events/ports.rs - usage ports live in
core/src/usage/ports.rs - upstream Codex and fingerprint ports live in
core/src/gateway/ports.rs
Concrete implementations live in adapters.
SqliteAccountStorelives inadapters/src/sqlite/accounts.rsSqliteAdminSessionStorelives inadapters/src/sqlite/admin_sessions.rsOpenAiOAuthClientlives inadapters/src/oauth/openai.rsReqwestCodexClientlives inadapters/src/codex/client.rsCodexWebSocketPoolimplementation lives inadapters/src/codex/websocket/pool.rs
Migration Rules
Every migration step must move the codebase closer to this document.
Required order:
- Add architecture tests for directory shape and forbidden names.
- Move the root facade to
src/lib.rs. - Rename server HTTP boundaries to
admin_apiandopenai_api. - Move admin business code into
core/src/admin. - Move all core repository traits into
ports.rs. - Move all SQLx implementations into
adapters/src/sqlite. - Move all Reqwest/SSE/WebSocket/OAuth implementations into
adapters. - Collapse runtime into composition-only files.
- Collapse platform into config/crypto/identity/storage/logging/json primitives.
- Update facade exports after internal structure is correct.
- Run full verification only after the structure is fully aligned.
Do not use transitional aliases as a substitute for migration. A transitional alias is acceptable only if it is removed in the same migration series before claiming completion.
Architecture Tests
Architecture tests must enforce:
- every required Rust source file exists
- every required Rust source directory exists
- forbidden Rust source files and directories do not exist
- no extra Rust source files exist under
src/andcrates/*/src/ - no extra Rust source directories exist under
src/andcrates/*/src/ Cargo.tomlworkspace members match this document- root library path is
src/lib.rs corehas no project-crate dependenciescorehas noaxum,sqlx, or concretereqwest::Clientserverhas no SQLx queriesruntimehas no SQLx query strings and no Axum handlersadaptersowns concrete SQLx/Reqwest/OAuth/Codex IOcoreports are declared inports.rs- root facade does not hide invalid internal directory names
The required architecture test files are:
tests/architecture/dependency_direction.rs
tests/architecture/forbidden_imports.rs
tests/architecture/directory_shape.rs
Completion Criteria
The migration is complete only when all of the following are true:
- The Rust workspace source tree matches this document exactly.
- Forbidden names are absent from Rust source directories.
- Architecture tests enforce this document.
cargo fmt --checkpasses.cargo check --workspace --all-targetspasses.cargo test --workspacepasses.cargo clippy --workspace --all-targets --all-features --locked -- -D warningspasses.