Skip to content

Extended Examples

README.md

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:7000 lists 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 Application in-process and mounts it under /examples/<slug>/; no other ports needed
  • Live health — /api/status reports 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.greeting in application.yaml flows to the response via a BaseConfig section; QK_MINIMAL_API__GREETING overrides 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 /stats occupancy (: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 /session issues an opaque token (201), GET /me verifies 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 SecretStr signing 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_path is 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 /tasks accepted (202), GET /stats counters (:8124) (PYTHONPATH=examples/background-worker/src uv run python -m background_worker)

Background work as a provider-owned lifecycle:

  • Bounded pool — background_worker.concurrency coroutines 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; /stats reports 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 the X-Tenant-Id header (: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 optional multi_tenant_notes.max_notes_per_tenant cap returns 409

ai-assistant — the external-integration seam

  • REST API — POST /chat answers 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, SecretStr api_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 /stats counters (uv run python -m rates serve, :7073)

An FX rate desk that survives a hostile upstream:

  • Scriptable faults — flip healthy / flaky / down / slow live via a container-managed FaultController
  • 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 /graphql query/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 GraphQL errors entries
  • 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/events accepts events from curl or external tools
  • Vanilla JS EventSource client — no build step, no npm

rag-docs — RAG over our own docs

  • REST API — POST /ask {question} → cited answer, GET /stats corpus 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 — vector vs mmr strategies through a registry, no if/elif dispatch
  • In-memory vector store — chunked markdown upserted at boot
  • Cited answers — extractive synthesis with [n] path#chunk citations, 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_loop boots :8086)

auth-web — browser account lifecycle

Register, log in, manage sessions and passwords over quadkit-auth:

  • Cookie sessions — SessionCookieBackend with 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 + fetch against a pure JSON API via uv 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.action permissions 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_totp returns 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/me echoes 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 — BusMessage receives 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/ and orders/ each own their domain, protocol, service, provider and controller under src/storefront/modules/<name>/; nothing else in the tree is shared state
  • Export/import boundary — catalog exports ProductCatalogProtocol; orders imports CatalogModule and depends only on that protocol — OrderService never sees ProductCatalogService
  • Visibility enforced at boot — drop the import and the compiler raises ModuleVisibilityError naming the missing edge, not a runtime KeyError
  • 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's Controller automatically; adding a third bounded context never touches app.py

secrets-vault — Quadkit SecretsModule

A credential-rotation console over a real RotatableSecretStoreProtocol:

  • Versioned secrets — every write is a new version; list_versions() and get_current_version() come straight from quadkit-secrets
  • Manual and automatic rotation — force-rotate on demand, or run the age-checked path through RotationDecorator.get_rotated(), which only rotates once the configured max_age_seconds has 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_edge come straight from quadkit-graph's GraphProtocol, 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 404 ProblemDetail

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 only SqlAuditStore computes 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 FTS5 MATCH query, 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 typed search yaml 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 to ConsoleMailer via MailerModule's zero-config console_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 against InMemoryInboxStore, scoped and ownership-guarded by user_id
  • Sibling providers, no bundling module — InboxProvider is added directly alongside MailerModule since quadkit-notification has no module bundling mailer and inbox together
  • Client-tracked sent log — MailerProtocol has no "list sent" method, so the demo tracks what it has sent client-side, the same pattern graph-explorer/search-console use 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 real MemoryTaskQueue/WorkerPool; no simulated execution
  • DLQ-first retry story — quadkit-tasks wires no RetryPolicyProtocol by default, so failed jobs route straight to the real DeadLetterQueue; retrying from the console re-enqueues the same job through DeadLetterQueue.retry()
  • Real scheduled jobs — a @scheduled("*/1 * * * *") heartbeat job runs through the real TaskScheduler, backed by croniter
  • Global module visibility — TasksModule.configure(is_global=True) makes TaskProviderProtocol/ResultStore/WorkerPool resolvable from the demo's own provider even though they aren't in the module's exports list
  • Bugs found and fixed — building this demo surfaced and fixed a double-wrapped JobResult.data bug in quadkit-tasks' worker, plus two demo-side bugs (an empty-string job id on DLQ retry, and a falsy-0 purge that purged nothing) — see task-console/README.md for 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 named CircuitBreaker resolved from CircuitBreakerRegistryProtocol against an in-process aiohttp mock 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 RetryPolicy retry it
  • Circuit breaker playground — trip the http-lab-upstream breaker 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, and MigrationManager run against an in-memory mongomock-motor client; every call above the driver substitution is genuine, unmodified quadkit-nosql code
  • 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 CompositeResolver tries jwt_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 TenantConfigService and watch TenantValidator'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 (wraps quadkit-tenancy), identity (wraps quadkit-auth), and entitlements (wraps quadkit-features) each own their domain, protocol, service, provider, and controller under src/saas_platform/modules/<name>/
  • Export/import boundary — tenancy exports TenantDirectoryProtocol; both identity and entitlements import it to validate tenant membership without ever seeing the concrete TenantDirectoryService
  • Visibility enforced at boot — drop either import and the compiler raises ModuleVisibilityError naming the missing edge
  • Real multi-tenant auth — signup is tenant-scoped through real quadkit-auth password hashing, lockout tracking, and JWT issuance
  • Real feature flags — entitlement checks run through a real FlagManager with 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.yaml carries every runtime knob — server host/port and security toggles under web:, demo-specific knobs (scenarios, seeds, quotas) under a per-demo section (e.g. rates:, ops_console:). Python contains zero literal configuration; services receive a frozen DemoConfig through DI.
  • src/<pkg>/app.py is the composition root: build_modules() composes framework modules (WebModule, …) and create_app(config=…) is the injection seam; providers wire singletons and expose health_check; controllers are stateless HTTP adapters; services own domain logic behind contracts and return Result[T, E].
  • Errors speak RFC-9457 ProblemDetail; logging is structured (get_logger) — walkthroughs narrate with events, never print.
  • 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 root pyproject.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-demos runs tests + compile checks + smoke and is part of make ci; GitHub Actions enforces it in the Demos gate job
Explorer 1 file
examples