Skip to content

Latest commit

 

History

168 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

chutes-dropzone

Self-hosted Chutes workspace with:

  • an optional public landing page at /
  • OpenWebUI at /chat/
  • n8n at /n8n/ (optional — set DROPZONE_ENABLE_N8N=false for chat-only mode)
  • native Chutes SSO
  • Chutes quota/tier/account chrome inside the chat sidebar
  • per-service database isolation (compose mode)
  • optional shared e2ee-proxy routing for OpenAI-compatible traffic

chutes-dropzone keeps the same local-vs-domain deployment model as chutes-n8n-local, but turns it into a single-host AI workspace instead of a single-app install.

This repo supports two packaging shapes:

  • a single-container standalone image built from Dockerfile.local-repo
  • a Docker Compose deployment driven by deploy.sh

Quick Start

Docker

docker run --rm -it \
  --pull always \
  --platform linux/amd64 \
  -p 443:443 \
  ghcr.io/chutesai/chutes-dropzone:latest

See Standalone Image for persisting state, domain mode, reverse proxy setups, and building from source.

Kubernetes

For Kubernetes, the easiest supported path is the standalone image with:

  • INSTALL_MODE=domain
  • CHUTES_TRAFFIC_MODE=direct
  • DROPZONE_ENABLE_N8N=false (chat-only — set to true to include n8n)
  • one persistent volume mounted at /data

TLS can be handled two ways:

  • Ingress/LB handles TLS: leave ACME_EMAIL empty, expose port 80 only, point your ingress at the service
  • Caddy manages TLS: set ACME_EMAIL, expose ports 80 and 443, use a LoadBalancer service

Example manifest (defaults to chat-only with Caddy-managed TLS):

Typical flow:

  1. Replace the placeholder secrets and host values in that manifest. The example defaults to chat-beta.chutes.ai with DROPZONE_ENABLE_PUBLIC_LANDING=false and DROPZONE_ENABLE_N8N=false. Replace ghcr.io/chutesai/chutes-dropzone:<release-tag> with your release tag.
  2. Apply it: kubectl apply -f examples/kubernetes/standalone-domain-direct.yaml
  3. Point your DNS A/AAAA record for DROPZONE_HOST at the service's external IP.

Important notes:

  • Keep this as a single replica. The standalone image stores state under /data.
  • When ACME_EMAIL is empty, the standalone edge listens on HTTP only (port 80). Your ingress or load balancer must handle TLS.
  • When ACME_EMAIL is set, Caddy handles Let's Encrypt automatically. Expose ports 80 and 443 and use a LoadBalancer service for the simplest setup.

Repo-Based

macOS/Linux/WSL:

bash <(curl -fsSL -H 'Cache-Control: no-cache' \
  "https://raw.githubusercontent.com/chutesai/chutes-dropzone/main/deploy.sh?$(date +%s)")

The deploy script:

  • clones or refreshes chutesai/chutes-dropzone
  • runs deploy.sh
  • auto-clones sirouk/n8n-nodes-chutes beside it if missing
  • resets n8n-nodes-chutes to the checked-in pinned commit on clean reruns so local builds match CI and release images

When launched from a terminal, the deploy script prompts for install mode and the required Chutes OAuth settings even when invoked via curl ... | bash.

For headless or CI usage, preseed the required environment variables:

curl -fsSL https://raw.githubusercontent.com/chutesai/chutes-dropzone/main/deploy.sh | \
  INSTALL_MODE=local \
  CHUTES_OAUTH_CLIENT_ID=... \
  CHUTES_OAUTH_CLIENT_SECRET=... \
  bash

Manual clone:

git clone https://github.com/chutesai/chutes-dropzone.git
cd chutes-dropzone
./deploy.sh

If ../n8n-nodes-chutes is missing, deploy will clone:

https://github.com/sirouk/n8n-nodes-chutes.git

You can override that source if needed with:

CHUTES_N8N_NODES_GIT_URL=git@github.com:sirouk/n8n-nodes-chutes.git ./deploy.sh

You can also temporarily test a different branch or commit without changing the checked-in pin:

CHUTES_N8N_NODES_GIT_REF=<branch-or-commit> ./deploy.sh

Advanced CI/testing runs can also point deploy at prebuilt local app images and skip the heavy app rebuild:

DROPZONE_N8N_IMAGE=chutes-dropzone-n8n:local \
DROPZONE_OPENWEBUI_IMAGE=chutes-dropzone-openwebui:local \
SKIP_APP_BUILDS=true \
./deploy.sh --force

