Skip to content
Chapter 7 of 8 · Cloud model platformsProving the serving region

14 min read

Proving the serving region

On this page

There are two kinds of question about where a request was processed. One is architectural — which regions could this service reach, and under what conditions — and the previous chapter is about answering it. The other is forensic: a specific case reference, a specific timestamp, and a demand for evidence that this particular payload was inferred inside a particular geography. The two need completely different artefacts, and only the second one has a deadline attached.

Three sources could conceivably answer the forensic version, and they are not equally good. Working out which one can carry the weight is most of the exercise, because the answer determines what has to be running in production months before anyone asks. Attestation is the property that separates them: a record produced by the party being audited is weaker evidence than the same record produced by the processor, regardless of whether both are accurate.

Your own logs record what you asked for

The application log line for a model call exists and looks convincing.

json
{
  "ts": "2024-03-04T14:07:22.481Z",
  "case_ref": "CLM-2024-88431",
  "endpoint": "bedrock-runtime.eu-central-1.amazonaws.com",
  "model_id": "eu.anthropic.claude-sonnet-4-5-20250929-v1:0",
  "input_tokens": 26418,
  "output_tokens": 902,
  "latency_ms": 4310,
  "request_id": "8f2c9d41-6b0e-4b2a-9f77-1c3ad5e2b910"
}

Every field is accurate and none of them answers the question. endpoint records the host the client resolved and connected to, which is where the request was received. model_id records the inference profile, and the previous chapter established what that prefix does — the profile permits routing across the EU geography, so the field names the set of regions this request could have been served from rather than the one it was. The log is a record of intent: what this service asked the platform to do.

There is a second problem with it that matters more to an auditor than to an engineer. It is your log, written by your code, stored in a bucket your team can write to, and produced by the party whose compliance is in question. Even where the content is correct, self-attested evidence sits at the bottom of any evidence hierarchy, and an auditor who accepts it for a material control is doing her job badly.

That hierarchy is not a suspicion about anybody's honesty, and reading it as one is why engineers find it irritating. Evidence is ranked by how difficult it would have been for the interested party to produce something else instead. A log written by your code, in a format your team chose, into a store your team can write to, could say anything — not because somebody would falsify it, but because nothing about the artefact constrains what it says. A record produced by the processor is constrained by the processor's own systems and by the processor's own interests, which are not aligned with yours, and that constraint is the entire source of its value. The consequence worth carrying into a design review is that improving your own logging cannot move you up that hierarchy. It can only make your position on it better documented.

The request_id field is the one that earns its place, because it is the join key to everything else. If that field is missing from the business record, the rest of this chapter is unavailable, since the platform's records are addressed by request id and there is no way to get from a case reference to a request id after the fact.

Response metadata rarely names the serving region

The next place to look is the response itself. Providers return a request id in a header, and cloud platforms typically add their own trace headers, which is how the id got into the log above.

http
HTTP/1.1 200 OK
x-amzn-requestid: 8f2c9d41-6b0e-4b2a-9f77-1c3ad5e2b910
x-amzn-bedrock-input-token-count: 26418
x-amzn-bedrock-output-token-count: 902
x-amzn-bedrock-invocation-latency: 4310

Token counts, latency, an identifier, and nothing that says where inference ran. This is normal across platforms rather than an oversight: the serving region is an implementation detail of a routing layer whose whole purpose is to make routing decisions on your behalf, and exposing it per response would make it something clients depend on. Some platforms do surface a region field on some paths, so it is worth checking rather than assuming, and the check is one curl with -i against the endpoint actually used in production. What its absence cannot be treated as is evidence of anything.

The platform's records are the ones that can name it

The evidence that survives contact with an auditor comes from the platform's own logging, which exists in two layers with different content.

Control-plane audit logs — CloudTrail on AWS, Activity Log on Azure, Cloud Audit Logs on GCP — record that an API call happened, by which principal, at what time, against which endpoint. The control plane is the management surface of a cloud service, where resources are created, configured and invoked, and it is the layer every cloud provider has instrumented for compliance because that is where permissions are exercised. These are the platform's records rather than yours, they are the ones auditors are used to reading, and they are typically already enabled for management events. What they do not reliably record is the region inference was performed in, as distinct from the region the API call was made to.

That gap is structural rather than an omission somebody could close with a feature request. A control-plane record describes an API operation: this principal invoked this operation, against this endpoint, at this moment. From the control plane's point of view the operation is complete once it has been authorised and dispatched, because authorisation is what the control plane exists to record. The routing decision happens after that point, inside the service, on a path the control plane does not instrument — there are no permissions being exercised there to record. So the region a control-plane log names is the region the call was addressed to, which is the fact you already had from your own log line, now attested by a better party and still answering the wrong question.

Data-plane invocation logging is the layer that can. The data plane is the path an individual request takes through the service to be worked on, so its records are per invocation rather than per API operation, and that is where a serving region can appear at all. It is a separate feature, configured separately: on Bedrock it is PutModelInvocationLoggingConfiguration; on Azure OpenAI it is a diagnostic setting on the resource; on Vertex it is request-response logging into BigQuery. A record from it looks roughly like this:

json
{
  "schemaType": "ModelInvocationLog",
  "timestamp": "2024-03-04T14:07:22.481Z",
  "accountId": "412009887301",
  "requestId": "8f2c9d41-6b0e-4b2a-9f77-1c3ad5e2b910",
  "region": "eu-central-1",
  "inferenceRegion": "eu-west-1",
  "modelId": "eu.anthropic.claude-sonnet-4-5-20250929-v1:0",
  "operation": "Converse",
  "input": { "inputTokenCount": 26418 },
  "output": { "outputTokenCount": 902 }
}

The two region fields are the whole answer, and they disagree. The call was made to Frankfurt and served from Ireland, both inside the EU, which satisfies an EU-only clause and would not satisfy a Germany-only one. Field names differ by platform and by schema version, so confirm which field your platform populates for cross-region routing before promising anyone the record contains it — a five-minute check against a single test invocation is enough, and doing it during an audit rather than before one is how a simple answer becomes a two-week finding.

Evidence has to be switched on before the day it is needed

Invocation logging is off by default on every major platform, and the reason is that it writes prompts and completions somewhere. Turning it on is one call.

bash
aws bedrock put-model-invocation-logging-configuration \
  --region eu-central-1 \
  --logging-config '{
    "s3Config": {
      "bucketName": "acme-bedrock-invocation-logs-eu-central-1",
      "keyPrefix": "claims/"
    },
    "textDataDeliveryEnabled": false,
    "imageDataDeliveryEnabled": false,
    "embeddingDataDeliveryEnabled": false
  }'

The three false values are deliberate. With text delivery disabled the log records the metadata — request id, regions, model, token counts, timestamps — and not the prompt or completion, which is exactly the evidence an auditor needs and none of the content that would create a second copy of customer data to reason about. Where bodies are genuinely wanted for debugging, the bucket has to be in-region and inside the same boundary as everything else.

Both storage figures below are one multiplication each, and doing them is what settles the cost objection before it is raised. 252,000 records at roughly 1.4 KB apiece is about 350 MB, which is the metadata-only case. Turning body delivery on replaces the size of a record with the size of a document: a mean of 27,000 input tokens across 252,000 requests is just under seven billion tokens a month, and at the handful of bytes a token of English occupies that lands around 30 GB. Two orders of magnitude more data, and still a sum nobody needs to approve — which is exactly why cost is not the reason the feature ships off. It ships off because the second version writes prompts and completions into a bucket, and a bucket of prompts is a second copy of customer content with its own residency, retention and access questions attached.

Cost should never be the reason this is off. At 252,000 requests a month and roughly 1.4 KB per metadata-only record, the logs are about 350 MB a month, under a dollar of object storage plus a similar amount in request charges. Enabling body delivery at a mean 27,000 tokens of input takes it to roughly 30 GB a month and about $0.70 in storage, with the retention policy mattering far more than the rate. Retention is the second half of the problem: a log with a ninety-day lifecycle rule cannot answer a question about last March, and audits routinely look back twelve months. That period is a compliance parameter rather than an infrastructure one, and it should be set from the longest lookback any of your agreements permit.

