From 7c3730b322fa302b0d9bab33eb08e2334d4eb55b Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Wed, 13 May 2026 16:57:53 +0530 Subject: [PATCH] fix: fix semver options in update_remote.sh --- docs/deployment/update.mdx | 23 +++--- docs/integrations/telephony/agent-stream.mdx | 74 ++++++------------- scripts/update_remote.sh | 78 +++++++++++++++++--- 3 files changed, 101 insertions(+), 74 deletions(-) diff --git a/docs/deployment/update.mdx b/docs/deployment/update.mdx index aefb14e..bbea9a0 100644 --- a/docs/deployment/update.mdx +++ b/docs/deployment/update.mdx @@ -18,13 +18,14 @@ Dograh publishes two images — `dograh-api` and `dograh-ui` — to both contain - **GitHub Container Registry** — [github.com/orgs/dograh-hq/packages](https://github.com/orgs/dograh-hq/packages) - **Docker Hub** — [hub.docker.com/u/dograhai](https://hub.docker.com/u/dograhai) -Each release is published under two kinds of tags: +Each release is published under two kinds of tags. Note the formats differ between GitHub releases and the Docker image tags — `update_remote.sh` understands both and normalizes for you. -| Tag style | Example | When to use | -|-----------|---------|-------------| -| **Release tag** | `v1.29.0` | Stable, recommended for production | -| **Git commit SHA** | `a1b2c3d` | Bleeding edge — any commit merged to `main` | -| `latest` | `latest` | Tracks the most recent release tag | +| Where | Tag format | Example | When to use | +|-------|-----------|---------|-------------| +| GitHub release tag | `dograh-vX.Y.Z` | `dograh-v1.28.0` | What you see at [github.com/dograh-hq/dograh/releases](https://github.com/dograh-hq/dograh/releases) | +| Docker image tag (semver) | `X.Y.Z` | `1.28.0` | Stable, recommended for production | +| Docker image tag (SHA) | short SHA | `a1b2c3d` | Bleeding edge — any commit merged to `main` | +| Docker image tag (`latest`) | `latest` | `latest` | Tracks the most recent release tag | Always update **`dograh-api`** and **`dograh-ui`** to the **same tag**. The two images are built from the same commit and the UI expects API responses in a matching shape — mixing versions will break the app. `update_remote.sh` handles this for you automatically. @@ -48,10 +49,10 @@ curl -o update_remote.sh https://raw.githubusercontent.com/dograh-hq/dograh/main bash update_remote.sh ``` -You'll be prompted for the target version. Non-interactive callers can set it via environment variable and skip the confirmation prompt: +You'll be prompted for the target version, defaulting to the most recent release. Accepted forms: bare semver (`1.28.0`), v-prefixed (`v1.28.0`), the full GitHub tag (`dograh-v1.28.0`), or `main` for bleeding edge — the script normalizes them. Non-interactive callers can set it via environment variable and skip the confirmation prompt: ```bash -TARGET_VERSION=v1.29.0 DOGRAH_UPDATE_YES=1 bash update_remote.sh +TARGET_VERSION=1.28.0 DOGRAH_UPDATE_YES=1 bash update_remote.sh ``` After the script finishes, apply the update by recreating the stack: @@ -74,7 +75,7 @@ docker compose down docker compose up --pull always ``` -To pin a specific version instead of `latest`, edit `docker-compose.yaml` and change both `image:` lines for `api` and `ui` to the same tag (e.g. `:v1.29.0`), then run the commands above. +To pin a specific version instead of `latest`, edit `docker-compose.yaml` and change both `image:` lines for `api` and `ui` to the same tag (e.g. `:1.28.0` — Docker image tags use bare semver, no `v` prefix), then run the commands above. ## Verify the update @@ -122,8 +123,8 @@ git fetch # Track latest main: git pull -# Or pin to a specific release: -git checkout v1.29.0 +# Or pin to a specific release (git tag format is dograh-vX.Y.Z): +git checkout dograh-v1.28.0 # Pick up pipecat and other submodule bumps git submodule update --init --recursive diff --git a/docs/integrations/telephony/agent-stream.mdx b/docs/integrations/telephony/agent-stream.mdx index fcc18d5..432db9e 100644 --- a/docs/integrations/telephony/agent-stream.mdx +++ b/docs/integrations/telephony/agent-stream.mdx @@ -1,19 +1,16 @@ --- title: "Agent Stream" -description: "Stream audio to a Dograh agent over a WebSocket using inline provider credentials" +description: "Stream audio to a Dograh agent over a WebSocket" --- ## Overview -Agent Stream is a WebSocket endpoint that lets an external caller drive a Dograh agent run by passing everything inline in the query string — including the telephony provider's credentials. Unlike the standard inbound webhook flow, this path does **not** require a stored telephony configuration in your Dograh organization. You bring the credentials with you when you connect. +Agent Stream is a WebSocket endpoint that lets a telephony provider point its media stream at a single URL and drive a Dograh agent run. The agent UUID in the URL selects the agent; provider-specific identifiers in the query string (for Cloudonix: `Domain`) tell Dograh which stored telephony configuration to use for that call. The bearer token and other credentials are never passed in the URL — they live in the stored configuration and are used by Dograh to validate the session and to issue provider API calls (hangup, transfer) during the call. This is useful when: -- You manage your telephony tenant outside Dograh and only want Dograh as the agent runtime -- You're integrating Dograh into a SIP gateway or in-house dialer -- You need a single endpoint that doesn't need per-tenant DB rows for every caller - -The endpoint authenticates with a Dograh **API key** (so the connection itself is authorized against your organization), and routes the audio through the named provider's serializer. +- You're integrating Dograh into a SIP gateway or in-house dialer that already speaks a supported provider's streaming protocol +- You want one stable endpoint per agent rather than wiring up an inbound webhook per phone number Agent Stream currently supports the **Cloudonix** provider only. Other providers @@ -26,16 +23,15 @@ The endpoint authenticates with a Dograh **API key** (so the connection itself i ## Endpoint ``` -wss://app.dograh.com/api/v1/agent-stream/{workflow_uuid} +wss://app.dograh.com/api/v1/agent-stream/{agent_uuid} ``` -`{workflow_uuid}` is the agent's stable UUID (see [Get the Agent UUID](#get-the-agent-uuid) below). On self-hosted deployments, replace `app.dograh.com` with your backend host. +`{agent_uuid}` is the agent's stable UUID (see [Get the Agent UUID](#get-the-agent-uuid) below). On self-hosted deployments, replace `app.dograh.com` with your backend host. ## Prerequisites - A Dograh agent (workflow) — published or in draft is fine -- A Dograh API key with access to the organization that owns the agent -- Provider credentials (for Cloudonix: bearer token + domain ID) +- A Cloudonix telephony configuration in your Dograh organization whose `domain_id` matches the `Domain` you pass on the URL. Dograh uses the bearer token from this configuration to validate the call session and to issue provider API calls (hangup, transfer). ## Get the Agent UUID @@ -53,47 +49,25 @@ The Agent UUID is the workflow's stable identifier — it doesn't change when ve 2. Scroll to the **Agent UUID** section (also linked in the right-side nav) 3. Click the UUID code block, or use the **Copy UUID** button -## Create a Dograh API key - -Agent Stream auth uses your Dograh API key — not your provider's bearer token. The provider credentials go in separate query params (see [URL parameters](#url-parameters) below). - -1. From the dashboard, navigate to **API Keys** -2. Click **Create API key**, give it a name, and copy the generated key (it starts with `dg_`) -3. Store it securely — Dograh shows the full key only once at creation time - -For programmatic management, see the [API Keys reference](/api-reference/api-keys). - - - API keys are scoped to an organization. The agent referenced by the URL must - belong to the same organization as the key, otherwise the connection is - rejected with WebSocket close code `1008`. - - ## Connect to the WebSocket ### URL parameters | Param | Required | Description | | --- | --- | --- | -| `api_key` | Yes | Your Dograh API key (`dg_...`). Authorizes the connection. | | `provider` | Yes | Provider name. Currently only `cloudonix` is supported. | -| `session` | Yes (cloudonix) | Cloudonix domain bearer token used to drive the call (hangup, etc.). | -| `AccountSid` | Yes (cloudonix) | Cloudonix domain ID. | -| `CallSid` | Yes (cloudonix) | Cloudonix call SID for this session. | -| `callId` | No | SIP-side call identifier; persisted on the workflow run for record-keeping. | +| `Domain` | Yes (cloudonix) | Cloudonix domain ID. Dograh uses this to look up the matching stored telephony configuration and retrieve the bearer token used for provider API calls. | +| `callId` / `CallSid` | No | Call identifier from your side; persisted on the workflow run's `gathered_context` as `call_id`. The Cloudonix call SID used for streaming is taken from the `start` event payload, not this param. | | `from` | No | Caller phone number, persisted on the workflow run as `caller_number`. | | `to` | No | Called phone number, persisted on the workflow run as `called_number`. | ### Cloudonix example ``` -wss://app.dograh.com/api/v1/agent-stream/{workflow_uuid} - ?api_key=dg_xxxxxxxxxxxxxxxx - &provider=cloudonix - &session={CLOUDONIX_BEARER_TOKEN} - &AccountSid={CLOUDONIX_DOMAIN_ID} - &CallSid={CALL_SID} - &callId={SIP_CALL_ID} +wss://app.dograh.com/api/v1/agent-stream/{agent_uuid} + ?provider=cloudonix + &Domain={CLOUDONIX_DOMAIN_ID} + &callId={CALL_ID} &from=+15555550100 &to=+15555550199 ``` @@ -104,23 +78,22 @@ Use this URL inside the CXML `` your Cloudonix Voice Application returns - + ``` -The first two messages on the socket should be Cloudonix's standard `connected` and `start` events (Twilio-compatible framing). Dograh extracts `streamSid` and `callSid` from the `start` event and begins streaming audio. +The first two messages on the socket should be Cloudonix's standard `connected` and `start` events (Twilio-compatible framing). Dograh extracts `streamSid` and `callSid` from the `start` event payload, validates the session against Cloudonix using the bearer token from the stored telephony configuration matched by `Domain`, and then begins streaming audio. ## Workflow run lifecycle When the WebSocket is accepted, Dograh: -1. Resolves your API key to a user and organization -2. Looks up the workflow by `workflow_uuid`, scoped to that organization -3. Runs a quota check -4. Creates a new `WorkflowRun` (`call_type=inbound`, `mode=cloudonix`, name `WR-AGS-XXXXXXXX`) with the `from`/`to`/`callId`/`AccountSid` fields stamped on `initial_context` -5. Transitions the run to `running` and starts the agent pipeline +1. Looks up the workflow by `agent_uuid` +2. Runs a quota check against the workflow's owning user +3. Creates a new `WorkflowRun` (`call_type=inbound`, `mode=cloudonix`, name `WR-AGS-XXXXXXXX`) with the `from`/`to` numbers stamped on `initial_context`, the `callId`/`CallSid` stored as `call_id` on `gathered_context`, and `Domain` recorded under the run's `inbound_webhook` log +4. Transitions the run to `running` and starts the agent pipeline The run is visible under the agent's **Runs** tab as soon as it's minted, just like an inbound or outbound call. @@ -128,12 +101,11 @@ The run is visible under the agent's **Runs** tab as soon as it's minted, just l | Code | Reason | | --- | --- | -| `1008` | Auth or routing failure — invalid API key, unknown provider, workflow not found in your organization, or quota exceeded | +| `1008` | Routing failure — unknown provider, workflow not found, or quota exceeded | | `1011` | Server-side failure or unsupported provider for Agent Stream | -| `4400` | Provider-level handshake error — for cloudonix, missing `session`/`AccountSid` or malformed `connected`/`start` events | +| `4400` | Provider-level handshake error — for cloudonix, missing `Domain`, no matching telephony configuration, missing bearer token on the configuration, malformed `connected`/`start` events, or session validation failed against Cloudonix | ## Security notes -- Treat the URL as a secret — both the Dograh API key and the provider bearer token sit in the query string. Store and transmit it only over TLS, and avoid logging the raw URL in places where access is broader than your operations team. -- Rotate the Dograh API key from the dashboard if you suspect it has leaked. -- The Dograh API key authorizes the connection against your organization. The provider bearer token is only used by Dograh to drive provider-side actions (e.g. hangup) — it is not stored on the workflow run. +- Treat the URL as a secret — the agent UUID itself authorizes the connection. Store and transmit it only over TLS, and avoid logging the raw URL in places where access is broader than your operations team. +- No bearer tokens or provider secrets are passed in the URL. Provider credentials live in the stored telephony configuration (matched by `Domain` for Cloudonix) and are used server-side by Dograh to validate the session and issue provider API calls. diff --git a/scripts/update_remote.sh b/scripts/update_remote.sh index 0044c1f..45c2802 100755 --- a/scripts/update_remote.sh +++ b/scripts/update_remote.sh @@ -104,7 +104,8 @@ if [[ -z "$TARGET_VERSION" ]]; then if [[ -t 0 ]]; then echo "" - echo -e "${YELLOW}Target version (release tag like v1.29.0, or 'main' for bleeding edge):${NC}" + echo -e "${YELLOW}Target version. Accepted forms: bare semver (1.28.0), v-prefixed (v1.28.0),${NC}" + echo -e "${YELLOW}full git tag (dograh-v1.28.0), or 'main' for bleeding edge.${NC}" read -p "[$LATEST_TAG]: " TARGET_VERSION TARGET_VERSION="${TARGET_VERSION:-$LATEST_TAG}" else @@ -123,15 +124,67 @@ if [[ "$TARGET_VERSION" == "latest" ]]; then fi fi -# Validate the tag/branch actually exists by HEAD-ing the compose file at that ref. -RAW_BASE="https://raw.githubusercontent.com/$REPO/$TARGET_VERSION" + +# GitHub release tags use a 'dograh-v' prefix (e.g. dograh-v1.28.0); Docker +# image tags on Docker Hub drop both the prefix and the 'v' (e.g. ':1.28.0'). +# Users commonly type shortcuts like '1.28.0' or 'v1.28.0' — try all reasonable +# variants so the script accepts any of those forms. +TRY_TAGS=("$TARGET_VERSION") +case "$TARGET_VERSION" in + main|HEAD) + ;; # branch refs — leave as-is + dograh-*) + ;; # already in the full tag form + v*) + TRY_TAGS+=("dograh-$TARGET_VERSION") + ;; + *) + TRY_TAGS+=("dograh-v$TARGET_VERSION" "v$TARGET_VERSION" "dograh-$TARGET_VERSION") + ;; +esac + echo -e "${BLUE}Validating target version: $TARGET_VERSION...${NC}" -if ! curl -fsI "$RAW_BASE/docker-compose.yaml" >/dev/null 2>&1; then - echo -e "${RED}Error: docker-compose.yaml not found at $TARGET_VERSION${NC}" - echo -e "${RED}Check the tag exists at: https://github.com/$REPO/releases${NC}" +RESOLVED_TAG="" +for tag in "${TRY_TAGS[@]}"; do + if curl -fsI "https://raw.githubusercontent.com/$REPO/$tag/docker-compose.yaml" >/dev/null 2>&1; then + RESOLVED_TAG="$tag" + break + fi +done + +if [[ -z "$RESOLVED_TAG" ]]; then + echo -e "${RED}Error: could not find a git tag matching '$TARGET_VERSION'${NC}" + echo -e "${RED}Tried: ${TRY_TAGS[*]}${NC}" + echo -e "${RED}See available releases at: https://github.com/$REPO/releases${NC}" exit 1 fi -echo -e "${GREEN}✓ Target version is valid${NC}" + +if [[ "$RESOLVED_TAG" != "$TARGET_VERSION" ]]; then + echo -e "${GREEN}✓ Resolved '$TARGET_VERSION' to git tag '$RESOLVED_TAG'${NC}" +fi +TARGET_VERSION="$RESOLVED_TAG" +RAW_BASE="https://raw.githubusercontent.com/$REPO/$TARGET_VERSION" + +# Derive the Docker image tag from the git tag. Tags on Docker Hub use bare +# semver — strip the 'dograh-' prefix and the leading 'v'. +IMAGE_TAG="" +case "$TARGET_VERSION" in + dograh-v*) IMAGE_TAG="${TARGET_VERSION#dograh-v}" ;; + v*) IMAGE_TAG="${TARGET_VERSION#v}" ;; + main|HEAD) IMAGE_TAG="" ;; + *) [[ "$TARGET_VERSION" =~ ^[0-9] ]] && IMAGE_TAG="$TARGET_VERSION" ;; +esac + +# Verify the image tag actually exists on Docker Hub. If not (e.g. CI hasn't +# published yet), fall back to ':latest' rather than pinning to a missing tag. +if [[ -n "$IMAGE_TAG" ]]; then + if curl -fsI "https://hub.docker.com/v2/repositories/dograhai/dograh-api/tags/$IMAGE_TAG/" >/dev/null 2>&1; then + echo -e "${GREEN}✓ Image tag :$IMAGE_TAG found on Docker Hub${NC}" + else + echo -e "${YELLOW}Warning: image tag :$IMAGE_TAG not found on Docker Hub — leaving images at :latest${NC}" + IMAGE_TAG="" + fi +fi ############################################################################### ### Reconcile required keys that may be missing on older installs @@ -201,12 +254,13 @@ done echo -e "${BLUE}[2/5] Downloading docker-compose.yaml at $TARGET_VERSION...${NC}" curl -fsSL -o docker-compose.yaml "$RAW_BASE/docker-compose.yaml" -# Pin api/ui image tags for release tags (v*). Leave :latest alone if we're -# tracking a branch like 'main' so up --pull always still grabs the newest build. -if [[ "$TARGET_VERSION" =~ ^v ]]; then - sed -i.tmp -E "s#(dograh-(api|ui)):latest#\1:$TARGET_VERSION#g" docker-compose.yaml +# Pin api/ui image tags when we resolved one. For branch refs (main) IMAGE_TAG +# is empty, so the images stay at ':latest' and `up --pull always` grabs the +# newest build of that branch. +if [[ -n "$IMAGE_TAG" ]]; then + sed -i.tmp -E "s#(dograh-(api|ui)):latest#\1:$IMAGE_TAG#g" docker-compose.yaml rm -f docker-compose.yaml.tmp - echo -e "${GREEN}✓ docker-compose.yaml updated; images pinned to $TARGET_VERSION${NC}" + echo -e "${GREEN}✓ docker-compose.yaml updated; images pinned to :$IMAGE_TAG${NC}" else echo -e "${GREEN}✓ docker-compose.yaml updated (image tags left at :latest)${NC}" fi