Federation Setup
This guide pairs two Kamiwaza 1.2.0 clusters using the receiver-controlled
shared_idp identity mode. The receiver validates the caller's shared-realm
token, then applies its own onboarding, ReBAC, and policy gates.
receiver_realm is reserved for the future receiver-owned guest-identity
workflow and is rejected by Kamiwaza 1.2.0. peer_kc remains available for
compatibility, but new peer_kc pairs are refused unless the operator
explicitly enables ALLOW_UNTRUSTED_FEDERATION.
Prerequisites
- Two Kamiwaza 1.2.0 clusters with mutually reachable HTTPS endpoints.
- Istio selected through the supported deployment values for both clusters.
- Distinct cluster display names and distinct, mutually reachable hostnames.
- Gateway certificates whose SANs cover the hostnames used for Host and TLS SNI routing.
- One independently owned shared OIDC realm and its issuer and JWKS URLs.
- The shared issuer enrolled in
core.scheduler.trustedSharedIssuerson both clusters. - A native-realm cluster administrator on each cluster.
For an IP-based connection, provide both the connect address and the hostname:
{"ip": "10.0.0.12", "hostname": "fed-b.example.internal", "primary": true}
Kamiwaza connects to ip but sends hostname as the HTTP Host and TLS SNI.
Use the same hostname in the gateway certificate. Configure certificate SANs
with the current chart keys:
global:
ingress:
gateway:
ipAddresses:
- "10.0.0.12"
extraDnsNames:
- "fed-b.example.internal"
1. Verify both routes before pairing
In the console, select Cluster > Federations > Add Request, enter the peer address, and use Test Connection. Resolve DNS, TCP, TLS, or L7 failures before creating either record.
The API equivalent is admin-only:
curl --fail --silent --show-error \
--request POST \
--header "Authorization: Bearer $ADMIN_TOKEN" \
--header 'Content-Type: application/json' \
--data '{"remote_ips":[{"ip":"10.0.0.12","hostname":"fed-b.example.internal","primary":true}]}' \
"$LOCAL_API/cluster/federations/preflight" | jq .
Run the same check in the opposite direction. Pairing needs bidirectional reachability.
2. Create the receiver record
On the receiving cluster:
- Select Cluster > Federations > Add Request.
- Choose Wait for remote cluster.
- Enter the initiator's unique cluster name, connect address, and hostname.
- Enter a strong PSK from your approved secret manager.
- Choose shared_idp.
- Enter the shared issuer and JWKS URLs. If the issuer uses a private CA, supply the complete CA chain.
- Create the request.
The receiver should show a WAITING record.
3. Create and pair the initiator
On the initiating cluster:
- Select Cluster > Federations > Add Request.
- Choose Initiate connection.
- Enter the receiver's unique name, connect address, and hostname.
- Use Test Connection.
- Enter the same PSK and shared-realm values.
- Create the request and approve the confirmation.
Both cards should reach PAIRED and show Receiver-controlled with the same
shared issuer. Use Ping and verify that Last Ping updates.
Pairing automatically exchanges the platform CA certificates. Do not update
cluster_federations.remote_ca_cert with SQL and do not copy a raw PSK from
the database.
Raw API equivalent
The console uses a two-step API:
POST /api/cluster/federationscreates the receiver and initiator records.POST /api/cluster/federations/{id}/pairon the initiator drives the handshake.
Keep the PSK in mode-0600 request files so it does not appear in process
arguments or shell history. The initiator request shape is:
{
"remote_cluster_name": "fed-b",
"remote_ips": [
{
"ip": "10.0.0.12",
"hostname": "fed-b.example.internal",
"primary": true
}
],
"preshared_key": "read-from-a-private-file",
"callback_hostname": "fed-a.example.internal",
"role": "initiator",
"shared_issuer_url": "https://idp.example.internal/realms/federation",
"shared_jwks_url": "https://idp.example.internal/realms/federation/protocol/openid-connect/certs",
"shared_ca_pem": "optional-private-ca-chain"
}
The receiver request uses role: "receiver" and the reciprocal name and
route. Create the receiver first, then the initiator, then pair the returned
initiator ID:
curl --fail --silent --show-error \
--request POST --header "Authorization: Bearer $B_ADMIN_TOKEN" \
--header 'Content-Type: application/json' \
--data-binary @receiver-create-private.json \
"$B_API/cluster/federations" > receiver.json
curl --fail --silent --show-error \
--request POST --header "Authorization: Bearer $A_ADMIN_TOKEN" \
--header 'Content-Type: application/json' \
--data-binary @initiator-create-private.json \
"$A_API/cluster/federations" > initiator.json
FEDERATION_ID="$(jq -er .id initiator.json)"
curl --fail --silent --show-error \
--request POST --header "Authorization: Bearer $A_ADMIN_TOKEN" \
"$A_API/cluster/federations/$FEDERATION_ID/pair" | jq .
Delete the private request files after pairing.
4. Onboard receiver-local access
Pairing establishes trust; it does not grant access to datasets, models, or jobs. On the receiver, open the federation's Access panel and add each shared-realm subject that may use the pair. Assign only the required receiver-local grants, for example:
dataset:<dataset-urn>#viewerfor discovery and retrieval;- the exact model relation required for chat; and
cluster_jobs:__all__#executorfor job submission.
The receiver creates and controls the local brokered identity. Source-cluster administrator roles do not cross the federation boundary.
5. Verify the pair
Use the exact federation UUID when scripting. Name prefixes are rejected when they are ambiguous.
curl --fail --silent --show-error \
--request POST --header "Authorization: Bearer $A_ADMIN_TOKEN" \
"$A_API/cluster/federations/$FEDERATION_ID/ping" | jq -e '.reachable == true'
curl --fail --silent --show-error \
--header "Authorization: Bearer $SHARED_USER_TOKEN" \
"$A_API/mesh/$FEDERATION_ID/api/catalog/datasets/" | jq .
The catalog response must contain only datasets authorized on the receiver. Continue with Federated Retrieval and Job Submission.
Diagnose a failure
Run the stored-route diagnostic before changing records:
curl --fail --silent --show-error \
--request POST --header "Authorization: Bearer $A_ADMIN_TOKEN" \
"$A_API/cluster/federations/$FEDERATION_ID/diagnose" | jq .
Common stable reasons include dns_unresolvable, connection_failed,
connection_timeout, tls_untrusted_ca, tls_hostname_mismatch,
peer_identity_aliases_local, psk_propagation_timeout, and
peer_ca_projection_forbidden.
If CA inspection is required, support both deployed certificate layouts and
never assume root-ca lives in the kamiwaza namespace:
if kubectl get secret core-internal-ca-tls -n kamiwaza >/dev/null 2>&1; then
kubectl get secret core-internal-ca-tls -n kamiwaza \
-o jsonpath='{.data.ca\.crt}' | base64 -d
else
kubectl get secret root-ca -n kamiwaza-ca \
-o jsonpath='{.data.ca\.crt}' | base64 -d
fi
This command is diagnostic only. Normal pairing exchanges and stores the CA.
Disconnect
Use Disconnect in the federation card or call the disconnect endpoint. Disconnect blocks new mesh admission and revokes delegated authority. Running work is reconciled through the job lifecycle; confirm terminal status before deleting evidence or receiver-local grants.