Every controller in this repo is quietly running a database: metadata.name is a primary key, *Ref fields are foreign keys, and a Selector field is a join clause the API server evaluates for you on every reconcile.
The mapping is closer than it looks:
.spec.authSecretRef.nameOne real difference: there's no join engine underneath. Every Ref is resolved by a controller doing a live Get/List against etcd on each reconcile — the diagram below is the schema those lookups silently assume.
*Ref foreign key
label selector or name-derivation join (no stored FK)
erDiagram
NODE ||--o| BGPROUTER : "targetRef (1 per router node)"
NODE ||--o| NETWORKGATEWAY : "targetRef (1 per edge node)"
NODE ||--o| NAT66SHARD : "targetRef (1 per compute node)"
NODE ||--o{ SERVICEVIPBINDING : "targetRef"
SECRET ||--o{ BGPPEER : "authSecretRef"
BGPROUTER ||--o{ BGPPEER : "routerRef"
BGPROUTER |o..o{ BGPPEER : "routerSelector"
BGPROUTER ||--o{ BGPPOLICY : "routerRef"
BGPROUTER |o..o{ BGPPOLICY : "routerSelector"
BGPROUTER ||--o{ BGPVRFINSTANCE : "routerRef"
BGPROUTER |o..o{ BGPVRFINSTANCE : "routerSelector"
BGPROUTER ||--o{ BGPADVERTISEMENT : "routerRef"
VPC ||--o{ VPCATTACHMENT : "vpc"
VPC |o..o{ BGPVRFINSTANCE : "encoded in name + route target"
VPC |o..o{ NETWORKRULE : "vpcRef"
VPCATTACHMENT |o..o{ BGPADVERTISEMENT : "encoded in name (CNI-origin)"
VPCATTACHMENT |o..o{ NETWORKRULE : "vpcAttachmentRef"
BGPVRFINSTANCE |o..o{ BGPADVERTISEMENT : "shared VRFID (CNI-origin)"
NETWORKRULE ||--o{ BGPADVERTISEMENT : "1 per serving node × AF"
NAT66SHARD ||--o{ BGPADVERTISEMENT : "shard prefix"
NETWORKGATEWAY }o..o{ NETWORKRULE : "serves (namespace-broadcast, no FK)"
NODE {
string name PK
string node_role "compute | edge (label)"
}
SECRET {
string name PK
string namespace
}
VPC {
string id PK "48-bit, base62"
}
VPCATTACHMENT {
string id PK "16-bit, base62"
string vpc_id FK
}
BGPROUTER {
string name PK
string targetRef_name FK
int localASN
string routerID
string srv6Locator
string nodeID
int listenPort
}
BGPPEER {
string name PK
string routerRef_name FK "nullable"
string routerSelector "nullable"
int peerASN
string address
string authSecretRef_name FK "nullable"
}
BGPPOLICY {
string name PK
string routerRef_name FK "nullable"
string routerSelector "nullable"
string direction
object terms
}
BGPVRFINSTANCE {
string name PK "vpc+node derived"
string routerRef_name FK "nullable"
int vrfID
string routeDistinguisher
}
BGPADVERTISEMENT {
string name PK "origin-derived"
string routerRef_name FK
int vrfID "nullable"
string addressFamily
string prefixes
int localPreference "nullable"
}
NETWORKGATEWAY {
string name PK
string targetRef_name FK
}
NETWORKRULE {
string name PK
string vpcRef FK
string vpcAttachmentRef FK
string vipAddresses
string protocol
int port
string backends
}
NAT66SHARD {
string name PK
string targetRef_name FK
string shardAddress
string shardSID
}
SERVICEVIPBINDING {
string name PK "node+vip+port+proto derived"
string targetRef_name FK
string vipAddress
string backendAddress
string egressKind "veth | tap"
}
The dashed edges are the interesting ones. routerSelector is a real label-matching join with no stored key — several BGPPeer/BGPPolicy/BGPVRFInstance rows can resolve to one BGPRouter without any of them naming it. NetworkRule↔NetworkGateway has no FK at all in either direction: every gateway node in a namespace reconciles every rule identically (anycast, no primary), so the "join" is just "same namespace."
| Entity | Origin | Primary key | Written by | Read by |
|---|---|---|---|---|
| Node | core k8s | name | kubelet | BGPRouter, NetworkGateway, NAT66Shard, ServiceVIPBinding controllers |
| Secret | core k8s | namespace/name | operator | BGPPeer via authSecretRef |
| VPC | external id | 48-bit id (base62) | companion operator (go.datum.net/cloud) | CNI config, NetworkRule.vpcRef |
| VPCAttachment | external id | 16-bit id (base62) | companion operator | CNI config, NetworkRule.vpcAttachmentRef |
| BGPRouter | galactic-router | arbitrary name | operator, once per router-role node | galactic-router (embedded GoBGP) |
| BGPPeer | galactic-router | arbitrary name | operator | galactic-router |
| BGPPolicy | galactic-router | arbitrary name | operator | galactic-router |
| BGPVRFInstance | galactic-router | crdnames.BGPVRFInstanceName(vpc, node) | galactic-bgp (CNI chain) | galactic-router, usidresolver |
| BGPAdvertisement | galactic-router | CNI: vpc+attachment+node; gateway: rule-node-v4/v6; NAT66: shard-derived | galactic-bgp, galactic-gateway, NAT66Shard reconciler | galactic-router (GoBGP EVPN) |
| NetworkGateway | galactic-gateway | targetRef.name (node) | operator, once per gateway node | galactic-gateway |
| NetworkRule | galactic-gateway | tenant-chosen name | tenant (admission webhook not yet deployed) | every NetworkGateway in the namespace |
| NAT66Shard | galactic-router | one per node | controller-assigned | galactic-router (egress NAT66) |
| ServiceVIPBinding | galactic-router | crdnames.ServiceVIPBindingName(node, vip, port, proto) | controller-created | galactic-router (veth/tap egress VIP translation) |
Ten binaries live under cmd/. Most of them barely touch the API server — galactic-ipam and galactic-route read zero Kubernetes objects, and the whole CNI chain is one linear pipeline rather than ten independent schemas. A CRUD matrix says more per square inch than ten mostly-empty diagrams would:
| Entity | cni | veth | tap | ipam | bgp | route | router | gateway | nat66 | vrf |
|---|---|---|---|---|---|---|---|---|---|---|
| Node | R | R* | R* | – | R* | – | R | – | – | – |
| Secret | – | – | – | – | – | – | W | – | – | – |
| NetworkAttachmentDefinition multus | – | RW | RW | – | W | – | – | – | – | – |
| EndpointSlice core | – | – | – | – | C | – | – | – | – | R† |
| BGPRouter | – | – | – | – | R | – | RW | R | R | – |
| BGPPeer | – | – | – | – | – | – | RW | – | – | – |
| BGPPolicy | – | – | – | – | – | – | RW | – | – | – |
| BGPVRFInstance | – | – | – | – | C | – | RW | R | – | – |
| BGPAdvertisement | – | – | – | – | C | – | RW | C | C | – |
| NetworkGateway | – | – | – | – | – | – | – | RW | – | – |
| NetworkRule | – | – | – | – | – | – | – | RW | – | – |
| NAT66Shard | – | – | – | – | – | – | – | – | RW | – |
| ServiceVIPBinding | – | – | – | – | – | – | RW | – | – | – |
* fallback only — detectNodeNameFromAPI runs when the node name isn't already resolved from an env var or the conflist. † galactic-vrf holds a cluster-scoped watch, not a per-reconcile Get.
Two binaries are pure kernel plumbing with no API footprint at all: galactic-ipam persists allocations in on-disk marker files, and galactic-route installs VRF-table routes from conflist config alone — both by design, so a container's cmdDel never has to race a CRD read. galactic-cni (the installer) never runs a plugin or reconciler at all; its whole job is staging the other five CNI binaries onto the node and doing a one-shot Node identity check. And NetworkGateway/NetworkRule's "no FK" relationship from §02 shows up here too: nothing in this matrix targets a specific gateway node — every galactic-gateway process reconciles every rule in its namespace.
Six of the ten binaries above are pipeline stages or pure datapath processes with little or no relational structure of their own (see §04). The other four each run a real controller-runtime manager with its own bounded slice of the schema — those are worth their own diagram:
The tenant BGP/EVPN control plane — the only binary that ever talks to the embedded GoBGP server.
erDiagram
NODE ||--o| BGPROUTER : "targetRef; resolves IPv6 next-hop"
NODE ||--o{ SERVICEVIPBINDING : "targetRef (local match)"
SECRET ||--o{ BGPPEER : "authSecretRef"
BGPROUTER ||--o{ BGPPEER : "applies to GoBGP"
BGPROUTER ||--o{ BGPPOLICY : "applies to GoBGP"
BGPROUTER ||--o{ BGPVRFINSTANCE : "applies to GoBGP"
BGPROUTER ||--o{ BGPADVERTISEMENT : "applies to GoBGP + GC's orphan sweep"
The edge XDP DSR/anycast ingress gateway — owns NetworkGateway/NetworkRule, reads the BGP family only to resolve backend uSIDs.
erDiagram
NETWORKGATEWAY }o..o{ NETWORKRULE : "serves (namespace-broadcast, no FK)"
NETWORKRULE ||--o{ BGPADVERTISEMENT : "creates: 1 per serving node x AF"
BGPROUTER |o..o{ BGPADVERTISEMENT : "usidresolver reads (backend uSID)"
BGPVRFINSTANCE |o..o{ BGPADVERTISEMENT : "usidresolver reads (tenant-ownership check)"
One shard of the sharded, stateful NAT66 egress tier — its own XDP datapath, its own placement ring, deliberately not a galactic-gateway personality.
erDiagram
BGPROUTER |o..o{ NAT66SHARD : "watched; routes shard to its node's router"
NAT66SHARD ||--o{ BGPADVERTISEMENT : "creates this shard's egress prefix"
The Envoy Gateway ingress sidecar — VRF + SRv6 seg6 encap route lifecycle driven purely by a cluster-scoped EndpointSlice watch. Owns no CRD of its own.
erDiagram
VPCATTACHMENT |o..o{ ENDPOINTSLICE : "1 published per pod attachment, by galactic-bgp"
galactic-vrf is the outlier. It doesn't create, update, or even reference any CRD in this repo — it's a pure sink that turns someone else's EndpointSlice into local kernel state (a Linux VRF device and an SRv6 encap route), which is exactly why it earns the smallest diagram here rather than the emptiest.