Extended Examples
Demos
Forty-two runnable, fully-gated demo apps — each one is a living tutorial for Quadkit, built on the editable framework packages in this repository: seven canonical starter templates, a hub console, plus focused capability and infrastructure demos, all web-first and deterministic where the domain allows it.
The demos at a glance
example-hub — one port for the whole fleet
- Hub console —
http://127.0.0.1:7000lists every demo with live status (PYTHONPATH=examples/example-hub/src uv run python -m example_hub, :7000)
The launchpad for visitors:
- Single process — the hub boots each demo's real
Applicationin-process and mounts it under/examples/<slug>/; no other ports needed - Live health —
/api/statusreports every embedded demo; cards turn green as each child finishes booting - Standalone preserved — every demo still runs alone on its own port exactly as documented below
minimal-api — the smallest honest service
- REST API —
GET /hello(typed-config greeting),POST /echo(:8120) (PYTHONPATH=examples/minimal-api/src uv run python -m minimal_api)
The canonical starter template, distilled to its minimum:
- Canonical layout —
app/main/config/controllers/di/tests, each file one short read; the blueprint every copy of the template starts from - One typed knob —
minimal_api.greetinginapplication.yamlflows to the response via aBaseConfigsection;QK_MINIMAL_API__GREETINGoverrides it - Provider lifecycle — register/boot/health_check with the controller bound through the container, the pattern every larger demo repeats
websocket-echo — rooms, membership, and a cap
- Websocket —
WS /ws/echo/{room}echo with member counts;GET /statsoccupancy (:8121) (PYTHONPATH=examples/websocket-echo/src uv run python -m websocket_echo)
Websockets under the canonical layout:
- ConnectionRegistry service — rooms and membership live behind a service
the handler resolves at
register(); stats read the same state over HTTP - Room cap —
websocket_echo.max_room_members(0 = unlimited) refuses extra members with a typed{"type": "error", "reason": "room-full"}frame and close code 1013
auth-tokens — bearer tokens without cookies
- REST API —
POST /sessionissues an opaque token (201),GET /meverifies it (:8122) (PYTHONPATH=examples/auth-tokens/src uv run python -m auth_tokens)
Manual token auth as a teaching seam:
- TokenService — issue/verify behind a service with a
SecretStrsigning knob (auth_tokens.token_secret); no framework auth module required - Result + ProblemDetail — missing or bad tokens are
Err(AuthenticationError)values the Result bridge renders as RFC-9457 401 bodies, not raised HTTPErrors
database-tasks — SQLite behind a repository
- REST API — task CRUD:
POST /tasks,GET /tasks,GET /tasks/{id},POST /tasks/{id}/complete(:8123) (PYTHONPATH=examples/database-tasks/src uv run python -m database_tasks)
The repository pattern under the canonical layout:
- TaskRepository — owns the sqlite3 connection;
database_tasks.db_pathis the typed knob (:memory:by default, a file per environment in prod) - Off-loop handlers — storage calls run through
asyncio.to_thread, so the event loop never blocks on disk - 404/422 as values — unknown ids and blank titles are
Err(...)values mapped by the bridge, keeping handlers free of HTTP vocabulary
background-worker — work off the request path
- REST API —
POST /tasksaccepted (202),GET /statscounters (:8124) (PYTHONPATH=examples/background-worker/src uv run python -m background_worker)
Background work as a provider-owned lifecycle:
- Bounded pool —
background_worker.concurrencycoroutines consume an asyncio queue;boot()starts them,shutdown()drains them - Failure injection —
{"fail": true}tasks raise inside the pool and are counted, proving workers survive bad payloads - Honest 202s — submissions are accepted immediately;
/statsreports enqueued/processed/failed/pending as work completes
multi-tenant-notes — isolation by construction
- REST API —
POST /notes,GET /notes,GET /notes/{id}, all scoped by theX-Tenant-Idheader (:8125) (PYTHONPATH=examples/multi-tenant-notes/src uv run python -m multi_tenant_notes)
Tenant isolation with one header:
- Keyed by tenant — every store operation takes the tenant id, so cross-tenant reads are impossible by construction, not by convention
- Custom error mapping — a missing header is a 400 ProblemDetail via
ResultResponseMapper.register(MissingTenantError, 400); the optionalmulti_tenant_notes.max_notes_per_tenantcap returns 409
ai-assistant — the external-integration seam
- REST API —
POST /chatanswers with{"answer": ...}(:8126) (PYTHONPATH=examples/ai-assistant/src uv run python -m ai_assistant)
Quadkit ships no AI client; this is the boundary where any OpenAI-compatible endpoint plugs in:
- Protocol seam — the controller depends on
AssistantClient, not httpx; the provider binds the real client from typed config (gateway_url,SecretStrapi_key,model) - Same seam in tests — the provider constructor accepts a fake client, so tests exercise the real composition root with zero network and zero keys
resilient-rates — resilience patterns end to end
- REST API —
GET /rates/{pair},POST /scenario/{name}live fault flips,GET /statscounters (uv run python -m rates serve, :7073)
An FX rate desk that survives a hostile upstream:
- Scriptable faults — flip
healthy / flaky / down / slowlive via a container-managedFaultController - Retry + circuit breaker + timeout assembled from contract configs through a resilience pipeline factory
- Single-flight reads — per-key locks collapse concurrent misses
- Stale fallback — upstream failing? Serve the last known-good quote while retries exhaust or the circuit is open
- Deterministic — seeded random-walk quotes make failures reproducible
- Five-act walkthrough — the browser's Run 5-Act Demo control drives all resilience acts with live feedback
event-driven-orders — CQRS & event sourcing
- REST API —
POST /orders, lifecycle commands, read-model queries, outbox inspect/flush (uv run python -m orders serve, :7074)
A full order lifecycle driven by messages:
- Commands — place, pay, ship
- Domain events with handlers and read-side projections
- Notification side effects — customer-notification handlers subscribed on the event bus next to the read-model projection
- Transactional outbox — inspect and flush pending publishes
- Browser-first lifecycle — place, pay, ship, flush the outbox, or run the complete lifecycle from the order console
graphql-catalog — typed GraphQL API
- GraphQL endpoint —
POST /graphqlquery/mutation surface with introspection (uv run python -m catalog, :7076)
An in-memory product catalog behind a typed Strawberry schema:
- Schema at the composition root —
GraphQLModule.configure(query_class=..., mutation_class=...)receives root types closed over the injected service - Thin resolvers — every resolver delegates to
CatalogService; domain errors surface as standard GraphQLerrorsentries - Queries + mutations + variables — list/filter products, create, restock
- camelCase mapping for free —
price_cents→priceCents - Query console — presets, live JSON envelope, introspection-ready
realtime-monitor — realtime web console
A live ops dashboard with zero frontend dependencies:
- Server-sent events — history replay, then live stream with heartbeats
- WebSocket operator channel wired through the DI provider
- Live stats API powering header chips
- Publish from anywhere —
POST /api/eventsaccepts events from curl or external tools - Vanilla JS
EventSourceclient — no build step, no npm
rag-docs — RAG over our own docs
- REST API —
POST /ask {question}→ cited answer,GET /statscorpus stats (uv run python -m rag_docs serve, :7075)
Retrieval-augmented answers from the framework's documentation:
- Deterministic embeddings — stdlib-only hashing embedder, no model, byte-identical answers on re-run
- Pluggable retrieval —
vectorvsmmrstrategies through a registry, no if/elif dispatch - In-memory vector store — chunked markdown upserted at boot
- Cited answers — extractive synthesis with
[n] path#chunkcitations, plus a browser-guided three-question demo
support-agent — tool-calling ReAct agent
A support-desk agent driven by a scripted LLM:
- Real agent loop — THOUGHT/ACTION parsing through the framework's react strategy
- Three container-injected tools — order lookup, refund policy math, KB search
- Deterministic model boundary — scripted completions, byte-stable reruns
- Browser console — pick a scenario, ask, read the trace table
- Failure act included — unknown tools degrade to failed tool-call records
- Run —
PYTHONPATH=examples/support-agent/src uv run python -m support_agent
memory-chat — conversational memory, zero LLM
A concierge that remembers what you tell it:
- Facts persist — stated once, cited turns later via episodic + semantic stores
- Two-owner console — alice's allergies never leak into bob's session
- Demo replay — scripted two-session transcript proves recall AND isolation
- No model calls — deterministic template responder keeps runs byte-stable
- Run —
PYTHONPATH=examples/memory-chat/src uv run python -m memory_chat
ai-guardrails — guards + budgets, five acts live
One support-request pipeline, unprotected vs protected:
- Injection blocked · PII redacted end-to-end · Oversize blocked
- Restricted model denied · Budget exhausts after three paid turns
- Live audit trail — MODEL_DENIED / BUDGET_EXCEEDED rows in the sidebar
- Protection toggle — flip guards + governance off and watch the difference
- Run —
PYTHONPATH=examples/ai-guardrails/src uv run python -m guard_gate
prompt-lab — prompt authoring & A/B, zero LLM
Iterate on a support-reply prompt like a scientist:
- Two variants — terse v1 vs empathetic few-shot v2
- Real versioning — push revisions, inspect history, roll back live
- Deterministic A/B — criteria-scored over four seeded cases, byte-stable
- Lab console — render previews at any revision side-by-side with scores
- Run —
PYTHONPATH=examples/prompt-lab/src uv run python -m prompt_lab
feedback-loop — ratings become regression suites
Close the quality loop without a model call:
- Rate canned answers — 1–5 stars captured per trace id
- Low ratings promote — ≤2-rated exchanges become eval samples
- Real harness runs — QA-scored, tracked under seeded run ids
- Error analysis — mean/min/max scores and top failing cases printed
- Web console — ask, rate, inspect stats, and run regressions directly
from the browser (
python -m feedback_loopboots :8086)
auth-web — browser account lifecycle
Register, log in, manage sessions and passwords over quadkit-auth:
- Cookie sessions —
SessionCookieBackendwith revocation across browsers, HttpOnly by default - JWT claims on your profile — fresh token minted per visit, roles + permissions expanded from seeded RBAC definitions
- Lockout built in — 5 wrong passwords lock the account, constant-time verification prevents user enumeration
- Vanilla JS client — HTML views +
fetchagainst a pure JSON API viauv run python -m auth_web
auth-rbac — permission matrix console
Role-based access control with live authorize() verdicts:
- Seeded personas — viewer / editor / admin logins sharing one password
- Pattern grammar —
resource.actionpermissions with*wildcards and role inheritance (editor⊃viewer) - Live matrix — the grid recomputes via
authorize()per persona; a try-form runs any action/resource pair - Guarded resources — article create denies viewers with 403 + missing pattern
auth-mfa — TOTP challenge console
Two-factor authentication with pending-challenge sessions:
- Pending challenge flow — password issues a pre-auth cookie; only a valid TOTP/backup code upgrades it to a real session
- Enrollment with backup codes —
enable_totpreturns secret + provisioning URI + one-time codes, shown exactly once - Attempt capping — 3 wrong codes revoke the challenge back to login
- Disable needs password — re-verification before TOTP is removed
auth-apikeys — machine authentication
API-key management UI plus an X-API-Key-guarded JSON endpoint:
- Raw key shown once — hashes persist; the table shows prefixes only
- Scoped keys — issue with read/write scopes;
/api/meechoes identity - Revoke = instant 401 — revoked and garbage keys both rejected
- Cookie + key side by side — management needs a session, machines need a header
llm-router — deterministic LLM client patterns
Content generation and structured extraction without an API key:
- Scripted client — deterministic responses for repeatable tests
- Content generation — style control and retry handling
- Structured extraction — parse model output into typed product data
monitor-stack — the Quadkit MonitorModule
A browser console over the package's real observability protocols:
- Health registry — register and run a readiness check
- Metrics — counters, gauges, histograms, and instrument introspection
- Tracing — timed spans with IDs and attributes through DI
queue-worker — an automatic Quadkit consumer
Publish to one tasks topic and watch the package consumer handle messages:
- QueueProtocol —
QueueModule.stub()owns the backend and lifecycle - MessageConsumer — subscription starts at provider boot; no pull CLI
- Retry metadata —
BusMessagereceives the configured retry policy
rag-pipeline — Quadkit VectorModule retrieval
A complete retrieval pipeline without an external vector database:
- VectorStoreProtocol — create a dimensioned cosine collection at boot
- Chunking — split documents into indexable pieces
- Context synthesis — format ranked sources for generation
sql-repository — Quadkit DatabaseModule CRUD
A single task resource backed by an in-memory SQLite database:
- DatabaseProviderProtocol — schema, parameterized queries, and health
- Repository boundary — SQL stays out of the thin HTTP controller
- Browser mutations — create, update, delete, and aggregate stats
webhook-relay — Quadkit WebhookModule verification
A browser-visible inbound webhook flow without an external receiver:
- Subscriptions — package-managed URL validation and secret generation
- HMAC-SHA256 — verify canonical raw payloads in constant time
- Accepted ledger — keep the demo focused while making results visible
feature-flags — Quadkit FeatureFlagsModule
A release desk for controlled rollouts:
- Evaluation context — deterministic percentage, variant, and user-attribute decisions
- Runtime controls — force a flag on/off, clear overrides, and flush TTL cache
- Audit trail — inspect the package-owned FlagManager override history
approval-flow — Quadkit WorkflowModule
An interactive purchase approval state machine:
- Approval gates — manager and finance decisions through real StateMachine transitions
- ApprovalChain preview — run an ALL policy without mutating the request
- Retry and compensation — recover rejected or approved flows and inspect transition history
artifact-vault — Quadkit StorageModule
A browser object-storage workbench using the memory driver:
- Upload and metadata — content types, owner metadata, size, and ETag
- Preview and delete — exercise list, info, download, and delete operations
- Honest access capabilities — see public URL behavior and why memory has no presigned URL
event-timeline — Quadkit EventsModule + WebModule
A focused event journal for one in-memory stream:
- Publish and subscribe — publish checkout facts and watch a real EventBus subscriber record delivery
- Failure reporting — trigger a retrying handler failure while the bus continues to the projection subscriber
- History and replay — inspect store-assigned sequence numbers and run
replay_events()without duplicates
storefront — multi-module app layout
- REST API —
GET /api/catalog/products,POST /api/orders(uv run python -m storefront, :8103)
The one demo built from two bounded contexts instead of one flat package:
- Two
@modules, one app —catalog/andorders/each own their domain, protocol, service, provider and controller undersrc/storefront/modules/<name>/; nothing else in the tree is shared state - Export/import boundary —
catalogexportsProductCatalogProtocol;ordersimportsCatalogModuleand depends only on that protocol —OrderServicenever seesProductCatalogService - Visibility enforced at boot — drop the import and the compiler raises
ModuleVisibilityErrornaming the missing edge, not a runtimeKeyError - Cross-module resolution in
boot()—OrdersProvider.boot()resolves the catalog protocol only after every provider has registered, the moment cross-module lookups become valid - Auto-discovered controllers —
WebModule.configure(discover=[...])finds each module'sControllerautomatically; adding a third bounded context never touchesapp.py
secrets-vault — Quadkit SecretsModule
A credential-rotation console over a real RotatableSecretStoreProtocol:
- Versioned secrets — every write is a new version;
list_versions()andget_current_version()come straight fromquadkit-secrets - Manual and automatic rotation — force-rotate on demand, or run the
age-checked path through
RotationDecorator.get_rotated(), which only rotates once the configuredmax_age_secondshas elapsed - Rotation deadline warnings —
RotationDecorator.check_warnings()surfaces an approaching-deadline message before rotation is forced - Masked values, real store — the browser only ever sees the last few characters of a secret; the in-memory store still holds the real value
graph-explorer — Quadkit GraphModule
A knowledge-graph browser over a real in-memory GraphStoreProtocol:
- Node & edge CRUD —
create_node/get_node/create_edgecome straight fromquadkit-graph'sGraphProtocol, seeded with a small org chart - BFS traversal —
GraphProtocol.traverse()walks outgoing edges from any node up to a chosen depth and returns every path found - Multi-graph store — the demo resolves its graph by name from the
package-owned
GraphStoreProtocol.get_graph(), the same call a production app would make against Neo4j - RFC-9457 errors — looking up, connecting, or traversing an unknown
node raises
GraphNodeNotFoundError, mapped to a 404ProblemDetail
audit-trail — Quadkit AuditModule
A compliance console over a real HMAC-checksummed, SQL-backed audit log:
- Real tamper detection —
AuditVerifier.verify_recent()recomputes each entry's HMAC-SHA256 checksum and flags mismatches; the "tamper" button corrupts a stored checksum in place so the next verify genuinely (not theatrically) catches it - Retention preview —
PolicyBasedRetention.evaluate()/get_expiry()apply severity-based retention (critical events keep 2555 days, high 1095, everything else the 365-day default) to every recent entry - Dry-run purge —
AuditPurger.purge_expired(dry_run=True)reports how many entries would be deleted right now without touching the store - SQL, not memory —
AuditModule.configure(store_backend="sql")is required here because onlySqlAuditStorecomputes checksums; the in-memory store never does, so tamper detection needs the real backend
search-console — Quadkit SearchModule
A full-text search browser over a real SQLite FTS5-backed SearchEngineProtocol:
- BM25 ranking —
SearchEngineProtocol.search()runs a real FTS5MATCHquery, returning results ordered by BM25 relevance score - Faceted queries —
faceted_search()returns matching documents and aggregate category counts in a single call, powering the filter-chip UI - Zero-config module —
SearchModule.configure()takes no arguments; the backend is composed entirely from the typedsearchyaml section - Real document CRUD — indexing and deleting documents from the console writes/removes rows in the FTS5 virtual table immediately, no simulated state
notification-center — Quadkit mailer + inbox
A focused browser console over the real MailerProtocol and InboxService:
- Real email delivery —
MailerProtocol.send()resolves toConsoleMailerviaMailerModule's zero-configconsole_fallback; every send is logged to the server console, not simulated - Per-user inbox CRUD —
InboxService.send()/get_inbox()/mark_read()/mark_all_read()/delete()/clear_all()are all real writes againstInMemoryInboxStore, scoped and ownership-guarded byuser_id - Sibling providers, no bundling module —
InboxProvideris added directly alongsideMailerModulesincequadkit-notificationhas no module bundling mailer and inbox together - Client-tracked sent log —
MailerProtocolhas no "list sent" method, so the demo tracks what it has sent client-side, the same patterngraph-explorer/search-consoleuse for their own unlistable collections
task-console — Quadkit TasksModule
A background job console over the real worker pool, scheduler, and DLQ:
- Real job processing —
TaskProviderProtocol.enqueue_job()hands work to a realMemoryTaskQueue/WorkerPool; no simulated execution - DLQ-first retry story —
quadkit-taskswires noRetryPolicyProtocolby default, so failed jobs route straight to the realDeadLetterQueue; retrying from the console re-enqueues the same job throughDeadLetterQueue.retry() - Real scheduled jobs — a
@scheduled("*/1 * * * *")heartbeat job runs through the realTaskScheduler, backed bycroniter - Global module visibility —
TasksModule.configure(is_global=True)makesTaskProviderProtocol/ResultStore/WorkerPoolresolvable from the demo's own provider even though they aren't in the module'sexportslist - Bugs found and fixed — building this demo surfaced and fixed a
double-wrapped
JobResult.databug inquadkit-tasks' worker, plus two demo-side bugs (an empty-string job id on DLQ retry, and a falsy-0purge that purged nothing) — seetask-console/README.mdfor details
http-lab — Quadkit HTTP + resilience stack
A browser-first console over quadkit-http and quadkit-resilience:
- Real client, real resilience — every playground drives a real
HTTPClient,RetryPolicy, and a namedCircuitBreakerresolved fromCircuitBreakerRegistryProtocolagainst an in-processaiohttpmock upstream — no outbound network call ever leaves the sandbox - Retry playground — fire a route that fails transiently ~70% of the
time and watch the real
RetryPolicyretry it - Circuit breaker playground — trip the
http-lab-upstreambreaker OPEN after consecutive failures, then watch a HALF_OPEN probe recover it - SSRF gate — requests to disallowed hosts are rejected by the framework's own SSRF guard, not a demo-side check
nosql-console — Quadkit document store stack
A browser-first console over quadkit-nosql:
- Real repository, real driver surface — a
MongoDBDocumentStore,DocumentRepository, andMigrationManagerrun against an in-memorymongomock-motorclient; every call above the driver substitution is genuine, unmodifiedquadkit-nosqlcode - Query builder playground — chain
DocumentQueryBuilder.where()/.where_gte()/.sort_by()/.limit()and see the compiled filter - Specification playground — compose
AndExpr(FieldIn(...), FieldGt(...))and watch it convert to a MongoDB filter dict before execution
tenant-console — Quadkit multi-tenancy stack
A browser-first console over quadkit-tenancy:
- Real resolver chain — a
CompositeResolvertriesjwt_claim→header→subdomain→path, in priority order, against a request shape you submit - Real lifecycle service — create, activate, deactivate, and suspend
tenants through
TenantLifecycleService, each transition publishing a real domain event - Per-tenant config overrides — edit config through
TenantConfigServiceand watchTenantValidator's TTL cache invalidate
saas-platform — multi-module app layout, take two
- REST API —
POST /api/tenants,POST /api/identity/sign-up,GET /api/entitlements/{tenant_id}/{user_id}(uv run python -m saas_platform, :8113)
A second multi-module (Pattern 3) demo, this time with three bounded contexts, each one wrapping a real framework package instead of hand-rolled domain logic:
- Three
@modules, one app —tenancy(wrapsquadkit-tenancy),identity(wrapsquadkit-auth), andentitlements(wrapsquadkit-features) each own their domain, protocol, service, provider, and controller undersrc/saas_platform/modules/<name>/ - Export/import boundary —
tenancyexportsTenantDirectoryProtocol; bothidentityandentitlementsimport it to validate tenant membership without ever seeing the concreteTenantDirectoryService - Visibility enforced at boot — drop either import and the compiler
raises
ModuleVisibilityErrornaming the missing edge - Real multi-tenant auth — signup is tenant-scoped through real
quadkit-authpassword hashing, lockout tracking, and JWT issuance - Real feature flags — entitlement checks run through a real
FlagManagerwith percentage rollouts and variants, gated by tenant membership
Running them
# ── hub: one port serves every demo ───────────────────────────────
(cd examples/example-hub && PYTHONPATH=src uv run python -m example_hub) # fleet console (:7000)
# ── standalone mode: any demo on its own port ─────────────────────
# The hub is the recommended first glance; these commands are for local
# development when one console needs to run by itself.
# ── starter templates: the canonical layout, one per pattern ───────
PYTHONPATH=examples/minimal-api/src uv run python -m minimal_api # minimal API (:8120)
PYTHONPATH=examples/websocket-echo/src uv run python -m websocket_echo # websocket echo (:8121)
PYTHONPATH=examples/auth-tokens/src uv run python -m auth_tokens # bearer tokens (:8122)
PYTHONPATH=examples/database-tasks/src uv run python -m database_tasks # SQLite tasks (:8123)
PYTHONPATH=examples/background-worker/src uv run python -m background_worker # background worker (:8124)
PYTHONPATH=examples/multi-tenant-notes/src uv run python -m multi_tenant_notes # tenant notes (:8125)
PYTHONPATH=examples/ai-assistant/src uv run python -m ai_assistant # AI assistant (:8126)
PYTHONPATH=examples/resilient-rates/src uv run python -m rates # rate desk (:7073)
PYTHONPATH=examples/event-driven-orders/src uv run python -m orders # order console (:7074)
PYTHONPATH=examples/support-agent/src uv run python -m support_agent # agent console (:8082)
PYTHONPATH=examples/memory-chat/src uv run python -m memory_chat # memory chat (:8083)
PYTHONPATH=examples/ai-guardrails/src uv run python -m guard_gate # guardrails playground (:8084)
PYTHONPATH=examples/prompt-lab/src uv run python -m prompt_lab # prompt lab (:8085)
PYTHONPATH=examples/feedback-loop/src uv run python -m feedback_loop # feedback loop (:8086)
PYTHONPATH=examples/rag-docs/src uv run python -m rag_docs # RAG docs console (:7075)
PYTHONPATH=examples/graphql-catalog/src uv run python -m catalog # GraphQL catalog (:7076)
PYTHONPATH=examples/realtime-monitor/src uv run python -m ops_console # realtime dashboard (:7071)
# ── auth consoles ─────────────────────────────────────────────────
PYTHONPATH=examples/auth-web/src uv run python -m auth_web # account lifecycle (:8081)
PYTHONPATH=examples/auth-rbac/src uv run python -m rbac_console # permission matrix (:8090)
PYTHONPATH=examples/auth-apikeys/src uv run python -m apikey_console # machine auth keys (:8091)
PYTHONPATH=examples/auth-mfa/src uv run python -m mfa_console # TOTP challenge (:8092)
PYTHONPATH=examples/llm-router/src uv run python -m content_gen # LLM client patterns (:8093)
PYTHONPATH=examples/monitor-stack/src uv run python -m monitorstack # observability (:8094)
PYTHONPATH=examples/queue-worker/src uv run python -m queueworker # queue worker (:8095)
PYTHONPATH=examples/rag-pipeline/src uv run python -m ragdocs # RAG pipeline (:8096)
PYTHONPATH=examples/sql-repository/src uv run python -m taskapp # SQL repository (:8097)
PYTHONPATH=examples/webhook-relay/src uv run python -m webhookrelay # webhook relay (:8098)
PYTHONPATH=examples/feature-flags/src uv run python -m release_control # release control (:8099)
PYTHONPATH=examples/approval-flow/src uv run python -m approval_flow # approval flow (:8100)
PYTHONPATH=examples/artifact-vault/src uv run python -m artifact_vault # artifact vault (:8101)
PYTHONPATH=examples/event-timeline/src uv run python -m timeline_lab # events timeline (:8102)
PYTHONPATH=examples/storefront/src uv run python -m storefront # multi-module storefront (:8103)
PYTHONPATH=examples/secrets-vault/src uv run python -m secrets_vault # secrets vault (:8104)
PYTHONPATH=examples/graph-explorer/src uv run python -m graph_explorer # graph explorer (:8105)
PYTHONPATH=examples/audit-trail/src uv run python -m audit_trail # audit trail (:8106)
PYTHONPATH=examples/search-console/src uv run python -m search_console # search console (:8107)
PYTHONPATH=examples/notification-center/src uv run python -m notification_center # notification center (:8108)
PYTHONPATH=examples/task-console/src uv run python -m task_console # task console (:8109)
PYTHONPATH=examples/http-lab/src uv run python -m http_lab # HTTP lab (:8110)
PYTHONPATH=examples/nosql-console/src uv run python -m nosql_console # NoSQL console (:8111)
PYTHONPATH=examples/tenant-console/src uv run python -m tenant_console # tenant console (:8112)
PYTHONPATH=examples/saas-platform/src uv run python -m saas_platform # multi-module saas platform (:8113)
make test-demos # every demo test suite
Each demo boots the real framework — real DI graph, real cache backend, real resilience pipeline — no mocks where it matters.
Demo architecture (the Blueprint)
Every demo is built from one shape so the fleet reads like a single codebase:
application.yamlcarries every runtime knob — server host/port and security toggles underweb:, demo-specific knobs (scenarios, seeds, quotas) under a per-demo section (e.g.rates:,ops_console:). Python contains zero literal configuration; services receive a frozenDemoConfigthrough DI.src/<pkg>/app.pyis the composition root:build_modules()composes framework modules (WebModule, …) andcreate_app(config=…)is the injection seam; providers wire singletons and exposehealth_check; controllers are stateless HTTP adapters; services own domain logic behind contracts and returnResult[T, E].- Errors speak RFC-9457
ProblemDetail; logging is structured (get_logger) — walkthroughs narrate with events, neverprint. - Time, identity, hashing come from the framework's ambient capabilities (seeded randomness stays stdlib on purpose — determinism is the feature).
- Tests fake only at contract boundaries; every public route has an ASGI round-trip test.
Reviewer gates (same bar as the framework)
- Format + lint — root
ruff format --check ./ruff check .(demo-specific rule relaxations live in the rootpyproject.toml) - Tests — every demo runs its suite in the workspace env
(
make test-demos) - Compile check — demo sources are compile-gated (
make verify-demos) - One command —
make check-demosruns tests + compile checks + smoke and is part ofmake ci; GitHub Actions enforces it in the Demos gate job