Wiring microservices together over the hub overlay
The scenario
Several services are deployed independently — different specs, different rollout schedules, maybe different nodes, clusters, or even a developer laptop behind NAT. Each one needs to reach the others by a stable name without hard-coding IPs or standing up a service mesh. The Cornus server doubles as a star hub: each workload joins as a spoke, registers the services it hosts, and reaches other spokes' services by name — the hub relays the bytes.
What you'll use
- The workload-to-workload hub overlay and its relay model — see the workload hub.
- The
hub:block in the deploy spec for in-cluster workloads — see Deploy spec. cornus hubto join the overlay from anywhere, including a laptop — seecornus hub.
Walkthrough
Deploy the database and export it on the hub. The
hub:block joins the workload to the overlay.exportnames the services this workload hosts. If the hub cannot dial the pod directly, mark the exportdeliver: trueso the hub relays to the pod, which dials the port on localhost.yaml# db.yaml name: db image: cornus.example:5000/postgres:16 hub: identity: db export: - { name: db, port: 5432, deliver: true }shcornus deploy -f db.yaml --server http://cornus.example:5000 --detachDeploy the API and import the database by name.
importlists the services this workload reaches. For each import the backend allocates a synthetic loopback IP, wires a DNS record, and binds a caretaker listener — so a plain connection todb:5432from inside the API container funnels into the hub with no application changes.yaml# api.yaml name: api image: cornus.example:5000/api:v1 env: DATABASE_URL: postgres://db:5432/shop hub: identity: api export: - { name: api, port: 8080 } import: - { name: db, ports: [5432] }shcornus deploy -f api.yaml --server http://cornus.example:5000 --detachThe API reaches the database as
db:5432and offers itself asapifor anything else that imports it — neither side hard-codes an address.Reach an overlay service from a laptop. A developer behind NAT joins the same overlay as a spoke with
cornus hub, binding a local loopback port that forwards into the overlay'sdbservice. The server is resolved from--serveror the selected connection profile.shcornus hub --identity laptop --reach db=127.0.0.1:5432 # now: psql 'host=127.0.0.1 port=5432 dbname=shop ...'The same command can offer a locally running service to the overlay with
--register name=host:port, so a service under development on the laptop is reachable by name from the cluster while you iterate.
How it works
Each participant is a spoke; the server is the hub. A spoke registers each service it hosts in one of two modes:
- dial-direct — the service is registered with an address the hub can reach, and the hub dials it itself.
- delivery (relay) — the service is registered with no reachable address (
deliver: trueon an export, or everycornus hub --register). To reach it the hub opens an ingress stream back to the hosting spoke, which dials its own local target and splices. This is what makes a NAT'd laptop or a cross-cluster pod reachable — the hub never needs a route to it.
To reach a peer, the source spoke opens a data stream naming the service; the hub looks it up and either dials it or delivers via the owning spoke, then copies the bytes. Both TCP and UDP work (a /udp-style protocol: udp selects UDP). For in-cluster workloads the whole thing is declared in the hub: block; from a laptop or any host outside the cluster, cornus hub --register / --reach joins the same overlay from the CLI. The full field set — export, import, importDynamic, identity — is in the deploy spec reference.
Access is governed by two optional policy matrices, each enforced only when configured: a reach matrix (caller identity to allowed callee services, CORNUS_HUB_POLICY) and a register matrix (identity to hostable service names, CORNUS_HUB_REGISTER_POLICY). A spoke declares its identity, but under mTLS the identity is taken from the verified client certificate's CommonName, so policy keys on a credential the spoke cannot forge. See the workload hub for the identity and policy model.
Variations
- Dynamic discovery. Instead of a static
importlist, setimportDynamicwith a shared port set; the caretaker subscribes to hub catalog pushes and binds a listener at every cataloged service as services appear and vanish. - UDP services. Add
protocol: udpon an export / import for byte-copied UDP flows. - Cross-backend. Because the hub relays bytes, spokes on different backends or clusters — as long as they connect to the same hub — reach each other the same way.
See also: The workload hub · Networking and conduits · Deploy spec · cornus hub