TheYard infrastructure
Pinch or Ctrl+scroll to zoom; the text is selectable.
Source
·
TheYard
TheYard infrastructure: what runs today, how a merge becomes a roll, and the designed production target
TheYard on Azure, as it runs on 2026-09-09
Everything in the solid boxes is live and measured; the dashed group is the production design, defined in code and deliberately undeployed.
A request, left to right
HOSTING.md and ADR-004, ADR-007 record the chain; ADR-010 the Admin tab; ADR-015 the cache rules.
Visitor's browser
https://theyard.
stevenstout.biz
Hashed bundle files are
cached a year; the page,
the API and the docs
say no-cache.
ADR-015
DNS at Wix
theyard CNAME
theyard-edge.netlify.app
stevenstout.biz, www
A records at Netlify
Registrar-locked to Wix; the
Cloudflare zone is staged for
the transfer around Oct 30.
HTTPS edge: Netlify (free)
Let's Encrypt certificate, auto-renewed.
edge/_redirects
bare and www names: 301 to theyard
everything else: proxy 200 to origin
netlify.toml
rebuilds only when edge/ changes
ADR-007; not caching the API (measured)
Origin: Azure Container Instances, RG-THEYARD-SS, westus2
aci-theyard-ss 1 vCPU 1.5 GB port 8080
theyard-ss-zmnetj67bn5h2.westus2.azurecontainer.io
Runs image theyard:vN from the registry, pulled with the
user-assigned identity id-theyard-ss (AcrPull; Reader on the group).
Inside: one ASP.NET Core 10 process (Dockerfile, ADR-002)
Serves the built React SPA from wwwroot, /api/* over the catalogue it read at startup,
the docs with live code read from its own source (ADR-014), /healthz and /readyz,
and the Admin tab, which trades the identity for a token and reads its own
container group from management.azure.com (ADR-010).
resolve
TLS
:8080
Store: Azure SQL Database, RG-THEYARD-SS, westus3
sqldb-theyard-ss GP_S_Gen5_2 serverless free limit auto-pause 60m
The server is Entra-only: it has no SQL login and therefore no password. The
container authenticates as id-theyard-ss, which holds db_datareader and
db_datawriter and cannot create or alter a table.
The schema is api/TheYard.Database, hand-written DDL published by SqlPackage.
Entity Framework maps to it; a conformance test fails the build if they differ.
West US 3 because West US 2 refused: RegionDoesNotAllowProvisioning.
When the database is not there
A container that cannot reach the store serves the catalogue from the
JSON files instead and says so on /api/health. Bids stop outliving the
process; nothing else changes for a visitor.
Built in ADR-033 for a serverless database that auto-pauses. The first
thing it actually caught was a deploy whose connection string never got
substituted, and nobody using the site could tell.
The deploy warns when a roll lands here (ADR-039 addendum).
Store: Azure Cosmos DB, RG-THEYARD-SS, westus2
cosmos-theyard-ss free tier 1000 RU/s shared session consistency no keys
Local authentication is disabled on the account, so there is no key to leak; the
container authenticates as id-theyard-ss with a data-plane role that can read and
write documents and cannot create a container (ADR-059, ADR-061).
Four containers hold the catalogue, photos, accounts and bids as documents,
partitioned on the make, the style, the account and the bidder (ADR-058).
Nothing indexed; the definitions are infra/cosmos/*.json (ADR-040 addendum).
Both stores in one process, since 1.0.0.94
Every container opens both stores. A cookie set by the Store toggle at the top
of every page, or a header, picks which one serves a request; the default is
sql here and cosmos on the second group, aci-theyard-cosmos-ss, which runs the
same image on its own Azure address. Each Admin tab reads the other's
numbers through /api/admin/peer, and the proof card sends a visitor's requests
to the container itself on both stores and says, per path, whether the two
answer in the same time and whether a difference is the store or its distance.
ADR-066 the toggle, ADR-067 the proof, ADR-063 the peer read, ADR-064 the measurement.
the other store, per request
read once at startup, written on every bid
pulled by id-theyard-ss
az container create (roll)
A merge becomes a roll, no hands on it
ADR-009 records the pipeline, ADR-005 the version, ADR-012 the changelog line each version writes.
GitHub: push to main
The commit carries its
own changelog line, one
past the footer's number.
SteveStout/TheYard
CI (.github/workflows/ci.yml)
frontend: tsc, vitest, vite build
api: dotnet test (379 tests)
e2e: Playwright in Chrome (58)
A red job stops everything below.
read-only token
Deploy (.github/workflows/deploy.yml)
Fires on green CI. OIDC federated credential,
no secret stored anywhere. Version = 1.0.0.(11 + run).
docker build with APP_VERSION and APP_COMMIT,
push, render infra/aci-theyard.yaml,
az container create, verify origin and domain.
roles: AcrPush, ACI Contributor, Managed Identity Operator
Registry (ACR)
crtheyardsszmnetj67bn5h2
.azurecr.io
One tag per version,
theyard:v12 to v97 today.
push
green
image
The production target, designed and undeployed
infra/main.bicep with enableFrontDoor=true and computeKind=appservice; ADR-001 the decision, ADR-004 why it waits.
Azure Front Door
Global entry, TLS, the custom domain
on Azure instead of the Netlify edge.
fdProfile, fdEndpoint,
fdOriginGroup, fdOrigin, fdRoute
App Service (B1), the locked origin
Answers only Azure Front Door's backend
range, and only with this profile's ID in
the X-Azure-FDID header (siteLock).
plan, site, siteLock
The same image
Nothing about the container changes;
only the host does. The pipeline would
point at the App Service instead of the
container group, and get deployment slots.
Why it waits
The free trial forbids Front Door and
gave no App Service quota (measured
2026-08-31). Flipping two parameters
deploys this after an upgrade.
ADR-004
locked
runs
Source: docs/images/infrastructure.svg in the repository. Names, sizes and roles are the ones the records and the pipeline logs carry; the picture is redrawn when they change.