# AT-Jet Architecture Rationale

## Why AT-Jet Exists

AT-Jet is **not** a simple wrapper around axum and protobuf. It exists to solve a specific problem: **building production-grade HTTP APIs for global mobile users with Protocol Buffers**.

### The Problem

When building mobile APIs, teams face a choice:

```
Option A: JSON + REST
├── Easy to debug (human-readable)
├── Broad tooling support
└── ❌ 3-10x larger payloads than protobuf
└── ❌ No schema evolution guarantees
└── ❌ CPU-intensive parsing on mobile devices

Option B: gRPC
├── Built-in protobuf support
├── Strong typing
└── ❌ Poor CDN compatibility
└── ❌ HTTP/2 only (problematic on some networks)
└── ❌ Complex for simple request/response patterns

Option C: HTTP + Protobuf (DIY)
├── Best of both worlds
└── ❌ Significant boilerplate per project
└── ❌ Inconsistent error handling across projects
└── ❌ No established patterns for team
```

**AT-Jet chooses Option C and eliminates its downsides.**

### Our Users

AT-Jet targets a specific user profile:

```
┌─────────────────────────────────────────────────────────────────────┐
│  Global Mobile Application                                         │
│  ├── 100M+ users across continents                                 │
│  ├── Varying network conditions (2G to 5G)                         │
│  ├── Battery-conscious mobile devices                              │
│  └── Need: Small payloads, efficient parsing, CDN caching          │
└─────────────────────────────────────────────────────────────────────┘
                              │
                              ▼
                         ┌─────────┐
                         │   CDN   │  ← HTTP/1.1 compatible, cacheable
                         └────┬────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│  AT-Jet API Layer                                                   │
│  ├── HTTP/1.1 + HTTP/2 support                                     │
│  ├── Protobuf serialization                                         │
│  ├── Gzip compression                                               │
│  └── Standard REST semantics (GET, POST, PUT, DELETE)              │
└─────────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│  Backend Services (ZUS-RS)                                          │
│  └── Long-lived connections for internal RPC                        │
└─────────────────────────────────────────────────────────────────────┘
```

## What AT-Jet Provides (Beyond a Wrapper)

### 1. Type-Safe Protobuf Integration

**Without AT-Jet** (raw axum + prost):

```rust
// Every handler needs this boilerplate
async fn create_user(body: Bytes) -> impl IntoResponse {
    // Manual content-type checking
    // Manual size validation
    // Manual protobuf decoding
    // Manual error conversion
    // Manual response encoding
    // ... 30+ lines of repeated code
}
```

**With AT-Jet**:

```rust
async fn create_user(
    ProtobufRequest(req): ProtobufRequest<CreateUserRequest>
) -> ProtobufResponse<User> {
    ProtobufResponse::ok(User { id: 1, name: req.name })
}
```

This is not just convenience—it's **correctness by construction**:
- Content-type validation is automatic
- Body size limits are enforced
- Decoding errors are handled uniformly
- Response headers are set correctly

### 2. Unified Error Handling

Every Protobuf API needs consistent error responses. AT-Jet provides:

```rust
pub enum JetError {
    BadRequest(String),           // 400
    Unauthorized(String),         // 401
    Forbidden(String),            // 403
    NotFound(String),             // 404
    InvalidContentType { .. },    // 415
    BodyTooLarge { .. },          // 413
    Internal(String),             // 500
    // ...
}
```

All errors automatically convert to appropriate HTTP status codes and can be serialized to protobuf error responses.

### 3. Production-Ready Defaults

AT-Jet includes what production mobile APIs need:

| Feature | Why It Matters |
|---------|---------------|
| Gzip compression | 50-70% smaller responses over the wire |
| Request size limits | DoS protection (default 10MB) |
| Tracing initialization | Unified setup with Jaeger/OpenTelemetry support |
| Smart tracing | Health/metrics endpoints don't flood logs |
| Prometheus metrics | HTTP request instrumentation out of the box |
| JWT authentication | HMAC-based token validation middleware |
| Session management | In-memory sessions with TTL and idle timeout |
| Graceful shutdown | Clean Ctrl+C handling for k8s |
| CORS support | Browser-based debugging tools |
| Timeout middleware | Prevent hanging requests |

