# Architecture

How the project is structured and how the pieces fit together.

## Project Layout

```
porta/
├── Cargo.toml                    # Project manifest
├── Cargo.lock                    # Dependency lock file
├── docs/                         # Documentation (you are here)
└── src/
    ├── lib.rs                    # Library entry point
    ├── error.rs                  # Error types
    ├── pkce.rs                   # PKCE crypto
    ├── bin/
    │   └── cli.rs                # CLI binary
    └── anthropic/
        ├── mod.rs                # Module root + constants
        ├── oauth.rs              # OAuth flow
        ├── client.rs             # API client
        └── transform.rs          # Request/response transformation
```

## Module Dependency Graph

```
┌─────────────────────────────────────────────────────────────┐
│                        bin/cli.rs                           │
│                      (CLI Application)                      │
└─────────────────────────┬───────────────────────────────────┘
                          │ uses
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                         lib.rs                              │
│                    (Library Entry Point)                    │
│  pub mod error;                                             │
│  pub mod pkce;                                              │
│  pub mod anthropic;                                         │
└────────┬────────────────┬───────────────────┬───────────────┘
         │                │                   │
         ▼                ▼                   ▼
┌─────────────┐   ┌─────────────┐   ┌─────────────────────────┐
│  error.rs   │   │  pkce.rs    │   │    anthropic/mod.rs     │
│             │   │             │   │                         │
│ Error enum  │   │ Pkce struct │   │ Constants:              │
│ Result type │   │ generate()  │   │   CLIENT_ID             │
└─────────────┘   └─────────────┘   │   USER_AGENT            │
                                    │   TOOL_PREFIX           │
                                    │   REQUIRED_BETAS        │
                                    └────────┬────────────────┘
                                             │
                    ┌────────────────────────┼────────────────────────┐
                    │                        │                        │
                    ▼                        ▼                        ▼
          ┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
          │    oauth.rs     │     │   client.rs     │     │  transform.rs   │
          │                 │     │                 │     │                 │
          │ AnthropicOAuth  │────▶│ AnthropicClient │────▶│RequestTransform │
          │ - authorize_url │     │ - messages()    │     │ - transform_req │
          │ - exchange()    │     │ - auto refresh  │     │ - transform_res │
          │ - refresh()     │     │                 │     │                 │
          │                 │     │                 │     │                 │
          │ OAuthTokens     │     │                 │     │                 │
          │ AuthMode        │     │                 │     │                 │
          └─────────────────┘     └─────────────────┘     └─────────────────┘
```

## Module Descriptions

### `error.rs`

Defines the error types for the entire library.

```rust
pub enum Error {
    Http(reqwest::Error),       // HTTP request failures
    Json(serde_json::Error),    // JSON parsing errors
    OAuth(String),              // OAuth-specific errors
    TokenRefresh { status },    // Token refresh failures
    TokenExpired,               // Token expired, no refresh available
    Url(url::ParseError),       // URL parsing errors
}

pub type Result<T> = std::result::Result<T, Error>;
```

### `pkce.rs`

PKCE (Proof Key for Code Exchange) implementation for secure OAuth.

```rust
pub struct Pkce {
    pub verifier: String,   // Random 32-byte secret (base64url)
    pub challenge: String,  // SHA-256 of verifier (base64url)
}

impl Pkce {
    pub fn generate() -> Self;       // Create new PKCE pair
    pub fn method() -> &'static str; // Always "S256"
}
```

### `anthropic/mod.rs`

Module root with constants used throughout.

```rust
pub const CLIENT_ID: &str = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
pub const REDIRECT_URI: &str = "https://console.anthropic.com/oauth/code/callback";
pub const SCOPES: &str = "org:create_api_key user:profile user:inference";
pub const USER_AGENT: &str = "claude-cli/2.1.2 (external, cli)";
pub const TOOL_PREFIX: &str = "mcp_";
pub const REQUIRED_BETAS: &[&str] = &[
    "oauth-2025-04-20",
    "interleaved-thinking-2025-05-14",
];
```

### `anthropic/oauth.rs`

OAuth flow implementation.

```rust
pub enum AuthMode { Max, Console }

pub struct OAuthTokens {
    pub access_token: String,
    pub refresh_token: String,
    pub expires_at: u64,
}

pub struct AnthropicOAuth {
    client: reqwest::Client,
}

impl AnthropicOAuth {
    pub fn new() -> Self;
    pub fn authorize_url(&self, mode: AuthMode) -> Result<(String, String)>;
    pub async fn exchange(&self, code: &str, verifier: &str) -> Result<OAuthTokens>;
    pub async fn refresh(&self, refresh_token: &str) -> Result<OAuthTokens>;
}
```

### `anthropic/client.rs`

High-level API client with automatic token refresh.

```rust
pub struct AnthropicClient {
    http: reqwest::Client,
    oauth: AnthropicOAuth,
    tokens: Arc<RwLock<OAuthTokens>>,
}

impl AnthropicClient {
    pub fn new(tokens: OAuthTokens) -> Self;
    pub async fn messages(&self, body: &str) -> Result<String>;
    pub async fn messages_stream(&self, body: &str) -> Result<Stream>;
    pub async fn tokens(&self) -> OAuthTokens;
    pub async fn set_tokens(&self, tokens: OAuthTokens);
}
```

