difyctl CLI#
difyctl (@langgenius/difyctl) is the official command-line interface for Dify, built on TypeScript/Bun and compiled to self-contained native binaries. It lives in the cli/ directory of the main Dify monorepo. The current release is 1.17.1 on the stable channel .
A key design constraint is strict version compatibility between the CLI and the Dify server: both sides enforce that they speak the same API contract, and the CLI release pipeline is integrated with Dify's own GitHub Releases rather than being distributed separately.
Binary Distribution#
Five standalone binaries are built per release :
| Target ID | OS | Arch |
|---|---|---|
linux-x64 | Linux | x64 |
linux-arm64 | Linux | arm64 |
darwin-x64 | macOS | x64 |
darwin-arm64 | macOS | arm64 (Apple Silicon) |
windows-x64 | Windows | x64 (.exe) |
Binaries are compiled via bun build --compile using a Bun version pinned in cli/.bun-version for reproducible output . Each release also ships a difyctl-v<version>-checksums.txt manifest of SHA256 hashes .
difyctl assets are attached to the corresponding Dify GitHub Release β there is no separate CLI release. Stale assets from prior builds are pruned from that release automatically .
Automated Installers#
POSIX (Linux/macOS):
curl -fsSL https://raw.githubusercontent.com/langgenius/dify/main/cli/scripts/install-cli.sh | sh
Windows (PowerShell):
irm https://raw.githubusercontent.com/langgenius/dify/main/cli/scripts/install.ps1 | iex
Both installers share the same flow :
- Detect target β
uname -s/uname -m(POSIX) or environment detection (PowerShell) maps the host to one of the five build targets. - Resolve release β by priority:
DIFY_VERSIONenv var βDIFYCTL_VERSIONenv var β latest Dify release via GitHub API. - Download β fetches the binary and
checksums.txtdirectly from GitHub Releases. No authentication required. - Verify SHA256 β fail-closed: installation aborts if the checksum does not match.
- Install β places the binary at
$HOME/.local/bin/difyctl(POSIX) or$LOCALAPPDATA\difyctl\bin\difyctl.exe(Windows), overridable viaDIFYCTL_PREFIX.
Version pinning environment variables:
DIFY_VERSION=1.17.1β download the difyctl bundled with that Dify release.DIFYCTL_VERSION=1.17.1β search Dify releases to find the one containing that CLI version.
Release Pipeline#
The workflow is defined in .github/workflows/cli-release.yml. It is triggered by a Dify release event, workflow_dispatch, or workflow_call. A dry_run boolean input parameter (default: false) is available for workflow_dispatch and workflow_call triggers; when true, the workflow builds and checksums binaries but skips asset upload and stale-asset pruning.
Two-job structure:
validate: readscli/package.json, validates the release manifest schema (viarelease-naming.mjs), resolves the target Dify release tag, and runs acompat-checkto confirm the CLI's declaredminDify/maxDifybounds (both must be plainX.Y.Zversions withminDify β€ maxDify) match the Dify release.release: cross-compiles all five targets, generates the SHA256 checksums file, uploads assets to the Dify GitHub Release (unlessdry_runis true), and prunes stale assets (unlessdry_runis true).
Key properties:
- Immutable: A published version is never overwritten. Fixes require a version bump in
cli/package.json. - Serialized: Concurrency group
difyctl-releasewithcancel-in-progress: falseprevents parallel or mid-flight-cancelled releases . - Repository-guarded: The
validatejob only runs whengithub.repository == 'langgenius/dify'.
Version Compatibility Gating#
difyctl enforces a hard compatibility contract between the CLI and the Dify server via bidirectional version gates. This was introduced in PR #38367 alongside breaking /openapi/v1 path refactors.
The compatibility bounds are declared in cli/package.json:
"compat": { "minDify": "1.16.0", "maxDify": "1.17.1" }
Client-Side Gate#
Implemented in cli/src/version/enforce.ts. Before running any authenticated command, the CLI:
- Probes
GET /_versionon the server (2-second timeout, no retries). - Evaluates the response using
compat.ts, comparing the server version againstminDify/maxDify. - If
too_oldβ hard error with exit code 6 and an upgrade hint. - If compatible or
too_newβ caches the result per host for 1 hour viacompat-store.ts.
Fail-open on probe errors: network flakiness never blocks a command. Only positive ("compatible") results are cached β a just-upgraded server is usable immediately without waiting for cache expiry .
Server-Side Gate#
Implemented in api/controllers/openapi/_version_gate.py. A Flask before_app_request hook on the /openapi/v1 blueprint :
- Parses
User-Agent: difyctl/<semver> (<os>; <arch>; <channel>). - Returns HTTP 426 Upgrade Required if the client version is below
MIN_DIFYCTL_VERSION(currently1.17.0). - Ignores non-difyctl clients (curl, browsers, etc.) β only blocks requests with a difyctl User-Agent.
- Allowlist:
/_versionand/_healthare always reachable, even for outdated clients , so an old CLI can still discover the server version. - Because the hook is app-wide (not route-scoped), it fires even for paths that no longer exist β preventing a 404 from masking the real problem when an old CLI calls a removed endpoint .
Relationship to OpenAPI Path Changes#
The version gate was introduced alongside a breaking refactor of /openapi/v1 paths from verb-suffix style to resource-oriented AIP-136 style (e.g., /apps/{id}/run β /apps/{id}:run). The gate ensures older CLI versions fail fast with a clear upgrade message rather than receiving unexpected 404s .
Key Files#
| File | Purpose |
|---|---|
cli/package.json | Version, channel, compat bounds (minDify/maxDify), release targets |
.github/workflows/cli-release.yml | Release pipeline: validate β build β publish |
cli/scripts/release-naming.mjs | Manifest validation, compat-check, artifact naming rules |
cli/scripts/install-cli.sh | POSIX (Linux/macOS) tokenless installer |
cli/src/version/enforce.ts | Client-side version gate (probe + cache) |
cli/src/version/compat.ts | Compatibility evaluation (too_old / compatible / too_new) |
cli/src/cache/compat-store.ts | Per-host compatibility cache (1-hour TTL) |
api/controllers/openapi/_version_gate.py | Server-side HTTP 426 gate for outdated CLI versions |