ARCHITECTURE · NETWORK REQUIREMENTS

Everything it takes to run Attestree.

One container and a database on a server you own. Windows endpoints that only ever dial out, and still pick up a package change within about a minute. An optional detonation host. And a short list of public hosts, each off by default, used only when you act, or switchable. This is the whole map: every box, every connection, every port.

0 ports on endpoints package changes in ~1 minute every outbound host listed no phone-home
THE MAP

Who talks to whom.

  • inside your network
  • mutual TLS
  • outbound to the internet
  • optional / your call
Attestree deployment and network map Windows endpoints poll the platform about once a minute, so a change reaches them in about a minute, and together with operator browsers they connect outbound over HTTPS to the platform container on your server. The platform talks to its Postgres database on the internal Docker network and makes outbound HTTPS calls to GitHub, vendor download hosts and, optionally, the winget CDN and Rekor. Endpoints fetch installers from the platform, or from the vendor host, pinned to the same SHA-256, when the platform holds no copy. An optional detonation broker connects to the platform over mutual TLS and drives a Hyper-V or Proxmox host that runs a sandbox VM per package; the broker has no internet access of its own, except an opt-in recording proxy on Hyper-V. Nothing connects into an endpoint. YOUR WINDOWS FLEET YOUR OPERATORS YOUR SERVER · DOCKER HOST PUBLIC INTERNET · EGRESS ONLY DETONATION · OPTIONAL Windows 11 endpoint 23H2+ · one agent per machine Wep.Agent Windows service · SYSTEM local only Wep.Shim Apache-2.0 · no network code machine or user scope winget · installer pinned SHA-256 only no inbound ports package changes in ~1 min Console in a browser local accounts · 4 roles · TOTP Entra ID SSO on commercial tiers any modern browser TLS proxy Caddy overlay :18444, or your own wep-platform container · host :18080 → :8080 Agent API enroll · check-in · wake · spans Attested winget feed REST source + installer bytes Operator console sign-in · roles · TOTP Broker listener mTLS gRPC :8443 · optional Postgres 18 compose network host bind 127.0.0.1 only Data volume signing key key ring bytes (disk or Blob) GitHub winget manifests · CVE + KEV feed ON Vendor hosts installer bytes, one host per package AT DETONATION ghcr.io image pull, from the Docker host ON UPGRADE cdn.winget.microsoft.com console package search ON SEARCH rekor.sigstore.dev transparency log, the only upload OFF no telemetry · no licence call no phone-home of any kind Detonation broker own local account, not SYSTEM reaches the platform and its hypervisor, nothing else dials out · no internet by default Hypervisor Hyper-V on Windows Server or Proxmox VE (KVM) signed golden image, built from your own ISO Sandbox VM one per package, discarded after no network on Proxmox; Hyper-V only if needed HTTPS :443 out offline by default vendor copy, SHA-256 pinned · only when your server holds no bytes HTTPS outbound mTLS gRPC 1 2 3 4 5 6 7 8 9

Scroll the map sideways →

Shown: the self-hosted Community Edition. The commercial tier keeps the same endpoints, agent and detonation side, but its server side is a different build in your own Azure subscription, with its own map on the dedicated instance page.

CONNECTION BY CONNECTION

Nine wires, and what rides on each.

