Architecture overview¶
BCDock is a three-tier system. Knowing where the boundaries fall makes the rest of these pages - and a fair bit of the CLI behaviour - easier to reason about.
The three tiers¶
┌────────────────────────────────────────────────────────────────┐
│ Clients │
│ │
│ Portal (Next.js) bcdock CLI Third-party / agents │
│ │ │ │ │
└────────┼───────────────────────┼───────────────────────┼─────────┘
│ │ │
▼ ▼ ▼
┌────────────────────────────────────────────────────────────────┐
│ Platform API (C# / ASP.NET Core) │
│ │
│ Multi-tenancy │ Billing │ Quotas │ Auth │ DB state │ API surface│
└──────────────────────────┬─────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────┐
│ bcdock-infra (Go) │
│ │
│ VMs │ Images │ OS setup │ Containers │ SSH │ Secrets (Key Vault)│
└──────────────────────────┬─────────────────────────────────────┘
│
▼
Azure (compute, storage, DNS, Key Vault)
Platform API (C#)¶
ASP.NET Core 10. Owns:
- Multi-tenancy - companies, memberships, company-scoped query isolation
- Billing - subscriptions, usage records, dual-rate metering
- Quotas - per-tier env counts, trial caps, suspension
- Auth - OTP exchange, JWTs, API keys with scopes
- DB state - managed relational database; background-job processing for long-running work
- API surface - what the CLI, portal, and integrations call
The Platform never touches Azure directly. Anything that requires creating or destroying infrastructure routes through bcdock-infra.
bcdock-infra (Go)¶
Owns the how:
- VM provisioning and lifecycle (pools)
- Image builds - generic + versioned, the 2-stage system
- OS setup on pool VMs - Windows + Docker + BcContainerHelper
- Container lifecycle on the pool VM (via the pool agent)
- SSH operations for setup and debugging
- Secret management - fetching from Key Vault, never logging
The Platform calls bcdock-infra over an HTTP API; bcdock-infra streams progress back as NDJSON. The platform side owns the why (which env to create for which company); the infra side owns the how (the Docker container lifecycle and Tofu-managed VMs).
Clients¶
Three first-class clients of the Platform API, all consuming the same surface:
- Portal (
app.bcdock.io) - Next.js + React web application - CLI (
bcdock) - single static Go binary - Third-party / agents - anything that can speak HTTPS + JSON
There is no second, privileged API. Staff use the same Platform API as customers, with appropriately scoped tokens.
Service boundaries¶
The boundary between the Platform (C#) and bcdock-infra (Go) is load-bearing for the architecture. It enforces three things:
- Cloud agnosticism in the Platform. The C# layer doesn't import Azure SDK packages. If we ever needed to support a non-Azure substrate, only bcdock-infra would change.
- Auditability. Provisioning operations stream structured progress back as NDJSON; every line gets stamped with the env or pool ID and persisted into the platform's audit trail.
- Secret containment. Azure storage credentials and Key Vault tokens never leave bcdock-infra. The Platform requests "create a SAS for blob X" or "fetch secret Y"; bcdock-infra returns the result.
Pool layer¶
A pool is an Azure VM hosting 2-9 BC environments. We never stop pools - they always have a public FQDN, Traefik fronting them, and capacity for new environments. The autoscaler creates pools when capacity is tight and tears them down when they're idle.
Each pool runs:
- Traefik v3 - reverse proxy, terminates TLS, routes to the right BC container per request
- Pool agent - Windows executable that talks to the Platform over Traefik on
/pool-agent. Manages container lifecycle. - N BC containers - one per environment, each with its own database, admin password, and DNS subdomain
Detail lives in images and pools and URL shape.
Storage layer¶
Three storage classes:
- Managed relational database (Azure-hosted) - all platform state: users, companies, environments, usage records, audit log, provisioning logs. Single instance in
australiaeast(Sydney). - Azure Blob Storage (per region) - hibernation backup snapshots, data export ZIPs. 7-day soft-delete on hibernation backups.
- Azure Key Vault (core + per region) - TLS cert in core; per-env passwords and pool SSH keys in regional KVs.
Why per-region storage: hibernation backups are large and region-bound (you don't want to hibernate from US-West-2 and resume from australiaeast). Data export blobs are short-lived (24h SAS).
Cross-cutting¶
- Provisioning logs - every long-running operation streams structured progress (
type: log,type: result,type: errorover NDJSON). The Platform persists every line into a per-env / per-pool audit trail that customers can stream viabcdock env logs. - OpenTelemetry - exported to Azure Monitor (Application Insights). One trace per request, spans across the C#/Go boundary via baggage.
- DNS - Azure DNS zone for the production hostname (
bcdock.io). Per-pool A records, per-env CNAME records under*.bcdock.io. Wildcard TLS via DNS-01.
Where to read more¶
- Images and pools - 2-stage image system + pool model + autoscaler
- URL shape - per-env subdomains, wildcard TLS, agent and dev endpoints
- Hibernation - active vs stored states, the dual-rate billing model
- Anonymisation - anonymise-in-place for GDPR / APP, why and how