### `anthropic/transform.rs`

Request and response transformation to match Claude Code format.

```rust
pub struct RequestTransformer;

impl RequestTransformer {
    /// Transform outgoing request:
    /// - Inject "You are Claude Code..." system prompt
    /// - Add empty tools array if missing
    /// - Prefix tool names with "mcp_"
    /// - Sanitize "OpenCode" -> "Claude Code"
    pub fn transform_request(body: &str) -> Option<String>;

    /// Transform incoming response:
    /// - Remove "mcp_" prefix from tool names
    pub fn transform_response(chunk: &str) -> String;
}
```

### `bin/cli.rs`

CLI application for testing.

```rust
#[tokio::main]
async fn main() {
    // 1. Load tokens from ~/.porta/tokens.json
    // 2. If not found, run OAuth flow
    // 3. If expired, refresh
    // 4. Start interactive chat loop
    // 5. Save tokens on exit
}
```

## Data Flow

### Authentication Flow

```
User                CLI                  AnthropicOAuth           Anthropic
  │                  │                        │                      │
  │  run cli         │                        │                      │
  │─────────────────▶│                        │                      │
  │                  │                        │                      │
  │                  │  authorize_url(Max)    │                      │
  │                  │───────────────────────▶│                      │
  │                  │  (url, verifier)       │                      │
  │                  │◀───────────────────────│                      │
  │                  │                        │                      │
  │  open browser    │                        │                      │
  │◀─────────────────│                        │                      │
  │                  │                        │                      │
  │  visit URL       │                        │                      │
  │─────────────────────────────────────────────────────────────────▶│
  │                  │                        │                      │
  │  auth code       │                        │                      │
  │◀─────────────────────────────────────────────────────────────────│
  │                  │                        │                      │
  │  paste code      │                        │                      │
  │─────────────────▶│                        │                      │
  │                  │                        │                      │
  │                  │  exchange(code, verifier)                     │
  │                  │───────────────────────▶│                      │
  │                  │                        │  POST /oauth/token   │
  │                  │                        │─────────────────────▶│
  │                  │                        │  {access, refresh}   │
  │                  │                        │◀─────────────────────│
  │                  │  OAuthTokens           │                      │
  │                  │◀───────────────────────│                      │
  │                  │                        │                      │
  │                  │  save tokens           │                      │
  │                  │                        │                      │
```

### Request Flow

```
User Input          CLI              AnthropicClient    Transform        Anthropic API
    │                │                     │                │                  │
    │  "Hello"       │                     │                │                  │
    │───────────────▶│                     │                │                  │
    │                │                     │                │                  │
    │                │  messages(body)     │                │                  │
    │                │────────────────────▶│                │                  │
    │                │                     │                │                  │
    │                │                     │  check token   │                  │
    │                │                     │  (refresh if   │                  │
    │                │                     │   expired)     │                  │
    │                │                     │                │                  │
    │                │                     │  transform_request(body)          │
    │                │                     │───────────────▶│                  │
    │                │                     │                │                  │
    │                │                     │  + system prompt                  │
    │                │                     │  + empty tools                    │
    │                │                     │  + mcp_ prefix                    │
    │                │                     │                │                  │
    │                │                     │  transformed   │                  │
    │                │                     │◀───────────────│                  │
    │                │                     │                │                  │
    │                │                     │  POST /v1/messages?beta=true      │
    │                │                     │  Headers: Bearer, User-Agent, etc │
    │                │                     │─────────────────────────────────▶│
    │                │                     │                │                  │
    │                │                     │  response      │                  │
    │                │                     │◀─────────────────────────────────│
    │                │                     │                │                  │
    │                │                     │  transform_response               │
    │                │                     │───────────────▶│                  │
    │                │                     │  - mcp_ prefix │                  │
    │                │                     │◀───────────────│                  │
    │                │                     │                │                  │
    │                │  response           │                │                  │
    │                │◀────────────────────│                │                  │
    │                │                     │                │                  │
    │  "Hello! I'm   │                     │                │                  │
    │   Claude..."   │                     │                │                  │
    │◀───────────────│                     │                │                  │
```

## Thread Safety

The `AnthropicClient` uses `Arc<RwLock<OAuthTokens>>` for thread-safe token access:

```rust
pub struct AnthropicClient {
    tokens: Arc<RwLock<OAuthTokens>>,
    //      ^^^^^^^^^^^
    //      Arc = Atomic Reference Counting (shared ownership)
    //            RwLock = Read-Write Lock (many readers OR one writer)
}

// Reading tokens (multiple concurrent reads OK)
let tokens = self.tokens.read().await;

// Writing tokens (exclusive access)
let mut tokens = self.tokens.write().await;
*tokens = new_tokens;
```

This allows the client to be safely shared across async tasks.