Interactive deploy asks for:

  • INSTALL_MODE: local or domain
  • DROPZONE_ENABLE_PUBLIC_LANDING: show landing page at / or redirect to /chat/
  • CHUTES_TRAFFIC_MODE: direct or e2ee-proxy
  • DROPZONE_HOST for domain installs
  • TLS certificate management (yes for Let's Encrypt via Caddy, no for HTTP-only behind a reverse proxy)
  • ACME_EMAIL if TLS is managed
  • Chutes OAuth client ID and secret

After deploy:

  • https://<host>/ is either the landing page or a redirect to /chat/, depending on DROPZONE_ENABLE_PUBLIC_LANDING
  • https://<host>/chat/ opens OpenWebUI
  • https://<host>/n8n/ opens n8n (when DROPZONE_ENABLE_N8N=true)

If you deploy the standalone image on Render or another managed container platform with configurable health checks, set the service health-check path to /health. That endpoint is served directly by the embedded edge and is a better liveness probe than /static/site.webmanifest, which can briefly fail during app startup while OpenWebUI is still binding its internal upstream.

Required Chutes OAuth Callbacks

Register both exact redirect URIs on the same Chutes OAuth app:

  • https://<host>/oauth/oidc/callback
  • https://<host>/rest/sso/chutes/callback

Recommended scopes:

  • openid
  • profile
  • chutes:read
  • chutes:invoke

Routing Model

The public topology is fixed in v1:

  • / either serves a dark Chutes-branded landing page or redirects to /chat/
  • /chat/ is the human-friendly OpenWebUI entrypoint and redirects into OpenWebUI's native home route
  • /n8n/ reverse-proxies to n8n (returns 404 when DROPZONE_ENABLE_N8N=false)
  • /v1/* is exposed only when CHUTES_TRAFFIC_MODE=e2ee-proxy

DROPZONE_ENABLE_PUBLIC_LANDING=true preserves the launcher at /.

DROPZONE_ENABLE_PUBLIC_LANDING=false makes / return a redirect to /chat/, which is the recommended standalone domain deployment shape for chat-beta.chutes.ai.

Local installs intentionally stay on a single exact-cert host instead of subdomains.

Modes

Install Mode

  • local: uses https://e2ee-local-proxy.chutes.dev with an embedded certificate
  • domain: uses your real DROPZONE_HOST. TLS is managed by Caddy/ACME when ACME_EMAIL is set; otherwise the built-in edge listens on HTTP only for use behind a reverse proxy or ingress

Traffic Mode

  • direct: OpenWebUI and n8n use native Chutes endpoints
  • e2ee-proxy: OpenWebUI and n8n send OpenAI-compatible LLM traffic to the shared e2ee-proxy sidecar, while the public /v1/* edge path simply forwards into that same sidecar. n8n SSO text traffic stays on-proxy, and strict TEE-only model catalogs are enabled by default

Key Env Vars

See .env.example for the full set. The main operator-facing vars are:

  • DROPZONE_HOST
  • DROPZONE_ENABLE_PUBLIC_LANDING — show landing page at / (true) or redirect to /chat/ (false)
  • DROPZONE_ENABLE_N8N — include n8n (true, default) or chat-only mode (false)
  • ACME_EMAIL — set for Let's Encrypt TLS; leave empty for HTTP-only (TLS handled upstream)
  • POSTGRES_N8N_USER / POSTGRES_N8N_PASSWORD — per-service database credentials (compose mode)
  • POSTGRES_OPENWEBUI_USER / POSTGRES_OPENWEBUI_PASSWORD — per-service database credentials (compose mode)

Compatibility aliases still exist:

  • N8N_HOST mirrors DROPZONE_HOST
  • POSTGRES_DB falls back to POSTGRES_N8N_DB

OpenWebUI Runtime

OpenWebUI is kept env-authoritative:

  • WEBUI_URL=https://<host>/chat
  • OPENID_REDIRECT_URI=https://<host>/oauth/oidc/callback
  • ENABLE_PERSISTENT_CONFIG=false
  • ENABLE_OAUTH_PERSISTENT_CONFIG=false
  • ENABLE_OAUTH_SIGNUP=true
  • DEFAULT_USER_ROLE=user
  • BYPASS_MODEL_ACCESS_CONTROL=true
  • ENABLE_OAUTH_EMAIL_FALLBACK=true
  • ENABLE_LOGIN_FORM=false
  • ENABLE_PASSWORD_AUTH=false
  • OAUTH_USERNAME_CLAIM=username

At startup, Dropzone also seeds OpenWebUI runtime config so model backends use the signed-in Chutes OAuth token (system_oauth) for completions, while the global model picker is ordered TEE-first, grouped by provider/lab, and then sorted newest-first within each lab using model version and dated release hints from the Chutes model IDs.

Dropzone keeps that ordering fresh in the background with a server-side sync worker. By default, OpenWebUI refreshes its upstream model cache every 5 minutes and the worker reseeds MODEL_ORDER_LIST on the same cadence, so newly published Chutes models settle into the intended order without a redeploy.

Web search is also enabled by default with OpenWebUI's safe_web loader. Compose deployments run a private SearXNG sidecar with a curated no-key engine set, including DuckDuckGo, Google, Startpage, Reuters, Wikipedia, arXiv, Semantic Scholar, PubMed, GitHub, Stack Overflow, and package/documentation sources. The sync worker keeps the persisted OpenWebUI retrieval config aligned with .env, including conservative defaults for result count, loader concurrency, fetch length, SSL verification, and proxy trust. Operators can still disable it with ENABLE_WEB_SEARCH=false, narrow sources with WEB_SEARCH_DOMAIN_FILTER_LIST, or set WEB_SEARCH_ENGINE=duckduckgo to use OpenWebUI's DDGS backend. Standalone single-container deployments need SEARXNG_QUERY_URL pointed at an external/private SearXNG service for SearXNG; without that, they fall back to DDGS auto with backend retries.

For Chutes models that advertise native tool support, the sync worker also seeds web_search and image_generation into defaultFeatureIds and sets function_calling=native. That makes web and image available to the selected LLM by default without forcing a search or image job on every turn. Operators can set OPENWEBUI_DEFAULT_FEATURE_IDS="" to keep those tools available but off by default, or provide a comma-list such as web_search.

Image generation uses the same native tool path. The chat palette stays clean when the tools menu is collapsed; the image on/off switch and image model selector live inside the tools menu.

When CHUTES_TRAFFIC_MODE=direct, OpenWebUI talks straight to https://llm.chutes.ai/v1 and n8n resolves text traffic against the native Chutes LLM endpoints.

When CHUTES_TRAFFIC_MODE=e2ee-proxy, both OpenWebUI and n8n send invocation traffic through the shared e2ee-proxy sidecar. The sidecar's /v1/models catalog still sources model metadata anonymously from https://llm.chutes.ai/v1/models, then applies local TEE-only filtering when ALLOW_NON_CONFIDENTIAL=false.

Chutes currently does not advertise an email scope or email claim in the live OIDC discovery document, so OpenWebUI uses its synthetic-email fallback for user creation.

Deploy also auto-promotes any existing OAuth-created OpenWebUI users stuck in pending to user, so earlier failed SSO attempts recover cleanly after a redeploy.

Normal runtime is SSO-only. A service account (svc-dropzone@internal.chutes.local) is seeded for internal config sync; it is never exposed to users.

Break-Glass Recovery

n8n and OpenWebUI both get a generated service account (svc-dropzone@internal.chutes.local) during deploy. Password login is disabled — users authenticate exclusively via Chutes SSO.

To recover OpenWebUI access temporarily, edit .env, set:

ENABLE_OAUTH_SIGNUP=false
ENABLE_LOGIN_FORM=true
ENABLE_PASSWORD_AUTH=true

then restart the stack, use the seeded OpenWebUI admin account, and revert those values afterward.

Standalone Image

Dockerfile.local-repo packages:

  • n8n
  • OpenWebUI
  • OpenResty
  • Caddy
  • the landing page
  • bundled n8n-nodes-chutes
  • starter workflows

Published image:

  • ghcr.io/chutesai/chutes-dropzone:latest
  • release tags are also published as semver tags when releases are cut

Releases are published for linux/amd64.

Build locally:

docker buildx build --load \
  -t chutes-dropzone:local-repo \
  -f Dockerfile.local-repo .

Run interactively:

docker run --rm -it \
  -p 80:80 -p 443:443 \
  chutes-dropzone:local-repo

Persistent standalone state lives under /data:

  • /data/.n8n
  • /data/openwebui
  • /data/caddy
  • /data/.env

Releases

release.sh is the interactive helper for cutting a GitHub release that drives the published GHCR image.

./release.sh --dry-run
./release.sh --version v0.1.0
./release.sh --yes

It:

  • proposes the next patch version from existing tags
  • prints the pinned refs and image inputs used by CI and release builds
  • verifies the checked-in n8n, OpenWebUI, caddy, and e2ee-proxy pins before publishing
  • requires a clean worktree before publishing
  • uses gh to create the GitHub release that triggers .github/workflows/release.yml

Verification

./scripts/smoke-test.sh --syntax
./scripts/smoke-test.sh
./scripts/e2e-test.sh

The smoke and e2e coverage now validate:

  • landing page reachability at /
  • OpenWebUI reachability at /chat/
  • n8n reachability at /n8n/
  • native n8n SSO flow
  • the fake Chutes IdP OIDC surface used by OpenWebUI
  • local proxy /v1/* behavior in proxy mode
  • GitHub Actions runs the destructive local E2E suite in both direct and e2ee-proxy traffic modes

About

Self-hosted Chutes apps

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages