# D-101 IPv6 family-matrix charm research (2026-07-09/10)

Basis for `overlays/dc-dc-ipv6-family-matrix.yaml` (tooling gap register item
#13). Researched directly against real charm source/config via WebFetch/
WebSearch -- not inferred from training-data memory of charm behavior, which
this repo's own discipline treats as a real risk class (the OpenTofu module
work earlier this session found a real syntax bug from exactly that mistake).

## Method

Fetched actual `config.yaml` files and, where a config option's semantics
were ambiguous from prose alone, actual charm source/commits, from the
`openstack/charm-*` and `openstack-charmers/charm-layer-ovn` repositories
(GitHub mirrors of the OpenDev-hosted canonical source) and
`bugs.launchpad.net`. Every claim below is labeled CONFIRMED (read the
actual file/commit) or INFERRED-BY-PATTERN (not independently checked,
reasoned from a shared framework) -- do not treat the two as equal
confidence.

## Findings

### 1. `prefer-ipv6` is a real, shared charms.openstack-family config option

**CONFIRMED** directly in `charm-keystone/config.yaml`: a boolean, default
`False`, enabling IPv6 support.

**CONFIRMED** via `charm-nova-cloud-controller`'s actual "Dual Stack VIPs"
commit (`1fa5f7a673006bc6faaf9d377e791448a5ed661b`): setting `prefer-ipv6:
true` causes the charm's HAProxy config template to add `bind
:::{{ports[0]}}` ALONGSIDE the existing `bind *:{{ports[0]}}` -- i.e. this
is a genuinely ADDITIVE dual-stack switch (HAProxy listens on both address
families simultaneously), not an either/or toggle. The commit message
states this explicitly: "HAProxy always listens on both IPv4 and IPv6
allowing connectivity on either protocol."

**INFERRED-BY-PATTERN, NOT independently confirmed per-charm:** glance,
neutron-api, placement, cinder, barbican, magnum, openstack-dashboard,
ceph-radosgw all build on the same charms.openstack base classes as
keystone/nova-cloud-controller, so the same `prefer-ipv6` option and
additive-dual-stack HAProxy behavior is EXPECTED -- but each charm's real
`config.yaml` was not individually fetched this session. Confirm via `juju
config <app>` once any of these charms is actually deployed, before
trusting the overlay is complete for all eleven.

### 2. The existing `vip:` option takes additional v6 addresses, appended

**CONFIRMED**: `charm-keystone/config.yaml`'s `vip` option description:
"Virtual IP(s) to use to front API services in HA configuration. If
multiple networks are being used, a VIP should be provided for each
network, separated by spaces." This repo's `bundle.yaml` already uses this
exact mechanism today (D-020's triple-VIP pattern -- e.g. `keystone`'s `vip:
"10.12.4.50 10.12.8.50 10.12.12.50"`, one address per Juju-space binding).
Adding v6/GUA addresses to the SAME space-separated list, once
`prefer-ipv6: true` makes HAProxy listen on both families, is the
mechanism -- confirmed consistent with finding #1's dual-stack commit,
though the EXACT mapping of "which list position pairs with which network"
inside the charm's own context-building code was not traced line-by-line
this session. Treat the overlay's VIP ordering (existing v4 entries first,
then the new v6/GUA entries appended) as the reasonable, but not
byte-for-byte charm-source-confirmed, convention.

### 3. `ceph-mon` has a real, separate `prefer-ipv6` + network-CIDR mechanism

**CONFIRMED** directly in `charm-ceph-mon/config.yaml`:
- `prefer-ipv6` (bool, default False) -- per the charm's OWN description,
  "If set to False (default) IPv4 is expected," reading as a straight
  EITHER/OR switch for THIS charm specifically (unlike the additive
  HAProxy behavior in finding #1) -- which is actually the correct shape
  for D-101's ULA-ONLY storage/replication planes (no v4 needed at all,
  so a clean switch to v6-only is exactly right, not a limitation).
- `ceph-public-network` / `ceph-cluster-network` (string, space-delimited
  CIDR list) -- these map directly to D-101's own cited mechanism name,
  `ms_bind_ipv6` (the underlying Ceph daemon config the charm sets when
  `prefer-ipv6` is enabled).
- A real caveat quoted from the charm's own docs: "these charms do not
  currently support IPv6 privacy extension. In order for this charm to
  function correctly, the privacy extension must be disabled and a
  non-temporary address must be configured/available on your network
  interface." This is a real host/OS-level prerequisite for every ceph-mon
  unit's storage/replication interface -- not something an overlay file
  can set, needs to be part of Phase-0/node-prep discipline.

### 4. OVN has NO explicit IPv6/encapsulation charm-config option

**CONFIRMED**: fetched the FULL `charm-layer-ovn/config.yaml` (the shared
base for `ovn-central`/`ovn-chassis`) -- 25 options total, none related to
IPv6, address binding, or encapsulation IP. geneve-over-v6 for the
data-tenant plane is therefore not a charm-config concern at all; it
follows whichever IP family the unit's bound interface on that plane
actually has. Since data-tenant is ULA-only (no v4 present) per D-101, this
needs no overlay entry -- the absence of an entry IS the correct
configuration, not an oversight.

### 5. Vault's cert-issuance code has no IPv4/IPv6 distinction

**CONFIRMED**: fetched `charm-vault/src/lib/charm/vault_pki.py`'s actual
`sort_sans()` function -- it splits SANs into "IP SANs" vs. "name SANs"
using `charmhelpers.contrib.network.ip.is_ip()`, with NO further IPv4/IPv6
differentiation anywhere in the function or its caller
(`generate_certificate()`, which just joins whatever IP SANs it's given
into a comma-separated string). D-109's "Vault issues v4+IPv6-SAN certs"
requirement is technically supported by the charm's own code path, not
merely assumed.

### 6. A real, open upstream risk for Octavia's lb-mgmt-net over IPv6

**CONFIRMED** (real, still-referenced Launchpad bug reports, not
speculation): #1911788 ("IPv6 mgmt network not working, octavia can't talk
to amphora," OpenStack Octavia Charm) and #1913409 ("octavia_amp_network
does not support IPv6," kolla-ansible, describing the same underlying
Octavia limitation from a different deployment tool). #1911788's root cause
traces to an OVN/LXD/MAAS hostname-resolution mismatch (Neutron's
`binding_host_id` carrying a short hostname while OVN's controller expects
the FQDN) -- it was marked a duplicate of #1896630, "managing /etc/hosts
for containers." That is the SAME CLASS of problem this repo's own D-008
bootstrap order (static /etc/hosts bootstrap -> os-public-hostname -> vault
certs -> Designate, reactivated by D-106 for VR1) already exists to
harden against -- so this is NOT necessarily a fatal blocker here, but it
IS a real, open, documented upstream risk for exactly the kind of network
(Octavia's amphora management network) D-101 wants to put on ULA-only IPv6.
`overlays/dc-dc-ipv6-family-matrix.yaml` deliberately does NOT attempt an
Octavia lb-mgmt-net IPv6 change for this reason -- that needs its own
explicit decision (accept the risk and test for real once DC1 exists, or
keep Octavia's lb-mgmt-net dual-stack/v4 as a deliberate, logged D-101
exception), presented here rather than silently forced through.

## What this closes vs. what remains open

CLOSES (mechanism): the "no overlay file exists, charm option names
unconfirmed" half of gap #13 -- a real, sourced overlay now exists at
`overlays/dc-dc-ipv6-family-matrix.yaml`, covering 9 of Octavia's 11 sibling
dual-stack API charms directly plus ceph-mon's ULA-only switch, with OVN
correctly requiring no entry.

STAYS OPEN: (a) the 8 INFERRED-BY-PATTERN charms' `prefer-ipv6` option
needs live `juju config` confirmation once any of them is actually
deployed; (b) the exact VIP-list-position-to-network mapping inside each
charm's context-building code was not traced to source; (c) Octavia's
lb-mgmt-net IPv6 question is a real, open decision, not resolved by this
research; (d) none of this has been applied or tested against a live
model -- UNVALIDATED, same posture as every other OpenTofu/overlay artifact
built this session.
