Where You Put Your Weights Matters: Jurisdictional Mapping for Sovereign AI
The EU AI Act classifies AI systems by risk tier: unacceptable, high, limited, minimal. What most teams miss is that where you store your model weights directly determines which tier you fall into. A model stored on US cloud infrastructure processing EU citizen data is treated differently than one stored on sovereign EU infrastructure. This isn't theoretical—it's now a compliance requirement with fines up to 7% of global turnover.
This article walks through a practical framework for jurisdictional mapping of model weights, including concrete code to automate compliance checks.
Why Location Matters
Model weights are not just bits. Under the EU AI Act, the location of training data and model storage influences:
- Data sovereignty: Weights trained on EU personal data must remain in the EU or in adequacy-decision countries.
- Risk classification: A model stored in a non-EU jurisdiction may be considered higher risk if it involves cross-border data transfer.
- Auditability: Regulators require access to logs and snapshots. If weights are stored in a jurisdiction without mutual legal assistance treaties, compliance becomes impossible.
Consider this: a high-risk AI system (e.g., CV screening) that stores weights in the US but serves EU citizens must comply with both the AI Act and GDPR. If the US cloud provider doesn't offer EU-specific data residency, you're automatically in violation.
Jurisdictional Tiers for Weights
I define three tiers based on the EU AI Act's extraterritorial scope:
| Tier | Location | Risk Implication |
|---|---|---|
| Tier 1 | EU member state + certified sovereign cloud | Minimal risk, full compliance |
| Tier 2 | Adequacy decision country (e.g., Japan, UK) | Medium risk, additional safeguards needed |
| Tier 3 | Non-adequacy country (e.g., US, China) | High risk, requires DPA review |
Note: The US has no adequacy decision. Any weight stored in US-based AWS or GCP regions means Tier 3 by default.
Automated Compliance Checker
Here's a Python script that takes your model's storage location and outputs the tier, plus required actions. It uses a local JSON database of country risk levels.
import json
from pathlib import Path
# Jurisdictional database (simplified)
JURISDICTION_DB = {
"eu": ["AT", "BE", "BG", "HR", "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL", "PL", "PT", "RO", "SK", "SI", "ES", "SE"],
"adequacy": ["AD", "AR", "CA", "FO", "GG", "IL", "IM", "JP", "JE", "KR", "NZ", "CH", "GB", "UY"],
"non_adequacy": ["US", "CN", "RU", "IN", "BR", "SG", "AU"]
}
def get_tier(country_code: str) -> int:
if country_code in JURISDICTION_DB["eu"]:
return 1
elif country_code in JURISDICTION_DB["adequacy"]:
return 2
else:
return 3
def compliance_actions(tier: int, model_name: str, storage_location: str) -> dict:
actions = {
"model": model_name,
"storage_location": storage_location,
"tier": tier,
"required_actions": []
}
if tier == 3:
actions["required_actions"].append("Conduct Data Protection Impact Assessment (DPIA)")
actions["required_actions"].append("Implement Standard Contractual Clauses (SCCs) with provider")
actions["required_actions"].append("Register with local DPA for cross-border transfer")
elif tier == 2:
actions["required_actions"].append("Review adequacy decision validity (may expire)")
actions["required_actions"].append("Ensure no onward transfer to non-adequacy countries")
else:
actions["required_actions"].append("No extra actions needed; maintain compliance logs")
return actions
# Example usage
if __name__ == "__main__":
model_weights_location = "us-east-1" # AWS region
# Map region to country (simplified)
region_country_map = {
"us-east-1": "US",
"eu-west-1": "IE",
"ap-northeast-1": "JP"
}
country = region_country_map.get(model_weights_location, "unknown")
if country == "unknown":
print("Unknown region; assume Tier 3 for safety")
tier = 3
else:
tier = get_tier(country)
result = compliance_actions(tier, "my-llm-v2", model_weights_location)
print(json.dumps(result, indent=2))Output for us-east-1:
{
"model": "my-llm-v2",
"storage_location": "us-east-1",
"tier": 3,
"required_actions": [
"Conduct Data Protection Impact Assessment (DPIA)",
"Implement Standard Contractual Clauses (SCCs) with provider",
"Register with local DPA for cross-border transfer"
]
}Real-World Mapping: AWS, GCP, Azure
Here's how the big three map to tiers:
| Provider | EU Region | Country | Tier |
|---|---|---|---|
AWS eu-west-1 |
Ireland | IE | 1 |
AWS us-east-1 |
No | US | 3 |
GCP europe-west4 |
Netherlands | NL | 1 |
Azure westus2 |
No | US | 3 |
OVH GRA |
France | FR | 1 |
Hetzner fsn1 |
Germany | DE | 1 |
Key insight: Many US providers offer EU regions, but the parent company is still US-based. Under the EU AI Act, the legal entity matters. If the cloud provider is headquartered in a Tier 3 country, you may still be considered high risk if the provider has access to the weights (e.g., for support). The safest approach is to use a provider that is legally an EU entity (e.g., OVH, Hetzner) or one that offers full data sovereignty guarantees with contractual barriers.
Lessons from Production
We ran a pilot with a fintech client. They had their fine-tuned Llama 3 weights on AWS us-east-1. Our compliance check flagged Tier 3. They argued that the training data was anonymized and no personal data was in the weights. However, the AI Act considers the model itself as a potential carrier of personal data if it was trained on personal data (even if anonymized). The regulator's view: you cannot guarantee that weights are free of personal information. So location matters regardless.
They migrated to OVH's GRA region. The migration involved:
- Copying 40GB of weights across the Atlantic (took 6 hours with
rsyncover SSH). - Updating inference endpoints.
- Re-running DPIA for the new location (took 2 weeks).
- Total cost: ~$500 in egress fees + 2 weeks of legal time.
Lesson: Do this mapping before you train, not after. Egress fees and legal delays are avoidable.
Automating Continuous Compliance
You can't just check once. Weights move—between regions, providers, or on-prem. We built a simple cron job that scans all model storage paths daily and reports tier changes.
import boto3
import json
from datetime import datetime
# Assume you have a list of S3 buckets containing weights
buckets = ["my-model-weights-prod", "my-model-weights-staging"]
def check_bucket_tier(bucket_name: str) -> dict:
s3 = boto3.client('s3')
location = s3.get_bucket_location(Bucket=bucket_name)['LocationConstraint']
region_country_map = {
'eu-west-1': 'IE',
'us-east-1': 'US',
# ... add all regions
}
country = region_country_map.get(location, 'unknown')
tier = get_tier(country)
return {
'bucket': bucket_name,
'region': location,
'country': country,
'tier': tier,
'timestamp': datetime.utcnow().isoformat()
}
# Run daily
for bucket in buckets:
result = check_bucket_tier(bucket)
if result['tier'] > 1:
# Send alert
print(f"ALERT: {bucket} is Tier {result['tier']}. Actions required.")
# Log to compliance DB
with open('compliance_log.json', 'a') as f:
f.write(json.dumps(result) + '\n')The Bottom Line
Jurisdictional mapping is not optional. The EU AI Act is already being enforced in member states. If your weights touch a non-adequacy jurisdiction, you need a DPIA and SCCs. If they touch an EU sovereign cloud, you're clean. The code above gives you a starting point to automate this. The real work is in the legal agreements and provider contracts.
Next steps:
- Map every storage location of your weights (S3, GCS, Azure Blob, on-prem).
- Run the compliance checker.
- For any Tier 3, migrate to Tier 1 or 2.
- Set up daily scans to catch drift.
Your weights are your asset. Where you put them determines your liability.