---
rfc: 0018
title: Cross-Region Kid Taxonomy & PoP Signer Replication
status: RATIFIED
version: 1.0.0
date: 2026-05-07
authors: [agentic-architect]
audience: [cloudflare-native-edge, devex-protocol-sec, edge-kubelet-engineer, cto]
requires: [RFC-0006-v1.0, RFC-0017]
intersects: [RFC-0015, RFC-0016, RFC-0020-v2]
---

# RFC-0018 — Cross-Region `kid` Taxonomy & PoP Signer Replication

> Audience: Cloud Agents (LLMs) and the gateway implementer. Closed enums and
> regex-pinned identifiers; no free-string namespaces.

---

## §1. Status

- **DRAFT** (2026-05-07).
- Intersects:
  - [`docs/rfcs/0017-d1-schema-and-migration-contract.md`](./0017-d1-schema-and-migration-contract.md)
    §7 — supersedes the "Sprint 3 issuer-global `kid`" ruling on a 90-day
    timer (§7 below).
  - [`docs/rfcs/RFC-0020-cf-native-custody.md`](./RFC-0020-cf-native-custody.md)
    §4 — one SignerDO per `kid`.

---

## §2. Problem

`gw-sig-1` was locked in by RFC-0017 §7 as **issuer-global** so that a
runtime-token issued at PoP-A and refreshed at PoP-B would not break RFC-0016
§6.5.1 sub/kid binding. That ruling explicitly deferred the "proper namespace"
to this RFC.

Multi-region rollout (Sprint 4 → Sprint 5) needs **per-PoP signers** — both for
blast-radius reduction (compromise of `iad` does not compromise `fra`) and for
independent rotation cadence. The single global `kid` cannot express this.

This RFC defines the kid taxonomy, the PoP replication model, and the
verifier-side compat path that lets us migrate without breaking already-issued
RFC-0016 tokens.

---

## §3. Kid Taxonomy (NORMATIVE)

```
kid_regex = ^gw-sig\.[a-z0-9]+\.edge-signer\.\d+$
```

- Component 2 (`[a-z0-9]+`) — region/PoP code. **Open-string regex CONFIRMED
  (CEO-locked 2026-05-07):** Cloudflare adds and retires PoPs continuously and
  outside our release cadence; pinning a closed enum here would force an RFC
  bump on every CF colo-set change. Initial values mirror CF colo codes
  (`iad`, `fra`, `nrt`, …); manifest validators MUST accept any string
  matching the regex. The reserved value `global` (§3.1 alias only) is the
  sole exception to free PoP-code allocation.
- Component 3 (`\d+`) — monotonic generation counter per region, bumped on
  rotation (§9).
- Examples: `gw-sig.iad.edge-signer.1`, `gw-sig.fra.edge-signer.1`,
  `gw-sig.iad.edge-signer.2` (post first IAD rotation).

### §3.1 Legacy alias

`gw-sig-1` (RFC-0017 §7 production value) is **reserved as a legacy alias**
mapping to:

```
gw-sig-1  ⟷  gw-sig.global.edge-signer.1
```

- The reserved region code `global` exists *only* for this alias. New SignerDOs
  MUST NOT be provisioned with region `global`.
- JWKS MUST advertise both `kid` strings (alias + canonical) pointing at
  identical pubkey material during the §7 transition window.
- `global` is removed from the reserved set on alias retirement (T+90d).

---

## §4. SignerDO Binding

- One SignerDO instance per regional `kid`. Address: `idFromName(kid)`.
  (Authoritative SignerDO contract: [RFC-0020 v2 §4](./RFC-0020-cf-native-custody.md);
  deployment locus — separate `signer-worker` script behind service binding —
  is locked in [RFC-0020 v2 §4.0](./RFC-0020-cf-native-custody.md).)
- The legacy `gw-sig-1` alias resolves to the same DO as
  `gw-sig.global.edge-signer.1` — i.e. `idFromName("gw-sig.global.edge-signer.1")`.
  The dispatcher performs alias normalisation **before** DO lookup so the DO
  itself only sees the canonical form.
- Per-region SignerDOs are independent DO instances with **independent
  keypairs** (see §5).

---

## §5. PoP Replication Model — PULL-ONLY

**NORMATIVE:** Each region's SignerDO holds an **independent** keypair.

- **No cross-region private-key replication.** Ever. Not at genesis, not on
  rotation, not on failover.
- **No "primary region"**. Regions are peers.
- Verifiers obtain pubkey material exclusively via JWKS (§6) — a *pull* by the
  verifier, not a *push* between regions.

Rationale: cross-region private-key replication is a key-extraction attack
surface (replication channel auth, in-transit confidentiality, race-vs-rotation
correctness, atomic-cutover proof). We accept a slightly larger JWKS payload
in exchange for eliminating the entire class.

Trade-off acknowledged: a runtime token issued by `iad` cannot be re-signed by
`fra`. Mid-connection refresh that crosses regions surfaces a kid change. This
is exactly the case RFC-0016 §6.5.1 review (CEO 48h SLA, S4-D4) is sized to
cover — see §7 below.

---

## §6. JWKS Shape with Multi-Kid

