The café that doesn't exist

It sells coffee it cannot ship, on chains that cost nothing to use. Every counter in here is a real piece of the stack wearing an apron — including the one in the corner that has never been switched on.

An isometric pixel café: checkered floor, a chalk menu board on one wall, a shelf of bean bags and a window onto open space on the other.

THE BAR

An ordinary coffee shop, built properly

Catalog, cart, checkout, accounts, orders — the part with no blockchain in it at all. A Blazor Web App on .NET 10 with interactive server rendering, split into the five projects Clean Architecture asks for, and it would still work with every wallet in the building unplugged.

RUNTIME
.NET 10 · Blazor Server
DATA
EF Core · PostgreSQL · Identity
SHAPE
Domain → Application → Infrastructure → Web

01

The Bar

One page, twelve moving parts

/products is the honest half of this project: a catalog with no wallet anywhere in it. It is worth pulling apart because everything else in the room hangs off the same spine — a routable Blazor page, a service interface, a repository, EF Core, Postgres.

Component tree · Components/Pages/Products.razor

  • Products.razor @page "/products" · InteractiveServer · PersistentComponentState
    • HeadContent links css/catalog.css, the Tailwind build of Styles/catalog.input.css
    • CatalogHero
    • FilterSidebar category · origin · roast · process · price range · sort
    • ProductCard one per visible product, @key'd by slug · 6 per page
      • RatingStars
      • LucideIcon

Where a card's data actually comes from

  1. ProductCard Components/Storefront
  2. CatalogProductCardMapper DTO → view model
  3. IProductService Application layer
  4. IProductRepository Infrastructure
  5. Postgres EF Core · products

The page never touches a DbContext. It injects IProductService, which is the only thing the Web project is allowed to know about — the Domain entity behind it carries name, slug, description, price, stock, image, category, origin, roast level, process and an active flag, and the sidebar's facets are all derived from those columns at runtime rather than hardcoded.

Prerender
The product list is stashed with PersistentComponentState during the server render and read back when the circuit connects, so the catalog is not fetched twice and the grid does not flash empty on hydration.
Filtering
Category, origin, roast and process are held as HashSet<string> on the component; price is a floor/ceiling pair derived from the loaded set. Filtering and paging both happen in memory, over a catalog small enough that a round trip per keystroke would be the slower design.
Styling
Two systems, deliberately: Tailwind utilities for the catalog grid, component-scoped .razor.css for everything with a name. The pixel wing you are looking at right now is the second kind.

02

The Roaster

What keeps the heat on

Everything below is Bicep in infra/, deployed to one resource group on Azure Container Apps. No portal clicking: a GitHub Actions workflow federates into Azure over OIDC, so there is no cloud credential stored in the repository at all.

Resource group · thiscafeteria-prod-rg · westus

Container Apps Environment — Consumption

web

0.5 vCPU · 1 GiB

min 1 · max 2

ingress :8080

worker

0.25 vCPU · 0.5 GiB

min 1 · max 1

no ingress

sepolia-bundler

0.5 vCPU · 1 GiB

internal only :4338

not deployed → 06

Postgres Flexible

Burstable B1ms · 32 GB

Storage · Blob

receipts container

Service Bus

wallet-status

order-processing

Key Vault

every secret env var

Communication Services

transactional email

Container Registry

web · worker · rundler

Two identities
The apps run as a user-assigned managed identity holding AcrPull, Key Vault Secrets User, Storage Blob Data Contributor and Service Bus Sender/Receiver. CI/CD is a separate identity, federated to GitHub over OIDC, holding AcrPush and Container Apps Contributor. Neither can do the other's job.
The queue
Checkout writes an order-processing message; the Worker — its own container, no ingress, one replica — drains it and updates Postgres. That split is why an order survives a web replica restart mid-checkout.
Why westus
Not a preference. This is an Azure for Students subscription: eastus2 is blocked for both Postgres Flexible Server and Container Apps environments by resource-specific capacity restrictions, and mexicocentral does not offer Container Apps at all. westus was the region that actually worked end to end.

03

The Till

Nine networks, three of them real

The registry ships nine chain definitions and every one of them is disabled. A chain becomes visible only when a committed deployment manifest under deployments/ proves its contracts exist — the manifest is the source of truth, and ChainRegistry.Validate rejects one that claims a capability whose contract address is missing.

Chain definitions · live ones carry a manifest

Ethereum Sepolia

11155111

manifest present

BNB Testnet

97

manifest present

Solana Devnet

Anchor program

manifest present

Hedera Testnet

no manifest

Avalanche Fuji

no manifest

Linea Sepolia

no manifest

Base Sepolia

no manifest

Monad Testnet

no manifest

Arbitrum Sepolia

no manifest

What Sepolia's manifest actually addresses

CAFE
0x15DbED39271D2788a9Be63ffB34C8E2DdED8754A
COFFEE
0x4056E7F5FD1584C3db6223c9483761Dcb30Bf21C
Liquid vault
0x0a7b7d7eb39ca835ef5854875bb97885d23d1df3
EntryPoint 0.7
0x7d75859d1e2be07b0c18c0ef3dd062b69bcc4217
ERC-8004 registry
0x45d9e43a0ce744eaa29982a31a5b5ad48d1d6106
ERC-7683 resolver
0xb5bf1af688316d724f1fec07b6867c770cab82e6
ERC-8183 escrow
0x44f97d09090e27962d2665f0b95a753273a89350

