Skip to main content
Version: 1.2.0

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.trustedSharedIssuers on 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:

  1. Select Cluster > Federations > Add Request.
  2. Choose Wait for remote cluster.
  3. Enter the initiator's unique cluster name, connect address, and hostname.
  4. Enter a strong PSK from your approved secret manager.
  5. Choose shared_idp.
  6. Enter the shared issuer and JWKS URLs. If the issuer uses a private CA, supply the complete CA chain.
  7. Create the request.

The receiver should show a WAITING record.

3. Create and pair the initiator​

On the initiating cluster:

  1. Select Cluster > Federations > Add Request.
  2. Choose Initiate connection.
  3. Enter the receiver's unique name, connect address, and hostname.
  4. Use Test Connection.
  5. Enter the same PSK and shared-realm values.
  6. 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:

  1. POST /api/cluster/federations creates the receiver and initiator records.
  2. POST /api/cluster/federations/{id}/pair on 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>#viewer for discovery and retrieval;
  • the exact model relation required for chat; and
  • cluster_jobs:__all__#executor for 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.