The numbers match the map. Arrows point from whoever opens the connection, which is what your firewall cares about.

  1. 1

    Endpoint agent → Platform

    HTTPS · your public URL — :18080 by default, :18444 with the Caddy overlay, :443 behind your own proxy

    Enrollment; a wake probe about once a minute; a full check-in whenever the probe says something changed, and every 15 minutes regardless; signed-span and inventory upload; the ring-staged agent update; and the winget feed the agent registers as a machine-wide source. A change to an endpoint’s packages, ring or groups raises a wake for it, so it reaches the machine in about a minute without a port open on it; anything else, such as an agent-rollout step, lands at the next 15-minute check-in.

    AUTH An enrollment token (single-use by default), then a session token that rotates on every check-in; the winget feed uses its own per-endpoint feed token; once the endpoint has a registered device key, each check-in is also signed with it. Desired state carries an HMAC keyed by that check-in’s session secret; the agent checks it and fails closed.

  2. 2

    Operator browser → Platform

    HTTPS · same port as the agents

    The console: catalog, rings, fleet, audit, settings.

    AUTH Local accounts, four policy-enforced roles, TOTP two-factor. Entra ID SSO on commercial tiers.

  3. 3

    Platform → Postgres 18

    Postgres wire · :5432 on the internal compose network

    All operational state: catalog, fleet, rings and the signed audit log.

    AUTH Password from your .env. The host binding is 127.0.0.1 only; do not publish it.

  4. 4

    Platform → Public internet

    HTTPS · :443 outbound

    Downloads only: winget manifests and installers at ingest, the CVE and KEV feeds. The full allowlist is below.

    AUTH HTTPS only. The CVE feed is limited to a pinned host set; every fetch is screened again on each redirect hop, private address ranges are refused, and an installer’s SHA-256 is checked as it streams in.

  5. 5

    Detonation broker → Platform

    gRPC over mutual TLS · host :18443 → container :8443

    The broker takes detonation jobs over one long-lived stream, pulls each installer from the platform (never from the internet), and pushes back the result and derived artifacts. Only with the broker overlay switched on.

    AUTH An enrollment token once, then a client certificate issued by the platform’s own broker CA. The broker pins the platform’s key instead of trusting the OS store, and listens on nothing itself.

  6. 6

    Broker → Hypervisor

    Local Hyper-V, or the Proxmox API · Proxmox: :8006 API + :8765 result extractor (mTLS)

    Clones the golden image, starts the sandbox, harvests the result disk.

    AUTH Proxmox: least-privilege API token and pinned TLS keys. The hypervisor is not trusted for integrity; the signed template record is.

  7. 7

    Hypervisor → Sandbox VM

    VM lifecycle · no connection to your server

    One short-lived VM per package, cloned from your signed golden image. It installs the package, writes its result to its own disk and halts; the broker reads that disk.

    AUTH The golden image is built from your own Windows ISO and signed with your own key. None is published.

  8. 8

    Sandbox VM → Wherever the installer dials

    whatever a real installer uses · offline by default

    A sandbox starts with no network. On Proxmox it stays that way: the guest has no network adapter. On Hyper-V, an installer that needs the network gets its adapter connected, and its traffic is captured as evidence if you provisioned a recording proxy; without one it runs connected but unrecorded. Where that switch leads is your decision.

    AUTH n/a. This is the thing under test.

  9. 9

    Endpoint winget → Vendor download host

    HTTPS · :443 outbound, from the endpoint

    Only for a version whose installer bytes your server does not hold: one attested before the byte store was set up, or one whose kept bytes were since cleaned up. Everything detonated with the byte store on comes from your server instead.

    AUTH winget refuses any file whose SHA-256 differs from the one pinned at ingest, so the vendor host can serve the bytes but cannot change them.

THE DETONATION BROKER

What it can touch, and what it cannot.

It drives VMs, so it looks powerful on a map. Its reach is deliberately small: one outbound connection to your platform, and its own hypervisor.

REACHES
Your platform, outbound only
One gRPC connection to :18443: enroll once, keep a control stream open, pull installer bytes, push results back. Plus a best-effort “please re-enroll me” call to the same URL.
Its own hypervisor
Hyper-V: local cmdlets, as a dedicated local account in Hyper-V Administrators, to clone, start, stop and delete sandbox VMs and read their result disks. Proxmox: the PVE API on :8006 with a least-privilege token, and the result extractor on :8765.
Its own files
Its keys and state under %ProgramData%\Wep\Broker, and the golden image you point it at.
DOES NOT REACH
  • The internet. Installers come from your platform, already pinned to a SHA-256. One opt-in exception: on Hyper-V, the recording proxy relays a sandbox’s allowlisted traffic.
  • Your endpoints, your Postgres, your directory. It has no credentials for any of them.
  • Anything from your network. It listens on nothing, with two exceptions: the Proxmox bring-up wizard opens a single-use HTTPS listener on one NIC, firewalled to the PVE host, and closes it when done; and the opt-in Hyper-V recording proxy listens on the sandbox-only switch.
  • SYSTEM or domain rights. It runs as its own local account, not LocalSystem; on Proxmox the bring-up wizard uses the root password, read from an environment variable rather than a command-line flag, to mint that limited token.
FIREWALL RULES

What to open, and what to allow out.

Inbound

Only your server takes connections, and on Proxmox the PVE host, from the broker. Ports are the reference compose defaults; change any of them in .env.