Compiled with solc 0.8.24, 200 optimizer runs, viaIR — recorded in the manifest alongside the deploy block, so a redeploy that changes the compiler is visible in the diff rather than silent.

The gate
BlockchainManifestLoader reads the manifest, ChainRegistry.Validate rejects an inconsistent one, and ChainsController filters /api/chains on Enabled. Both chain selectors read that endpoint, so an unfinished connection cannot be advertised by accident.
Still off
marketplacePayment needs an escrow or the legacy pool deployed and addressed; legacyExit needs the legacy pool. Neither committed manifest declares them, so both stay dark on every chain.

04

The Cellar

A receipt you can carry

CafeLiquidStakingVault.sol is an ERC-4626 vault — plus Ownable2Step, Pausable and ReentrancyGuard. You deposit CAFE, you get stCAFE shares, and COFFEE rewards stream to whoever holds those shares. Being ERC-4626 is the whole point: the receipt is an ordinary ERC-20 that can move without unwinding the position.

Deposit → hold → redeem

  1. CAFE the asset
  2. deposit() or mint()
  3. stCAFE ERC-4626 shares
  4. redeem() or withdraw()
  5. CAFE back shares burned

Rewards run on a separate rail. The owner calls notifyRewardAmount(amount, duration) to open a streaming period; rewardPerShare() accrues against it, earned(account) reads a holder's balance, and claimRewards() pays out in COFFEE without touching the staked CAFE.

Transfers are honest
The vault overrides _update so a stCAFE transfer settles rewards for both sides before the balance moves. Sending your receipt away hands over the future stream, not the rewards you already earned.
Two chains, one shape
The same deposit/redeem/claim vocabulary runs on EVM and on a Solana Anchor program, and both Web and Worker reconcile every recorded operation independently, using chain-qualified identities and cursors so a Sepolia event can never be mistaken for a devnet one.
What it is not
This is liquid staking of the application's own CAFE token. It is not validator staking of ETH, SOL, BNB or anybody else's native currency, and nothing here earns real yield.

05

The Back Room

Agents that have to ask

The procurement lab hands escrowed test missions to agents running on ERC-4337 smart accounts. The interesting constraint is the one it refuses to relax: every action is signed by your wallet. There is no unattended mode to turn on.

A mission, end to end

  1. Agent registeredERC-8004
  2. Permission grantedscoped epoch
  3. Funds escrowedERC-8183
  4. Intent resolvedERC-7683
  5. UserOperationwallet-signed
  6. Settledreconciled twice
Smart accounts
EntryPoint 0.7, a simple account factory, and a verifying paymaster that can sponsor gas. Sponsorship is metered and revocable — a reverted sponsored operation still costs the paymaster, so it is counted against a budget rather than billed as gas.
The crew upstairs
The robots wandering the homepage are the same idea at play without the money: a 194-parameter policy trained offline, then run per frame in the browser against the exact physics the trainer used. Nobody choreographed them.
Missing piece
Everything above assumes something will accept a UserOperation. Today that is a public bundler. The thing meant to replace it is sitting in the corner of the room, switched off — section 06.

06

The Dark Node NOT RUNNING

Provisioned, wired, never switched on

There is a fourth Container App in infra/main.bicep that has never existed in Azure. It is a private Rundler instance — a self-hosted ERC-4337 bundler for the lab — and it is behind a Bicep conditional that is still false. It is in this room because a thing you have designed and not yet turned on is part of the truth about a system.

thiscafeteria-prod-sepolia-bundler

Image
Rundler v0.11.0, built from contracts/evm/rundler/Dockerfile
Ingress
Internal only, port 4338 — an unauthenticated bundler RPC never goes public
Shape
0.5 vCPU · 1 GiB · one always-on replica, no scale-to-zero
Serves
EntryPoint 0.7 at 0x7d75…4217, the one the lab already uses
Secrets
Node RPC URL and signer key, both read from Key Vault at start
Switch
enableSepoliaBundler = false in infra/main.bicepparam

Two things stand between it and a green light

  1. A tracer-capable Sepolia RPC

    Rundler's safe mode validates a UserOperation with debug_traceCall using a custom JavaScript tracer. Ordinary public Sepolia endpoints do not offer that, so this needs a provider endpoint that does — and that credential is not going in the repository.

  2. A dedicated funded signer

    The bundler needs its own Sepolia-funded beneficiary key, explicitly not the contract deployer key. One compromised bundler should not be able to touch anything that was deployed.

Both land in Key Vault, then Bicep is redeployed with enableSepoliaBundler=true, and the final proof runs from a one-off workload inside the same Container Apps environment against the internal FQDN — not by making the RPC public for the length of a test.

Nothing on this page is a roadmap slide. Every address, replica count and disabled flag above is read out of the repository as it stands today, including the parts that do not work yet.

An unhandled error has occurred. Reload x

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please retry or reload the page.

Still there?

You've been inactive for a while. For your security, you'll be signed out in 60 seconds.