# Jurisdiction rules

Jurisdiction rules add regional content restrictions to your project's policy. Your integration supplies the viewer's country and performs any required age check. [Global mandatory rules](/docs/guides/mandatory-rules) continue to apply everywhere.

## Jurisdictions served

The policy editor shows global rules under **Mandatory rules** and regional rules under **Jurisdiction rules**, with a country or European Union badge on each rule. Both groups are read-only.

Select the jurisdictions your project serves in project settings. `global` always applies and cannot be removed. Other jurisdictions are opt-in. Mandatory rules only raise a decision: a regional age restriction cannot weaken a policy block, and a regional block does not delete content for viewers elsewhere.

## Existing assets and rollout

Changing the jurisdictions served starts a background refresh of completed assets that still have stored bytes or extracted text. Existing evidence may already support a newly applicable rule. Otherwise, requests for that jurisdiction are withheld with `403` / `asset/regional-evidence-required` until usable evidence is available. This is not a finding that the asset is harmful, and it does not turn its base status into a global block.

Before enabling a jurisdiction on a live library, retain the originals you will need, test representative viewer countries, and prepare an unavailable/pending notice during the refresh. Retrieve affected assets with `region` to check readiness. If classification fails, use [processing-failure recovery](/docs/guides/asset-lifecycle#recover-from-processing-failure). Assets whose content was removed are not included in this background refresh: submit a fresh copy from your storage with a new `externalId` if the existing evidence is insufficient. Do not treat missing evidence as permission to serve your own copy.

## Regional content rules

The regional catalogue currently includes:

| Jurisdiction          | Additional outcome                                                                                                                                                                                            | Official sources                                                                                                                                                                                         |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Germany (`DE`)        | Withhold qualifying Nazi symbols and public Holocaust denial, with supported lawful-purpose exceptions.                                                                                                       | [StGB §§86, 86a and 130](https://www.gesetze-im-internet.de/stgb/BJNR001270871.html).                                                                                                                    |
| Austria (`AT`)        | Withhold qualifying Nazi symbols and public Holocaust denial; non-promotional pictorial and other statutory exceptions are assessed separately.                                                               | [Abzeichengesetz §§1–2](https://www.ris.bka.gv.at/GeltendeFassung.wxe?Abfrage=Bundesnormen\&Gesetzesnummer=10005262) and [Verbotsgesetz §3h](https://ris.bka.gv.at/eli/stgbl/1945/13/A1P3h/NOR40258110). |
| France (`FR`)         | Withhold qualifying public Holocaust denial. A blanket online Nazi-symbol rule is not included.                                                                                                               | [Law of 29 July 1881, Article 24 bis](https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000043982451/2026-08-22).                                                                                  |
| United Kingdom (`GB`) | Age-restrict pornographic images and video; your app must confirm a reliable age check.                                                                                                                       | [Ofcom: age checks to protect children online](https://www.ofcom.org.uk/online-safety/protecting-children/age-checks-to-protect-children-online).                                                        |
| European Union (`EU`) | Age-restrict pornographic images and video with a reliable age check as an opted-in product standard.                                                                                                         | [European Commission: protection-of-minors guidelines under DSA Article 28](https://digital-strategy.ec.europa.eu/en/library/commission-publishes-guidelines-protection-minors).                         |
| Australia (`AU`)      | Age-restrict pornographic images and video, qualifying high-impact violent media and material promoting or instructing suicide, self-injury or disordered eating. Your app must confirm a reliable age check. | [eSafety: Age-Restricted Material Codes](https://www.esafety.gov.au/industry/codes/faq-access-to-online-porn-and-other-adult-content).                                                                   |
| Brazil (`BR`)         | Age-restrict pornographic images and video; your app must confirm a reliable age check.                                                                                                                       | [ECA Digital, Law 15,211, Article 9](https://planalto.gov.br/ccivil_03/_ato2023-2026/2025/lei/l15211.htm).                                                                                               |

Non-pornographic nudity alone does not activate the pornography rules. A topic mention, recovery or support alone is insufficient for Australia's self-harm rule.

The opted-in EU adult-content rule is a product standard informed by DSA guidance, not a claim that every service has the same legal obligations. This catalogue covers specific content and delivery rules; it does not automate every duty in a jurisdiction. Regional rules can affect custom policies that otherwise allow the material.

Sources checked on 27 October 2026. The linked legislation and regulator guidance describe their own service scope and exceptions; the table describes the controls implemented by CalmLens.

## Decisions and precedence

Global matches record `decision.source: "mandatory-rule"` and the stable `decision.rule` key. Stricter regional outcomes appear in `decision.regions`; they do not change the base decision. EU rules apply to viewers in its member states when EU is selected. See [Mandatory and regional decisions](/docs/guides/classification-results#mandatory-and-regional-decisions) for the report fields.

The effective action is the strongest of the base decision, the matching country entry and an opted-in EU entry for an EU member. A country entry wins a tie with EU. With no applicable regional entry, use the base decision. A regional `block` withholds the asset even if the request has a signed URL or API key. A regional `age-restrict` uses the existing age-restricted delivery opt-in. Global blocks and safety holds still apply everywhere.

## Viewer region

Pass optional `viewerClaims` on the CDN request or SDK media component. The asset API returns the stored classification and policy decisions; it does not return a viewer-specific `delivery` object, and you do not need an asset lookup before requesting media.

| Claim                       | When omitted                                              | When supplied                                                                                                 |
| --------------------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `viewerClaims.region`       | Country inferred from the connection reaching CalmLens.   | Your viewer's uppercase ISO country code, such as `DE` or `GB`. `EU` is a jurisdiction, not a viewer country. |
| `viewerClaims.ageConfirmed` | `false`, including requests with API keys or signed URLs. | Your application's confirmation that age-restricted content may be delivered.                                 |

Claims are customer assertions. CalmLens does not verify physical location, age or the method your application used. Your application chooses how to establish and enforce those facts. Claims do not override blocks, safety holds or private-asset permissions. Passing a country does not enable additional jurisdictions: configure the jurisdictions your project serves first.

### SDK media component

**React**

```tsx
import type { Asset } from "@calmlens/sdk/app";
import { CalmLensMedia } from "@calmlens/sdk/react";

// Claims are optional customer assertions evaluated by the CDN.
// ageConfirmed stays false until your app completes the required age check.
export function RegionalPhoto({
  asset,
  region,
  ageConfirmed,
}: {
  asset: Asset;
  region: string;
  ageConfirmed: boolean;
}) {
  return (
    <CalmLensMedia asset={asset} viewerClaims={{ region, ageConfirmed }} alt="Uploaded photo" />
  );
}
```

**Vue**

```vue
<script setup lang="ts">
import type { Asset } from "@calmlens/sdk/app";
import { CalmLensMedia } from "@calmlens/sdk/vue";

// Your app supplies optional viewer context to the CDN.
defineProps<{ asset: Asset; region: string; ageConfirmed: boolean }>();
</script>

<template>
  <CalmLensMedia :asset="asset" :viewer-claims="{ region, ageConfirmed }" alt="Uploaded photo" />
</template>
```

**Svelte**

```svelte
<script lang="ts">
  import type { Asset } from "@calmlens/sdk/app";
  import { CalmLensMedia } from "@calmlens/sdk/svelte";

  // Your app supplies optional viewer context to the CDN.
  let { asset, region, ageConfirmed }: { asset: Asset; region: string; ageConfirmed: boolean } = $props();
</script>

<CalmLensMedia {asset} viewerClaims={{ region, ageConfirmed }} alt="Uploaded photo" />
```

**Plain JavaScript**

```javascript
import "@calmlens/sdk/elements";

// Your app supplies optional viewer context to the CDN.
export function renderRegionalPhoto(container, { asset, region, ageConfirmed }) {
  const media = document.createElement("calmlens-media");
  media.asset = asset;
  media.setAttribute("viewer-claims", JSON.stringify({ region, ageConfirmed }));
  media.setAttribute("alt", "Uploaded photo");
  container.replaceChildren(media);
}
```

Pass `viewerClaims` directly to the component. Without an explicit region, the CDN infers it when the browser requests the media. A stored base status of `allowed` can still result in a regional refusal. The component shows its unavailable state if media cannot load; a failed image load alone does not identify the reason. The reveal callback asks your application to decide whether to set `viewerClaims.ageConfirmed`; it does not set it automatically.

### Explain a regional restriction

The stored report's `classification.moderation.policy.decision.regions` describes recorded regional actions. The CDN evaluates the current asset and project rules for each request, including cache hits. It returns an error rather than restricted media. A proxy or SDK `cdn.serve` caller can inspect the status and JSON error code; ordinary image elements expose a load failure, not the JSON body.

| Error code                         | Meaning                                                                                                                                  |
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `viewer/invalid-claims`            | The supplied claims are malformed, contain an invalid country or a non-boolean age confirmation.                                         |
| `viewer/region-required`           | Regional controls apply, but neither an explicit country nor a usable connection country is available.                                   |
| `viewer/age-confirmation-required` | This content is age-restricted and `ageConfirmed` is omitted or false.                                                                   |
| `asset/regional-evidence-required` | The asset needs classification evidence for an applicable jurisdiction. See [Existing assets and rollout](#existing-assets-and-rollout). |
| `asset/not-available`              | Delivery is refused, including regional blocks and other asset restrictions.                                                             |

### Proxies and request country

When the browser requests CalmLens directly, the fallback country comes from its connection. When your backend proxies media, the fallback may identify your backend instead. Forward the country from the incoming viewer request as `viewerClaims.region`. A VPN can change the visible connection country; CalmLens does not recover physical location behind it. The HTTP `Origin` header is not used for country detection.

SDK CDN methods accept the claims object. At the HTTP level, send either a JSON-encoded `viewerClaims` query parameter or the `X-CalmLens-Viewer-Claims` header containing JSON. The query parameter takes precedence if both are present. Media elements use the URL form; proxies can use the header form.

```ts
const response = await fetch(assetRenderedUrl, {
  headers: {
    "X-CalmLens-Viewer-Claims": JSON.stringify({
      region: viewerCountry,
      ageConfirmed: false,
    }),
  },
});
if (!response.ok) return new Response(await response.text(), { status: response.status });
return new Response(response.body, {
  headers: { "Content-Type": response.headers.get("Content-Type") ?? "application/octet-stream", "Cache-Control": "private, no-store" },
});
```

For an application that never uses age-restricted content, leave age confirmation omitted or explicitly false on every path, including server-side downloads and processing jobs. Authentication does not confirm age.

### Signed URLs and caching

Supply `viewerClaims` when creating a signed URL and forward the returned URL unchanged. Claims are included in its signature; do not append or change them afterwards. Unsigned public requests also accept claims. Signing is an access option, not a requirement for claims.

A proxy cache must not bypass delivery checks. The simplest integration forwards each request to CalmLens and honours its cache headers. A cache separated only by country and age confirmation can still serve stale permissions after moderation or project settings change. Viewer-dependent content responses use `private, no-store` for downstream caches; CalmLens can reuse its own bytes after checking the current request.

### Self-hosted content

If you serve your own bytes, your application must apply the stored base and matching country/EU decisions, plus any age check, before returning them. Base-status webhooks alone do not describe regional eligibility. Subscribe to `asset.region-restricted` to invalidate recorded decisions, and refresh after project jurisdiction settings change. An event is not an exhaustive access list. Keep delivery unavailable if required classification evidence is missing.

## Age-check responsibilities

Your app performs age assurance. Use the age-check standard listed for the applicable jurisdictions above: `verified` requires a reliable check rather than a self-declared age; `any` permits your ordinary age check. Apply the stricter standard when both EU and a member country apply, including when the base policy already age-restricts and `decision.regions` is empty. Set `viewerClaims.ageConfirmed: true` only after confirming the viewer passed. CalmLens does not receive age-provider results or verify a person's age. See [native age checks](/docs/guides/native-age-checks).