### 4. Client Library Included

Most "server frameworks" stop at the server. AT-Jet provides a matching client:

```rust
let client = JetClient::new("https://api.example.com")?;

// Type-safe request/response
let user: User = client.post("/api/user", &create_request).await?;
```

This ensures:
- Consistent content-type handling
- Matching serialization/deserialization
- Shared error types between client and server
- Team can use the same patterns for service-to-service calls

### 5. AT Team Conventions Embedded

AT-Jet is pre-configured with our team's coding standards:

```rust
// Prevents panics in production
#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used))]
```

Plus:
- Nightly rustfmt configuration
- Standard error handling patterns
- Consistent project structure

## Architecture Decisions

### Decision 0: Protobuf Over JSON

**The Problem: JSON Version URL Nightmare**

With JSON APIs, schema changes often require new API versions:

```
Month 1:  /api/v1/getUser  →  { "name": "John", "email": "john@x.com" }
Month 3:  /api/v2/getUser  →  { "name": "John", "email": "...", "age": 25 }
Month 6:  /api/v3/getUser  →  { "name": "John", "emailAddress": "...", "age": 25 }
Month 12: /api/v1, /v2, /v3, /v4, /v5... (maintenance nightmare)
```

Why? Because JSON uses **field names** on the wire. Renaming, adding, or removing fields can break clients.

**Protobuf Solution: Field Numbers**

```
┌─────────────────────────────────────────────────────────────────────────┐
│                    Protobuf Schema Evolution                             │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│   message User {                                                        │
│     string name = 1;          // Field NUMBER, not name, is on wire     │
│     string email = 2;                                                   │
│     int32 age = 3;            // Added later - old clients ignore it    │
│     string phone = 4;         // Added later - old clients ignore it    │
│   }                                                                      │
│                                                                          │
│   Wire format: [1: "John"] [2: "john@x.com"] [3: 25] [4: "123456"]      │
│                                                                          │
│   • Old client + new server: client ignores unknown fields ✓            │
│   • New client + old server: missing fields get default value ✓         │
│   • ONE endpoint forever: /api/getUser (no /v1, /v2, /v3...)           │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘
```

**JSON vs Protobuf Comparison**

| Scenario | JSON | Protobuf |
|----------|------|----------|
| Add new field | May break old clients | Old clients ignore it ✓ |
| Remove field | Breaks clients expecting it | Clients use default ✓ |
| Rename field | Must version URL or break clients | Rename freely, number stays ✓ |
| Version URLs | /v1, /v2, /v3... forever | One URL forever ✓ |
| Payload size | Large (names in every message) | 3-10x smaller ✓ |
| Parse speed | Slow (string parsing) | Fast (binary) ✓ |
| Type safety | Runtime errors | Compile-time errors ✓ |
| Debugging | Human readable ✓ | Need tools |

**AT-Jet's Approach: Best of Both Worlds**

```
┌─────────────────────────────────────────────────────────────────────────┐
│                                                                          │
│   Production:  Protobuf (efficient, type-safe, evolvable)               │
│   Debugging:   JSON with debug key (human-readable)                     │
│                                                                          │
│   ┌─────────────────────────────────────────────────────────────────┐   │
│   │  Mobile App (Production)                                        │   │
│   │  Content-Type: application/x-protobuf                           │   │
│   │  → Fast, small, schema evolution works                          │   │
│   └─────────────────────────────────────────────────────────────────┘   │
│                                                                          │
│   ┌─────────────────────────────────────────────────────────────────┐   │
│   │  Developer (Debugging)                                          │   │
│   │  Content-Type: application/json                                 │   │
│   │  X-Debug-Format: <debug-key>                                    │   │
│   │  → Human-readable, easy to inspect                              │   │
│   └─────────────────────────────────────────────────────────────────┘   │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘
```

