galactic · schema notes

The Schema Underneath the CRDs

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.

01 Reading Kubernetes as relational

The mapping is closer than it looks:

Relational concept
Kubernetes/Galactic equivalent
Table
CREATE TABLE bgp_peers
kind: BGPPeer
Row
one tuple
one BGPPeer object
Primary key
id (per table)
(namespace, name) — unique per Kind
Foreign key
router_id → bgp_routers.id
spec.routerRef.name → BGPRouter
Dynamic join
WHERE labels @> selector
spec.routerSelector (matchLabels)
Index
CREATE INDEX ON bgp_peers(auth_secret)
field indexer, e.g. .spec.authSecretRef.name
Cascade / trigger
ON DELETE cascade / AFTER trigger
owner reconcile loop + a GC controller sweeping orphans every 5m

One 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.

02 Entity-relationship diagram

CRD defined & owned in this repo external identifier / owned by the companion VPC operator explicit *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."

03 Table reference

EntityOriginPrimary keyWritten byRead by
Nodecore k8snamekubeletBGPRouter, NetworkGateway, NAT66Shard, ServiceVIPBinding controllers
Secretcore k8snamespace/nameoperatorBGPPeer via authSecretRef
VPCexternal id48-bit id (base62)companion operator (go.datum.net/cloud)CNI config, NetworkRule.vpcRef
VPCAttachmentexternal id16-bit id (base62)companion operatorCNI config, NetworkRule.vpcAttachmentRef
BGPRoutergalactic-routerarbitrary nameoperator, once per router-role nodegalactic-router (embedded GoBGP)
BGPPeergalactic-routerarbitrary nameoperatorgalactic-router
BGPPolicygalactic-routerarbitrary nameoperatorgalactic-router
BGPVRFInstancegalactic-routercrdnames.BGPVRFInstanceName(vpc, node)galactic-bgp (CNI chain)galactic-router, usidresolver
BGPAdvertisementgalactic-routerCNI: vpc+attachment+node; gateway: rule-node-v4/v6; NAT66: shard-derivedgalactic-bgp, galactic-gateway, NAT66Shard reconcilergalactic-router (GoBGP EVPN)
NetworkGatewaygalactic-gatewaytargetRef.name (node)operator, once per gateway nodegalactic-gateway
NetworkRulegalactic-gatewaytenant-chosen nametenant (admission webhook not yet deployed)every NetworkGateway in the namespace
NAT66Shardgalactic-routerone per nodecontroller-assignedgalactic-router (egress NAT66)
ServiceVIPBindinggalactic-routercrdnames.ServiceVIPBindingName(node, vip, port, proto)controller-createdgalactic-router (veth/tap egress VIP translation)

04 Per-binary data footprint

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 cnivethtapipambgproute routergatewaynat66vrf
Node RR*R*–R*– R–––
Secret –––––– W–––
NetworkAttachmentDef​inition multus –RWRW–W– ––––
EndpointSlice core ––––C– –––R†
BGPRouter ––––R– RWRR–
BGPPeer –––––– RW–––
BGPPolicy –––––– RW–––
BGPVRFInstance ––––C– RWR––
BGPAdvertisement ––––C– RWCC–
NetworkGateway –––––– –RW––
NetworkRule –––––– –RW––
NAT66Shard –––––– ––RW–
ServiceVIPBinding –––––– RW–––
W owns / applies (full reconcile loop) C creates, then hands off to another binary R reads only – no dependency

* 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.

05 Schemas of the four reconcilers

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:

galactic-router cmd/galactic-router

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"

galactic-gateway cmd/galactic-gateway

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)"

galactic-nat66 cmd/galactic-nat66

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"

galactic-vrf cmd/galactic-vrf

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.