ON PORT FROM NOTES
Your server tcp/18080 (or 443 / 18444 behind TLS) Endpoints and operator browsers The only port most deployments open. Endpoints that are not the Docker host itself need HTTPS: winget refuses a plain-HTTP source on anything but loopback.
Your server tcp/18443 Detonation broker(s) Only with the broker overlay. TLS, mutual after a one-time token enrollment.
Your server tcp/55432 Nobody else Postgres, bound to 127.0.0.1 by default for local backups. Keep it that way.
Proxmox host tcp/8006, tcp/8765 The broker VM only Proxmox API and the result extractor. Only on Proxmox detonation.
Windows endpoints none n/a The agent only dials out, and still picks up a package or ring change within about a minute through its wake probe. A detonation broker takes nothing from your network either.

Outbound allowlist

Every host the platform can reach on its own, HTTPS on tcp/443 apart from its own URL. Each one is a download of public data or a destination you configured. Nothing of yours is uploaded unless you switch on the transparency log.

FLOW HOSTS DEFAULT IF BLOCKED
Platform image pull
Install and upgrade, from the Docker host
  • ghcr.io
  • pkg-containers.githubusercontent.com
  • Docker Hub (postgres, and caddy with the overlay)
You run it Pull fails loudly. Mirroring the unmodified image to an internal registry is allowed.
CVE corpus + CISA KEV
Scheduled sync
  • api.github.com
  • github.com
  • release-assets.githubusercontent.com
  • objects.githubusercontent.com
  • raw.githubusercontent.com
On CVE data goes stale and says so after about a day. Nothing else breaks; an offline bundle import covers air-gapped sites.
winget manifests
When you ingest a package, or armed unattended updates ingest one
  • raw.githubusercontent.com
On demand Ingest is refused. Fail-closed.
Version list
The ingest dialog
  • api.github.com
On demand Type the version by hand instead.
Installer download
When a broker detonates a version
  • the vendor host named in each manifest
On demand That version cannot be detonated, and your server never holds its bytes. Fail-closed. HTTPS only.
Package search
When you search in the ingest dialog; cached 6 h
  • cdn.winget.microsoft.com
On demand Search shows an error. Paste a winget-pkgs URL or publisher.id@version instead.
winget name index
About hourly once enabled, as a conditional GET
  • cdn.winget.microsoft.com
Off Name and moniker lookup stays basic. A background refresh, separate from search above.
Upstream monitors
Freshness, drift and withdrawal checks
  • raw.githubusercontent.com
  • ingested packages’ vendor hosts
Off No freshness badges, drift detection or auto-heal. Gated twice: at deploy time and in the console.
Rekor transparency log
Per attestation, once enabled
  • rekor.sigstore.dev, or your own mirror
Off n/a. The only upload path in the product, and it is off by default.
Startup self-check
Once at boot
  • your own public URL, on its own scheme and port
Always A warning in the log. Never blocks anything.
Installer byte store
Only if you choose Azure Blob in Settings › Storage
  • your Azure Blob account
Off n/a. Your own storage account, reached only when you configure it.

These are the server’s. For every version that was detonated with the byte store on, an endpoint needs exactly one host: your server, which serves the attested installer bytes itself. A version attested before the byte store existed, or whose kept bytes were since cleaned up, is still downloaded by winget from the vendor host in its manifest (flow 9), pinned to the same SHA-256, so endpoints that install it need that host too. Why it matters is on the vendor CDN and proxy pages.

WHAT YOU NEED

Three things, and one of them is optional.

SERVER

One Docker host

  • x86-64 (linux/amd64) Linux, or Windows / macOS with Docker Desktop. No arm64 image.
  • Docker 24+ with Compose v2.
  • One Compose stack: the platform and a Postgres 18 sidecar, a backup scheduler, and short-lived setup jobs that create the database role the platform connects as and take a backup before each upgrade. Backups need a recipient certificate you generate once.
  • A URL your endpoints can reach, set as WEP_PUBLIC_BASE_URI before first boot.
ENDPOINTS

Windows 11 machines

  • Windows 11 23H2 or later, physical or VM.
  • An agent MSI installed elevated with a one-time enrollment token. It is self-contained: no .NET runtime to install.
  • Network reach to your server on one port, and trust in its TLS certificate (push the root by Group Policy or Intune; the agent never adds one). Nothing inbound.
  • Trust in the Certum root that signs the agent and shim; pre-stage it on disconnected images.
TLS