This gives you:
- **Production efficiency** - Protobuf for mobile clients
- **Schema evolution** - No more version URL nightmare
- **Developer experience** - JSON available for debugging/testing
- **Safety** - JSON requires debug key, preventing accidental production use

---

### Decision 1: HTTP/1.1 + HTTP/2 (Not gRPC)

**Why not gRPC?**

| Factor | gRPC | AT-Jet (HTTP + Protobuf) |
|--------|------|-------------------------|
| CDN caching | ❌ Difficult | ✅ Standard HTTP caching |
| Network compatibility | HTTP/2 only | HTTP/1.1 + HTTP/2 |
| Browser debugging | Requires special tools | Standard browser devtools |
| Load balancer support | Specialized L7 | Any HTTP LB |
| Complexity | Bidirectional streams | Simple request/response |

For mobile APIs with simple request/response patterns, HTTP + Protobuf provides better infrastructure compatibility without sacrificing the benefits of protobuf.

#### Why gRPC Has Poor CDN Compatibility

CDN caching is critical for mobile apps serving global users - it reduces latency and server load. gRPC struggles with CDNs for several reasons:

**1. POST for Everything (Not Cacheable)**

```
REST/AT-Jet:                          gRPC:
GET /users/123                        POST /UserService/GetUser
     │                                     │
     ▼                                     ▼
CDN sees GET request ──▶ CACHEABLE    CDN sees POST request ──▶ NOT CACHED
```

gRPC uses POST for ALL operations, including reads. CDNs don't cache POST requests by default because POST typically means "create/modify data."

**2. No Natural Cache Keys**

```
REST/AT-Jet:
  URL: /api/users/123?fields=name,email
       └────────────────────────────────┘
                  Cache Key (obvious)

gRPC:
  URL: /UserService/GetUser
  Body: <binary: id=123, fields=[name,email]>
        └─────────────────────────────────────┘
          CDN cannot inspect binary body to form cache key
```

**3. gRPC-Specific Binary Framing**

gRPC adds a 5-byte prefix to every message that CDNs don't understand:

```
Standard HTTP Response:              gRPC Response:
┌─────────────────────┐              ┌─────────────────────┐
│ HTTP Headers        │              │ HTTP Headers        │
├─────────────────────┤              ├─────────────────────┤
│ Protobuf Body       │              │ 1 byte: compressed? │ ← gRPC framing
│ (pure binary)       │              │ 4 bytes: length     │ ← gRPC framing
└─────────────────────┘              │ Protobuf Body       │
                                     ├─────────────────────┤
                                     │ HTTP Trailers       │ ← Status code here!
                                     └─────────────────────┘
```

**4. HTTP Trailers for Status**

gRPC sends status codes in HTTP trailers (after the body), not in HTTP status codes:

```
REST/AT-Jet:                          gRPC:
HTTP/1.1 404 Not Found                HTTP/2 200 OK
                                      ...body...
                                      grpc-status: 5 (NOT_FOUND)  ← Trailer
```

Many CDNs and proxies either don't support trailers or strip them, breaking gRPC error handling.

**5. HTTP/2 Requirement**

gRPC requires HTTP/2. While modern CDNs support HTTP/2, some edge cases cause issues:
- Origin connections may fall back to HTTP/1.1
- Some corporate proxies don't support HTTP/2
- Mobile networks in developing regions may have HTTP/2 issues

**Summary: CDN Compatibility Comparison**

| Aspect | gRPC | AT-Jet (HTTP + Protobuf) |
|--------|------|--------------------------|
| Read requests | POST (not cacheable) | GET (cacheable by default) |
| Cache-Control | Non-standard | Standard HTTP headers work |
| Response body | 5-byte prefix + proto | Pure protobuf (or JSON) |
| Status codes | In HTTP trailers | Standard HTTP status codes |
| Cache key | Requires body inspection | URL + query params |
| CDN support | Needs gRPC-aware proxy | Any HTTP CDN works |

### Decision 2: Axum as Foundation

Why axum over alternatives?

