From cbd1e56145128f8533f1e83cdf766d8f52696f11 Mon Sep 17 00:00:00 2001 From: jermbo Date: Thu, 9 Jul 2026 21:53:01 -0400 Subject: [PATCH 1/6] docs: add glossary to enhance project vocabulary and navigation - Introduced a new glossary document to define key terms and concepts used throughout the project, aiding both newcomers and existing contributors in understanding the shared vocabulary. - Updated the README to include a link to the new glossary, improving documentation navigation and accessibility for users seeking clarification on terminology. --- docs/README.md | 22 ++-- docs/future-features/README.md | 24 ++-- docs/future-features/auth-training-wheels.md | 5 +- docs/future-features/error-practice-routes.md | 13 +- docs/future-features/guided-challenges.md | 1 + docs/future-features/http-inspector.md | 3 +- .../multi-language-snippets.md | 1 + docs/future-features/plans/README.md | 69 ++++++++++ .../plans/auth-training-wheels-decisions.md | 101 +++++++++++++++ .../auth-training-wheels-implementation.md | 105 +++++++++++++++ .../plans/error-practice-routes-decisions.md | 82 ++++++++++++ .../error-practice-routes-implementation.md | 84 ++++++++++++ .../plans/guided-challenges-decisions.md | 81 ++++++++++++ .../plans/guided-challenges-implementation.md | 120 ++++++++++++++++++ .../plans/http-inspector-decisions.md | 66 ++++++++++ .../plans/http-inspector-implementation.md | 103 +++++++++++++++ .../multi-language-snippets-decisions.md | 64 ++++++++++ .../multi-language-snippets-implementation.md | 74 +++++++++++ .../plans/query-builder-decisions.md | 68 ++++++++++ .../plans/query-builder-implementation.md | 100 +++++++++++++++ .../plans/response-shape-viewer-decisions.md | 66 ++++++++++ .../response-shape-viewer-implementation.md | 94 ++++++++++++++ .../plans/scratch-endpoints-decisions.md | 98 ++++++++++++++ .../plans/scratch-endpoints-implementation.md | 104 +++++++++++++++ .../shareable-playground-links-decisions.md | 64 ++++++++++ ...areable-playground-links-implementation.md | 83 ++++++++++++ docs/future-features/query-builder.md | 3 +- docs/future-features/response-shape-viewer.md | 3 +- docs/future-features/scratch-endpoints.md | 5 +- .../shareable-playground-links.md | 1 + docs/glossary.md | 64 ++++++++++ 31 files changed, 1737 insertions(+), 34 deletions(-) create mode 100644 docs/future-features/plans/README.md create mode 100644 docs/future-features/plans/auth-training-wheels-decisions.md create mode 100644 docs/future-features/plans/auth-training-wheels-implementation.md create mode 100644 docs/future-features/plans/error-practice-routes-decisions.md create mode 100644 docs/future-features/plans/error-practice-routes-implementation.md create mode 100644 docs/future-features/plans/guided-challenges-decisions.md create mode 100644 docs/future-features/plans/guided-challenges-implementation.md create mode 100644 docs/future-features/plans/http-inspector-decisions.md create mode 100644 docs/future-features/plans/http-inspector-implementation.md create mode 100644 docs/future-features/plans/multi-language-snippets-decisions.md create mode 100644 docs/future-features/plans/multi-language-snippets-implementation.md create mode 100644 docs/future-features/plans/query-builder-decisions.md create mode 100644 docs/future-features/plans/query-builder-implementation.md create mode 100644 docs/future-features/plans/response-shape-viewer-decisions.md create mode 100644 docs/future-features/plans/response-shape-viewer-implementation.md create mode 100644 docs/future-features/plans/scratch-endpoints-decisions.md create mode 100644 docs/future-features/plans/scratch-endpoints-implementation.md create mode 100644 docs/future-features/plans/shareable-playground-links-decisions.md create mode 100644 docs/future-features/plans/shareable-playground-links-implementation.md create mode 100644 docs/glossary.md diff --git a/docs/README.md b/docs/README.md index de836ef..f5cd3cf 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,16 +16,18 @@ Documentation for [SampleAPIs](https://sampleapis.com) — a free, no-auth playg ## Sections -| Section | What it covers | -| --- | --- | -| [Architecture](./architecture/README.md) | The two apps, the tech stack, and how a request flows | -| [API Surface](./api/README.md) | REST conventions, queries, CRUD, rate limits, errors, service routes | -| [Endpoint Data](./data/README.md) | The JSON datasets — format, registry, resets, custom endpoints | -| [Client Features](./features/README.md) | Routing, state, the Playground, styling | -| [Operations](./operations/README.md) | Local dev, Docker, deployment, testing | -| [Contributing](./contributing/README.md) | Setup, code style, pull request flow | -| [Decisions](./decisions/README.md) | Why it's built this way — trade-offs and revisit triggers | -| [Future Features](./future-features/README.md) | Proposed features under consideration, one evaluable proposal per page | +| Section | What it covers | +| ----------------------------------------------------------- | ---------------------------------------------------------------------- | +| [Architecture](./architecture/README.md) | The two apps, the tech stack, and how a request flows | +| [API Surface](./api/README.md) | REST conventions, queries, CRUD, rate limits, errors, service routes | +| [Endpoint Data](./data/README.md) | The JSON datasets — format, registry, resets, custom endpoints | +| [Client Features](./features/README.md) | Routing, state, the Playground, styling | +| [Operations](./operations/README.md) | Local dev, Docker, deployment, testing | +| [Contributing](./contributing/README.md) | Setup, code style, pull request flow | +| [Decisions](./decisions/README.md) | Why it's built this way — trade-offs and revisit triggers | +| [Future Features](./future-features/README.md) | Proposed features under consideration, one evaluable proposal per page | +| [Implementation Roadmap](./future-features/plans/README.md) | Per-feature implementation plans and decision logs, with review status | +| [Glossary](./glossary.md) | Shared vocabulary — project terms defined once | ## Page conventions diff --git a/docs/future-features/README.md b/docs/future-features/README.md index c7380fc..c119729 100644 --- a/docs/future-features/README.md +++ b/docs/future-features/README.md @@ -10,19 +10,21 @@ audience: [developer, architect] Feature proposals for taking SampleAPIs from "a place to fetch sample data" to a genuine learning tool. Each page stands alone: the problem it solves, a proposed approach grounded in the current code, effort and risk, and open questions to settle before building. Nothing here is committed work — pages carry `status: proposed` until one is accepted (move it into the relevant section and document what was built) or rejected (record why in [Decisions](../decisions/README.md)). +**Every proposal now has an implementation plan and a decision log.** Start at the **[Implementation Roadmap](./plans/README.md)** — it tracks review status per feature and explains how to answer the open decisions. + ## Proposals -| Page | One-liner | Learning impact | Effort | -| --- | --- | --- | --- | -| [HTTP Inspector](./http-inspector.md) | Mini Network tab next to the Playground console | High | Small | -| [Query Builder](./query-builder.md) | Visual UI for the filter/sort/pagination syntax | High | Small–Medium | -| [Guided Challenges](./guided-challenges.md) | Per-API exercises with validated solutions | Very high | Large | -| [Shareable Playground Links](./shareable-playground-links.md) | Encode playground code in a URL | Medium | Small | -| [Response Shape Viewer](./response-shape-viewer.md) | Derived field/type view per endpoint, copy as TypeScript | Medium | Small–Medium | -| [Multi-Language Snippets](./multi-language-snippets.md) | curl / Python / Node tabs for the current endpoint | Medium | Small | -| [Error Practice Routes](./error-practice-routes.md) | httpbin-style delay/status/flaky endpoints | High | Medium | -| [Auth Training Wheels](./auth-training-wheels.md) | Opt-in demo API that teaches token auth | Medium–High | Medium–Large | -| [Scratch Endpoints](./scratch-endpoints.md) | User-supplied temporary CRUD endpoints | High | Large | +| Page | One-liner | Learning impact | Effort | +| ------------------------------------------------------------- | -------------------------------------------------------- | --------------- | ------------ | +| [HTTP Inspector](./http-inspector.md) | Mini Network tab next to the Playground console | High | Small | +| [Query Builder](./query-builder.md) | Visual UI for the filter/sort/pagination syntax | High | Small–Medium | +| [Guided Challenges](./guided-challenges.md) | Per-API exercises with validated solutions | Very high | Large | +| [Shareable Playground Links](./shareable-playground-links.md) | Encode playground code in a URL | Medium | Small | +| [Response Shape Viewer](./response-shape-viewer.md) | Derived field/type view per endpoint, copy as TypeScript | Medium | Small–Medium | +| [Multi-Language Snippets](./multi-language-snippets.md) | curl / Python / Node tabs for the current endpoint | Medium | Small | +| [Error Practice Routes](./error-practice-routes.md) | httpbin-style delay/status/flaky endpoints | High | Medium | +| [Auth Training Wheels](./auth-training-wheels.md) | Opt-in demo API that teaches token auth | Medium–High | Medium–Large | +| [Scratch Endpoints](./scratch-endpoints.md) | User-supplied temporary CRUD endpoints | High | Large | ## Suggested sequence diff --git a/docs/future-features/auth-training-wheels.md b/docs/future-features/auth-training-wheels.md index 25cf4f6..00c6799 100644 --- a/docs/future-features/auth-training-wheels.md +++ b/docs/future-features/auth-training-wheels.md @@ -13,7 +13,7 @@ status: proposed ## Problem -SampleAPIs' pitch is *no auth* — that's what makes it frictionless. But authentication is the very next wall every learner hits on a real API: 401s, `Authorization` headers, expired tokens. There is currently nowhere to practice that flow in a consequence-free way; real APIs require signup, and mistakes there have stakes. +SampleAPIs' pitch is _no auth_ — that's what makes it frictionless. But authentication is the very next wall every learner hits on a real API: 401s, `Authorization` headers, expired tokens. There is currently nowhere to practice that flow in a consequence-free way; real APIs require signup, and mistakes there have stakes. ## Proposal @@ -46,7 +46,7 @@ Design points: ## Effort & risk -**Medium–large** — mechanically medium (signed tokens, middleware, one dataset, docs), but it needs design care: if the demo ever *feels* like the site is growing auth requirements, it damages the core promise. Naming, placement, and copy matter as much as code. CORS must expose the `WWW-Authenticate`/hint headers for browser learners. +**Medium–large** — mechanically medium (signed tokens, middleware, one dataset, docs), but it needs design care: if the demo ever _feels_ like the site is growing auth requirements, it damages the core promise. Naming, placement, and copy matter as much as code. CORS must expose the `WWW-Authenticate`/hint headers for browser learners. ## Open questions @@ -60,6 +60,7 @@ Design points: ## Related +- **Planning:** [Implementation plan](./plans/auth-training-wheels-implementation.md) · [Decision log](./plans/auth-training-wheels-decisions.md) - [Error Responses](../api/error-responses.md) - [Guided Challenges](./guided-challenges.md) - [REST Conventions](../api/rest-conventions.md) diff --git a/docs/future-features/error-practice-routes.md b/docs/future-features/error-practice-routes.md index 0083012..cec09fe 100644 --- a/docs/future-features/error-practice-routes.md +++ b/docs/future-features/error-practice-routes.md @@ -19,12 +19,12 @@ Real-world API work is mostly about what goes wrong — timeouts, 500s, flaky ne A small set of service routes (httpbin is the precedent), documented alongside the existing [Service Routes](../api/service-routes.md): -| Route | Behavior | Teaches | -| --- | --- | --- | -| `/service/delay/:seconds` | Responds after N seconds (capped, e.g. ≤ 10) | Timeouts, `AbortController`, loading states | -| `/service/status/:code` | Returns that status with a matching JSON body | Status-code handling, error branching | -| `/service/flaky` | Fails ~50% of requests with a 500 | Retry with backoff | -| `/service/slow-drip` | Streams the body slowly | Progress, streaming reads | +| Route | Behavior | Teaches | +| ------------------------- | --------------------------------------------- | ------------------------------------------- | +| `/service/delay/:seconds` | Responds after N seconds (capped, e.g. ≤ 10) | Timeouts, `AbortController`, loading states | +| `/service/status/:code` | Returns that status with a matching JSON body | Status-code handling, error branching | +| `/service/flaky` | Fails ~50% of requests with a 500 | Retry with backoff | +| `/service/slow-drip` | Streams the body slowly | Progress, streaming reads | Paired with **starter snippets** in the Playground ("retry with exponential backoff", "timeout with AbortController") so each route arrives with the exercise it exists for, and with [Guided Challenges](./guided-challenges.md) content once that ships. @@ -51,6 +51,7 @@ Paired with **starter snippets** in the Playground ("retry with exponential back ## Related +- **Planning:** [Implementation plan](./plans/error-practice-routes-implementation.md) · [Decision log](./plans/error-practice-routes-decisions.md) - [Service Routes](../api/service-routes.md) - [Error Responses](../api/error-responses.md) - [HTTP Inspector](./http-inspector.md) — makes the deliberate failures visible diff --git a/docs/future-features/guided-challenges.md b/docs/future-features/guided-challenges.md index 53efee6..5c5a256 100644 --- a/docs/future-features/guided-challenges.md +++ b/docs/future-features/guided-challenges.md @@ -57,6 +57,7 @@ flowchart LR ## Related +- **Planning:** [Implementation plan](./plans/guided-challenges-implementation.md) · [Decision log](./plans/guided-challenges-decisions.md) - [HTTP Inspector](./http-inspector.md) — its fetch wrapper enables request-level checks - [Shareable Playground Links](./shareable-playground-links.md) — classroom distribution of exercises - [Error Practice Routes](./error-practice-routes.md) — enables error-handling challenges diff --git a/docs/future-features/http-inspector.md b/docs/future-features/http-inspector.md index 370497d..8aef054 100644 --- a/docs/future-features/http-inspector.md +++ b/docs/future-features/http-inspector.md @@ -44,7 +44,7 @@ The panel renders as a second tab next to **Output** in the Playground's output ## Effort & risk -**Small** — one bootstrap addition, one panel component, some CSS. Risks are minor: the wrapper must not swallow network errors (report them as a failed-request row *and* rethrow), and header access is limited to CORS-exposed headers, so the server may need an `Access-Control-Expose-Headers` tweak for rate-limit headers to be visible. +**Small** — one bootstrap addition, one panel component, some CSS. Risks are minor: the wrapper must not swallow network errors (report them as a failed-request row _and_ rethrow), and header access is limited to CORS-exposed headers, so the server may need an `Access-Control-Expose-Headers` tweak for rate-limit headers to be visible. ## Open questions @@ -57,6 +57,7 @@ The panel renders as a second tab next to **Output** in the Playground's output ## Related +- **Planning:** [Implementation plan](./plans/http-inspector-implementation.md) · [Decision log](./plans/http-inspector-decisions.md) - [Playground](../features/playground.md) - [Rate Limiting](../api/rate-limiting.md) - [Error Practice Routes](./error-practice-routes.md) — pairs naturally; the inspector makes deliberate errors visible diff --git a/docs/future-features/multi-language-snippets.md b/docs/future-features/multi-language-snippets.md index 3755450..981c220 100644 --- a/docs/future-features/multi-language-snippets.md +++ b/docs/future-features/multi-language-snippets.md @@ -46,5 +46,6 @@ These are **copy-only**: a syntax-highlighted block with a copy button, clearly ## Related +- **Planning:** [Implementation plan](./plans/multi-language-snippets-implementation.md) · [Decision log](./plans/multi-language-snippets-decisions.md) - [Playground](../features/playground.md) - [REST Conventions](../api/rest-conventions.md) diff --git a/docs/future-features/plans/README.md b/docs/future-features/plans/README.md new file mode 100644 index 0000000..6b33f79 --- /dev/null +++ b/docs/future-features/plans/README.md @@ -0,0 +1,69 @@ +--- +title: Implementation Roadmap +description: Review-ordered index of implementation plans and decision logs for every proposed feature +audience: [developer, architect] +status: awaiting-decisions +--- + +[Wiki Home](../../README.md) › [Future Features](../README.md) + +# Implementation Roadmap + +Every [proposal](../README.md) now has two companion documents: an **implementation plan** (user stories, architecture grounded in the current code, build phases, verification) and a **decision log** (the choices only the maintainer can make, each with options and a recommendation). This page is the review dashboard — work through it one feature at a time, in any order, across separate sessions. + +## How to review a feature + +1. Skim the **proposal** (you've seen these) for the framing. +2. Read the **implementation plan** — it says how it would actually be built and in what phases. +3. Answer the **decision log**: each decision has a `**Decision:** _Pending_` line. Edit the file, or just state your answers in a session ("For HTTP Inspector: D1 = A, D2 = yes, D3 = A") and have the doc updated. _Accepting all recommendations is a valid one-line answer._ +4. When a log has no pending decisions, flip its row below to ✅ **ready to build**. + +Statuses: 🟡 awaiting decisions · ✅ ready to build · 🔨 in progress · 🚢 shipped (page moves out of future-features) · ❌ rejected (rationale recorded in [Decisions](../../decisions/README.md)). + +## The features + +Ordered by the suggested build sequence; dependencies noted. "Key decision" is the one to think hardest about. + +| # | Feature | Plan | Decisions | Pending | Key decision | Status | +| --- | -------------------------------------------------------------- | ------------------------------------------------------ | ------------------------------------------------ | ------- | ------------------------------------------- | ------ | +| 1 | [HTTP Inspector](../http-inspector.md) | [plan](./http-inspector-implementation.md) | [log](./http-inspector-decisions.md) | 3 | Panel layout (tabs vs. interleaved) | 🟡 | +| 2 | [Multi-Language Snippets](../multi-language-snippets.md) | [plan](./multi-language-snippets-implementation.md) | [log](./multi-language-snippets-decisions.md) | 3 | Language set (permanent maintenance) | 🟡 | +| 3 | [Shareable Playground Links](../shareable-playground-links.md) | [plan](./shareable-playground-links-implementation.md) | [log](./shareable-playground-links-decisions.md) | 3 | What a link pins (code vs. code + endpoint) | 🟡 | +| 4 | [Response Shape Viewer](../response-shape-viewer.md) | [plan](./response-shape-viewer-implementation.md) | [log](./response-shape-viewer-decisions.md) | 3 | Sample size (also governs #5) | 🟡 | +| 5 | [Query Builder](../query-builder.md) | [plan](./query-builder-implementation.md) | [log](./query-builder-decisions.md) | 3 | Operator scope (what the UI teaches) | 🟡 | +| 6 | [Error Practice Routes](../error-practice-routes.md) | [plan](./error-practice-routes-implementation.md) | [log](./error-practice-routes-decisions.md) | 4 | Namespace (permanent URL contract) | 🟡 | +| 7 | [Guided Challenges](../guided-challenges.md) | [plan](./guided-challenges-implementation.md) | [log](./guided-challenges-decisions.md) | 4 | Check expressiveness (shapes contributions) | 🟡 | +| 8 | [Auth Training Wheels](../auth-training-wheels.md) | [plan](./auth-training-wheels-implementation.md) | [log](./auth-training-wheels-decisions.md) | 5 | **D0 go/no-go** + promise guardrails | 🟡 | +| 9 | [Scratch Endpoints](../scratch-endpoints.md) | [plan](./scratch-endpoints-implementation.md) | [log](./scratch-endpoints-decisions.md) | 5 | **D0 build/defer/reject** | 🟡 | + +## Dependencies and shared machinery + +```mermaid +flowchart TD + HI[1. HTTP Inspector
fetch wrapper + __net events] --> GC[7. Guided Challenges
request-level checks] + SL[3. Shareable Links] -.enhances.-> GC + EP[6. Error Practice Routes] -.enables tracks.-> GC + RS[4. Response Shape Viewer] <-->|shared deriveShape\(\) + sample fetch| QB[5. Query Builder] + HI -.CORS exposedHeaders.-> QB + ML[2. Multi-Language Snippets] + GC -.demand signal.-> SC[9. Scratch Endpoints
gated on D0] + AU[8. Auth Training Wheels
gated on D0] +``` + +Hard rules that fall out of the plans: + +- **#1 before #7** — challenges assert on the fetch wrapper's events; build the wrapper once, in the inspector. +- **#4 and #5 share `deriveShape()`** and one sample fetch; whichever builds first creates the utility (and the tab shell, per [Shape Viewer D3](./response-shape-viewer-decisions.md#d3--relationship-to-the-query-builder)). +- The **CORS `exposedHeaders` change** (one line, in the [inspector plan](./http-inspector-implementation.md)) unblocks header reading for #1, #5, and #8 — worth doing with whichever lands first. +- **#8 and #9 are decision-gated**: their D0s (go/no-go; build/defer/reject) are maintainer calls that precede any code. +- **#2 and #3 are fully independent** — good candidates whenever a small win is wanted. + +## Cross-cutting note: client unit testing + +The client has no test runner today (lint + `tsc` only). Three roadmap pieces are pure functions that genuinely deserve unit tests: `deriveShape()`/`toTypeScript()` (#4/#5), `buildQueryString()` (#5), and the challenge check evaluator (#7). The first of those to be built should bring **vitest** with it (it fits the existing Vite setup); the plans each note this rather than deciding it — treat "add vitest" as accepted the first time one of those features is greenlit. + +## Related + +- [Future Features](../README.md) — the proposals these plans elaborate +- [Decisions](../../decisions/README.md) — where accepted/rejected outcomes get recorded permanently +- [Glossary](../../glossary.md) — shared vocabulary used throughout these plans diff --git a/docs/future-features/plans/auth-training-wheels-decisions.md b/docs/future-features/plans/auth-training-wheels-decisions.md new file mode 100644 index 0000000..de3d93e --- /dev/null +++ b/docs/future-features/plans/auth-training-wheels-decisions.md @@ -0,0 +1,101 @@ +--- +title: Auth Training Wheels — Decisions +description: Go/no-go framing plus open decisions for the token-auth demo API +audience: [developer, architect] +status: awaiting-decisions +--- + +[Wiki Home](../../README.md) › [Future Features](../README.md) › [Plans](./README.md) + +# Auth Training Wheels — Decisions + +Decisions needed before the [implementation plan](./auth-training-wheels-implementation.md) is buildable. **D0 comes first** — the proposal itself flagged the tension with the site's founding "no auth" premise, and that call belongs to the maintainer, made once, in writing. If D0 is _no_, the rest are moot. + +## D0 — Go/no-go, and the promise-protection guardrails + +**Question:** Build an auth-teaching demo at all — and if so, under which non-negotiable guardrails? + +**Why it matters:** "No keys, no sign-up" is the identity of the site. A feature that _teaches_ auth can coexist with that identity only if it can never be mistaken for the site _acquiring_ auth. + +| Option | Pros | Cons | +| --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | +| **A. Build, with written guardrails (recommended)** | Auth is the #1 wall learners hit right after leaving SampleAPIs; a consequence-free practice ground is squarely on-mission; tokens-without-accounts keeps the anonymous posture literal | Even labeled well, it's one more concept on the site; guardrail discipline must outlive this doc | +| B. Don't build | Zero risk to the promise; effort goes to challenges instead | The gap stays; learners' first 401 happens on a real API with real stakes | +| C. Defer until Guided Challenges ships, then build as a challenge track's backend | Arrives with its curriculum attached; sequencing matches the roadmap anyway | Same work later; the standalone demo has value before challenges exist | + +**Proposed guardrails (accepting option A means accepting these, edit as needed):** + +1. Exactly **one** demo API requires a token; every other endpoint stays keyless, forever. +2. It is never presented as a feature of the platform — always as a _lesson_. Naming, badges, and copy say "demo"/"practice"; it never appears in hero/landing copy. +3. Minting requires no input whatsoever: no email, no name, no captcha. One click / one POST. +4. Tokens carry no identity and nothing about minting is logged beyond standard request logs. +5. If any future change would require auth on a regular endpoint, that's a different decision page, not an extension of this one. + +**Recommendation:** A. The guardrails, not the code, are what this decision is really about; once accepted they should be recorded as a page in [Decisions](../../decisions/README.md) when the feature ships. + +**Decision:** _Pending_ + +## D1 — Credential mechanisms + +**Question:** Bearer header only, or also an `?api_key=` query-param variant? + +| Option | Pros | Cons | +| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | +| **A. Bearer only (recommended)** | One clear lesson; the header is the pattern that generalizes (and the one beginners fumble); smaller middleware matrix | Query-param keys are still common in the wild (maps, weather APIs) and go untaught | +| B. Bearer + `api_key` on the same dataset | Two ubiquitous patterns, one vault; cheap to add mechanically | Two ways to succeed muddies the teaching 401s ("which method was I supposed to use?"); doubles the failure-copy matrix | + +**Recommendation:** A for v1. If a challenges auth-track later wants the api-key lesson, add it _then_, as a separate exercise stage. + +**Decision:** _Pending_ + +## D2 — Token implementation + +**Question:** Hand-rolled JWT-format HMAC (Node `crypto`, zero deps) or the `jsonwebtoken` library? + +| Option | Pros | Cons | +| ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| **A. Hand-rolled JWT-shape, ~40 owned lines (recommended)** | Zero dependencies (matches the custom-router philosophy); the whole mechanism is readable by contributors — itself documentation; surface is tiny (HS256 sign/verify, exp check) | Owned crypto-adjacent code demands careful tests (timing-safe compare, base64url edges); no library hardening | +| B. `jsonwebtoken` dependency | Battle-tested verification, familiar API | A dependency (plus transitive) for two functions; version churn; the site uses none of its breadth | + +**Recommendation:** A — with `crypto.timingSafeEqual` for signature comparison and the phase-1 test matrix from the plan. The demo protects fake lab inventory that resets weekly; the threat model is a teaching aid's, but write it as if it weren't. + +**Decision:** _Pending_ + +## D3 — Token lifetime + +**Question:** Default TTL, given the goal that learners should _experience_ expiry? + +| Option | Pros | Cons | +| -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | +| **A. 45 min default + `?ttl=` param, 30 s floor / 60 min ceiling (recommended)** | Long enough to never interrupt a normal session; the `ttl` param makes expiry a deliberate 60-second exercise instead of an ambush | Two things to document (default + param) | +| B. Short default (~10 min) | Everyone hits expiry organically | Expiry mid-exercise reads as "the site is broken" to the very audience this serves | +| C. Long default (24 h) | Never interrupts | Nobody experiences expiry; half the lesson evaporates | + +**Recommendation:** A — expiry as an invited exercise (the starter snippet mints with `ttl=60` in its expiry act), not a surprise. + +**Decision:** _Pending_ + +## D4 — Client UI scope for v1 + +**Question:** How much client surface ships with the server pieces? + +| Option | Pros | Cons | +| -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | +| **A. Badge + mint button with countdown + starter snippets (recommended)** | The one-click promise is literal; the countdown makes expiry visible; snippets carry the whole lesson | Medium client work (registry flag plumbing, new details-page affordance) | +| B. Server + docs only; mint via POST from the Playground | Ships fastest; the POST _is_ arguably part of the lesson | "One click, no signup" becomes "read the docs first" — weakens exactly the framing D0 depends on | +| C. A plus a dedicated lesson page walking the whole flow | Strongest pedagogy | That page is really a Guided Challenges track wearing a costume — wait and build it there | + +**Recommendation:** A, treating C as the future challenges track. + +**Decision:** _Pending_ + +## Ready-to-build checklist + +- [ ] D0 answered (and guardrails edited/accepted) +- [ ] D1–D4 answered +- [ ] Roadmap status updated in [Plans](./README.md) + +## Related + +- [Implementation plan](./auth-training-wheels-implementation.md) · [Proposal](../auth-training-wheels.md) · [Roadmap](./README.md) +- [Decisions](../../decisions/README.md) — where the accepted D0 guardrails get their permanent page diff --git a/docs/future-features/plans/auth-training-wheels-implementation.md b/docs/future-features/plans/auth-training-wheels-implementation.md new file mode 100644 index 0000000..f54b195 --- /dev/null +++ b/docs/future-features/plans/auth-training-wheels-implementation.md @@ -0,0 +1,105 @@ +--- +title: Auth Training Wheels — Implementation Plan +description: User stories, architecture, and build phases for the opt-in token-auth demo API +audience: [developer, architect] +status: awaiting-decisions +--- + +[Wiki Home](../../README.md) › [Future Features](../README.md) › [Plans](./README.md) + +# Auth Training Wheels — Implementation Plan + +Plan for the [Auth Training Wheels proposal](../auth-training-wheels.md): one clearly-marked demo API requiring a bearer token minted with a single click — no account, no email. The mechanics are ordinary Express middleware; the delicate part is protecting the site's "no keys, no sign-up" promise, which is why this feature's [decision log](./auth-training-wheels-decisions.md) starts with a go/no-go framing decision (D0). + +## User stories + +1. **Meet the 401.** As a learner, when I fetch the demo API without credentials I get a 401 whose body tells me exactly what's missing and where to get it — the failure _is_ the first lesson. +2. **Mint without friction.** As a learner, I can get a token with one click on the site (or one `POST /auth-demo/token` from code) — no form, no email, nothing stored about me. +3. **Use the header.** As a learner, I add `Authorization: Bearer ` to my Playground fetch and the same request now returns 200 — experiencing the exact mechanic every real API will demand. +4. **Experience expiry.** As a learner, I can watch a token expire and see the _expired_ 401 (worded differently from the _missing_ 401), then re-mint — the full lifecycle, compressed into one sitting. +5. **The promise holds.** As any visitor, every other API remains keyless; the demo is visibly labeled a lesson, and nothing about the site suggests auth is creeping in. + +## Architecture + +```mermaid +sequenceDiagram + participant L as Learner + participant T as POST /auth-demo/token + participant M as Auth middleware + participant J as jsonRouter (vault dataset) + L->>M: GET /auth-demo/vault (no header) + M-->>L: 401 { error, message: "No Authorization header…", hint } + L->>T: POST (optionally ?ttl=60) + T-->>L: { token, tokenType: "Bearer", expiresIn } + L->>M: GET + Authorization: Bearer … + M->>J: verified → pass through + J-->>L: 200 data +``` + +### Tokens — stateless, signed, JWT-shaped + +- **No server-side storage.** Token = `base64url(header).base64url(payload).base64url(HMAC-SHA256(secret, header.payload))` — the real JWT wire format, built with Node's built-in `crypto`. Zero new dependencies, consistent with the owned-code posture of [Why a Custom JSON Router](../../decisions/why-custom-json-router.md), and what learners see decodes on jwt.io like the tokens they'll meet in the wild. (Hand-rolled vs. `jsonwebtoken` dependency is [D2](./auth-training-wheels-decisions.md#d2--token-implementation).) +- **Payload**: `{ iat, exp }` and nothing else — deliberately no user identity, reinforcing "no accounts". +- **Secret**: `AUTH_DEMO_SECRET` env var with a dev fallback; documented in [deployment](../../operations/deployment.md). Rotating it invalidates outstanding tokens, which is acceptable (and even a realistic lesson). +- **Lifetime**: default per [D3](./auth-training-wheels-decisions.md#d3--token-lifetime); a `?ttl=` mint parameter (floor ~30 s) exists so classrooms can demonstrate expiry in minutes, not by waiting. + +### Routes and middleware + +- `POST /auth-demo/token` — mints and returns `{ token, tokenType: "Bearer", expiresIn }`. Rate-limited (minting is cheap but shouldn't be a loop target). +- **Auth middleware** guards `/auth-demo/vault/*`, then hands off to the standard [jsonRouter](../../../server/utils/jsonRouter.js) over a new `vault.json` dataset — full CRUD/query behavior behind the header, so everything learners know still works once authenticated. +- **Every failure teaches**, in the documented [error shape](../../api/error-responses.md) plus a `hint`: + - No header → 401 "No Authorization header — the format is `Authorization: Bearer `; mint one at POST /auth-demo/token." + - Malformed / bad signature → 401 "That doesn't look like a token from this API…" + - Expired → 401 "Your token expired at