A certificate for remote endpoints

  • Same-host homelab: plain HTTP on localhost is fine.
  • Anything else: HTTPS, because winget will not use a plain-HTTP source off loopback.
  • Use the bundled Caddy overlay with your own certificate, or your existing reverse proxy.
  • The platform adds no protocol or cipher knob. Endpoint TLS ends at your proxy, on its settings: Caddy’s defaults with the overlay.
OPTIONAL

A detonation host

  • Hyper-V on Windows Server 2025, or Proxmox VE with a Windows broker VM.
  • A golden image (about 40 GB) you build from your own Windows ISO and sign with your own key. On Proxmox the build host must be a separate machine.
  • On Hyper-V, the broker installer also expects Docker Desktop on that host.
  • Without one, nothing is verified, so by default no winget version is served.
  • Sandboxes start offline; on Hyper-V, where a connected one leads is yours to choose.
WHAT YOU CAN CONFIGURE

Every switch that changes the map.

Defaults are the reference compose, as of platform v0.248.0. Config keys are set as environment variables with __ for : and need a restart; many are listed read-only in the console under Settings › Deployment config. Console settings are stored in the database and survive upgrades.

NETWORK & TLS

Where it listens

Platform port
18080
.env · WEP_PLATFORM_PORT
Public URL agents call back to
required
.env · WEP_PUBLIC_BASE_URI
Plain HTTP, no TLS
off
.env · WEP_ALLOW_PLAIN_HTTP the sample .env turns it on, for a localhost lab
TLS front door
none
Caddy overlay, or your own proxy
Trusted proxy networks
built-in
Console · Settings › Network 10.0.0.0/16 and 10.244.0.0/16 until you set your own; applies on restart
Postgres host binding
localhost
WEP_POSTGRES_BIND / WEP_POSTGRES_PORT 127.0.0.1:55432, for psql and pg_dump on the host
DATA

Where things are kept

Installer byte store
local disk
Console · Settings › Storage or Azure Blob, refused until you attest public access is blocked, versioning and delete protection are on
Audit retention
unbounded
Console · Settings › Audit partition health whole months are pruned, and each prune is signed; pruning also needs Wep:Audit:Prune:Enabled, which ships off
Installer bytes cleanup
off
Wep:Catalog:RetainedByteGc:Enabled while off, kept bytes are never deleted and storage grows
Audit signing key
local file
Data volume a Key Vault in your subscription on commercial tiers
Backups
daily, 14 kept
.env · WEP_BACKUP_SCHEDULE, _AT, _KEEP at 02:00 UTC: databases and keys, encrypted to a certificate you make once with backup keygen and keep off the host; one more before every upgrade. Installer bytes sit beside them unencrypted. Restore is not yet proven end to end: keep your own volume backups until it is
DETONATION

How packages get checked

Backend
manual SBOM
Broker overlay + mint a broker in the console Hyper-V, or Proxmox/KVM through the wizard on the broker host
Golden image in use
none
Console · Brokers, or Settings › All settings (advanced) the broker overlay seeds win11-pro-baseline; it must match what the broker advertises
Parallel detonations per broker
1
Broker config
Recording proxy for sandbox traffic
off
Opt-in on the Hyper-V broker host you build its switch and image yourself; Proxmox sandboxes are always offline
OUTBOUND

What the server may reach

CVE + KEV sync
on
Wep:Cve:Sync:Enabled changes hourly, a full refresh weekly; flagged stale after 24 hours
Offline CVE bundle
off
Wep:Cve:BundlePath pin its signer with Wep:Cve:BundleTrustedPublicKeyPath
Offline OSV bundle
off
Wep:Cve:OsvBundlePath pin its signer with Wep:Cve:OsvBundleTrustedPublicKeyPath; without one, an unsigned bundle is accepted with a warning
winget name index refresh
off
Wep:Catalog:WingetIndex:Enabled name search at ingest still asks the winget CDN when you search
Upstream freshness and drift
off
EgressAllowed in config, then Console · Settings › All settings (advanced) two gates: deployment and console
Rekor transparency log
off
Wep:Rekor:Mode · Wep:Rekor:Endpoint point it at your own mirror instead of the public log
AUTOMATION

What runs without a click

