Connecting to the Backend
Connect to a Union backend
By the end of this tutorial, you will initialize flyte-sdk with either a backend endpoint or a base64-encoded API key, understand which authentication settings are forwarded to the connection, and use the resulting client services for remote operations. The supported high-level entry point is flyte.init(...); it creates and stores a ClientSet for later calls.
Prerequisites
You need:
- flyte-sdk installed in the environment where the initialization code runs.
- Either a backend endpoint or an API key.
_initialize_clientraisesInitializationErrorwhen neither is supplied. - Network access to the backend and, for a secure connection, certificate configuration that allows the backend certificate to be verified. Secure transport is the default.
- For endpoint authentication, a usable authentication configuration. The default
auth_typeis"Pkce", which can invoke an interactive browser flow unless you configure headless or another supported authenticator.
Step 1: Initialize with an endpoint
Pass the backend address to flyte.init:
import flyte
flyte.init(
endpoint="dns:///flyte-admin.example.com:443",
auth_type="Pkce",
)
flyte.init sanitizes the endpoint and forwards it to _initialize_client. Because endpoint is present, _initialize_client awaits ClientSet.for_endpoint. The auth_type setting is also forwarded to the channel and authentication layers. The call is synchronous at the public API because init is decorated with syncify, although the underlying initialization function and ClientSet factory are asynchronous.
If your deployment uses an internal CA, provide its certificate file. If the gRPC connection itself must be plaintext, set insecure=True explicitly:
import flyte
flyte.init(
endpoint="flyte-admin.example.com:443",
ca_cert_file_path="/etc/flyte/ca.pem",
)
After this call, remote methods can obtain the stored client through get_client(). For example, Project.get checks the client and invokes the Admin-backed project_domain_service:
from flyte.remote import Project
project = Project.get("my-project")
The Project.get implementation is asynchronous internally but is exposed with the SDK's synchronous wrapper. The backend must be reachable and the selected authentication flow must complete before the request succeeds.
Step 2: Initialize with an API key
Use the API key instead of an endpoint when the key contains the backend connection information:
import flyte
flyte.init(api_key=api_key)
Here, api_key is the string supplied by your Union deployment; the example deliberately does not embed a credential. flyte.init selects ClientSet.for_api_key when no endpoint is supplied. If both endpoint and api_key are passed, the endpoint branch wins:
import flyte
flyte.init(
endpoint="flyte-admin.example.com:443",
api_key=api_key,
)
The API-key format is decoded by decode_api_key in remote/_client/auth/_auth_utils.py. Base64 decoding must produce exactly four colon-separated fields in this order:
endpoint:client_id:client_secret:organization
An empty organization is returned as the string "None". Since decoding uses .split(":") and unpacks four values, malformed input—or a field containing an additional colon—can raise a decoding or unpacking error.
ClientSet.for_api_key decodes the key once to record its endpoint, then passes the key to create_channel, which decodes it again for authentication. The API-key path sets auth_type to "ClientSecret", regardless of an auth_type value supplied alongside the key. Do not configure an API-key connection expecting PKCE, external-command, or device-flow authentication.
Step 3: Pass connection and authentication settings
The initialization path forwards the following options to ClientSet.for_endpoint or ClientSet.for_api_key:
| Setting | Effect in flyte-sdk |
|---|---|
endpoint | Selects endpoint-based connection and identifies the backend address. |
api_key | Selects API-key connection; the decoded key supplies the endpoint and client credentials. |
insecure | Creates an insecure gRPC channel when True; it defaults to False. |
insecure_skip_verify | When secure credentials are otherwise not supplied, obtains the server certificate and uses it for the channel. It is distinct from insecure. |
ca_cert_file_path | Reads a CA certificate and passes it to grpc.ssl_channel_credentials; the same setting is used for HTTP verification. |
auth_type | Selects Pkce, ClientSecret, ExternalCommand, or DeviceFlow for endpoint authentication. |
client_id | Supplies the OAuth public client identifier. |
client_credentials_secret | Supplies the client-credentials secret used by service authentication. |
command | Supplies the command used by ExternalCommand authentication to return a token. |
proxy_command | Supplies a command for proxy authorization; proxy interceptors are added when configured. |
http_proxy_url | Configures the HTTP proxy used for OAuth/token requests. |
auth_client_config | Supplies a local ClientConfig override for authentication configuration. |
headless | Passes headless behavior to the authentication stack. |
rpc_retries | Is forwarded as initialization configuration; retry handling is not implemented directly by ClientSet. |
For example, endpoint initialization with an external token command and a proxy command uses the same keyword forwarding path as the production _initialize_client implementation:
import flyte
flyte.init(
endpoint="flyte-admin.example.com:443",
auth_type="ExternalCommand",
command=["flyte-auth", "token"],
proxy_command=["flyte-auth", "proxy-token"],
rpc_retries=3,
)
The authenticator factory accepts exactly the four auth_type values listed above; an unrecognized value raises ValueError. A client secret configured through platform configuration can also cause the platform configuration to select ClientSecret automatically.
Step 4: Inspect the low-level client and its services
The high-level initializer stores the client, but you can use the asynchronous factory directly when your application already runs in an async context:
from flyte.remote._client.controlplane import ClientSet
async def connect(endpoint: str) -> ClientSet:
client = await ClientSet.for_endpoint(endpoint)
print(client.endpoint)
return client
ClientSet.for_endpoint creates an authenticated grpc.aio channel through create_channel and constructs the service bundle around that channel. The bundle exposes:
metadata_serviceandproject_domain_service, both backed by the Admin service stub.task_servicefor task operations.run_servicefor run and action operations.dataproxy_servicefor data transfer operations.logs_servicefor run logs.secrets_servicefor secrets.
Remote operations select the property that matches the backend operation. Task deployment invokes task_service.DeployTask:
await get_client().task_service.DeployTask(
task_service_pb2.DeployTaskRequest(task_id=task_id, spec=spec)
)
Run creation invokes run_service.CreateRun:
resp = await get_client().run_service.CreateRun(
run_service_pb2.CreateRunRequest(
run_id=run_id,
project_id=project_id,
task_spec=task_spec,
inputs=inputs.proto_inputs,
run_spec=run_definition_pb2.RunSpec(
overwrite_cache=self._overwrite_cache,
interruptible=wrappers_pb2.BoolValue(value=self._interruptible),
annotations=annotations,
labels=labels,
envs=env_kv,
),
),
)
These are production call patterns from _deploy.py and _run.py; the request values are prepared by the surrounding deployment and run code.
What authentication does during channel creation
remote._client.auth re-exports create_channel, AuthType, ClientConfig, and authentication exceptions. ClientSet supplies either the endpoint or API key to create_channel, rather than adding credentials to each service call itself.
For an endpoint connection, create_channel first creates an unauthenticated channel. It uses that channel to obtain server OAuth and public-client metadata. It then builds the final secure or insecure channel and attaches default metadata interceptors for unary-unary, unary-stream, stream-unary, and stream-stream calls. Proxy-authentication interceptors are added when proxy_command is configured, followed by the OAuth/authentication interceptors. Consequently, the first authenticated operation can involve metadata discovery and the configured OAuth flow.
Secure channels use default SSL credentials unless you provide custom credentials through the channel options, a CA file, or insecure_skip_verify. insecure=True instead creates grpc.aio.insecure_channel. These settings do not mean the same thing: insecure removes TLS from the gRPC channel, while insecure_skip_verify remains on the secure setup and bootstraps the server certificate when needed.
For direct use of the authentication surface, the package exports the factory and configuration types:
from flyte.remote._client.auth import AuthType, ClientConfig, create_channel
create_channel requires an endpoint or an API key. API-key decoding must also produce an endpoint before channel construction can continue. In normal application code, prefer flyte.init or the matching ClientSet factory so the endpoint, channel, and service stubs are kept together.
Step 5: Close a directly-created client
A directly-created ClientSet owns the gRPC channel it stores internally. Close it asynchronously when your application is finished:
from flyte.remote._client.controlplane import ClientSet
async def use_client(endpoint: str) -> None:
client = await ClientSet.for_endpoint(endpoint)
try:
print(client.endpoint)
finally:
await client.close()
ClientSet.close(grace=None) awaits the underlying channel's close method. It closes _channel; it does not explicitly close every HTTP session that may have been created for OAuth or proxy operations, so do not treat close() as a general-purpose HTTP resource manager.
Troubleshooting and next steps
ClientNotInitializedError: Callflyte.initwith a valid endpoint or API key before using remote entities.get_client()raises this error when no storedClientSetexists.- The factory was not awaited:
ClientSet.for_endpointandClientSet.for_api_keyare asynchronous. Useawaitin async code; the publicflyte.initwrapper handles this for its synchronous-facing API. - Unexpected browser interaction: Endpoint authentication defaults to
Pkce. Configureheadlessor select a supported non-PKCE authenticator where appropriate. - API-key authentication mismatch: API keys force
ClientSecret; supplying anotherauth_typedoes not change that behavior. - TLS confusion: Use
insecure=Trueonly for an insecure gRPC channel.insecure_skip_verify=Truefollows a separate secure certificate-bootstrap path. - Unsupported convenience factories:
ClientSet.for_serverless()andClientSet.from_env()currently raiseNotImplementedError; they are not supported connection paths in this version.
Once initialization succeeds, continue with the service that matches your operation: task_service for deployment, run_service for runs, dataproxy_service for uploads and downloads, project_domain_service for project APIs, logs_service for logs, and secrets_service for secret operations.