| Framework | Pros | Cons |
|-----------|------|------|
| **axum** | Type-safe extractors, tower ecosystem, active development | Newer |
| actix-web | Mature, fast | Actor model complexity |
| warp | Composable filters | Less intuitive |
| rocket | Declarative | Sync-first design |

Axum's extractor pattern maps perfectly to our Protobuf request/response model.

### Decision 3: Separate from ZUS-RS

AT-Jet is a separate project from ZUS-RS because:

1. **Different transport**: HTTP vs TCP
2. **Different users**: Mobile clients vs backend services
3. **Different dependencies**: axum/reqwest vs tokio-only
4. **Different concerns**: CDN compatibility vs low latency

However, they work together:

```
Mobile Client ──HTTP/Protobuf──▶ AT-Jet API ──ZUS-RS──▶ Backend Services
```

### Decision 4: Convention Over Configuration

AT-Jet makes opinionated choices:

| Choice | Value | Rationale |
|--------|-------|-----------|
| Content-Type | `application/x-protobuf` | Industry standard |
| Max body size | 10MB | Reasonable for mobile uploads |
| Default timeout | 30s | Matches common mobile timeouts |
| Compression | gzip | Universal support |

These can be overridden, but sensible defaults reduce configuration burden.

### Decision 5: Dual-Format with Debug Key Authorization

AT-Jet supports both Protobuf (production) and JSON (debugging), but **JSON requires explicit authorization** via the `X-Debug-Format` header.

**Why not allow unrestricted JSON?**

```
┌─────────────────────────────────────────────────────────────────────┐
│  Schema Evolution Problem                                           │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  Protobuf:                          JSON:                           │
│  ┌─────────────────────┐           ┌─────────────────────┐          │
│  │ message User {      │           │ { "name": "John" }  │          │
│  │   string name = 1;  │           │                     │          │
│  │   int32 age = 2;    │ ← NEW     │ { "name": "John",   │          │
│  │ }                   │   FIELD     │   "age": 25 }     │ ← BREAKS │
│  └─────────────────────┘           └─────────────────────┘          │
│                                                                     │
│  Old client reads new data:                                         │
│  - Protobuf: ✅ Unknown field 2 is ignored                          │
│  - JSON: ❌ May fail if schema validation is strict                  │
│                                                                     │
│  New client reads old data:                                         │
│  - Protobuf: ✅ Missing field 2 uses default (0)                     │
│  - JSON: ❌ "age" key missing, null handling varies                  │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘
```

**The debug key approach:**

| Approach | Pros | Cons |
|----------|------|------|
| **Allow JSON freely** | Easy debugging | Clients may use in production, breaking on schema changes |
| **Disable JSON completely** | Safe | Poor developer experience |
| **Debug key authorization** ✅ | Safe + debuggable | Requires key distribution |

We chose debug key authorization because:
1. **Production safety** - Clients can't accidentally use JSON
2. **Developer experience** - JSON available for debugging/testing
3. **Auditability** - Keys identify who's using JSON
4. **Flexibility** - Keys can be rotated or revoked

**Implementation:**

```rust
// Configure at startup (empty = JSON disabled)
configure_debug_keys(vec![
    "alice-dev-key".to_string(),
    "bob-dev-key".to_string(),
]);

// Handler uses dual-format types
async fn create_user(
    ApiRequest { body, format }: ApiRequest<CreateUserRequest>
) -> ApiResponse<User> {
    ApiResponse::ok(format, user)
}

// Client must provide valid key for JSON
// X-Debug-Format: alice-dev-key
```

**Format selection flow:**

```
Request arrives
     │
     ▼
┌─────────────────────────────────────┐
│ Content-Type: application/json?     │
└─────────────────────────────────────┘
     │ Yes                    │ No
     ▼                        ▼
┌─────────────────┐    ┌─────────────────┐
│ X-Debug-Format  │    │ Decode as       │
│ header valid?   │    │ Protobuf        │
└─────────────────┘    └─────────────────┘
     │ Yes    │ No
     ▼        ▼
┌────────┐ ┌────────────┐
│ Decode │ │ 415 Error  │
│ as JSON│ │ (rejected) │
└────────┘ └────────────┘
```

