Azure APIM Emulator

latest release →

Prove an API Management policy before it reaches a tenant.

A clean-room Go implementation of the Azure API Management contract: the ARM management plane, the gateway, and the policy engine with its C# expression language. It runs on a laptop, starts in under a second, and accepts the same SDKs, the same policy XML and the same Ocp-Apim-Subscription-Key a real instance does.

Switching to real Azure is configuration, not a code path.

Every capability below names the check that proves it. Where the evidence is a document rather than a tenant, this page says so.

witnesses, every green claim names one
/policies implemented, derived from Microsoft's pages
/C# expression members bound
/of Microsoft's policy snippets parse
rows verified against a real tenant

The problem this solves

API Management policy is code that runs only in Azure. That makes it the part of a platform you can test least and break most expensively.

You cannot run it locally

A rate-limit or validate-jwt policy has no local runtime. The feedback loop is deploy, wait, call, read the trace, repeat, against a billed instance shared with colleagues.

The gateway, the policy engine and the expression language, on your machine

The policy language is C#, and nothing checks it

Expressions like @(context.Request.Headers.GetValueOrDefault("x")) are validated by a tenant at save time and by nothing before that. A typo is a 500 in production.

A compiler for the documented subset, derived from Microsoft's own reference

"Works on the emulator" proves nothing on its own

An emulator that agrees with itself is a mirror. The only evidence worth having comes from something you did not write: Microsoft's SDKs, a certified OpenID provider, gRPC and SOAP clients.

Third-party clients drive every protocol in CI, and the ledger says which

What it does

Each capability carries its evidence tier and an honest status. The tiers are ranked: a packaged third-party client over a network outranks Microsoft's own SDK in-process, which outranks our own tests.

ARM management plane sdk-verified

Services, APIs, operations, products, subscriptions, named values, backends, certificates, loggers, diagnostics and more, over the 2024-05-01 stable contract. Conditional requests, collection queries, the error envelope, and Entra-authenticated calls.

Microsoft's Go, JavaScript, Python and .NET SDKs drive it in CI

Gateway and protocols witnessed

HTTP routing, WebSockets and SSE, GraphQL with synthetic resolvers, gRPC over real HTTP/2, SOAP from WSDL, and an MCP server. Subscription keys, products and operation matching behave as the tenant's do.

grpc-js, node soap, a reference GraphQL and the official MCP client, each over a network

Policy engine

XML policies compiled and run in every section, with inheritance across service, product, API and operation scope. Rate limits and quotas count per subscription in the windows Microsoft documents. The attribute surface of each policy is derived from the vendored reference page, not transcribed, so an attribute Azure rejects is rejected here.

the inventory is regenerated from Microsoft's pages and gated in CI

C# expression language

A hand-written parser and evaluator for the documented subset: the context object, lambdas, interpolated strings, new, casts, null-conditionals, named arguments, and the .NET members Microsoft allows. The member list is derived from two Microsoft sources at pinned commits; any disagreement between them stays visible.

of Microsoft's own policy snippets parse, enforced as a ratchet

JWT validation witnessed

validate-jwt fetches signing keys and issuer from an OpenID discovery endpoint with the cache Microsoft documents, requires exp by default, and honours clock-skew, require-scheme and the output variable.

an OpenID Certified provider mints the tokens; it found an expired-token hole on its first run

AI gateway witnessed

Token rate limits per minute and token quotas over fixed periods, token metrics with dimensions, and semantic caching's absence stated plainly. Streamed answers are counted byte-for-byte.

OpenAI's own client drives the surface in CI

Workspaces, RBAC, gateways partial

Workspace scoping across every resource family, Azure RBAC with deny-by-default, and self-hosted and workspace gateways that serve only their associated APIs. Networking, private endpoints and custom domains are modelled; tiers and SKUs enforce on opt-in.

witnessed where green; the ledger names what remains

Developer portal planned

Not started, and deprioritised on purpose: the management and gateway surfaces are what a platform team tests against. The ledger carries it as a row so the gap is visible rather than forgotten.

parity.md, row P3

Compared against a real tenant

The differential harness is built and has never run: it needs a real APIM instance and a bearer token. Until it does, every green row on this page rests on Microsoft's documents and third-party clients, which is the strongest evidence available and still not Azure.

e2e/differential skips without APIM_AZURE_SERVICE_URL

Honest status

The numbers above are not typed into this page. The build fails if they are.

Where the evidence comes from

Every capability the parity ledger grades green must name a witness in docs/witnesses.json, and CI fails if a named witness stops existing. A renamed test that leaves a green row behind is a lie the reader cannot see, so the manifest is what makes the claim checkable.

ci:a CI job driving a packaged external client over a network. The strongest tier. sdk:Microsoft's own client does the talking, in-process. go:our own client on both ends. Proves the emulator agrees with itself. verifiedcompared against a running Azure instance.

Three ledgers are derived rather than written. The policy inventory and the expression member list are regenerated from Microsoft's vendored pages at pinned commits, and a hand-edit that disagrees with the derivation fails CI. The policy corpus is a ratchet: an expression that parsed yesterday must parse today.

Get it

One binary, or one container. No database to provision.

docker pull ghcr.io/calvinchengx/azure-apim-emulator:latest

Or the Go module, the Homebrew tap, or a release archive: see installation.

Run the whole thing

With the Entra emulator beside it, the management plane authenticates the way Azure does.

git clone https://github.com/calvinchengx/azure-apim-emulator
cd azure-apim-emulator
make up        # entra-emulator pinned, apim built from source
make status    # the pair is healthy

Then follow the quickstart: publish an API, attach a policy, call it through the gateway with a subscription key, and read the trace.