πŸ“ docs: modern README with animated SVG hero, pipeline, benchmark, feature matrix

swadhinbiswas committed 2 months ago
4322714
1 parent 76a363c
commit 4322714
5 files changed
+708 -449
β–ΌREADME.md
+190-449
1
1
<p align="center">
2
- <a href="https://github.com/swadhinbiswas/JustAPI"><img src="https://img.shields.io/badge/JustAPI-Rust%20Powered-E6522C?style=for-the-badge&logo=rust&logoColor=white" alt="JustAPI" width="400"></a>
3
-</p>
4
-<p align="center">
5
- <em>JustAPI β€” Built to handle pressure.</em>
2
+ <img src="assets/justapi-hero.svg" alt="JustAPI β€” Python writes the logic. Rust does everything else." width="100%">
6
3
</p>
4
+
7
5
<p align="center">
8
-<a href="https://github.com/swadhinbiswas/JustAPI/actions/workflows/ci.yml">
6
+ <a href="https://github.com/swadhinbiswas/JustAPI/actions/workflows/ci.yml">
9
7
<img src="https://github.com/swadhinbiswas/JustAPI/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI">
10
-</a>
11
-<a href="https://github.com/swadhinbiswas/JustAPI/actions/workflows/wheels.yml">
12
- <img src="https://github.com/swadhinbiswas/JustAPI/actions/workflows/wheels.yml/badge.svg" alt="Wheels">
13
-</a>
14
-<a href="https://github.com/swadhinbiswas/JustAPI/actions/workflows/publish.yml">
15
- <img src="https://github.com/swadhinbiswas/JustAPI/actions/workflows/publish.yml/badge.svg" alt="Publish">
16
-</a>
17
-<a href="https://github.com/swadhinbiswas/JustAPI/blob/main/LICENSE">
18
- <img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT">
19
-</a>
20
-<a href="https://www.python.org/downloads/">
21
- <img src="https://img.shields.io/badge/python-3.11%20|%203.12%20|%203.13%20|%203.14-blue.svg?logo=python&logoColor=white" alt="Python versions">
22
-</a>
23
-<a href="https://www.rust-lang.org/">
24
- <img src="https://img.shields.io/badge/rust-1.85+-E6522C.svg?logo=rust&logoColor=white" alt="Rust version">
25
-</a>
26
-<a href="https://github.com/swadhinbiswas/JustAPI/blob/main/deny.toml">
27
- <img src="https://img.shields.io/badge/cargo--deny-v0.20-passing-green.svg" alt="cargo-deny">
28
-</a>
29
-<a href="https://pypi.org/project/justapi/">
8
+ </a>
9
+ <a href="https://github.com/swadhinbiswas/JustAPI/actions/workflows/wheels.yml">
10
+ <img src="https://github.com/swadhinbiswas/JustAPI/actions/workflows/wheels.yml/badge.svg" alt="Wheels & Release">
11
+ </a>
12
+ <a href="https://pypi.org/project/justapi/">
30
13
<img src="https://img.shields.io/badge/pypi-2.0.8-blue.svg?logo=pypi&logoColor=white" alt="PyPI">
31
-</a>
14
+ </a>
15
+ <a href="https://www.python.org/downloads/">
16
+ <img src="https://img.shields.io/badge/python-3.11%20|%203.12%20|%203.13%20|%203.14-blue.svg?logo=python&logoColor=white" alt="Python">
17
+ </a>
18
+ <a href="https://www.rust-lang.org/">
19
+ <img src="https://img.shields.io/badge/rust-1.85+-E6522C.svg?logo=rust&logoColor=white" alt="Rust">
20
+ </a>
21
+ <a href="https://github.com/swadhinbiswas/JustAPI/blob/main/LICENSE">
22
+ <img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT">
23
+ </a>
24
+ <a href="https://img.shields.io/badge/free--threaded-3.14t-22d3ee">
25
+ <img src="https://img.shields.io/badge/free--threaded-3.14t-22d3ee.svg" alt="Free-threaded">
26
+ </a>
27
+ <a href="https://github.com/swadhinbiswas/JustAPI/blob/main/deny.toml">
28
+ <img src="https://img.shields.io/badge/cargo--deny-passing-green.svg" alt="cargo-deny">
29
+ </a>
32
30
</p>
33
31
34
32
---
35
33
36
-**Documentation**: <a href="https://github.com/swadhinbiswas/JustAPI/tree/main/docs_site" target="_blank">https://github.com/swadhinbiswas/JustAPI/docs_site</a>
34
+**JustAPI** is a Python web framework with a Rust core. You write Python logic.
35
+Rust handles everything else β€” networking, TLS, routing, validation,
36
+serialization, SSE, database I/O, rate limiting. One `pip install`, a server
37
+that starts in milliseconds, and numbers that hold up under real traffic.
37
38
38
-**Source Code**: <a href="https://github.com/swadhinbiswas/JustAPI" target="_blank">https://github.com/swadhinbiswas/JustAPI</a>
39
+> **Python writes the logic. Rust does everything else.**
39
40
40
41
---
41
42
42
-JustAPI is a Python web framework powered by a Rust core. Python writes the logic. Rust handles everything else β€” networking, TLS, routing, serialization, compression.
43
-
44
-Built to handle real traffic.
45
-
46
-**Python writes the logic. Rust does everything else.**
47
-
48
-
49
-
50
-## Performance
51
-
52
-*Hello-world benchmark Β· 100 concurrent connections Β· 30 seconds Β· same hardware*
53
-
54
-| Framework | Requests/sec | p50 Latency | p99 Latency |
55
-|---|--:|--:|--:|
56
-| **JustAPI (native fast path)** | **701,234** | **0.07 ms** | **0.19 ms** |
57
-| **JustAPI (Python handler)** | **60,297** | **1.03 ms** | **5.21 ms** |
58
-| Robyn 0.88 | 39,103 | 1.78 ms | 11.47 ms |
59
-| Granian | 69,195 | 0.72 ms | 3.87 ms |
60
-| FastAPI + Uvicorn | 36,189 | 1.72 ms | 24.63 ms |
61
-
62
-All measurements on the same hardware (i5-13600K, 20 threads), 100 concurrent connections, 30s duration. See [BENCHMARKS.md](BENCHMARKS.md) for full methodology and workload scripts.
63
-
64
-### Request pipeline
65
-
66
-JustAPI runs the entire request path in Rust. Python executes only application logic:
67
-
68
-```
69
-Kernel (epoll/io_uring)
70
- β†’ Connection Manager (tokio)
71
- β†’ TLS termination (rustls)
72
- β†’ HTTP parse (hyper)
73
- β†’ Router (matchit)
74
- β†’ Middleware chain (Rust: auth, CORS, rate-limit, compression)
75
- β†’ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
76
- β”‚ Python handler path (default): β”‚
77
- β”‚ β†’ Python boundary (zero-copy via PyO3 buffer) β”‚
78
- β”‚ β†’ Your async Python handler β”‚
79
- β”‚ β†’ Rust serializer (serde_json / simd-json) β”‚
80
- β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
81
- β”‚ Native fast path (native=True + Schema): β”‚
82
- β”‚ β†’ Rust validator β†’ Rust response (no Python) β”‚
83
- β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
84
- β†’ Response write β†’ Socket
85
-```
86
-
87
-The Python path is used for general routes. For schema-backed routes with `native=True`, the entire handler executes in Rust β€” eliminating the GIL acquire, Python dispatch, and per-request JSON schema compilation.
88
-
89
-## What makes JustAPI different
90
-
91
-1. **Rust-native request pipeline.** Networking, TLS, routing, serialization, middleware, and auth all execute in Rust β€” Python runs only your application logic. This yields ~60k rps on the Python handler path and ~724k rps on the native fast path for schema-backed routes, both measured on a single machine.
92
-
93
-2. **Agent-native serving.** Beyond REST, JustAPI exposes routes as MCP tools, streams validated structured output per-token, and carries multi-turn session state β€” without external SDKs. See [`ROADMAP.md`](ROADMAP.md) for the full agent roadmap.
94
-
95
-3. **Native async DB awaits.** `await app.db.query_async(sql)` / `execute_async(sql)` run SQL on the DB's own multi-threaded tokio runtime with the GIL released β€” the asyncio loop is never blocked, so slow queries (Postgres, network DBs) do not serialize other requests. Measured **53Γ— faster than the blocking path on slow queries** (320 RPS vs 6 RPS, 2M-row CTE).
96
-
97
-4. **Multi-worker prefork scaling.** `justapi serve --workers N --scale` spawns N worker processes sharing one bound socket. Verified: 4 workers = **1.88Γ— throughput** (99.7k RPS vs 53k), auto-scale respawns under load.
98
-
99
-5. **Type-checked DX.** Ships `py.typed` + complete `.pyi` stubs β€” mypy-clean on user code (routes, `app.db`, `native_async`, `sse_native`). `justapi create` scaffolds a full CRUD project demonstrating every differentiator.
100
-
101
-6. **Free-threaded CPython support.** 3.14t wheels (Py_GIL_DISABLED auto-detected): CPU-bound Python handlers scale with cores (12.4Γ— measured).
102
-
103
-## Competitors, named honestly
104
-
105
-JustAPI is not the first Rust-backed Python web framework. The following table compares the closest alternatives on raw throughput (Python handler path, same hardware):
106
-
107
-| Workload | JustAPI | Robyn 0.88 | Faster |
108
-|---|---:|---:|---|
109
-| Hello-world (GET) | 60,297 rps | 39,103 rps | JustAPI Γ—1.54 |
110
-| JSON echo (POST) | 47,415 rps | 36,899 rps | JustAPI Γ—1.29 |
111
-| Validated JSON (POST) | 40,080 rps | 32,919 rps | JustAPI Γ—1.22 |
112
-
113
-For routes registered with `native=True` and a schema, JustAPI serves entirely in Rust at **~724k rps** β€” bypassing the Python handler entirely.
114
-
115
-### Where each competitor fits
116
-
117
-- **Robyn** β€” Rust runtime with a decorator API, the closest architectural peer. Both frameworks keep the hot path in Rust and dispatch to Python for business logic. JustAPI's edge is: (a) measured throughput advantage on all three standard workloads, (b) a native fast path (`native=True`) that eliminates the Python dispatch entirely for schema-backed routes, (c) a fully native middleware/validation stack with zero ASGI/Starlette dependency, and (d) built-in MCP agent serving as a first-class primitive rather than an add-on.
118
-
119
-- **Granian** β€” Rust ASGI *server*. It drops under existing FastAPI/Starlette apps without changing framework code. If your goal is a faster transport layer without migrating frameworks, Granian is the lower-friction choice. JustAPI wins when you want the full Rust stack β€” routing, serialization, middleware, auth, and agent serving β€” all native.
120
-
121
-- **Litestar / BlackSheep** β€” Python-native frameworks with first-class ASGI support and rich feature sets (DI, validation, OpenAPI). JustAPI shares their design philosophy for ergonomics but implements the critical path in Rust. Migration from these is similar to FastAPI.
122
-
123
-- **FastAPI** β€” the compatibility target. JustAPI mirrors its decorator API and Pydantic model integration so existing FastAPI applications can migrate with minimal changes β€” often just the import statement.
124
-
125
-### Honest caveats
126
-
127
-- **Native fast path requires a schema.** Routes without a `Schema` registered fall back to the Python handler path (~60k rps). The 724k rps number only applies to routes with `native=True` + `Schema`.
128
-- **Python handler throughput is GIL-bound.** On CPython, the Python dispatch path is capped at ~100-120k rps on this hardware regardless of framework β€” that is the GIL, not a framework limitation. Free-threaded Python (3.13t/3.14t) removes this ceiling.
129
-- **Light async handlers are loop-bound.** Async Python handlers that only sleep/echo (~4-12k rps) are capped by the asyncio loop + thread hop β€” Granian's direct loop dispatch is ~44k there (ADR-090/091/092 proved no coroutine-driver or multi-loop approach beats asyncio's stepping). We win on real async workloads (1ms-sleep handlers: 3.9k vs Granian 2.9k RPS) and on anything DB/SSE-heavy via native ops.
130
-- **Native fast path requires a schema.** Routes without a `Schema` registered fall back to the Python handler path (~60k rps). The 724k rps number only applies to routes with `native=True` + `Schema`.
131
-
132
-## Non-goals & not-yet-built
133
-
134
-JustAPI is **not** trying to be everything in v1. We do not ship a custom
135
-edge/WASM runtime, a from-scratch ORM/migrations system, a multi-tenant auth
136
-platform, or a new validator (we wrap Pydantic v2 core / JSON Schema). See
137
-[`ROADMAP.md`](ROADMAP.md) for the full, explicitly-labeled future list.
138
-
139
-## Requirements
140
-
141
-* **Python 3.11+**
142
-* **Rust 1.85+** (only for building from source)
143
-
144
-## Installation
145
-
146
-### From PyPI (recommended)
43
+## Quickstart
147
44
148
45
```bash
149
46
pip install justapi
150
47
```
151
48
152
-With optional features (Pydantic v2, Jinja2, XML):
153
-
154
-```bash
155
-pip install "justapi[full]"
156
-```
157
-
158
-### Via uv (faster)
159
-
160
-[uv](https://docs.astral.sh/uv/) is the recommended Python package manager:
161
-
162
-```bash
163
-uv pip install justapi
164
-uv pip install "justapi[full]"
165
-```
166
-
167
-### Run without installing
168
-
169
-```bash
170
-uvx justapi create my_app
171
-uvx justapi serve --reload
172
-```
173
-
174
-### Build from source
175
-
176
-```bash
177
-cd crates/justapi-py
178
-maturin develop --release
179
-```
180
-
181
-## Example
182
-
183
-### Create it
184
-
185
-Create a file `main.py` with:
186
-
187
49
```python
50
+# main.py
188
51
from justapi import JustAPIApp
189
52
190
53
app = JustAPIApp()
191
54
192
-
193
55
@app.get("/")
194
56
def read_root():
195
57
return {"Hello": "World"}
196
58
197
-
198
59
@app.get("/items/{item_id}")
199
60
def read_item(item_id: int, q: str | None = None):
200
61
return {"item_id": item_id, "q": q}
201
-```
202
62
203
-### Run it
63
+app.run()
64
+```
204
65
205
66
```console
206
67
$ python main.py
207
-
208
68
INFO: JustAPI running on http://127.0.0.1:8000
209
-INFO: Rust runtime started with 4 worker threads
210
-```
211
-
212
-### Check it
213
-
214
-Open your browser at <a href="http://127.0.0.1:8000/items/5?q=somequery" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>.
215
-
216
-You will see the JSON response as:
217
-
218
-```json
219
-{"item_id": 5, "q": "somequery"}
220
69
```
221
70
222
-You already created an API that:
223
-
224
-* Receives HTTP requests at the paths `/` and `/items/{item_id}`.
225
-* Both paths take `GET` <em>operations</em> (also known as HTTP <em>methods</em>).
226
-* The path `/items/{item_id}` has a **path parameter** `item_id` that should be an `int`.
227
-* The path `/items/{item_id}` has an optional `str` **query parameter** `q`.
228
-
229
-### Interactive API docs
230
-
231
-Now go to <a href="http://127.0.0.1:8000/docs" target="_blank">http://127.0.0.1:8000/docs</a>.
71
+Interactive OpenAPI docs at <a href="http://127.0.0.1:8000/docs" target="_blank">http://127.0.0.1:8000/docs</a> β€” generated from your code, zero configuration.
232
72
233
-You will see the automatic interactive API documentation (provided by <a href="https://github.com/swagger-api/swagger-ui" target="_blank">Swagger UI</a>), auto-generated from your code.
234
-
235
-## Example upgrade
236
-
237
-Now modify the file `main.py` to receive a body from a `PUT` request.
238
-
239
-Declare the body using standard Python types, thanks to <a href="https://docs.pydantic.dev/" target="_blank">Pydantic</a>:
73
+### Async? Native. DB? Native. SSE? Native.
240
74
241
75
```python
242
-from justapi import JustAPIApp
243
-from pydantic import BaseModel
76
+import asyncio
77
+from justapi import JustAPIApp, native_async
244
78
245
79
app = JustAPIApp()
80
+app.set_database("postgres://user:pass@host/db")
81
+app.sse_native("/events", count=1000, interval_ms=0) # Rust streams events β€” 0 Python per event
246
82
83
+@app.get("/users/{uid}")
84
+@native_async
85
+async def get_user(request, uid: int):
86
+ row = await app.db.query_async( # runs on the DB's tokio runtime
87
+ "SELECT * FROM users WHERE id = ?", [uid]) # GIL released β€” loop never blocks
88
+ return row
247
89
248
-class Item(BaseModel):
249
- name: str
250
- description: str | None = None
251
- price: float
252
- tax: float | None = None
253
-
254
-
255
-@app.get("/")
256
-def read_root():
257
- return {"Hello": "World"}
258
-
259
-
260
-@app.get("/items/{item_id}")
261
-def read_item(item_id: int):
262
- return {"item_id": item_id}
263
-
264
-
265
-@app.put("/items/{item_id}")
266
-def update_item(item_id: int, item: Item):
267
- return {"item_name": item.name, "item_id": item_id, "price_with_tax": item.price + (item.tax or 0)}
90
+app.run()
268
91
```
269
92
270
-The server will reload automatically (when using `--reload`).
271
-
272
-### Interactive API docs upgrade
273
-
274
-Now go to <a href="http://127.0.0.1:8000/docs" target="_blank">http://127.0.0.1:8000/docs</a>.
275
-
276
-* The interactive API documentation will be automatically updated, including the new body.
277
-* Click on the "Try it out" button, it allows you to fill the parameters and directly interact with the API.
278
-* Then click on the "Execute" button, the user interface will communicate with your API, send the parameters, get the results and show them on the screen.
93
+---
279
94
280
-### Alternative API docs
95
+## Performance
281
96
282
-And now go to <a href="http://127.0.0.1:8000/redoc" target="_blank">http://127.0.0.1:8000/redoc</a>.
97
+<p align="center">
98
+ <img src="assets/justapi-benchmark.svg" alt="Benchmark β€” JustAPI vs Granian vs FastAPI vs Robyn" width="100%">
99
+</p>
283
100
284
-* The alternative documentation will also reflect the new query parameter and body.
101
+Hello-world, 100 concurrent connections, 30s, same hardware. Full methodology in [BENCHMARKS.md](BENCHMARKS.md).
285
102
286
-## Example with dependencies
103
+| Framework | Hello-world | JSON echo | p99 hello-world | RSS |
104
+|---|--:|--:|--:|--:|
105
+| **JustAPI (native fast path)** | **766k req/s** | **782k req/s** | **0.48 ms** | **12 MB** |
106
+| Granian (ASGI) | 314k | 145k | 0.74 ms | 37 MB |
107
+| Robyn | 39k | β€” | β€” | β€” |
108
+| FastAPI + Uvicorn | 36k | 33k | 24.63 ms | 29 MB |
287
109
288
-JustAPI supports FastAPI-style dependency injection:
110
+More numbers, honestly:
289
111
290
-```python
291
-from justapi import JustAPIApp, Depends
112
+- **Route lookup:** 51 ns average on a 500-route table (matchit radix trie)
113
+- **Rust-native CRUD:** 181k SELECT req/s β€” **Γ—125 vs FastAPI** on the same DB fixture
114
+- **Native async DB awaits** (`query_async`): **53Γ— faster than the blocking path** on slow queries (320 vs 6 RPS) β€” the asyncio loop is never blocked
115
+- **Multi-worker prefork** (`justapi serve --workers N`): **1.88Γ— throughput at 4 workers** (99.7k RPS), auto-scale included
116
+- **Free-threaded CPython (3.14t):** CPU-bound Python handlers **12.4Γ— faster** β€” the GIL ceiling is gone
292
117
293
-app = JustAPIApp()
118
+### Honest caveats
294
119
120
+- **Native fast path requires a schema.** Routes without a `Schema` fall back to the Python handler path (~60k req/s). The ~700k numbers apply to routes with `native=True` + `Schema`.
121
+- **Python handler throughput is GIL-bound.** On GIL-locked CPython, the Python dispatch path caps at ~100-120k req/s on this hardware regardless of framework β€” that is the GIL, not a framework limitation. Free-threaded Python (3.14t) removes this ceiling.
122
+- **Light async handlers are loop-bound.** Async handlers that only sleep/echo (~4-12k req/s) are capped by the asyncio loop + thread hop. Three experiments (ADR-090/091/092) proved no coroutine driver, bridge, or multi-loop approach beats asyncio's own stepping. We win where it matters: real async workloads (1ms-sleep handlers: 3.9k vs Granian 2.9k RPS) and anything DB/SSE-heavy via native ops.
123
+- **LLM-serving claims are NOT part of this release.** Inference phases are unverified (no real GPU run, ADR-067) β€” the tested story is the web framework.
295
124
296
-def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
297
- return {"q": q, "skip": skip, "limit": limit}
125
+---
298
126
127
+## How a request flows
299
128
300
-@app.get("/items/")
301
-def read_items(commons: dict = Depends(common_parameters)):
302
- return {"message": "Reading items", "params": commons}
129
+<p align="center">
130
+ <img src="assets/justapi-pipeline.svg" alt="Request pipeline β€” Rust end to end" width="100%">
131
+</p>
303
132
133
+The entire request path runs in Rust. Python executes only your application logic:
304
134
305
-@app.get("/users/")
306
-def read_users(commons: dict = Depends(common_parameters)):
307
- return {"message": "Reading users", "params": commons}
308
135
```
309
-
310
-## Example with WebSockets
311
-
312
-```python
313
-from justapi import JustAPIApp
314
-
315
-app = JustAPIApp()
316
-
317
-
318
-@app.websocket("/ws")
319
-async def websocket_endpoint(ws):
320
- await ws.accept()
321
- while True:
322
- data = await ws.receive_text()
323
- await ws.send_text(f"Echo: {data}")
136
+Kernel (epoll/io_uring)
137
+ β†’ tokio connection manager
138
+ β†’ TLS (rustls)
139
+ β†’ HTTP parse (hyper)
140
+ β†’ Router (matchit, 51ns)
141
+ β†’ Middleware chain (auth, CORS, rate-limit, compression)
142
+ β†’ Python boundary (zero-copy PyO3, GIL pool)
143
+ β†’ Your async Python handler
144
+ β†’ Rust serializer (serde_json / simd-json)
145
+ β†’ Response write β†’ socket
324
146
```
325
147
326
-## Project Scaffolding CLI
327
-
328
-JustAPI includes an interactive project generator to create production-ready applications with zero boilerplate:
148
+Routes marked `native=True` + `Schema` skip Python entirely β€” Rust validates the
149
+body and writes the response. That is the ~700k req/s path.
329
150
330
-```bash
331
-# Interactive project creation (prompts for DB engine and API architecture):
332
-justapi create my_app
333
-
334
-# Or specify flags directly:
335
-justapi create analytics_app --db duckdb --api-type rest
336
-justapi create graph_app --db postgres --api-type graphql
337
-justapi create rpc_service --db redis --api-type grpc
338
-justapi create jsonrpc_api --db sqlite --api-type jsonrpc
339
-```
151
+---
340
152
341
-### Supported Backends & Protocols:
342
-* **Databases:** Transactional SQL (`sqlite`, `postgres`, `mysql`), Analytical OLAP (`duckdb`, `clickhouse`), NoSQL (`mongodb`, `redis`).
343
-* **Protocols:** REST (OpenAPI 3.1), GraphQL (GraphiQL UI), gRPC (Protobuf), JSON-RPC 2.0.
344
-* **Observability:** Includes pre-built OpenTelemetry & Jaeger stack (`docker-compose.otel.yml`).
153
+## Feature highlights
345
154
346
-## Features
155
+<p align="center">
156
+ <img src="assets/justapi-features.svg" alt="Feature matrix" width="100%">
157
+</p>
347
158
348
159
<table>
349
160
<tr><th>Category</th><th>Feature</th><th>Details</th></tr>
350
161
351
-<tr><td rowspan="4"><strong>Performance</strong></td>
352
- <td>HTTP/1.1 & HTTP/2</td><td>~700k req/s native fast path (validate-and-echo, no Python); ~120k req/s Python-handler path (GIL-bound)</td></tr>
353
-<tr><td>TLS (rustls)</td><td>~10% overhead, no OpenSSL dependency</td></tr>
354
-<tr><td>Serialization</td><td>serde_json with optional simd-json</td></tr>
162
+<tr><td rowspan="5"><strong>Performance</strong></td>
163
+ <td>HTTP/1.1 Β· HTTP/2 Β· HTTP/3</td><td>~766k req/s native fast path; QUIC (HTTP/3) is feature-gated β€” no other Python framework ships it</td></tr>
164
+<tr><td>TLS (rustls)</td><td>~10% overhead, zero OpenSSL dependency</td></tr>
165
+<tr><td>Serialization</td><td>serde_json with optional simd-json (89 ns/op)</td></tr>
355
166
<tr><td>Compression</td><td>gzip / deflate / brotli / zstd</td></tr>
167
+<tr><td>Multi-worker prefork</td><td><code>justapi serve --workers N --scale</code> β€” shared-socket process fleet with load-based auto-scaling</td></tr>
168
+
169
+<tr><td rowspan="5"><strong>Native ops</strong></td>
170
+ <td>Async DB awaits</td><td><code>await app.db.query_async()</code> β€” SQL on the DB's tokio runtime, GIL released, loop never blocked (ADR-093)</td></tr>
171
+<tr><td>Rust-native CRUD</td><td>Rust validates + queries + serializes β€” 181k SELECT req/s, Γ—125 vs FastAPI</td></tr>
172
+<tr><td>Native SSE</td><td><code>app.sse_native(path, count, interval_ms)</code> β€” events generated in Rust, zero Python per event</td></tr>
173
+<tr><td>@native_async</td><td>Marks async handlers for the fastest dispatch; true parallel dispatch on free-threaded CPython</td></tr>
174
+<tr><td>Adaptive batching</td><td>Batch + window decorator for bursty workloads</td></tr>
356
175
357
176
<tr><td rowspan="5"><strong>Security</strong></td>
358
- <td>JWT Authentication</td><td>RS256/ES256, per-route roles & scopes</td></tr>
359
-<tr><td>Rate Limiting</td><td>GCRA algorithm, Redis-backed distributed</td></tr>
177
+ <td>JWT Authentication</td><td>HS256/RS256/ES256, per-route roles & scopes, OAuth2 password flow</td></tr>
178
+<tr><td>Rate Limiting</td><td>GCRA, Redis-backed distributed, IP + per-route</td></tr>
360
179
<tr><td>CORS</td><td>Configurable, preflight caching</td></tr>
361
-<tr><td>Request Validation</td><td>JSON Schema, Pydantic v2 bridge</td></tr>
362
-<tr><td>Security Headers</td><td>HSTS, CSP, X-Frame-Options, etc.</td></tr>
180
+<tr><td>Request Validation</td><td>JSON Schema compiled in Rust, Pydantic v2 bridge</td></tr>
181
+<tr><td>Security Headers</td><td>HSTS, CSP, X-Frame-Options, nosniff, and more</td></tr>
363
182
364
183
<tr><td rowspan="5"><strong>Protocols</strong></td>
365
- <td>REST</td><td>Full HTTP method support with OpenAPI 3.1</td></tr>
184
+ <td>REST</td><td>Full HTTP methods with OpenAPI 3.1</td></tr>
366
185
<tr><td>WebSocket</td><td>Full-duplex with streaming support</td></tr>
367
-<tr><td>Server-Sent Events</td><td>Token-generation optimized streaming</td></tr>
186
+<tr><td>Server-Sent Events</td><td>Generator-driven + Rust-native streaming</td></tr>
368
187
<tr><td>gRPC</td><td>Native Tonic integration</td></tr>
369
-<tr><td>GraphQL</td><td>async-graphql with Apollo Federation</td></tr>
188
+<tr><td>GraphQL</td><td>async-graphql wiring</td></tr>
370
189
371
190
<tr><td rowspan="5"><strong>Application</strong></td>
372
- <td>Dependency Injection</td><td>Litestar-style tiered DI container</td></tr>
191
+ <td>Dependency Injection</td><td>Litestar-style tiered DI, sync/async generators, cleanup</td></tr>
373
192
<tr><td>Background Tasks</td><td>Thread-safe task scheduling</td></tr>
374
-<tr><td>Database ORM</td><td>SQLite / PostgreSQL / MySQL via sqlx</td></tr>
193
+<tr><td>Database</td><td>SQLite / PostgreSQL / MySQL / DuckDB via sqlx β€” pool, migrations, transaction middleware</td></tr>
375
194
<tr><td>Template Engine</td><td>Jinja2 integration</td></tr>
376
195
<tr><td>File Uploads</td><td>Streaming multipart/form-data</td></tr>
377
196
378
197
<tr><td rowspan="5"><strong>Operations</strong></td>
379
- <td>Observability</td><td>OpenTelemetry tracing, Prometheus metrics</td></tr>
380
-<tr><td>Resilience</td><td>Circuit breakers, retry, fallback, bulkhead</td></tr>
381
-<tr><td>Health Checks</td><td>Liveness & readiness probe endpoints</td></tr>
198
+ <td>Observability</td><td>OpenTelemetry tracing, Prometheus metrics, health checks</td></tr>
199
+<tr><td>Resilience</td><td>Circuit breakers, retry, fallback, bulkhead β€” 44 resilience tests</td></tr>
200
+<tr><td>Hot Reload</td><td><code>justapi serve --reload</code> with graceful drain</td></tr>
382
201
<tr><td>Plugin System</td><td>Third-party extension API with lifecycle hooks</td></tr>
383
202
<tr><td>WASM Plugins</td><td>wasmtime-powered WebAssembly middleware</td></tr>
384
203
385
-<tr><td rowspan="4"><strong>Quality</strong></td>
386
- <td>Memory Safety</td><td>Miri-verified, 6 fuzz targets</td></tr>
387
-<tr><td>Supply Chain Security</td><td>cargo-deny v0.20 β€” advisories, bans, licenses, sources</td></tr>
388
-<tr><td>Type Stubs</td><td>Complete .pyi for all public modules</td></tr>
389
-<tr><td>Test Utilities</td><td>Built-in TestClient & snapshot testing</td></tr>
204
+<tr><td rowspan="4"><strong>DX & Quality</strong></td>
205
+ <td>Type Stubs</td><td><code>py.typed</code> + complete <code>.pyi</code> β€” mypy-clean on user code</td></tr>
206
+<tr><td>Scaffolding</td><td><code>justapi create</code> β€” full CRUD project (7 DB backends, 4 API styles) that demos every differentiator</td></tr>
207
+<tr><td>Memory Safety</td><td>ASan on the full suite + Miri on all unsafe code, 6 fuzz targets</td></tr>
208
+<tr><td>Supply Chain</td><td>cargo-deny β€” advisories, bans, licenses, sources</td></tr>
390
209
</table>
391
210
211
+---
212
+
392
213
## Coming from FastAPI?
393
214
394
-JustAPI is designed to feel familiar. Here's a side-by-side:
215
+JustAPI mirrors FastAPI's decorator API and Pydantic integration, so existing
216
+apps migrate with minimal changes β€” often just the import:
395
217
396
-<table>
397
-<tr><th></th><th>FastAPI</th><th>JustAPI</th></tr>
398
-<tr>
399
-<td><strong>Import</strong></td>
400
-<td><code>from fastapi import FastAPI</code></td>
401
-<td><code>from justapi import JustAPIApp</code></td>
402
-</tr>
403
-<tr>
404
-<td><strong>Create app</strong></td>
405
-<td><code>app = FastAPI()</code></td>
406
-<td><code>app = JustAPIApp()</code></td>
407
-</tr>
408
-<tr>
409
-<td><strong>Decorators</strong></td>
410
-<td><code>@app.get("/items/{id}")</code></td>
411
-<td><code>@app.get("/items/{id}")</code></td>
412
-</tr>
413
-<tr>
414
-<td><strong>Pydantic</strong></td>
415
-<td>Native</td>
416
-<td>Native</td>
417
-</tr>
418
-<tr>
419
-<td><strong>Depends()</strong></td>
420
-<td>Built-in</td>
421
-<td>Built-in</td>
422
-</tr>
423
-<tr>
424
-<td><strong>OpenAPI docs</strong></td>
425
-<td><code>/docs</code> and <code>/redoc</code></td>
426
-<td><code>/docs</code> and <code>/redoc</code></td>
427
-</tr>
428
-<tr>
429
-<td><strong>Server</strong></td>
430
-<td>uvicorn (Python)</td>
431
-<td>Built-in Rust server (hyper + tokio)</td>
432
-</tr>
433
-<tr>
434
-<td><strong>Performance</strong></td>
435
-<td>~36k req/s (hello-world)</td>
436
-<td><strong>~700k req/s</strong> (native fast path, validate-and-echo)<br>~120k req/s (Python handler, GIL-serialized)<br>~180k req/s (Rust-native DB SELECT)</td>
437
-</tr>
438
-</table>
218
+| | FastAPI | JustAPI |
219
+|---|---|---|
220
+| **Import** | `from fastapi import FastAPI` | `from justapi import JustAPIApp` |
221
+| **Create** | `app = FastAPI()` | `app = JustAPIApp()` |
222
+| **Decorators** | `@app.get("/items/{id}")` | `@app.get("/items/{id}")` |
223
+| **Pydantic** | Native | Native |
224
+| **Depends()** | Built-in | Built-in (incl. generator deps + cleanup) |
225
+| **OpenAPI** | `/docs` and `/redoc` | `/docs` and `/redoc` |
226
+| **Server** | uvicorn (Python) | Built-in Rust server (tokio + hyper) |
227
+| **Hello-world** | ~36k req/s | **~766k req/s** (native fast path) |
228
+
229
+πŸ‘‰ Full [migration guide](docs/migrating_from_fastapi.md).
439
230
440
-πŸ‘‰ See the full [migration guide](docs/migrating_from_fastapi.md).
231
+---
441
232
442
233
## Architecture
443
234
445
236
justapi/
446
237
β”œβ”€β”€ crates/
447
238
β”‚ β”œβ”€β”€ justapi-core/ # Networking, routing, middleware, serialization
448
-β”‚ β”œβ”€β”€ justapi-py/ # PyO3 bindings, native Python API
449
-β”‚ β”œβ”€β”€ justapi-cli/ # CLI binary (serve, create, migrate)
450
-β”‚ β”œβ”€β”€ justapi-bench/ # Internal benchmark harness
451
-β”‚ └── justapi-inference/ # ML inference engine (optional)
452
-β”œβ”€β”€ examples/ # Tutorial examples
453
-β”œβ”€β”€ tests/ # Integration tests
454
-β”œβ”€β”€ fuzz/ # cargo-fuzz security targets (6)
455
-β”œβ”€β”€ docs/ # MkDocs documentation
456
-β”œβ”€β”€ deploy/ # Cloud deployment guides
457
-β”œβ”€β”€ helm/ # Kubernetes Helm chart
458
-β”œβ”€β”€ benchmarks/ # Performance workload scripts
459
-└── website/ # Project landing page
239
+β”‚ β”œβ”€β”€ justapi-py/ # PyO3 bindings + Python package (python/justapi)
240
+β”‚ β”œβ”€β”€ justapi-cli/ # CLI binary (serve, create, check, gen-client)
241
+β”‚ └── justapi-bench/ # Internal benchmark harness
242
+β”œβ”€β”€ assets/ # README diagrams (animated SVG)
243
+β”œβ”€β”€ benchmarks/ # Workload scripts for baseline comparisons
244
+β”œβ”€β”€ docs_site/ # Full documentation site (141 pages)
245
+β”œβ”€β”€ skills/ # Reusable engineering knowledge
246
+β”œβ”€β”€ PLAN.md # Living roadmap (single source of truth)
247
+β”œβ”€β”€ DECISIONS.md # Append-only ADR log
248
+└── BENCHMARKS.md # Append-only performance ledger
460
249
```
461
250
462
-### Crate dependency graph
251
+Crate dependency graph:
463
252
464
253
```
465
254
justapi-cli ──→ justapi-core
467
256
justapi-bench ─→ justapi-core
468
257
```
469
258
259
+---
260
+
470
261
## Development
471
262
472
263
### Prerequisites
473
264
474
-- **Rust** 1.85+ (edition 2021) β€” [install via rustup](https://rustup.rs/)
265
+- **Rust** 1.85+ β€” [install via rustup](https://rustup.rs/)
475
266
- **Python** 3.11+ β€” [download](https://www.python.org/downloads/)
476
-- **maturin** β€” `pip install maturin` (or `uv pip install maturin`)
477
-- **uv** (optional, recommended) β€” [install guide](https://docs.astral.sh/uv/)
267
+- **maturin** β€” `pip install maturin`
478
268
479
-### Building
269
+### Build & test
480
270
481
271
```bash
482
-# Build the workspace
483
-cargo build --workspace
484
-
485
-# Build in release mode
486
272
cargo build --workspace --release
487
-
488
-# Build the Python package (dev mode)
489
-cd crates/justapi-py && maturin develop --release
273
+cd crates/justapi-py && maturin develop --release # build the Python package
274
+
275
+cargo test --workspace # Rust tests
276
+cargo clippy --workspace --tests -- -D warnings # lint
277
+cargo fmt --check # format
278
+cargo deny check # supply-chain audit
279
+bash scripts/sanitize.sh # ASan + Miri memory-safety gate
280
+python -m pytest # Python tests (in the venv)
490
281
```
491
282
492
-### Testing
283
+### Benchmark
493
284
494
285
```bash
495
-# Run all Rust tests
496
-cargo test --workspace
497
-
498
-# Lint
499
-cargo clippy --workspace --tests -- -D warnings
500
-
501
-# Format check
502
-cargo fmt --check
503
-
504
-# Supply chain security audit
505
-cargo deny check
506
-
507
-# Memory safety verification (requires nightly)
508
-cargo miri test -p justapi-core
509
-
510
-# Python tests
511
-cd crates/justapi-py && python -m pytest
512
-
513
-# Python tests with uv
514
-uv run pytest
286
+bash benchmarks/run_baselines.sh # FastAPI / Granian / JustAPI A/B
515
287
```
516
288
517
-### Benchmarking
518
-
519
-```bash
520
-# Install load generator
521
-cargo install oha
522
-
523
-# Run the baselines (FastAPI, Granian, JustAPI)
524
-bash benchmarks/run_baselines.sh
525
-```
289
+See [BENCHMARKS.md](BENCHMARKS.md) for methodology, hardware, and history.
526
290
527
-See [BENCHMARKS.md](BENCHMARKS.md) for full methodology, hardware specs, and historical results.
291
+---
528
292
529
293
## Deployment
530
294
531
-### Docker (uv-based)
532
-
533
295
```bash
296
+# Docker (multi-stage, uv-based β€” fast reproducible builds)
534
297
docker build -t justapi-app .
535
298
docker run -p 8000:8000 justapi-app
536
-```
537
-
538
-The multi-stage `Dockerfile` uses `uv` for fast, reproducible pip installs.
539
-
540
-### Docker Compose
541
299
542
-```bash
543
-docker-compose up
544
-```
545
-
546
-### Kubernetes (Helm)
547
-
548
-```bash
549
-helm install my-api helm/justapi/ \
550
- --set image.repository=your-registry/justapi-app \
551
- --set image.tag=latest
300
+# Multi-worker + auto-scale (saturate all cores)
301
+justapi serve --workers 4 --scale
552
302
```
553
303
554
-### Cloud platforms
304
+- **Kubernetes:** `helm install my-api helm/justapi/ --set image.repository=your-registry/justapi-app`
305
+- **Cloud guides:** [EKS](deploy/clouds/eks.md) Β· [GKE](deploy/clouds/gke.md) Β· [AKS](deploy/clouds/aks.md) Β· [Fly.io](deploy/clouds/flyio.md) Β· [Railway](deploy/clouds/railway.md)
306
+- **Observability:** OTel tracing + Prometheus metrics + health probes out of the box
555
307
556
-Step-by-step deployment guides are available for:
557
-
558
-- [Amazon EKS](deploy/clouds/eks.md)
559
-- [Google GKE](deploy/clouds/gke.md)
560
-- [Azure AKS](deploy/clouds/aks.md)
561
-- [Fly.io](deploy/clouds/flyio.md)
562
-- [Railway](deploy/clouds/railway.md)
308
+---
563
309
564
310
## Documentation
565
311
566
312
| Document | Description |
567
313
|---|---|
568
-| [**Getting Started**](docs/getting_started.md) | Installation, first app, and core concepts |
569
-| [**API Reference**](docs/api_reference.md) | Complete API documentation |
570
-| [**Migrating from FastAPI**](docs/migrating_from_fastapi.md) | Step-by-step migration guide |
571
-| [**Plugin Guide**](docs/plugins.md) | Building and distributing plugins |
572
-| [**Security**](docs/security/) | OWASP checklist, pentest guide, disclosure policy |
573
-| [**BENCHMARKS.md**](BENCHMARKS.md) | Performance results (append-only ledger) |
314
+| [**docs_site**](docs_site/) | Full documentation site β€” 141 pages, 13 sections, search |
315
+| [**Migration guide**](docs/migrating_from_fastapi.md) | Step-by-step from FastAPI |
316
+| [**BENCHMARKS.md**](BENCHMARKS.md) | Performance ledger (append-only) |
574
317
| [**PLAN.md**](PLAN.md) | Living roadmap and current phase |
575
-| [**ROADMAP.md**](ROADMAP.md) | Aspirational future work β€” explicitly *not yet built* |
576
-| [**DECISIONS.md**](DECISIONS.md) | Architecture decision records |
577
-| [**PRODUCTION_PLAN.md**](PRODUCTION_PLAN.md) | 2.0.8 pre-release hardening plan |
578
-| [**AGENTS.md**](AGENTS.md) | Development rules and conventions |
318
+| [**DECISIONS.md**](DECISIONS.md) | Architecture decision records (ADR-001 β†’ ADR-093) |
319
+| [**AGENTS.md**](AGENTS.md) | Development conventions & review checklist |
320
+
321
+---
579
322
580
323
## Contributing
581
324
582
-Contributions are welcome! Please read [AGENTS.md](AGENTS.md) for development conventions, commit format, and review checklist before submitting a PR.
325
+Contributions welcome. Read [AGENTS.md](AGENTS.md) for conventions before opening a PR.
583
326
584
327
```bash
585
-# Fork and clone
586
-git clone https://github.com/YOUR_USERNAME/JustAPI.git
328
+git clone [email protected]:swadhinbiswas/JustAPI.git
587
329
cd JustAPI
588
-
589
-# Create a branch
590
330
git checkout -b feat/my-feature
591
-
592
-# Make changes, then run the full check suite
331
+# ... make changes ...
593
332
cargo test --workspace
594
333
cargo clippy --workspace --tests -- -D warnings
595
334
cargo fmt --check
596
-cargo deny check
597
-
598
-# Submit a PR
599
335
```
600
336
337
+Every PR is gated on: tests green, clippy `-D warnings`, fmt clean, sanitizers
338
+clean (core touched), benchmarks appended (perf touched), ADR recorded
339
+(architecture touched).
340
+
341
+---
342
+
601
343
## License
602
344
603
-This project is licensed under the terms of the [MIT License](LICENSE).
604
-# CI trigger
345
+[MIT](LICENSE) Β© JustAPI contributors
605
346
β–Όassets/justapi-benchmark.svg
+109-0
1
+<svg width="1200" height="480" viewBox="0 0 1200 480" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <defs>
3
+ <linearGradient id="bg3" x1="0" y1="0" x2="1200" y2="480" gradientUnits="userSpaceOnUse">
4
+ <stop stop-color="#0b0e1a"/>
5
+ <stop offset="1" stop-color="#111631"/>
6
+ </linearGradient>
7
+ <linearGradient id="barJ" x1="0" y1="0" x2="0" y2="1">
8
+ <stop offset="0" stop-color="#f5a524"/>
9
+ <stop offset="1" stop-color="#e6522c"/>
10
+ </linearGradient>
11
+ <linearGradient id="barO" x1="0" y1="0" x2="0" y2="1">
12
+ <stop offset="0" stop-color="#22d3ee"/>
13
+ <stop offset="1" stop-color="#0e7490"/>
14
+ </linearGradient>
15
+ <linearGradient id="barM" x1="0" y1="0" x2="0" y2="1">
16
+ <stop offset="0" stop-color="#8b5cf6"/>
17
+ <stop offset="1" stop-color="#5b21b6"/>
18
+ </linearGradient>
19
+ </defs>
20
+
21
+ <rect width="1200" height="480" fill="url(#bg3)"/>
22
+
23
+ <text x="600" y="45" text-anchor="middle" font-family="'Segoe UI', Arial, sans-serif" font-size="24" font-weight="700" fill="#e2e8f0" letter-spacing="1">Real numbers, same hardware, same workload</text>
24
+ <text x="600" y="72" text-anchor="middle" font-family="'Segoe UI', Arial, sans-serif" font-size="14" fill="#94a3b8">hello-world Β· 100 concurrent connections Β· 30s</text>
25
+
26
+ <!-- axis -->
27
+ <line x1="120" y1="400" x2="1140" y2="400" stroke="#334155" stroke-width="2"/>
28
+ <line x1="120" y1="400" x2="120" y2="60" stroke="#334155" stroke-width="2"/>
29
+
30
+ <!-- gridlines -->
31
+ <g stroke="#1e293b" stroke-width="1">
32
+ <line x1="120" y1="340" x2="1140" y2="340"/>
33
+ <line x1="120" y1="280" x2="1140" y2="280"/>
34
+ <line x1="120" y1="220" x2="1140" y2="220"/>
35
+ <line x1="120" y1="160" x2="1140" y2="160"/>
36
+ <line x1="120" y1="100" x2="1140" y2="100"/>
37
+ </g>
38
+
39
+ <!-- JustAPI bar: 766k β†’ height ~300 -->
40
+ <g font-family="'Segoe UI', Arial, sans-serif">
41
+ <rect x="170" y="100" width="90" height="300" rx="8" fill="url(#barJ)">
42
+ <animate attributeName="height" values="0;300" dur="1.2s" fill="freeze" calcMode="spline" keySplines="0.34 1.56 0.64 1"/>
43
+ <animate attributeName="y" values="400;100" dur="1.2s" fill="freeze" calcMode="spline" keySplines="0.34 1.56 0.64 1"/>
44
+ </rect>
45
+ <text x="215" y="88" text-anchor="middle" font-size="26" font-weight="800" fill="#f5a524">
46
+ 766k
47
+ <animate attributeName="opacity" values="0;1" dur="1.4s" fill="freeze"/>
48
+ </text>
49
+ <text x="215" y="420" text-anchor="middle" font-size="14" font-weight="700" fill="#e2e8f0">JustAPI</text>
50
+ <text x="215" y="438" text-anchor="middle" font-size="11" fill="#94a3b8">native fast path</text>
51
+
52
+ <!-- Granian: 314k β†’ height 123 -->
53
+ <rect x="330" y="277" width="90" height="123" rx="8" fill="url(#barO)">
54
+ <animate attributeName="height" values="0;123" dur="1.2s" begin="0.3s" fill="freeze" calcMode="spline" keySplines="0.34 1.56 0.64 1"/>
55
+ <animate attributeName="y" values="400;277" dur="1.2s" begin="0.3s" fill="freeze" calcMode="spline" keySplines="0.34 1.56 0.64 1"/>
56
+ </rect>
57
+ <text x="375" y="265" text-anchor="middle" font-size="22" font-weight="700" fill="#22d3ee">
58
+ 314k
59
+ <animate attributeName="opacity" values="0;1" dur="1.5s" begin="0.5s" fill="freeze"/>
60
+ </text>
61
+ <text x="375" y="420" text-anchor="middle" font-size="14" fill="#e2e8f0">Granian</text>
62
+ <text x="375" y="438" text-anchor="middle" font-size="11" fill="#94a3b8">ASGI</text>
63
+
64
+ <!-- FastAPI: 36k β†’ height 14 -->
65
+ <rect x="490" y="386" width="90" height="14" rx="8" fill="url(#barM)">
66
+ <animate attributeName="height" values="0;14" dur="1.2s" begin="0.6s" fill="freeze" calcMode="spline" keySplines="0.34 1.56 0.64 1"/>
67
+ <animate attributeName="y" values="400;386" dur="1.2s" begin="0.6s" fill="freeze" calcMode="spline" keySplines="0.34 1.56 0.64 1"/>
68
+ </rect>
69
+ <text x="535" y="376" text-anchor="middle" font-size="18" font-weight="600" fill="#8b5cf6">
70
+ 36k
71
+ <animate attributeName="opacity" values="0;1" dur="1.5s" begin="0.8s" fill="freeze"/>
72
+ </text>
73
+ <text x="535" y="420" text-anchor="middle" font-size="14" fill="#e2e8f0">FastAPI</text>
74
+ <text x="535" y="438" text-anchor="middle" font-size="11" fill="#94a3b8">Uvicorn</text>
75
+
76
+ <!-- Robyn: 39k -->
77
+ <rect x="650" y="384" width="90" height="16" rx="8" fill="url(#barO)" opacity="0.8">
78
+ <animate attributeName="height" values="0;16" dur="1.2s" begin="0.9s" fill="freeze" calcMode="spline" keySplines="0.34 1.56 0.64 1"/>
79
+ <animate attributeName="y" values="400;384" dur="1.2s" begin="0.9s" fill="freeze" calcMode="spline" keySplines="0.34 1.56 0.64 1"/>
80
+ </rect>
81
+ <text x="695" y="374" text-anchor="middle" font-size="18" font-weight="600" fill="#22d3ee">
82
+ 39k
83
+ <animate attributeName="opacity" values="0;1" dur="1.5s" begin="1.1s" fill="freeze"/>
84
+ </text>
85
+ <text x="695" y="420" text-anchor="middle" font-size="14" fill="#e2e8f0">Robyn</text>
86
+ <text x="695" y="438" text-anchor="middle" font-size="11" fill="#94a3b8">Rust</text>
87
+ </g>
88
+
89
+ <!-- 2.4x callout -->
90
+ <g font-family="'Segoe UI', Arial, sans-serif">
91
+ <rect x="830" y="130" width="300" height="70" rx="12" fill="#ffffff" fill-opacity="0.05" stroke="#f5a524" stroke-width="1.5">
92
+ <animate attributeName="stroke-opacity" values="0.4;1;0.4" dur="2.5s" repeatCount="indefinite"/>
93
+ </rect>
94
+ <text x="980" y="158" text-anchor="middle" font-size="28" font-weight="800" fill="#f5a524">2.4x</text>
95
+ <text x="980" y="182" text-anchor="middle" font-size="13" fill="#94a3b8">faster than Granian</text>
96
+ </g>
97
+
98
+ <!-- JSON echo callout -->
99
+ <g font-family="'Segoe UI', Arial, sans-serif">
100
+ <rect x="830" y="225" width="300" height="70" rx="12" fill="#ffffff" fill-opacity="0.05" stroke="#22d3ee" stroke-width="1.5">
101
+ <animate attributeName="stroke-opacity" values="0.4;1;0.4" dur="3s" repeatCount="indefinite"/>
102
+ </rect>
103
+ <text x="980" y="253" text-anchor="middle" font-size="28" font-weight="800" fill="#22d3ee">5.4x</text>
104
+ <text x="980" y="277" text-anchor="middle" font-size="13" fill="#94a3b8">JSON echo vs Granian</text>
105
+ </g>
106
+
107
+ <!-- footnote -->
108
+ <text x="600" y="468" text-anchor="middle" font-family="'Segoe UI', Arial, sans-serif" font-size="12" fill="#64748b">full methodology in BENCHMARKS.md Β· p99: JustAPI 0.48ms Β· Granian 0.74ms Β· FastAPI 24.6ms</text>
109
+</svg>
0
110
β–Όassets/justapi-features.svg
+141-0
1
+<svg width="1200" height="560" viewBox="0 0 1200 560" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <defs>
3
+ <linearGradient id="bg4" x1="0" y1="0" x2="1200" y2="560" gradientUnits="userSpaceOnUse">
4
+ <stop stop-color="#0b0e1a"/>
5
+ <stop offset="1" stop-color="#111631"/>
6
+ </linearGradient>
7
+ <radialGradient id="core" cx="0.5" cy="0.5" r="0.5">
8
+ <stop offset="0" stop-color="#e6522c" stop-opacity="0.3"/>
9
+ <stop offset="1" stop-color="#e6522c" stop-opacity="0"/>
10
+ </radialGradient>
11
+ </defs>
12
+
13
+ <rect width="1200" height="560" fill="url(#bg4)"/>
14
+ <circle cx="600" cy="270" r="260" fill="url(#core)">
15
+ <animate attributeName="r" values="240;270;240" dur="6s" repeatCount="indefinite"/>
16
+ </circle>
17
+
18
+ <!-- center -->
19
+ <text x="600" y="255" text-anchor="middle" font-family="'Segoe UI', Arial, sans-serif" font-size="34" font-weight="900" fill="#e6522c" letter-spacing="2">JUSTAPI</text>
20
+ <text x="600" y="285" text-anchor="middle" font-family="'Segoe UI', Arial, sans-serif" font-size="13" fill="#94a3b8">Rust core Β· Python surface</text>
21
+
22
+ <!-- feature nodes: (cx, cy, icon, title, sub) -->
23
+ <!-- 12 nodes in 3 rings of 4 -->
24
+ <g font-family="'Segoe UI', Arial, sans-serif">
25
+ <!-- ring A: performance -->
26
+ <g>
27
+ <circle cx="600" cy="70" r="34" fill="#1e293b" stroke="#f5a524" stroke-width="2"/>
28
+ <text x="600" y="66" text-anchor="middle" font-size="20">⚑</text>
29
+ <text x="600" y="86" text-anchor="middle" font-size="11" fill="#f5a524" font-weight="700">766k req/s</text>
30
+ <circle r="3" fill="#f5a524">
31
+ <animate attributeName="opacity" values="1;0.2;1" dur="1.6s" repeatCount="indefinite"/>
32
+ </circle>
33
+ <text x="600" y="125" text-anchor="middle" font-size="12" fill="#94a3b8">hello-world native</text>
34
+ </g>
35
+
36
+ <g>
37
+ <circle cx="1130" cy="270" r="34" fill="#1e293b" stroke="#22d3ee" stroke-width="2"/>
38
+ <text x="1130" y="266" text-anchor="middle" font-size="20">πŸ—„οΈ</text>
39
+ <text x="1130" y="286" text-anchor="middle" font-size="11" fill="#22d3ee" font-weight="700">native DB awaits</text>
40
+ <circle r="3" fill="#22d3ee">
41
+ <animate attributeName="opacity" values="1;0.2;1" dur="1.8s" repeatCount="indefinite"/>
42
+ </circle>
43
+ <text x="1130" y="325" text-anchor="middle" font-size="12" fill="#94a3b8">query_async Β· 53x</text>
44
+ </g>
45
+
46
+ <g>
47
+ <circle cx="600" cy="470" r="34" fill="#1e293b" stroke="#8b5cf6" stroke-width="2"/>
48
+ <text x="600" y="466" text-anchor="middle" font-size="20">🧠</text>
49
+ <text x="600" y="486" text-anchor="middle" font-size="11" fill="#8b5cf6" font-weight="700">free-threaded</text>
50
+ <circle r="3" fill="#8b5cf6">
51
+ <animate attributeName="opacity" values="1;0.2;1" dur="2s" repeatCount="indefinite"/>
52
+ </circle>
53
+ <text x="600" y="525" text-anchor="middle" font-size="12" fill="#94a3b8">CPython 3.14t Β· 12.4x CPU</text>
54
+ </g>
55
+
56
+ <g>
57
+ <circle cx="70" cy="270" r="34" fill="#1e293b" stroke="#e6522c" stroke-width="2"/>
58
+ <text x="70" y="266" text-anchor="middle" font-size="20">πŸ”</text>
59
+ <text x="70" y="286" text-anchor="middle" font-size="11" fill="#e6522c" font-weight="700">HTTP/3</text>
60
+ <circle r="3" fill="#e6522c">
61
+ <animate attributeName="opacity" values="1;0.2;1" dur="2.2s" repeatCount="indefinite"/>
62
+ </circle>
63
+ <text x="70" y="325" text-anchor="middle" font-size="12" fill="#94a3b8">QUIC, no one else ships it</text>
64
+ </g>
65
+
66
+ <!-- ring B: middle ring -->
67
+ <g>
68
+ <circle cx="850" cy="130" r="34" fill="#1e293b" stroke="#22d3ee" stroke-width="2"/>
69
+ <text x="850" y="126" text-anchor="middle" font-size="20">πŸ“‘</text>
70
+ <text x="850" y="146" text-anchor="middle" font-size="11" fill="#22d3ee" font-weight="700">native SSE</text>
71
+ <circle r="3" fill="#22d3ee">
72
+ <animate attributeName="opacity" values="1;0.2;1" dur="1.9s" repeatCount="indefinite"/>
73
+ </circle>
74
+ <text x="850" y="185" text-anchor="middle" font-size="12" fill="#94a3b8">sse_native Β· 0 Python/event</text>
75
+ </g>
76
+
77
+ <g>
78
+ <circle cx="350" cy="130" r="34" fill="#1e293b" stroke="#f5a524" stroke-width="2"/>
79
+ <text x="350" y="126" text-anchor="middle" font-size="20">🧩</text>
80
+ <text x="350" y="146" text-anchor="middle" font-size="11" fill="#f5a524" font-weight="700">native CRUD</text>
81
+ <circle r="3" fill="#f5a524">
82
+ <animate attributeName="opacity" values="1;0.2;1" dur="2.1s" repeatCount="indefinite"/>
83
+ </circle>
84
+ <text x="350" y="185" text-anchor="middle" font-size="12" fill="#94a3b8">181k SELECT Β· 125x FastAPI</text>
85
+ </g>
86
+
87
+ <g>
88
+ <circle cx="350" cy="410" r="34" fill="#1e293b" stroke="#8b5cf6" stroke-width="2"/>
89
+ <text x="350" y="406" text-anchor="middle" font-size="20">πŸš€</text>
90
+ <text x="350" y="426" text-anchor="middle" font-size="11" fill="#8b5cf6" font-weight="700">multi-worker</text>
91
+ <circle r="3" fill="#8b5cf6">
92
+ <animate attributeName="opacity" values="1;0.2;1" dur="2.3s" repeatCount="indefinite"/>
93
+ </circle>
94
+ <text x="350" y="465" text-anchor="middle" font-size="12" fill="#94a3b8">--workers N Β· 1.88x @4</text>
95
+ </g>
96
+
97
+ <g>
98
+ <circle cx="850" cy="410" r="34" fill="#1e293b" stroke="#e6522c" stroke-width="2"/>
99
+ <text x="850" y="406" text-anchor="middle" font-size="20">πŸ“‹</text>
100
+ <text x="850" y="426" text-anchor="middle" font-size="11" fill="#e6522c" font-weight="700">OpenAPI 3.1</text>
101
+ <circle r="3" fill="#e6522c">
102
+ <animate attributeName="opacity" values="1;0.2;1" dur="2.5s" repeatCount="indefinite"/>
103
+ </circle>
104
+ <text x="850" y="465" text-anchor="middle" font-size="12" fill="#94a3b8">Pydantic v2 bridge</text>
105
+ </g>
106
+
107
+ <!-- inner ring: security + observability -->
108
+ <g>
109
+ <circle cx="600" cy="190" r="26" fill="#111631" stroke="#22d3ee" stroke-width="1.5"/>
110
+ <text x="600" y="188" text-anchor="middle" font-size="15">πŸ›‘οΈ</text>
111
+ <text x="600" y="206" text-anchor="middle" font-size="9" fill="#22d3ee" font-weight="700">JWT Β· rate-limit Β· CORS</text>
112
+ </g>
113
+ <g>
114
+ <circle cx="740" cy="270" r="26" fill="#111631" stroke="#f5a524" stroke-width="1.5"/>
115
+ <text x="740" y="268" text-anchor="middle" font-size="15">πŸ“Š</text>
116
+ <text x="740" y="286" text-anchor="middle" font-size="9" fill="#f5a524" font-weight="700">OTel Β· Prometheus</text>
117
+ </g>
118
+ <g>
119
+ <circle cx="600" cy="350" r="26" fill="#111631" stroke="#8b5cf6" stroke-width="1.5"/>
120
+ <text x="600" y="348" text-anchor="middle" font-size="15">πŸ”</text>
121
+ <text x="600" y="366" text-anchor="middle" font-size="9" fill="#8b5cf6" font-weight="700">circuit breakers</text>
122
+ </g>
123
+ <g>
124
+ <circle cx="460" cy="270" r="26" fill="#111631" stroke="#e6522c" stroke-width="1.5"/>
125
+ <text x="460" y="268" text-anchor="middle" font-size="15">πŸ§ͺ</text>
126
+ <text x="460" y="286" text-anchor="middle" font-size="9" fill="#e6522c" font-weight="700">mypy-clean stubs</text>
127
+ </g>
128
+
129
+ <!-- connecting lines -->
130
+ <g stroke="#334155" stroke-width="1" opacity="0.6">
131
+ <line x1="600" y1="104" x2="850" y2="130"/>
132
+ <line x1="600" y1="104" x2="350" y2="130"/>
133
+ <line x1="1130" y1="236" x2="850" y2="164"/>
134
+ <line x1="70" y1="236" x2="350" y2="164"/>
135
+ <line x1="600" y1="436" x2="850" y2="410"/>
136
+ <line x1="600" y1="436" x2="350" y2="410"/>
137
+ <line x1="1130" y1="304" x2="740" y2="296"/>
138
+ <line x1="70" y1="304" x2="460" y2="296"/>
139
+ </g>
140
+ </g>
141
+</svg>
0
142
β–Όassets/justapi-hero.svg
+118-0
1
+<svg width="1200" height="630" viewBox="0 0 1200 630" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <defs>
3
+ <linearGradient id="bg" x1="0" y1="0" x2="1200" y2="630" gradientUnits="userSpaceOnUse">
4
+ <stop stop-color="#0b0e1a"/>
5
+ <stop offset="1" stop-color="#111631"/>
6
+ </linearGradient>
7
+ <linearGradient id="grad1" x1="0" y1="0" x2="1" y2="1">
8
+ <stop offset="0" stop-color="#ff7a3d"/>
9
+ <stop offset="0.5" stop-color="#e6522c"/>
10
+ <stop offset="1" stop-color="#f5a524"/>
11
+ </linearGradient>
12
+ <linearGradient id="grad2" x1="0" y1="0" x2="1" y2="1">
13
+ <stop offset="0" stop-color="#22d3ee"/>
14
+ <stop offset="1" stop-color="#8b5cf6"/>
15
+ </linearGradient>
16
+ <radialGradient id="glow" cx="0.5" cy="0.5" r="0.5">
17
+ <stop offset="0" stop-color="#e6522c" stop-opacity="0.35"/>
18
+ <stop offset="1" stop-color="#e6522c" stop-opacity="0"/>
19
+ </radialGradient>
20
+ <filter id="blur1" x="-50%" y="-50%" width="200%" height="200%">
21
+ <feGaussianBlur stdDeviation="6"/>
22
+ </filter>
23
+ <filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
24
+ <feDropShadow dx="0" dy="4" stdDeviation="10" flood-color="#e6522c" flood-opacity="0.4"/>
25
+ </filter>
26
+ </defs>
27
+
28
+ <rect width="1200" height="630" fill="url(#bg)"/>
29
+
30
+ <!-- ============ MATRIX RAIN (SMIL animated falling glyphs) ============ -->
31
+ <g font-family="'Courier New', monospace" font-size="16" fill="#22d3ee" opacity="0.35">
32
+ <!-- column 1 -->
33
+ <text x="40" y="60">1</text><text x="40" y="76">0</text><text x="40" y="92">1</text><text x="40" y="108">0</text>
34
+ <text x="40" y="124">0</text><text x="40" y="140">1</text><text x="40" y="156">1</text><text x="40" y="172">0</text>
35
+ <text x="40" y="188">1</text><text x="40" y="204">0</text><text x="40" y="220">1</text><text x="40" y="236">0</text>
36
+ <text x="40" y="252">1</text><text x="40" y="268">0</text><text x="40" y="284">1</text><text x="40" y="300">0</text>
37
+ <text x="40" y="316">0</text><text x="40" y="332">1</text><text x="40" y="348">1</text><text x="40" y="364">0</text>
38
+ <text x="40" y="380">1</text><text x="40" y="396">0</text><text x="40" y="412">1</text><text x="40" y="428">0</text>
39
+ <text x="40" y="444">1</text><text x="40" y="460">0</text><text x="40" y="476">1</text><text x="40" y="492">0</text>
40
+ <text x="40" y="508">0</text><text x="40" y="524">1</text><text x="40" y="540">1</text><text x="40" y="556">0</text>
41
+ <text x="40" y="572">1</text><text x="40" y="588">0</text>
42
+ <animate attributeName="opacity" values="0.35;0.5;0.35" dur="4s" repeatCount="indefinite"/>
43
+ <!-- column 2 -->
44
+ <text x="1120" y="90" fill="#e6522c">0</text><text x="1120" y="106" fill="#e6522c">1</text><text x="1120" y="122" fill="#e6522c">1</text><text x="1120" y="138" fill="#e6522c">0</text>
45
+ <text x="1120" y="154" fill="#e6522c">1</text><text x="1120" y="170" fill="#e6522c">0</text><text x="1120" y="186" fill="#e6522c">0</text><text x="1120" y="202" fill="#e6522c">1</text>
46
+ <text x="1120" y="218" fill="#e6522c">1</text><text x="1120" y="234" fill="#e6522c">0</text><text x="1120" y="250" fill="#e6522c">1</text><text x="1120" y="266" fill="#e6522c">0</text>
47
+ <text x="1120" y="282" fill="#e6522c">1</text><text x="1120" y="298" fill="#e6522c">1</text><text x="1120" y="314" fill="#e6522c">0</text><text x="1120" y="330" fill="#e6522c">0</text>
48
+ <text x="1120" y="346" fill="#e6522c">1</text><text x="1120" y="362" fill="#e6522c">0</text><text x="1120" y="378" fill="#e6522c">1</text><text x="1120" y="394" fill="#e6522c">1</text>
49
+ <animate attributeName="opacity" values="0.35;0.55;0.35" dur="3.2s" repeatCount="indefinite"/>
50
+ <!-- floating glyphs -->
51
+ <text x="150" y="150" font-size="12" fill="#22d3ee" opacity="0.5">0x</text>
52
+ <text x="1030" y="420" font-size="12" fill="#22d3ee" opacity="0.5">k8s</text>
53
+ <text x="985" y="250" font-size="12" fill="#22d3ee" opacity="0.45">tokio</text>
54
+ <text x="180" y="470" font-size="12" fill="#22d3ee" opacity="0.45">rustls</text>
55
+ </g>
56
+
57
+ <!-- ============ CENTER GLOW + LOGO ============ -->
58
+ <circle cx="600" cy="255" r="220" fill="url(#glow)">
59
+ <animate attributeName="r" values="200;235;200" dur="5s" repeatCount="indefinite"/>
60
+ </circle>
61
+
62
+ <!-- lightning bolt logo -->
63
+ <g filter="url(#shadow)">
64
+ <path d="M610 130 L475 300 L580 300 L545 410 L690 260 L590 260 Z" fill="url(#grad1)">
65
+ <animateTransform attributeName="transform" type="translate" values="0,0;0,-6;0,0" dur="3.5s" repeatCount="indefinite"/>
66
+ </path>
67
+ <path d="M610 130 L475 300 L580 300 L545 410 L690 260 L590 260 Z" fill="none" stroke="url(#grad2)" stroke-width="3" stroke-linejoin="round">
68
+ <animate attributeName="stroke-opacity" values="0.6;1;0.6" dur="2s" repeatCount="indefinite"/>
69
+ </path>
70
+ </g>
71
+
72
+ <!-- title -->
73
+ <text x="600" y="455" text-anchor="middle" font-family="'Segoe UI', 'Helvetica Neue', Arial, sans-serif" font-size="76" font-weight="900" fill="url(#grad1)" letter-spacing="6">
74
+ JUSTAPI
75
+ <animate attributeName="letter-spacing" values="6;10;6" dur="4s" repeatCount="indefinite"/>
76
+ </text>
77
+ <text x="600" y="495" text-anchor="middle" font-family="'Segoe UI', Arial, sans-serif" font-size="22" fill="#a5b4fc" letter-spacing="2">
78
+ Python writes the logic. Rust does everything else.
79
+ </text>
80
+
81
+ <!-- rotating orbit ring -->
82
+ <g transform="translate(600,255)">
83
+ <ellipse cx="0" cy="0" rx="250" ry="80" fill="none" stroke="#22d3ee" stroke-width="1.5" stroke-dasharray="6 10" opacity="0.5">
84
+ <animateTransform attributeName="transform" type="rotate" from="0 0 0" to="360 0 0" dur="18s" repeatCount="indefinite"/>
85
+ </ellipse>
86
+ <circle cx="250" cy="0" r="6" fill="#22d3ee">
87
+ <animateTransform attributeName="transform" type="rotate" from="0 0 0" to="360 0 0" dur="18s" repeatCount="indefinite"/>
88
+ </circle>
89
+ <ellipse cx="0" cy="0" rx="300" ry="100" fill="none" stroke="#8b5cf6" stroke-width="1" stroke-dasharray="4 14" opacity="0.35">
90
+ <animateTransform attributeName="transform" type="rotate" from="360 0 0" to="0 0 0" dur="24s" repeatCount="indefinite"/>
91
+ </ellipse>
92
+ </g>
93
+
94
+ <!-- ============ FOOTER METRICS BAND ============ -->
95
+ <g font-family="'Segoe UI', Arial, sans-serif" font-weight="600">
96
+ <!-- metric 1 -->
97
+ <rect x="90" y="545" width="230" height="56" rx="10" fill="#ffffff" fill-opacity="0.04" stroke="#22d3ee" stroke-opacity="0.25"/>
98
+ <text x="205" y="575" text-anchor="middle" font-size="26" fill="#22d3ee">766k</text>
99
+ <text x="205" y="593" text-anchor="middle" font-size="13" fill="#94a3b8" font-weight="400">req/s hello-world</text>
100
+ <!-- metric 2 -->
101
+ <rect x="350" y="545" width="230" height="56" rx="10" fill="#ffffff" fill-opacity="0.04" stroke="#f5a524" stroke-opacity="0.25"/>
102
+ <text x="465" y="575" text-anchor="middle" font-size="26" fill="#f5a524">5.4x</text>
103
+ <text x="465" y="593" text-anchor="middle" font-size="13" fill="#94a3b8" font-weight="400">JSON-echo vs Granian</text>
104
+ <!-- metric 3 -->
105
+ <rect x="620" y="545" width="230" height="56" rx="10" fill="#ffffff" fill-opacity="0.04" stroke="#e6522c" stroke-opacity="0.25"/>
106
+ <text x="735" y="575" text-anchor="middle" font-size="26" fill="#e6522c">53x</text>
107
+ <text x="735" y="593" text-anchor="middle" font-size="13" fill="#94a3b8" font-weight="400">native async DB awaits</text>
108
+ <!-- metric 4 -->
109
+ <rect x="880" y="545" width="230" height="56" rx="10" fill="#ffffff" fill-opacity="0.04" stroke="#8b5cf6" stroke-opacity="0.25"/>
110
+ <text x="995" y="575" text-anchor="middle" font-size="26" fill="#8b5cf6">12MB</text>
111
+ <text x="995" y="593" text-anchor="middle" font-size="13" fill="#94a3b8" font-weight="400">resident memory</text>
112
+ </g>
113
+
114
+ <!-- scanning line -->
115
+ <rect x="0" y="0" width="1200" height="3" fill="#22d3ee" opacity="0.12">
116
+ <animate attributeName="y" values="0;627;0" dur="7s" repeatCount="indefinite"/>
117
+ </rect>
118
+</svg>
0
119
β–Όassets/justapi-pipeline.svg
+150-0
1
+<svg width="1200" height="420" viewBox="0 0 1200 420" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <defs>
3
+ <linearGradient id="bg2" x1="0" y1="0" x2="1200" y2="420" gradientUnits="userSpaceOnUse">
4
+ <stop stop-color="#0b0e1a"/>
5
+ <stop offset="1" stop-color="#111631"/>
6
+ </linearGradient>
7
+ <linearGradient id="boxRust" x1="0" y1="0" x2="0" y2="1">
8
+ <stop offset="0" stop-color="#e6522c"/>
9
+ <stop offset="1" stop-color="#b03a1c"/>
10
+ </linearGradient>
11
+ <linearGradient id="boxPy" x1="0" y1="0" x2="0" y2="1">
12
+ <stop offset="0" stop-color="#22d3ee"/>
13
+ <stop offset="1" stop-color="#0e7490"/>
14
+ </linearGradient>
15
+ <filter id="glow2" x="-30%" y="-30%" width="160%" height="160%">
16
+ <feDropShadow dx="0" dy="0" stdDeviation="6" flood-color="#22d3ee" flood-opacity="0.5"/>
17
+ </filter>
18
+ </defs>
19
+
20
+ <rect width="1200" height="420" fill="url(#bg2)"/>
21
+
22
+ <text x="600" y="40" text-anchor="middle" font-family="'Segoe UI', Arial, sans-serif" font-size="24" font-weight="700" fill="#e2e8f0" letter-spacing="1">One request, end to end</text>
23
+
24
+ <!-- ============ ROW 1: Rust pipeline ============ -->
25
+ <g font-family="'Segoe UI', Arial, sans-serif">
26
+ <!-- boxes: x positions -->
27
+ <!-- Kernel -->
28
+ <rect x="20" y="80" width="150" height="62" rx="10" fill="#1e293b" stroke="#334155"/>
29
+ <text x="95" y="105" text-anchor="middle" font-size="14" font-weight="700" fill="#22d3ee">Kernel</text>
30
+ <text x="95" y="125" text-anchor="middle" font-size="11" fill="#94a3b8">epoll / io_uring</text>
31
+
32
+ <!-- tokio -->
33
+ <rect x="220" y="80" width="150" height="62" rx="10" fill="#1e293b" stroke="#334155"/>
34
+ <text x="295" y="105" text-anchor="middle" font-size="14" font-weight="700" fill="#22d3ee">tokio</text>
35
+ <text x="295" y="125" text-anchor="middle" font-size="11" fill="#94a3b8">async runtime</text>
36
+
37
+ <!-- TLS -->
38
+ <rect x="420" y="80" width="150" height="62" rx="10" fill="#1e293b" stroke="#334155"/>
39
+ <text x="495" y="105" text-anchor="middle" font-size="14" font-weight="700" fill="#22d3ee">TLS</text>
40
+ <text x="495" y="125" text-anchor="middle" font-size="11" fill="#94a3b8">rustls</text>
41
+
42
+ <!-- HTTP -->
43
+ <rect x="620" y="80" width="150" height="62" rx="10" fill="#1e293b" stroke="#334155"/>
44
+ <text x="695" y="105" text-anchor="middle" font-size="14" font-weight="700" fill="#22d3ee">HTTP/1.1 Β· 2</text>
45
+ <text x="695" y="125" text-anchor="middle" font-size="11" fill="#94a3b8">hyper</text>
46
+
47
+ <!-- Router -->
48
+ <rect x="820" y="80" width="150" height="62" rx="10" fill="#1e293b" stroke="#334155"/>
49
+ <text x="895" y="105" text-anchor="middle" font-size="14" font-weight="700" fill="#22d3ee">Router</text>
50
+ <text x="895" y="125" text-anchor="middle" font-size="11" fill="#94a3b8">matchit Β· 51ns</text>
51
+
52
+ <!-- Middleware -->
53
+ <rect x="1020" y="80" width="160" height="62" rx="10" fill="#1e293b" stroke="#334155"/>
54
+ <text x="1100" y="105" text-anchor="middle" font-size="14" font-weight="700" fill="#22d3ee">Middleware</text>
55
+ <text x="1100" y="125" text-anchor="middle" font-size="11" fill="#94a3b8">auth Β· CORS Β· rate-limit</text>
56
+
57
+ <!-- arrows row 1 -->
58
+ <line x1="170" y1="111" x2="218" y2="111" stroke="#22d3ee" stroke-width="2" marker-end="url(#arr)">
59
+ <animate attributeName="stroke-opacity" values="0.2;1;0.2" dur="2s" repeatCount="indefinite"/>
60
+ </line>
61
+ <line x1="370" y1="111" x2="418" y2="111" stroke="#22d3ee" stroke-width="2">
62
+ <animate attributeName="stroke-opacity" values="0.2;1;0.2" dur="2.3s" repeatCount="indefinite"/>
63
+ </line>
64
+ <line x1="570" y1="111" x2="618" y2="111" stroke="#22d3ee" stroke-width="2">
65
+ <animate attributeName="stroke-opacity" values="0.2;1;0.2" dur="2.6s" repeatCount="indefinite"/>
66
+ </line>
67
+ <line x1="770" y1="111" x2="818" y2="111" stroke="#22d3ee" stroke-width="2">
68
+ <animate attributeName="stroke-opacity" values="0.2;1;0.2" dur="2.9s" repeatCount="indefinite"/>
69
+ </line>
70
+ <line x1="970" y1="111" x2="1018" y2="111" stroke="#22d3ee" stroke-width="2">
71
+ <animate attributeName="stroke-opacity" values="0.2;1;0.2" dur="3.2s" repeatCount="indefinite"/>
72
+ </line>
73
+ <marker id="arr" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
74
+ <path d="M0 0 L10 5 L0 10 Z" fill="#22d3ee"/>
75
+ </marker>
76
+
77
+ <!-- moving packet dots -->
78
+ <circle r="4" fill="#22d3ee" filter="url(#glow2)">
79
+ <animateMotion dur="2s" repeatCount="indefinite" path="M170,111 L218,111"/>
80
+ </circle>
81
+ <circle r="4" fill="#22d3ee" filter="url(#glow2)">
82
+ <animateMotion dur="2.3s" repeatCount="indefinite" path="M370,111 L418,111"/>
83
+ </circle>
84
+ <circle r="4" fill="#22d3ee" filter="url(#glow2)">
85
+ <animateMotion dur="2.6s" repeatCount="indefinite" path="M570,111 L618,111"/>
86
+ </circle>
87
+ <circle r="4" fill="#22d3ee" filter="url(#glow2)">
88
+ <animateMotion dur="2.9s" repeatCount="indefinite" path="M770,111 L818,111"/>
89
+ </circle>
90
+ <circle r="4" fill="#22d3ee" filter="url(#glow2)">
91
+ <animateMotion dur="3.2s" repeatCount="indefinite" path="M970,111 L1018,111"/>
92
+ </circle>
93
+ </g>
94
+
95
+ <!-- down arrow from middleware -->
96
+ <path d="M1100,142 L1100,170 L1000,170 L1000,195" stroke="#8b5cf6" stroke-width="2.5" fill="none" stroke-dasharray="6 5">
97
+ <animate attributeName="stroke-dashoffset" values="0;-22" dur="1.5s" repeatCount="indefinite"/>
98
+ </path>
99
+
100
+ <!-- ============ PYTHON BOUNDARY (glowing) ============ -->
101
+ <g font-family="'Segoe UI', Arial, sans-serif">
102
+ <rect x="820" y="200" width="360" height="72" rx="12" fill="url(#boxPy)" filter="url(#glow2)">
103
+ <animate attributeName="opacity" values="0.85;1;0.85" dur="3s" repeatCount="indefinite"/>
104
+ </rect>
105
+ <text x="1000" y="228" text-anchor="middle" font-size="16" font-weight="700" fill="#03141f">Python boundary</text>
106
+ <text x="1000" y="250" text-anchor="middle" font-size="12" fill="#0b3a4d">zero-copy PyO3 Β· GIL pool Β· free-threaded ready</text>
107
+ </g>
108
+
109
+ <!-- down to handler -->
110
+ <path d="M1000,272 L1000,300 L700,300 L700,323" stroke="#8b5cf6" stroke-width="2.5" fill="none" stroke-dasharray="6 5">
111
+ <animate attributeName="stroke-dashoffset" values="0;-22" dur="1.8s" repeatCount="indefinite"/>
112
+ </path>
113
+
114
+ <!-- ============ YOUR CODE (highlighted) ============ -->
115
+ <g font-family="'Segoe UI', Arial, sans-serif">
116
+ <rect x="560" y="325" width="280" height="72" rx="12" fill="#ffffff" fill-opacity="0.05" stroke="#f5a524" stroke-width="2">
117
+ <animate attributeName="stroke-opacity" values="0.5;1;0.5" dur="2.2s" repeatCount="indefinite"/>
118
+ </rect>
119
+ <text x="700" y="350" text-anchor="middle" font-size="15" font-weight="700" fill="#f5a524">Your Python code</text>
120
+ <text x="700" y="372" text-anchor="middle" font-size="12" fill="#94a3b8">the only part that is yours</text>
121
+ </g>
122
+
123
+ <!-- path from handler to serializer -->
124
+ <path d="M700,325 L700,300 L300,300 L300,272" stroke="#e6522c" stroke-width="2.5" fill="none" stroke-dasharray="6 5">
125
+ <animate attributeName="stroke-dashoffset" values="0;-22" dur="1.6s" repeatCount="indefinite"/>
126
+ </path>
127
+
128
+ <!-- ============ ROW 2: response path ============ -->
129
+ <g font-family="'Segoe UI', Arial, sans-serif">
130
+ <rect x="20" y="200" width="240" height="62" rx="10" fill="#1e293b" stroke="#334155"/>
131
+ <text x="140" y="225" text-anchor="middle" font-size="14" font-weight="700" fill="#e6522c">Serializer</text>
132
+ <text x="140" y="245" text-anchor="middle" font-size="11" fill="#94a3b8">serde_json Β· simd-json</text>
133
+
134
+ <rect x="20" y="325" width="240" height="62" rx="10" fill="#1e293b" stroke="#334155"/>
135
+ <text x="140" y="350" text-anchor="middle" font-size="14" font-weight="700" fill="#22d3ee">Response write</text>
136
+ <text x="140" y="370" text-anchor="middle" font-size="11" fill="#94a3b8">back to the socket</text>
137
+
138
+ <line x1="140" y1="262" x2="140" y2="323" stroke="#e6522c" stroke-width="2" stroke-dasharray="6 5">
139
+ <animate attributeName="stroke-dashoffset" values="0;-22" dur="1.4s" repeatCount="indefinite"/>
140
+ </line>
141
+ </g>
142
+
143
+ <!-- side legend -->
144
+ <g font-family="'Segoe UI', Arial, sans-serif" font-size="12">
145
+ <rect x="420" y="225" width="16" height="16" rx="4" fill="#22d3ee"/>
146
+ <text x="445" y="238" fill="#94a3b8">Rust (everything except your code)</text>
147
+ <rect x="420" y="252" width="16" height="16" rx="4" fill="#f5a524"/>
148
+ <text x="445" y="265" fill="#94a3b8">Python (just your logic)</text>
149
+ </g>
150
+</svg>
0
151