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.