The third prerequisite is the join, and it is the one most often missing. The platform's log is keyed by request id and knows nothing about case references, customers or tenants, so a lookup that starts from "this customer's claim" needs a durable mapping from the business record to the request id. That means persisting the request id on the row rather than only emitting it to a log stream with its own shorter retention, and persisting it for every call rather than only for the ones that failed. A single database column, written on the success path, is the difference between answering in an afternoon and grepping a year of logs for a timestamp.

The asymmetry underneath that is what makes the column non-optional rather than tidy. The platform's index runs from request id to record, and there is no index in the other direction, because nothing at the platform knows your case references, your customers or your tenants. No query starting from a case reference can reach a record. The one instant at which both halves of the join exist in the same process is the instant the response arrives, so capturing the id there is not one way of solving the problem but the only way — once the response object has been discarded, the association is gone and cannot be rebuilt from anything either party still holds. A timestamp and a token count will narrow a year of invocation records to a handful of candidates, which is a plausible answer rather than a proof, and the distance between those two words is the subject of this chapter.

SourceNames the serving regionAttested byEnabled by defaultUseful lookback
Application logsNo — records intentYouYesYour own retention
Response headersUsually notThe platform, transientlyYesNone; not stored
Control-plane audit logSometimes the API region onlyThe platformManagement events, yes90 days, then archive
Data-plane invocation logYes, where the field existsThe platformNoWhatever you configure
Configuration snapshotNo — names the permitted setYou, or a config serviceVariesWhatever you configure

Evidence of a transaction and evidence of a control

Where a platform does not emit a serving region at all, the per-request question has no direct answer, and the useful move is to change what is being evidenced. Auditors work in two modes: evidence that a specific transaction was handled correctly, and evidence that a control was designed appropriately and operated continuously over a period. The second mode is what most compliance regimes are actually built on, and it is available even when per-request proof is not.

The control-operation version looks like a recorded configuration state, sampled continuously by something other than the deploying team, showing that the region-bound inference profile was the one configured for every hour of the audit period, with any change producing an alert and a ticket. A cloud configuration service that records resource state on change gives this for the deployment; a scheduled probe that reads back the running configuration and writes a timestamped record gives it for the application. What it demonstrates is that no path existed by which a request could have been served outside the geography, which is a stronger claim than one request's log line and is often easier to produce.

The two modes complement each other. Per-request records answer "what happened to this customer's data", which is what a data subject request — a person exercising a legal right to be told what you hold about them, or to have it deleted — or an incident investigation needs. Control records answer "was this true all year", which is what an annual audit needs. Neither proves as much as it appears to: an invocation log records what the platform's own service says it did, which is the right level of evidence and is still an assertion by the processor. It also says nothing about the abuse-monitoring copy, support access or the subprocessor chain, because those are handled by different systems with different logs, most of which you cannot read at all.

Who decides when residency and availability conflict

The conflict the previous chapter left open — cross-region routing keeps a service inside its availability SLO and outside a customer's EU-only clause — is not resolved by better logging, and it is not an engineering decision. It becomes tractable when engineering stops treating it as a question to answer and starts treating it as a memo to write.

The memo needs three measured numbers. First, the availability cost of pinning to one region, taken from the platform's own incident history and your throttling metrics rather than from intuition: over the last four quarters, how many minutes were served from outside the home region, and what the error rate would have been without that. Second, the alternative's cost, which is the price and latency of a geography-bound or sovereign deployment SKU and the list of models it does not carry. Third, the exposure: how many customers have the strict clause and what revenue sits behind them.

With those, the decision belongs to the people who own the commitments in tension. The contract owner or data protection officer interprets what the clause requires, because that is a legal reading and engineers are consistently wrong about it in both directions. The product or business owner chooses between reduced availability for everyone, a more expensive deployment, and going back to the customer for a written exception. Engineering implements whichever is chosen and makes the chosen state observable, so that the next auditor gets an answer in an afternoon.

What engineering must not do is settle it quietly by leaving the profile as it is, because that is also a decision — the choice to prioritise the availability SLO over the residency clause — made by whoever last edited a configuration file. Everything in this topic so far has treated the endpoint's capacity as something the platform hands you and administers. The remaining question is the one procurement asks after the first throttling incident: whether to stop being metered and start reserving capacity outright.

Cloud model platforms — read the current version at https://daviddominguez.dev/learn/en/cloud-model-platforms/proving-the-serving-region

Back to top