- Single endpoint: `/.well-known/jwks.json` (unchanged from RFC-0020 v2 §8).
- Shape:

```json
{
  "keys": [
    {
      "kid":             "gw-sig.iad.edge-signer.1",
      "region":          "iad",
      "status":          "active",
      "alg":             "Ed25519+ML-DSA-65",
      "ed25519_pub_b64": "...",
      "mldsa65_pub_b64": "...",
      "not_before":      "2026-05-14T00:00:00Z"
    },
    {
      "kid":             "gw-sig-1",
      "region":          "global",
      "status":          "alias",
      "alias_of":        "gw-sig.global.edge-signer.1",
      "alg":             "Ed25519+ML-DSA-65",
      "ed25519_pub_b64": "...",
      "mldsa65_pub_b64": "..."
    }
    // … one entry per active kid, plus rotating-in/-out entries during overlap
  ]
}
```

- `status` enum: `active | rotating-in | rotating-out | alias | retired`.
  `retired` MUST NOT appear; `alias` only appears during the §7 transition.
- Pagination concern: at ~20 entries (10 regions × dual-kid overlap + alias)
  the response is still a few KB. **values TBD W2** — pagination cutover
  threshold; deferred until 10+ regions are live.

---

## §7. RFC-0016 Compatibility — Bounded-Breaking

- **Window:** existing `gw-sig-1` JWS continues to verify via the §3.1 alias
  for **90 days** post-RFC-0018 ratification.
- **Day 0 → 90:** verifiers see both `gw-sig-1` (alias) and
  `gw-sig.<region>.edge-signer.<n>` entries in JWKS; either binds correctly
  under RFC-0016 §6.5.1.
- **Day 90:** alias retires. `gw-sig-1` is removed from JWKS. Any token still
  carrying it MUST be re-issued.
- The CEO 48h SLA on RFC-0016 §6.5.1 review (S4-D4) is the authoritative
  compatibility-review path for this transition; this RFC defers the
  sub/kid-binding wording to that review.

---

## §8. Verifier Behaviour (NORMATIVE)

- Verifiers MUST resolve `kid` via JWKS lookup. Hardcoding any `kid` value —
  including `gw-sig-1` — is **forbidden**.
- Sprint 4 W2 task (owner: `🦀 edge-kubelet-engineer`): audit
  `edge/crates/transport/` (specifically the JWKS resolver path —
  **file path TBD W2**, candidate `edge/crates/transport/src/jwks.rs` per
  brief; confirm during audit) for hardcoded `kid` literals; raise PR to
  remove. Acceptance: `grep -nE '"gw-sig[-.]'` over `edge/crates/**` returns
  zero matches outside test fixtures.
- On JWKS cache miss for a presented `kid`, verifier MUST refetch JWKS once
  before failing. Failure code: `E_KID_UNKNOWN`.

---

## §9. Per-Region Rotation

- Each region's SignerDO rotates independently via `SignerDO.rotate()`
  (RFC-0020 v2 §4.3).
- The generation counter (component 3 of the kid regex) increments on each
  rotation: `gw-sig.iad.edge-signer.1` → `gw-sig.iad.edge-signer.2`.
- "Global rotation" is not a primitive — it is `N × regional rotation`,
  optionally orchestrated by a CEO runbook.
- Overlap windows are per-region; default 7d (RFC-0020 v2 §6). Two regions
  MAY be mid-rotation simultaneously; JWKS will show four entries for that
  pair.

---

## §10. Open Questions

1. **Cross-region attestation chain.** Does PoP `fra`'s `AttestationDoc`
   chain to a different root than `iad`'s? CF does not currently document a
   per-colo workload-attestation root. (**values TBD W2**; same root cause
   as RFC-0020 v2 §12 Q3.)
2. **Pagination threshold for JWKS.** §6 — at what entry count do we
   paginate, and what shape? **values TBD W2**; deferred until 10+ regions
   are live.
3. **RFC-0016 §6.5.1 amendment text.** Does the kid-set-aware refresh rule
   need a normative diff in RFC-0016, or is the CEO 48h SLA review (S4-D4)
   sufficient as a no-change memo? Owner: `devex-protocol-sec`. (Mirrors
   RFC-0020 v2 §12 Q4.)

> **Locked-and-removed (CEO 2026-05-07):**
> - **Region code allowlist** — §3 stays open-string `[a-z0-9]+`; CF adds PoPs
>   continuously, closed enum is operationally untenable. CONFIRMED.
> - **Federation across CF accounts** — OUT OF SCOPE; single-account directive
>   confirmed. Reopen if/when multi-account custody is reconsidered.

---

## §11. Handoff

- **Next persona:** `☁️ cloudflare-native-edge` (JWKS aggregation across
  per-region SignerDOs) and `🦀 edge-kubelet-engineer` (§8 verifier audit).
- **Expected artifacts:** JWKS aggregator implementation; verifier-audit PR
  removing hardcoded `kid`; RFC-0016 §6.5.1 amendment (or formal "no change
  needed" memo from `devex-protocol-sec`).

---

## Changelog

### 2026-05-07 v1.0.0: RATIFIED. CEO auto-approve directive 2026-05-07; merged via PR #48 (0018/0020).