## What AT-Jet is NOT

1. **Not a general-purpose web framework** - Use axum directly for HTML, JSON APIs
2. **Not a replacement for ZUS-RS** - Different use cases (see [BEST_PRACTICES.md](../../mirror_projects/zus-rs/docs/BEST_PRACTICES.md))
3. **Not a gRPC alternative** - If you need streaming, use gRPC
4. **Not opinionated about proto definitions** - Bring your own .proto files

## Comparison with Alternatives

### vs. Using axum + prost directly

| Aspect | Raw axum + prost | AT-Jet |
|--------|-----------------|--------|
| Lines of code per handler | ~30 | ~5 |
| Error handling | Manual | Unified |
| Content negotiation | Manual | Automatic |
| Client library | Write your own | Included |
| JWT auth middleware | Write your own | Built-in (`jwt` feature) |
| Tracing setup | Manual | `init_tracing()` with sensible defaults |
| Metrics | Manual | Built-in (`metrics` feature) |
| Graceful shutdown | Manual | `serve_with_shutdown()` |
| Team conventions | External docs | Embedded |

### vs. tonic (gRPC)

| Aspect | tonic | AT-Jet |
|--------|-------|--------|
| Protocol | gRPC/HTTP2 | HTTP/1.1+2 |
| CDN support | Poor | Excellent |
| Service definition | Required .proto | Optional |
| Learning curve | gRPC concepts | REST concepts |
| Streaming | Bidirectional | Request/Response only |

### vs. JSON APIs

| Aspect | JSON | AT-Jet (Protobuf) |
|--------|------|-------------------|
| Payload size | Larger | 3-10x smaller |
| Parse speed | Slower | Faster |
| Schema evolution | Manual | Built-in |
| Type safety | Runtime | Compile-time |
| Debugging | Human-readable | JSON available with debug key |

Note: AT-Jet provides JSON format for debugging/testing when a valid debug key is provided via `X-Debug-Format` header. This gives you the best of both worlds: efficient Protobuf in production, human-readable JSON during development.

## Future Roadmap

### Phase 1: Core Framework (v0.1–v0.4)
- [x] Server with Protobuf extractors
- [x] Client with type-safe requests
- [x] Basic middleware (CORS, compression, tracing)
- [x] Error handling framework
- [x] Dual-format support (Protobuf + JSON with debug key authorization)

### Phase 2: Production Hardening (v0.5–v0.6, current)
- [ ] Rate limiting middleware
- [x] Authentication middleware (Basic Auth)
- [x] JWT authentication — optional `jwt` feature with HMAC-based validation and middleware layer
- [x] Session management — optional `session` feature with in-memory store, TTL, and idle timeout
- [ ] Request ID propagation
- [x] Metrics (Prometheus) — optional `metrics` feature with HTTP instrumentation and scrape endpoint
- [x] Smart tracing — health/metrics endpoints automatically downgraded to TRACE level
- [x] Tracing initialization — unified setup with optional Jaeger/OpenTelemetry (`tracing-otel` feature)
- [x] Graceful shutdown — `serve_with_shutdown()` for clean Ctrl+C handling
- [x] Startup banner — pre-tracing service info output for k8s environments

### Phase 3: Developer Experience
- [ ] OpenAPI spec generation from proto
- [ ] Proto-to-client codegen
- [ ] Testing utilities
- [ ] Documentation generator

### Phase 4: Advanced Features
- [ ] Response caching with ETags
- [ ] Partial responses (field masks)
- [ ] Batch endpoints
- [ ] Optional ZUS-RS integration for backend calls

## Conclusion

AT-Jet exists because:

1. **Mobile users deserve efficient APIs** - Protobuf reduces bandwidth and battery usage
2. **Teams deserve consistency** - Shared patterns reduce bugs and onboarding time
3. **Infrastructure deserves compatibility** - HTTP works everywhere
4. **Developers deserve productivity** - Less boilerplate, more features

It's not a wrapper—it's an **opinionated, production-ready foundation** for mobile API development at AT.