Unattended winget updates
off
Armed by an in-console ceremony
Verifying the installer a waiting endpoint needs
on
Console · Settings › Rollout & safety since platform 0.242.1: hourly, at most 3 sandbox runs at a time, each downloading a vendor installer; a config off always wins
Start an agent rollout on a new release
off
Console · Settings › Rollout & safety auto-arm; or arm a release by hand on the Agent rollout page
Promote an agent rollout ring by ring
off
Wep:AgentRollout:Evaluator:FiringEnabled promotes healthy rings and halts a bad release; while off, an armed rollout does not move. Pause, resume and abort are in the console
Rollback proposals
off
Console · Settings › Rollout & safety
winget self-heal on endpoints
off
Console · Settings › winget self-heal never reboots on its own
What an update does while the app is open
Wait
Per package: Wait, Remind, Ask then close, Close without asking Wait installs once the app is closed. Remind shows one notice per update. Ask then close gives the signed-in user a countdown they can postpone: by default a deadline 4 hours out, up to 3 postpones of 4 hours, then 15 minutes. Close without asking closes with no prompt, on unattended endpoints. An agent older than 0.248.0 reads Remind as Wait.
Finishing an agent update that waits for a reboot
on
Console · Settings › Agent update resume without the reboot, after 72 hours, if the new agent already runs cleanly
Emergency stop
available
Console, typed confirmation
ROLLOUT

Timing and limits

Ring soak minimums
1 h to 7 d
Console · Settings › Catalog policy canary 1 h, pilot 24 h, broad 72 h, all 7 days; per tenant, up to 30 days
Unattended update dwell
24 to 72 h
Policy › Update grant canary 24 h, pilot 48 h, broad 72 h; never shorter than the ring’s soak minimum; widening a grant takes a typed confirmation
Highest ring automation reaches
set per grant
Policy › Update grant canary up to all (all needs an acknowledgement); packages and publishers can be excluded
Pipeline sweep
every 15 min
Wep:UpdatePipeline a ring halts when more than 5% of its installs fail, over 14 days of evidence
Updates in flight at once
3
Policy › Update grant
Waiver ceiling and expiry
90 days
Console · Settings › All settings (advanced), platform admin only expiry 90 days, 180 at most; the file-count ceiling is twice the observed count, at most 1,000,000
Export limits
capped
Console · Settings › Audit partition health 10,000 audit spans, 5,000 catalog versions, 100,000 inventory rows per export
ACCESS

Who gets in

Local accounts, four roles
on
Console · Settings › Users & roles
TOTP two-factor
per user
Console, with recovery codes
Console session caps
off
Console · Settings › Users & roles commercial tiers enforce them from the start: admin 8 h or 1 h idle, other roles 12 h or 8 h idle. Off, a session is 8 hours, sliding
Remember me
14 days
Wep:Community:RememberMe sliding, never past 30 days
Sign-in throttling
on
Wep:Community:AuthRateLimit 20 failed attempts per network per 5 minutes; an account locks for 15 minutes after 5 failures
First-run setup token
generated
.env · WEP_COMMUNITY_SETUP_TOKEN pin your own for unattended installs
Entra ID SSO
commercial
Set up with your instance optional Microsoft Graph role re-check

Not configurable, on purpose

TLS versions and cipher suites

OS and .NET defaults: SChannel on Windows, OpenSSL in the server container. A proxy you put in front is yours to tune.

Cryptographic algorithms

No plugin seam, no algorithm setting. Standard primitives only.

Telemetry

Nothing to switch off, because nothing is there.

Ports on endpoints

None, and no setting adds one. Package and ring changes still land in about a minute, because the agent asks.

TRUST ON EVERY HOP

Reaching a box is not the same as being believed by it.

Standard cryptography only, through the OS and .NET: TLS, ECDSA P-256 with SHA-256, HMAC-SHA-256, Authenticode. No home-grown handshake anywhere on the wire.

Desired state is integrity-checked

Every desired-state response carries an HMAC-SHA-256 keyed by that check-in’s one-time session secret. The agent verifies it and fails closed by default, so a tampered or injected response cannot add a package or arm a repair.

Installers are pinned

Every winget install is pinned to a SHA-256 the platform recorded: the vendor installer’s from ingest, or a derived MSI’s. Bytes your server keeps come from your own feed; anything else is the vendor’s copy and has to match the same hash. Chocolatey and Scoop packages are not served until they can be attested.

The shim is checked before it runs

The agent validates the shim’s Authenticode signature at start-up and refuses to run it otherwise. The shim itself is Apache-2.0 and has no network code at all.

Brokers prove who they are

