Connecting to Workflow Services
The fuzzball workflow connect command provides a convenient way to interact with a service
running in a workflow. Depending on the service definition, it can either open the service’s
public endpoint in a web browser or run a client program locally that connects to the service.
The Fuzzball Web UI provides a Connect button that offers one-click access to running workflows that expose service endpoints. The button appears in two locations:
- Workflow detail page — in the toolbar at the top of the page
- Workflows list table — inline in each workflow row
The two locations behave differently while a workflow is starting up.
On the workflow detail page, the button appears once all of the following hold:
- The workflow has at least one named service endpoint defined
- The workflow is in a running state
- At least one of those endpoints belongs to a service that is currently running
The page polls the workflow’s status every ten seconds while any stage is pending or started, so the button becomes active shortly after the service starts. You do not need to reload the page.
In the workflows list table, the button appears only once it is ready to use.
The button links to the constructed endpoint URL but does not verify that the endpoint is actually reachable. “Running” here means the orchestrator has started the service container, not that the application inside it has finished booting, so the target service may still be starting up when the button becomes clickable.
On the workflow detail page, a workflow exposing a single endpoint gets a Connect button that opens
it in a new browser tab. A workflow exposing several turns the button into a menu listing each one as
service / endpoint; choosing one opens that endpoint in a new tab. Entries are ordinary links, so
middle-click, modifier-click and “copy link address” work as anywhere else.
Only named endpoints belonging to a currently-running service are offered, ordered alphabetically by
service name and, within a service, in the order its endpoints are declared. An endpoint whose service
has not started yet is left out: it would answer 503.
The workflows list table has room for one link per row, so it offers the first endpoint in that same
order. Use the workflow detail page, or the CLI fuzzball workflow connect command with the --name
and --endpoint flags, to reach a specific one.
The Connect button is ideal for quickly accessing web-based services such as Jupyter, RStudio, or other browser-accessible applications. For workflows that require client-script mode (see below), use the fuzzball workflow connect CLI command instead.
$ fuzzball workflow connect WORKFLOW_ID [flags] [-- COMMAND...]Arguments:
| Argument | Required | Description |
|---|---|---|
WORKFLOW_ID | Yes | The ID of a running workflow |
COMMAND... | No | Arguments forwarded to the client program when the service is configured for client-script mode |
Flags:
| Flag | Description |
|---|---|
--name | Name of the service to connect to. If omitted and the workflow defines a single service, that service is selected automatically; otherwise the CLI prompts interactively. |
--endpoint | Name of the service endpoint to use. If omitted and the service defines a single endpoint, that endpoint is selected automatically; otherwise the CLI prompts interactively. |
The command waits for the target service to become ready (its readiness probe must pass) before opening the browser or launching the client program. While waiting, container logs for the service are streamed to the terminal.
fuzzball workflow connect supports two modes, selected automatically based on the workflow
definition:
If the service has no fuzzball.io/connect/<service-name> annotation declared on the workflow,
the command resolves the URL of the selected endpoint and opens it in the default web browser.
This is the simplest mode and is well suited to services that expose a web UI such as Jupyter
or RStudio.
If the workflow declares an annotation of the form fuzzball.io/connect/<service-name> whose
value is a command to run inside the service container, that command is executed and its
standard output is treated as a client script. The script is saved to a temporary file, made
executable, and run locally. Any extra arguments supplied after -- on the fuzzball workflow connect command line are forwarded to the script.
This mode is useful when connecting to a service requires more than just a URL — for example, to fetch a generated access token or TLS configuration and then launch a local client with the right parameters.
Connect to the only service in a workflow (auto-select service and endpoint):
$ fuzzball workflow connect <workflow-id>Connect to a specific service by name:
$ fuzzball workflow connect --name jupyter <workflow-id>Select a specific endpoint within a service:
$ fuzzball workflow connect --name api-server --endpoint grpc <workflow-id>Forward arguments to a client script (client-script mode):
$ fuzzball workflow connect --name code-server <workflow-id> -- --workspace /home/user/projectThe start commands accept a --connect flag that starts the workflow and then runs
workflow connect against the new workflow as soon as it is ready.
$ fuzzball workflow start --connect my-workflow.fuzz
$ fuzzball workflow catalog start --connect <template>When invoked this way, and the selected service has a fuzzball.io/connect/<service-name>
annotation, the CLI prompts for arguments to pass to the client script.
To enable client-script mode for a service, add a workflow-level annotation whose key is
fuzzball.io/connect/<service-name> and whose value is the command to execute inside the
service container. The command must write the client script to standard output. The script
itself runs locally on the user’s machine.
When the connect command runs inside the service container, Fuzzball injects a number of environment variables that can be used to render runtime values into the generated client script. The most useful ones are:
| Variable | Description |
|---|---|
FB_WORKFLOW_ID | ID of the running workflow |
FB_ACCOUNT_ID | ID of the account that owns the workflow |
FB_GROUP_ID | ID of the group that owns the workflow |
FB_JOB_NAME | Name of the service (matches the service key in the workflow) |
FB_ENDPOINT_URL | Public URL of the first endpoint of the service (e.g. https://...) |
FB_ENDPOINT_HOST | Hostname portion of the first endpoint URL |
FB_ENDPOINT_PATH | Path portion of the first endpoint URL |
FB_ENDPOINT_URL_<NAME> | Public URL of the endpoint named <NAME> (uppercased) |
FB_ENDPOINT_HOST_<NAME> | Hostname portion of the endpoint named <NAME> |
FB_ENDPOINT_PATH_<NAME> | Path portion of the endpoint named <NAME> |
FB_SERVICE_<SERVICE>_ENDPOINT_{URL,HOST,PATH,ID}_<NAME> | The same for the endpoint named <NAME> of any service <SERVICE> in the workflow (see reaching another service’s endpoints) |
Any user-defined env entries declared on the service are also available to the connect
command. Combined with files embedded via the service’s files section, this is enough to
generate a fully parameterized client script without baking values into the container image.
The example below defines a vLLM service that exposes an OpenAI-compatible API on a public
HTTP endpoint and ships a connect command that emits a shell script to launch a local coding
agent (opencode) wired up to the running model.
The service uses two embedded files:
/app/agent-script.tmpl— the template of the local script, with placeholders such as__ENDPOINT_URL__and__WORKFLOW_ID__that will be substituted at connect time./app/generate-agent-command.sh— invoked by thefuzzball.io/connect/vllmannotation, itsed-substitutes the placeholders with values taken from the runtime environment (FB_ENDPOINT_URL,FB_WORKFLOW_ID, and the user-definedVLLM_API_KEY/VLLM_MODEL) and writes the resulting script to standard output.
version: v4
volumes:
data:
use: ephemeral
size: 5GB
annotations:
fuzzball.io/connect/vllm: "sh /app/generate-agent-command.sh"
services:
vllm:
image:
uri: docker://vllm/vllm-openai:latest
persist: true
network:
ports:
- name: openai-api
port: 8000
protocol: tcp
endpoints:
- name: openai-vllm
port-name: openai-api
protocol: http
type: subdomain
scope: public
mounts:
/home/vllm: data
resource:
cpu:
cores: 8
memory:
size: 32GB
devices:
nvidia.com/gpu: 1
annotations:
nvidia.com/gpu.model: "H100"
env:
- HOME=/home/vllm
- VLLM_API_KEY=replace-me
- VLLM_MODEL=meta-llama/Llama-3.1-8B-Instruct
script: |
#!/bin/sh
cd $HOME && exec vllm serve "$VLLM_MODEL" \
--tensor-parallel-size 1 \
--host 0.0.0.0 \
--port 8000
readiness-probe:
tcp-socket:
port: 8000
initial-delay-seconds: 60
period-seconds: 30
failure-threshold: 60
success-threshold: 1
files:
/app/agent-script.tmpl: |-
#!/usr/bin/env bash
if [ "$#" -ne 1 ]; then
echo "project directory path is missing"
exit 1
fi
mkdir -p /tmp/__WORKFLOW_ID__
base64 -w0 <<EOF |
{
"\$schema": "https://opencode.ai/config.json",
"model": "local/__MODEL__",
"provider": {
"local": {
"npm": "@ai-sdk/openai-compatible",
"name": "LocalAI (local)",
"options": {
"baseURL": "__ENDPOINT_URL__v1",
"apiKey": "__API_KEY__",
"headers": {
"keep_alive": -1
}
},
"models": {
"__MODEL__": {
"name": "__MODEL__",
"options": {
"max_tokens": 16000
}
}
}
}
}
}
EOF
cat > /tmp/__WORKFLOW_ID__/agent.config
docker run --rm -it \
-v $1:/home/agent/workspace \
--entrypoint /bin/bash \
docker/sandbox-templates:opencode \
-c "mkdir -p /home/agent/.config/opencode && \
cat >/home/agent/.config/opencode/opencode.json \
<(echo $(cat /tmp/__WORKFLOW_ID__/agent.config) | base64 -d) && \
exec opencode"
/app/generate-agent-command.sh: |-
sed -e "s|__ENDPOINT_URL__|$FB_ENDPOINT_URL|g" \
-e "s|__API_KEY__|$VLLM_API_KEY|g" \
-e "s|__MODEL__|$VLLM_MODEL|g" \
-e "s|__WORKFLOW_ID__|$FB_WORKFLOW_ID|g" \
/app/agent-script.tmpl
A user can then start the workflow and immediately drop into the local agent, passing the project directory as the only positional argument forwarded to the script:
$ fuzzball workflow start --connect vllm.fuzz
# (prompt) Enter arguments to pass to client script: /home/user/projects/myappOr, against an already-running workflow:
$ fuzzball workflow connect --name vllm <workflow-id> -- /home/user/projects/myappIn both cases Fuzzball:
- Waits for the
vllmservice readiness probe to pass. - Runs
sh /app/generate-agent-command.shinside thevllmcontainer. - Captures the substituted script from standard output, writes it to a temporary file with
executable permissions, and runs it locally with
/home/user/projects/myappas$1.
- Declare the annotation
fuzzball.io/connect/<service-name>on the workflow. - Embed any helper files (template, substitution script, certificates, etc.) under the
service’s
filessection so they are available inside the container. - The annotation command should print the final, ready-to-run client script to standard output. It must not depend on standard input, since the connect machinery does not forward a TTY to the in-container command.
- Use
FB_*environment variables and any service-levelenventries to inject runtime values into the generated script.
Services without this annotation always use browser mode. See the
workflow syntax reference for the
full set of services and network configuration options, and the
workflow endpoints page for details
on how endpoint URLs are constructed.