---
title: Components
description: Webshop, API, MCP server, Auth0 proxy, backoffice, Auth0, and storage.
sidebar:
  order: 2
---

The API owns the shop database schema, both Durable Objects, Fern, Poppy conversations (JSON and native event
streams), the Auth0 proxy's internal notifications and the hourly leased jobs. The webshop is bound to the same shop
database and Durable Objects and runs the capabilities only it performs; for shared capabilities it is a client of the
API. MCP is a stateless API client. The backoffice keeps its own operator database and uses only the API for shop data.
The Auth0 proxy issues credentials and never forwards shop business traffic. My Account credentials and provider calls
stay in the webshop.

## The contract and the client

`@retail-demo/api` (`apps/api`) is the Shop API package. Its `exports` map exposes only `./contracts*` and `./client*`, never `.`.
`contracts/` is browser-safe and holds one nested oRPC contract: every procedure states its route (`openapi` metadata),
its callers and scopes (`api` metadata), its errors, a single object input and an output schema once. `client/` is
`createShopApi`, an `OpenAPILink` client over the Worker service binding, with a deadline and `problemOf` for the
single error shape. It routes with the contract it is given: the MCP server and backoffice pass the full contract, which
also validates input before sending; the webshop passes `minifiedContract` (`contracts/minified.json`, generated by
oRPC's `minifyRouterContract` and kept current by a test), so its bundle carries no schemas and the API validates. The webshop, MCP server and backoffice import these; nothing else may depend on
`@retail-demo/api`, and `apps/api/src` (the implementation) never imports the client. Token-endpoint code lives in
`@retail-demo/oauth-client`.

There is no dependency bag. The API's initial context is `{ env, request, waitUntil, record, ports? }` and typed
middleware stages add the rest; the MCP server uses the same idea with plain stage functions. See
[How the API is built on oRPC](/architecture/orpc).

## OAuth resource helpers

`@retail-demo/oauth-resource` verifies RFC 9068 access tokens with `typ: at+jwt`, a pinned issuer and audience, and JWKS
fetched from an explicitly configured tenant-domain URL. It checks required claims and a bounded RFC 8693 actor
chain, and distinguishes invalid tokens from unavailable JWKS. It also builds bearer challenges and RFC 9728
protected-resource metadata. The API and the MCP server both use these helpers.

Each component has one job. The table is the short version; the sections below say what each one owns and what it
deliberately does not.

| Component                        | Where it runs                          | Owns                                                                         |
| -------------------------------- | -------------------------------------- | ---------------------------------------------------------------------------- |
| Webshop (`apps/webshop`)         | `shop.sandrino.run`                    | Pages, sessions, cookies, OAuth redirects, Stripe Elements, My Account calls |
| Shop API (`apps/api`)            | `api.tarnandtimber.com`                | Shared capabilities, schema, Durable Objects, jobs, caller authorization     |
| MCP server (`apps/mcp`)          | `mcp.tarnandtimber.com`                | MCP protocol, tool authorization, token exchange                             |
| Proxy Auth0 (`apps/proxy-auth0`) | `login-shop` and `auth-shop` hosts     | The Auth0 custom-domain front, Poppy credentials                             |
| Backoffice (`apps/backoffice`)   | `backoffice.tarnandtimber.com`         | Operator dashboard, operator sessions and audit                              |
| Auth0                            | Tenant with custom domain `login-shop` | Identity, tokens, consent, tenant policy                                     |
| D1 and Durable Objects           | Cloudflare                             | Commerce data, `AccountWriter`, `ConversationHub`                            |

## Settlement

Agent checkout with a saved card (`checkoutSavedCard`) runs in the API. Browser checkout, guest checkout, the 3-D
Secure return and WebMCP `place_order` run in the storefront through the same `@retail-demo/shop-workflows` operations. The
storefront owns confirmation, Stripe Elements and redirects. Stripe calls the API's `/webhooks/stripe`, and the API's
hourly jobs recover pending cancellations and financial operations. My Account has no API endpoint and stays in the
webshop.

## Webshop

A TanStack Start application. It renders the storefront and keeps the browser session in the Auth0 SDK's encrypted cookie, which is its own session: it receives
[back-channel logout](/architecture/request-flows/back-channel-logout) and keeps a revocation list in Workers KV. It composes browser and Poppy session management for the account holder, while the Auth0 proxy owns Poppy session/grant records and exact revocation. Validated logout notifications also deny linked Poppy grants through the Auth0 proxy. It
handles OAuth sign-in redirects, Stripe Elements, WebMCP registration, and **all** calls to Auth0's My Account API.
It is bound to the shop database and to the API's `AccountWriter` and `ConversationHub`, and runs every capability only the storefront performs (card controls, browser checkout, account security, activity, connected agents, invitations, delegated sign-in, guest sessions, receipts) through the shared domain operations, from server-only modules. For shared capabilities (catalog, cart, orders, addresses, the card list, identity, saved-card checkout and the checkout reads) it is a client of the API: signed-in shoppers use their own access token alone (the API keeps no website session state), guests use a storefront
machine-to-machine token, and agent browsers use the dedicated web-agent-forwarder token. It has no scheduled handler and does
not hold the conversation hub's callback secret. It also exposes `/api` on the shop domain as a thin
[proxy](/architecture/shop-domain-proxies).

## Shop API

A plain Worker `fetch` over an oRPC `OpenAPIHandler`, with no Hono. It serves the contract under `/v1` and two OpenAPI
documents, and runs the domain workflows for the shared capabilities, Poppy conversations, webhooks and the backoffice reads, and owns the Durable Objects and scheduled jobs. It has no storefront-only endpoints. It classifies every token into a caller, resolves a live actor, applies guards, and runs the
operation. The [pipeline](/architecture/api-pipeline) is fixed and tested.

## MCP server

A stateless Hono Worker (Hono stays here; the API no longer uses it) hosting an MCP server over streamable HTTP with the official SDK. It verifies the MCP client's token, checks the
tool's required scopes, exchanges the token for an API token on behalf of the user, calls the API and formats the
result. It holds no long-lived user credential. Each tool has a definition in `apps/mcp/src/tools/` that owns its name,
input/output schemas, permissions, annotations and response renderer. Reusable MCP schema shapes live in
`tools/schemas/`, independently of the API contract. Calls use typed operations from the nested `@retail-demo/api/client`.

## Proxy Auth0 (an Auth0 extension)

The Auth0 proxy fronts the Auth0 custom domain. It runs the Poppy sign-in flows, grants, sessions and Custom Token
Exchange, and serves discovery, introspection and revocation. It issues and brokers credentials. It **never**
forwards resource traffic to the API or MCP server.

## Backoffice

The operator dashboard. Operators sign in with Okta. The backoffice server calls the API with its own machine token
and tells it which operator is acting. It keeps its own database for operator sessions and the audit log.

## Auth0

The identity provider: resource servers, clients, grants, consent, token issuance and tenant settings, all managed in
Terraform. See [How Auth0 is integrated](/architecture/auth0).

## Storage

`ShopDatabase` (D1) is bound to the API and the storefront. The API Worker declares the `AccountWriter` and
`ConversationHub` classes and the hourly cron; the storefront binds both classes from the API's script and has no
scheduled handler. The MCP server has no storage binding, and the Auth0 proxy has no API, MCP or storefront forwarding
path. The backoffice binds only `BackofficeDatabase`, for staff sessions and audit. Source and built-bundle checks keep
database code out of the storefront's browser build.

## Diagnostic ownership

ShopDatabase also retains five diagnostic tables; it is not a new database or collector. BackofficeDatabase continues to
own only operator sessions and the separate access ledger. “No shop database access” means no direct binding: auditors
can investigate authorized diagnostics through API procedures. Storefront, MCP and backoffice emit through distinct named
private API producer capabilities; the Auth0 proxy emits only synthetic HTTP streams, without app recorder or database access.
Genuine direct/forwarded Auth0 events remain visibly separate from simulation. See [diagnostic investigations](/runbooks/audit-investigations)
for actual service/runtime attribution, source evidence, bounded raw demo policy and 30-day retention/restore behavior.
