at-jet 0.7.2

High-performance HTTP + Protobuf API framework for mobile services
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
# 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.