A broker enrolls once with a token, then connects with a client certificate from the platform’s own broker CA, and pins the platform’s broker CA in return. The Proxmox host is treated as untrusted: before each clone the broker checks a signed template record and VM id it holds itself, though it cannot re-hash the template disk.

The database binds the platform too

Since platform 0.241.0, Community Edition connects to Postgres as a restricted role rather than as the database owner, so row-level security and the audit log’s insert-only rules apply to the platform itself. The reference compose creates the role; an install from before 0.241.0 moves to it once, by a runbook. A database superuser can still lift all of it, and the platform warns about a superuser connection rather than refusing one.

The installers are signed

Since platform 0.237.0 the agent and broker MSIs, the programs inside them and the wep-bundle-verify tool are Authenticode-signed with a Certum certificate and timestamped. Since 0.241.0 each release is checked on a clean machine before it is published, and fails if a signature does not verify.

The image is signed

Release images are cosign-signed and ship a CycloneDX SBOM, so you can verify what you pulled before you run it.

Audit spans are signed

Every audit span is signed with ECDSA P-256. The platform signs its own with a key your instance holds: a file on the data volume in Community Edition, a Key Vault in your Azure subscription on commercial tiers. From agent 0.240.0, each endpoint agent signs its spans with its own key, enrolled with the platform: in the TPM where the machine has a usable one, otherwise in the Windows software key store, from which a local administrator can extract it; the platform verifies them at ingest and again at export, and refuses an export window holding development-signed spans it cannot verify, from agents before 0.240.0 or one that could not create a key.

The full trust model, including what is still being built: /trust

WHAT IS NOT THERE

The connections we left out on purpose.

A security tool earns the right to run on your fleet partly by what it does not do. Verify it yourself: deny all egress, log what is attempted, and run a full cycle. Any destination not on this page is a bug we want to hear about.

No telemetry
No usage analytics, no crash reporting, no activation call. Commercial licences verify offline against a key in the product. There is no code path to firewall.
No inbound to endpoints
Nothing listens on a managed machine. The agent asks; the platform answers.
No revocation fetches from the server or broker
Those chains validate against pinned roots. On endpoints, Windows checks revocation for the signatures on our own binaries.
No NTP client
Time comes from the host, as it does for everything else on it.
No repair-time downloads
The VC++ runtime the winget repair needs ships inside the agent MSI.
No cloud unless you pick it
Azure endpoints appear only when you configure an Azure backend. Community Edition dials none.
AIR-GAPPED

What works with the cable pulled, stated plainly.

KEEPS WORKING
  • Inventory: winget, Chocolatey and plain uninstall entries.
  • Reconciliation and deployment of versions detonated with their installer bytes kept on your server.
  • Ring-staged agent updates with rollback.
  • The signed audit log and offline-verifiable evidence export.
  • CVE matching, from an offline bundle you import (signature-checked if you pin a key).
STOPS
  • Ingesting a new package or version. Manifests and installers come from the internet, and ingest is fail-closed without them.
  • Installing a version whose bytes your server does not hold, since winget would fetch it from the vendor.
  • There is no offline ingest path today.
  • Freshness, drift and upstream-withdrawal monitoring, which are off by default anyway.

The middle ground: let the server reach the ingest hosts, keep endpoints off the internet, and promote only versions whose installer bytes your server keeps, so the server is the one host an endpoint needs for its installs.

COMMERCIAL TIERS

A different server side, in your Azure subscription.

The Business tier is a dedicated instance we deploy and upgrade in your own Azure subscription. The endpoints, the agent and the detonation side do not change. The server side does: Container Apps behind a managed-certificate ingress, a private network, managed identities instead of passwords, and a deploy identity you own. Its full map, what you provide and what you grant us are on the dedicated instance page.

COMMUNITY COMMERCIAL
Runs on
Your Docker host
Azure Container Apps in your subscription
Database
Postgres 18 sidecar
Azure Database for PostgreSQL 18, no public access
Installer bytes
Data volume
Storage account behind a private endpoint
Image
Pulled from ghcr.io
Imported by digest into your own registry
Sign-in
Local accounts, roles, TOTP
Entra ID SSO
Audit signing key
File on the data volume
Key Vault in your subscription
Broker port
host :18443
:8443, closed until you list your broker ranges
Extra egress
None
Entra sign-in and the instance’s own Azure services; Microsoft Graph only if you turn on strict role checks
Who upgrades
You
Attestree

One server. No open ports on your fleet.

Free Community Edition — self-host up to 200 endpoints, no Azure.