Expose Service Endpoints#

Endpoint declarations tell users and other plugins how to talk to a running service. They also give the broker a concrete readiness contract: the process is not merely alive, it has become usable.

Declare an Endpoint#

Use EndpointSpec on the service:

import sys

from conda_broker.models import CondaService, EndpointSpec, HealthCheck, ProcessSpec

CondaService(
    name="my-provider.api",
    summary="Local API used by conda-my-provider",
    source="conda-my-provider",
    endpoints=(
        EndpointSpec(
            protocol="http",
            host="127.0.0.1",
            path="/health",
            port_env="PORT",
            url_env="SERVICE_URL",
        ),
    ),
    health_check=HealthCheck(type="http", endpoint="default"),
    process=ProcessSpec(
        argv=(sys.executable, "-m", "conda_my_provider.server"),
        env={"PYTHONUNBUFFERED": "1"},
    ),
)

When port is omitted, the broker allocates a free local port when the service starts. It injects that port into the child process before launch.

Read Endpoint Environment Variables#

Every endpoint receives automatic environment variables:

  • CONDA_BROKER_SERVICE_NAME

  • CONDA_BROKER_ENDPOINT_DEFAULT_PROTOCOL

  • CONDA_BROKER_ENDPOINT_DEFAULT_HOST

  • CONDA_BROKER_ENDPOINT_DEFAULT_PORT

  • CONDA_BROKER_ENDPOINT_DEFAULT_URL

For non-default endpoint names, the endpoint name is uppercased and non-alphanumeric characters become underscores. Names that collide after normalization are rejected, as are duplicate custom variable names or custom names that overwrite broker variables.

If port_env or url_env is configured, the broker also sets those custom variables. The example above sets PORT and SERVICE_URL.

IPv6 endpoint URLs use bracketed hosts, for example http://[::1]:8765/health.

Bind Health Checks to Endpoints#

Endpoint-bound health checks avoid duplicating host and port values:

HealthCheck(type="http", endpoint="default", interval_s=2, timeout_s=1)
HealthCheck(type="tcp", endpoint="control", interval_s=2, timeout_s=1)

Failed health checks remain unknown during start_period_s, which defaults to five seconds. That gives a process time to bind a dynamically assigned port before failed probes become restart triggers.

Use start_period_s=0 for services where an immediate failed health check should restart the process.

Wait for Readiness#

cb start starts processes; it is not a readiness gate. Use cb wait when a script needs the service to be usable:

cb start my-provider.api
cb wait my-provider.api --timeout 15

To combine explicit startup and readiness waiting:

cb wait my-provider.api --start --timeout 15

cb wait exits with status code zero when the service reports ready=true. It exits non-zero if the service stops, fails, or never becomes ready before the timeout.

Inspect Endpoints#

Use cb endpoint to see the resolved endpoint:

cb endpoint my-provider.api
cb endpoint my-provider.api default --json

Stopped services with static ports can still show endpoint URLs. Stopped services with broker-allocated ports show unresolved endpoint fields until the service starts.

For one launch, the broker holds socket reservations while allocating all dynamic endpoints, which prevents two endpoints from receiving the same port. It must release those sockets before the child can bind. Another process can still claim a port in that short interval, so services should exit on bind failure and let restart policy retry with a new allocation.

Query from Another Plugin#

Runtime decisions should use query helpers. They do not start the broker.

from conda_broker import Broker

service = Broker.current().service("my-provider.api")

if endpoint := service.endpoint(ready=True):
    use_api(endpoint.url)
else:
    use_inline_fallback()

Use service.wait(start=True) only from user-visible commands where starting the broker and service is an explicit action.