diff --git a/README.md b/README.md
index 7a33203c..c8e67d7e 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,11 @@
Smithy code generators for C++ — generate idiomatic C++ clients and servers from
[Smithy](https://smithy.io) models, plus the shared C++ runtime they build on.
+**Start here → [docs/quickstart.md](docs/quickstart.md):** empty directory to a generated C++
+client integration-testing a generated C++ server, in one Bazel module — no prior Smithy
+experience assumed. Day 2 (evolving the model) is
+[docs/model-evolution.md](docs/model-evolution.md).
+
- **Vendor-neutral:** implements Smithy and its protocol specs; nothing AWS-specific. The REST
protocol is [`alloy#simpleRestJson`](https://disneystreaming.github.io/smithy4s/docs/protocols/simple-rest-json/overview/)
(the neutral protocol smithy4s uses — so smithy-cpp clients and smithy4s services interoperate).
@@ -11,22 +16,40 @@ Smithy code generators for C++ — generate idiomatic C++ clients and servers fr
- **Bazel-native:** Bazel 9 is the sole supported build system for the repo and consumers.
- **Client tests server:** generated clients integration-test generated servers in CI.
-See [`docs/PLAN.md`](docs/PLAN.md) for the full phased plan and
-[`docs/adr/`](docs/adr/) for architecture decisions.
-
-## Status
-
-| Phase | Scope | Status |
-|---|---|---|
-| 0 | Foundations: Bazel 9 workspace, codegen skeleton, CI, ADRs, fixture models | ✅ Done |
-| 1 | C++ runtime library (`smithy/core`, `json`, `cbor`, `http`, `client`, `server`) | ✅ Done ([docs/runtime.md](docs/runtime.md)) incl. Boost.Beast production server transport (ADR-0006) |
-| 2 | Codegen plugin + type generation | ✅ Done — types generated for both fixtures, golden+compile+behavior tested ([docs/design/codegen-architecture.md](docs/design/codegen-architecture.md)) |
-| 3 | Client generation (simpleRestJson + rpcv2Cbor) | ✅ Done — serde + clients for both protocols with typed errors ([docs/generated-types.md](docs/generated-types.md)); official conformance suites green (documented exclusions) |
-| 4 | Server generation (simpleRestJson + rpcv2Cbor) | ✅ Done — handlers, routing, serde, all HTTP bindings incl. `@httpPayload`/`@httpPrefixHeaders`, constraint validation, parser strictness, content negotiation; official conformance suites green ([docs/server-guide.md](docs/server-guide.md)) |
-| 5 | Generated-client ↔ generated-server integration harness | ✅ Done — every fixture ships a generated integration suite: seeded random round-trips over loopback and real sockets, per-error mapping, unknown-member tolerance, mutation-checked ([docs/design/integration-testing.md](docs/design/integration-testing.md)) |
-| 6 | Bazel rules, CLI, packaging (BCR + Maven Central), docs site | 🔨 In progress — `smithy_cpp_{types,client,server}_library` rules run the generator hermetically inside the build graph, out-of-tree consumer module tested in CI, CLI via `bazel run //codegen:generator` ([docs/quickstart.md](docs/quickstart.md)); model-evolution loop documented and exercised in CI ([docs/model-evolution.md](docs/model-evolution.md)); BCR/Maven publishing deferred until production validation; docs site pending |
-| 7 | Hardening, fuzzing, v0.1.0 | ✅ Done — retries with full-jitter exponential backoff, gzip `@requestCompression` (client + server), client interceptors + server middleware (auth/logging/metrics seams), `@httpBearerAuth`/`@httpApiKeyAuth` credential wiring, generated `@paginated` paginators, Beast graceful drain + header limits, consumer CI across linux/macos/windows ([docs/production-guide.md](docs/production-guide.md)); **BeastHttpClient** production client transport with TLS both directions (ADR-0007); Google Benchmark suite (informational CI job); release engineering — [CHANGELOG](CHANGELOG.md), [versioning/compatibility policy](docs/versioning.md), **v0.1.0**; **protocol realignment to vendor-neutral** — dropped AWS `restJson1` for `alloy#simpleRestJson` + its conformance suite, Smithy 1.58, no `aws.*` on the generator classpath; **`smithy.cpp.protocols#jsonRpc2`** — JSON-RPC 2.0 over a single POST endpoint with an authored conformance suite, a calculator example with hand-rolled-peer interop tests, and a consumer overlay |
-| 8 | Bidirectional streaming (event streams, WebSockets) | Not started |
+## What works today (v0.1.0)
+
+- **Clients and servers for all three protocols**, each green against a conformance suite in CI
+ (the official alloy and rpcv2Cbor suites, an authored one for jsonRpc2) with documented,
+ must-shrink exclusion lists.
+- **The full generated surface:** typed structs/unions/enums, serde, HTTP bindings, constraint
+ validation with suite-exact `ValidationException` output (ReDoS-safe `@pattern`), typed
+ modeled errors, paginators, idempotency tokens, and gzip request compression.
+- **In-graph generation:** `smithy_cpp_{types,client,server}_library` run the generator
+ hermetically inside the Bazel build graph — no scripts, no JVM to install. An out-of-tree
+ consumer module is CI-tested on Linux/macOS/Windows, plus a CLI for generating elsewhere.
+- **Production serving and calling** over Boost.Beast with TLS in both directions, retries with
+ jittered backoff, client interceptors, server middleware, and bearer/API-key auth wiring.
+- **Hardening in CI:** sanitizer jobs, libFuzzer harnesses, hostile-input test banks, and every
+ fixture's generated client integration-testing its generated server.
+
+Not yet: bidirectional streaming (event streams/WebSockets) and Bazel Central Registry / Maven
+publishing — consumers pin a git commit for now. Roadmap and per-phase status live in
+[`docs/PLAN.md`](docs/PLAN.md).
+
+## Documentation
+
+| Doc | What it covers |
+|---|---|
+| [quickstart.md](docs/quickstart.md) | Model → generated client + server, from an empty directory |
+| [model-evolution.md](docs/model-evolution.md) | Day 2: changing the model, regeneration, drift detection |
+| [generated-types.md](docs/generated-types.md) | The Smithy → C++ mapping contract |
+| [server-guide.md](docs/server-guide.md) | What the generated server does before/after your handler |
+| [production-guide.md](docs/production-guide.md) | Real transports, TLS, retries, auth, middleware |
+| [runtime.md](docs/runtime.md) | The `smithy-cpp-runtime` library, module by module |
+| [development.md](docs/development.md) | Building, testing, and linting this repo |
+| [versioning.md](docs/versioning.md) | Compatibility policy; [CHANGELOG.md](CHANGELOG.md) has releases |
+| [PLAN.md](docs/PLAN.md) | The phased roadmap; [adr/](docs/adr) records architecture decisions |
+| [design/](docs/design) | Internals: codegen architecture, the integration-test harness; [fuzzing.md](docs/fuzzing.md) covers the fuzz setup |
## Building
diff --git a/docs/quickstart.md b/docs/quickstart.md
index 81941702..840cd5db 100644
--- a/docs/quickstart.md
+++ b/docs/quickstart.md
@@ -1,9 +1,44 @@
# Quick start: model → client + server in one Bazel module
-From an empty directory to a generated C++ client integration-testing a generated C++ server,
-without touching the generator's internals. The finished result of every step below lives at
+From an empty directory to a generated C++ client integration-testing a generated C++ server —
+no prior [Smithy](https://smithy.io) experience assumed, and no generator internals to learn.
+
+If Smithy is new to you: it's an interface-definition language. You describe a service once —
+its operations, their inputs and outputs, the errors they can raise — in a small `.smithy`
+text file, and code generators produce the client, the server scaffolding, and the wire
+handling in whatever language you need. smithy-cpp is that generator for C++. You write the
+model and the business logic; parsing, routing, validation, serialization, and error mapping
+are generated.
+
+The finished result of every step below lives at
[`examples/bazel-consumer/`](../examples/bazel-consumer) — CI builds that module standalone on
-every commit, so this tutorial cannot silently rot.
+every commit, so this tutorial cannot silently rot. How the pieces fit:
+
+```mermaid
+flowchart LR
+ subgraph model["you write: the model"]
+ M["model/todo.smithy
service + operations + error"]
+ B["model/bindings/simplerestjson.smithy
picks the wire protocol"]
+ end
+ subgraph gen["generated inside the build graph"]
+ C[":todo_client
TodoClient, typed inputs/outputs, serde"]
+ S[":todo_server
TodoServer: routing, parsing,
validation + TodoHandler interface"]
+ end
+ subgraph cpp["you write: the C++"]
+ H["MyHandler
implements TodoHandler"]
+ T["todo_integration_test.cc
client drives server end to end"]
+ end
+ M --> C
+ B --> C
+ M --> S
+ B --> S
+ S -. "pure-virtual
interface" .-> H
+ C --> T
+ H --> T
+```
+
+The integration test then drives the loop end to end: `TodoClient` → HTTP (in-memory loopback
+or a real socket) → `TodoServer` → your `MyHandler` → back out as a typed response.
## 1. Create a Bazel module
@@ -26,7 +61,8 @@ bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
bazel_dep(name = "rules_cc", version = "0.2.17")
```
-`.bazelrc` (C++20 is the runtime baseline; the generator runs on a hermetic Java 17 toolchain):
+`.bazelrc` (C++20 is the runtime baseline; the generator runs on a hermetic Java 17 toolchain,
+so you never install or invoke Java yourself):
```
common --enable_platform_specific_config
@@ -39,11 +75,102 @@ common --java_runtime_version=remotejdk_17
common --tool_java_runtime_version=remotejdk_17
```
-## 2. Write a model
+## 2. Write the model
-`model/todo.smithy` — a service, an operation or two, a modeled error. Keep the model
-protocol-agnostic, the upstream Smithy way: `@http` traits describe HTTP semantics without
-picking a wire protocol. Bind a concrete protocol in a small overlay file with `apply`:
+`model/todo.smithy` — a deliberately small task tracker: add a task, fetch it back, and one
+thing that can go wrong. This is the entire file:
+
+```smithy
+$version: "2.0"
+
+namespace acme.todo
+
+/// A tiny task tracker: create a task, fetch it back.
+service Todo {
+ version: "2026-01-01"
+ operations: [AddTask, GetTask]
+}
+
+@http(method: "POST", uri: "/tasks")
+operation AddTask {
+ input := {
+ @required
+ @length(min: 1, max: 256)
+ title: String
+ }
+
+ output := {
+ @required
+ taskId: String
+
+ @required
+ title: String
+ }
+}
+
+@readonly
+@http(method: "GET", uri: "/tasks/{taskId}")
+operation GetTask {
+ input := {
+ @required
+ @httpLabel
+ taskId: String
+ }
+
+ output := {
+ @required
+ taskId: String
+
+ @required
+ title: String
+
+ done: Boolean
+ }
+
+ errors: [NoSuchTask]
+}
+
+@error("client")
+@httpError(404)
+structure NoSuchTask {
+ @required
+ message: String
+}
+```
+
+Reading it as a Smithy newcomer:
+
+- **`namespace acme.todo`** scopes every name in the file; `acme.todo#Todo` is the service's
+ full identity (you'll pass it to the build rule in step 3).
+- **`service Todo`** is the entry point: it lists the operations clients can call. It becomes
+ `TodoClient`, the pure-virtual `TodoHandler` interface, and `TodoServer` in C++.
+- **`operation AddTask`** declares one callable action. The `input :=` / `output :=` blocks
+ are inline structure definitions — each becomes a plain C++ struct (`AddTaskInput`,
+ `AddTaskOutput`), and the operation becomes a method on both the client and the handler.
+- **The `@...` annotations are traits** — metadata attached to shapes and members. They do all
+ the heavy lifting:
+ - `@required` makes a member mandatory; non-required members map to `std::optional` in
+ C++ and to "absent on the wire" in JSON.
+ - `@length(min: 1, max: 256)` is a constraint: the generated *server* rejects violations
+ with a 400 `ValidationException` before your handler ever runs. (`@pattern`, `@range`,
+ and friends work the same way.)
+ - `@http` / `@httpLabel` describe HTTP semantics: `AddTask` is `POST /tasks` with the input
+ as the JSON body; `GetTask` binds `taskId` into the path as `GET /tasks/{taskId}`.
+ - `@error("client")` + `@httpError(404)` make `NoSuchTask` a modeled error: the server maps
+ it to a 404, and the client surfaces it as a *typed* value, not a string.
+- **`errors: [NoSuchTask]`** declares which errors an operation can raise, so both sides know
+ the full contract.
+
+On the wire, that model means:
+
+| Call | Request | Success | Error |
+|---|---|---|---|
+| `AddTask` | `POST /tasks` `{"title": "buy milk"}` | `{"taskId": "task-1", "title": "buy milk"}` | 400 `ValidationException` (e.g. empty title) |
+| `GetTask` | `GET /tasks/task-1` | `{"taskId": "task-1", "title": "buy milk", "done": false}` | 404 `NoSuchTask` |
+
+Notice the model never names a wire protocol — `@http` describes *HTTP semantics*, not an
+encoding. That's deliberate (and the upstream-Smithy way): the concrete protocol is bound in a
+tiny overlay file, using `apply` to attach a trait to the service from outside the base model:
```smithy
// model/bindings/simplerestjson.smithy
@@ -53,7 +180,9 @@ use alloy#simpleRestJson
apply Todo @simpleRestJson
```
-(Applying the trait directly on the service works too, if you only ever want one protocol.)
+(Applying the trait directly on the service works too, if you only ever want one protocol.
+Keeping it in an overlay lets the same model also serve rpcv2Cbor or jsonRpc2 — the consumer
+example binds all three side by side.)
## 3. Declare the generated libraries
@@ -94,22 +223,56 @@ done. (`smithy_cpp_types_library` exists too, for data types without a protocol.
## 4. Implement the handler and test it with the generated client
-The server library gives you a pure-virtual `TodoHandler` and a `TodoServer`; the client library
-a `TodoClient`. Wire them together over the in-memory loopback (or a real socket) exactly like
+The server library gives you a pure-virtual `TodoHandler`; implementing it is the only place
+business logic lives. One method per operation, typed input to typed `Outcome` (a value or an
+error — no exceptions):
+
+```cpp
+class InMemoryHandler final : public TodoHandler {
+ public:
+ smithy::Outcome AddTask(const AddTaskInput& input) override {
+ const std::string id = "task-" + std::to_string(next_id_++);
+ titles_[id] = input.title;
+ return AddTaskOutput{.taskId = id, .title = input.title};
+ }
+
+ smithy::Outcome GetTask(const GetTaskInput& input) override {
+ const auto it = titles_.find(input.taskId);
+ if (it == titles_.end()) {
+ smithy::Error error = smithy::Error::Modeled("NoSuchTask", "no task: " + input.taskId);
+ error.set_detail(NoSuchTask{.message = "no task: " + input.taskId});
+ return error; // the server turns this into the modeled 404
+ }
+ return GetTaskOutput{.taskId = input.taskId, .title = it->second, .done = false};
+ }
+
+ private:
+ int next_id_ = 1;
+ std::map titles_;
+};
+```
+
+Then wire the generated server to the generated client over the in-memory loopback (or a real
+socket) exactly like
[`todo_integration_test.cc`](../examples/bazel-consumer/todo_integration_test.cc):
```cpp
-TodoServer server(std::make_shared());
+TodoServer server(std::make_shared());
auto loopback = std::make_shared();
(void)loopback->Start(server.Handler());
smithy::ClientConfig config;
config.http_client = loopback;
auto client = *TodoClient::Create(std::move(config));
+
+auto added = client.AddTask(AddTaskInput{.title = "buy milk"}); // Outcome
+auto missing = client.GetTask(GetTaskInput{.taskId = "nope"});
+// missing.error().detail() is the typed 404 from the handler.
```
-Routing, serde, constraint validation (400 `ValidationException` before your handler runs),
-content negotiation, and modeled-error mapping are all generated — see
-[server-guide.md](server-guide.md) for what the server does on your behalf.
+Everything between the client call and your handler — routing, JSON parsing, constraint
+validation (400 `ValidationException` before your handler runs), content negotiation, and
+modeled-error mapping — is generated; see [server-guide.md](server-guide.md) for what the
+server does on your behalf.
## 5. Run it
@@ -139,3 +302,12 @@ Once the integration is running, the model keeps changing — new fields, new op
tightened constraints. [model-evolution.md](model-evolution.md) covers that loop: how edits
propagate through the build graph (or through regeneration for vendored output), how to review
generated diffs, and how CI catches drift and unimplemented handler methods.
+
+## Where to go next
+
+- [generated-types.md](generated-types.md) — the full Smithy → C++ mapping contract.
+- [server-guide.md](server-guide.md) — everything the generated server does before and after
+ your handler.
+- [production-guide.md](production-guide.md) — real transports, TLS, retries, middleware.
+- [Smithy's own docs](https://smithy.io/2.0/quickstart.html) — the language beyond what this
+ tutorial uses.