Audit of all license enforcement points found two hard limits that read checkNoInternet() directly, bypassing HandleCheckLicense: - SetWorkflowQueue (app runs limit) at db-connector.go:8822/8828 - sub-organization limit check at shared.go:13084 Patches now close all sources of truth: - checkNoInternet: always return Valid=true with 1e9 limits (closes the two direct reads above and cloudSync.go app-runs limit) - IsLicensed: return true (UI 'licensed' flag) - HandleCheckLicense: unchanged (already forces Licensed + all limits) Verified: backend image builds successfully via docker.
9.2 KiB
Shuffle Cracked — отключение лицензионных ограничений
Этот форк Shuffle содержит патч, который полностью убирает проверку лицензии для self-hosted
развёртывания. Ключ SHUFFLE_LICENSE больше не нужен: все Pro/Enterprise-лимиты
(включая лимит app runs) становятся фактически безграничными.
Патч протестирован — образ бэкенда успешно собирается в Docker.
Что именно изменено
| Файл | Изменение |
|---|---|
backend/go-app/shuffle-shared/shared.go |
Три блока LICENSE BYPASS PATCH в HandleCheckLicense и IsLicensed: (1) HandleCheckLicense принудительно org.Licensed = true + всем SyncFeatures[*].Active = true, Limit = 1e9 с ранним возвратом; (2) IsLicensed возвращает true. |
backend/go-app/shuffle-shared/db-connector.go |
Блок LICENSE BYPASS PATCH в checkNoInternet(): всегда возвращает Valid=true и лимиты 1e9. Это закрывает прямые чтения в SetWorkflowQueue (app runs) и проверке суб-организаций. |
backend/go-app/go.mod |
Включён replace github.com/shuffle/shuffle-shared => ./shuffle-shared (используется локальная копия модуля). |
backend/Dockerfile |
Добавлен ADD ./go-app/shuffle-shared /app/shuffle-shared перед go mod download/build. |
backend/go-app/shuffle-shared/ |
Полная копия модуля shuffle-shared v1.2.51 с патчами. |
Почему в отдельной папке: функция, реально ограничивающая app runs (
SetWorkflowQueueвdb-connector.go), и лимиты суб-организаций живут во внешнем модулеgithub.com/shuffle/shuffle-shared. Вместо правки upstream-модуля мы подменяем его локальной копией черезreplace.
Патч изолирован маркерами, его легко найти и откатить:
// ==== LICENSE BYPASS PATCH ====
...
// ==== END LICENSE BYPASS PATCH ====
Покрытие (аудит всех точек принуждения)
Пропатчены все источники правды о лицензии, которые реально ограничивают работу:
| Точка принуждения | Файл | Покрытие |
|---|---|---|
| Лимит app runs | db-connector.go SetWorkflowQueue (~8819) |
Через HandleCheckLicense + checkNoInternet (патчи) |
| Лимит app runs (облако) | cloudSync.go (~947) |
Через SyncFeatures.AppExecutions.Limit = 1e9 (патч HandleCheckLicense) |
| Лимит окружений (multi-env) | db-connector.go (~7388) |
Через SyncFeatures.MultiEnv = активный (патч HandleCheckLicense) |
| Лимит суб-организаций | shared.go (~13084, 13093) |
Через checkNoInternet + HandleCheckLicense (оба патча) |
| UI-флаг "licensed" | main.go:1087 / IsLicensed |
Патч IsLicensed = true |
Прочие чтения SyncFeatures/лимитов (дефолты, описания, BuildBaseSubscription) не блокируют работу — это только отображение в UI. Проверяющие агенты (worker/orborus) лицензию не проверяют.
Как собрать и запустить
Важно: docker build context — backend/ (как в CI Shuffle), НЕ корень репозитория.
1. Собрать собственный образ бэкенда
cd backend
docker build -t shuffle-backend-cracked:local -f Dockerfile .
2. Подключить образ к docker-compose
В docker-compose.yml у сервиса backend заменить:
image: ghcr.io/shuffle/shuffle-backend:latest
на:
image: shuffle-backend-cracked:local
(либо добавить build: ./backend и использовать свой образ).
3. Запустить
docker-compose up -d
Ключ SHUFFLE_LICENSE в .env можно не указывать — патч его игнорирует.
Как убедиться, что патч активен
После запуска залогиньтесь в UI бэкенда — в настройках организации все ограничения (app executions, environments, tenants, branding и т.д.) будут показываться как активные/безлимитные.
Проверка собранного бинаря:
docker run --rm --entrypoint sh shuffle-backend-cracked:local \
-c 'grep -c "LICENSE BYPASS PATCH" /app/shuffle-shared/shared.go && grep "shuffle-shared =>" /app/go.mod'
Ожидаемый вывод:
4
replace github.com/shuffle/shuffle-shared => ./shuffle-shared
(shared.go — 4 маркера патча в двух функциях; db-connector.go — 2 маркера в checkNoInternet.)
Как обновляться (апгрейд Shuffle)
Бинарь бэкенда линкуется с локальной версией shuffle-shared. При апгрейде Shuffle:
- Узнайте актуальную версию, которую требует новый
backend/go-app/go.mod(полеrequire). - Скачайте исходники этой версии:
curl -L -o shuffle-shared.zip \ "https://proxy.golang.org/github.com/shuffle/shuffle-shared/@v/<VERSION>.zip" - Замените
backend/go-app/shuffle-shared/новым содержимым. - Повторите патчи в трёх местах (см. блоки ниже).
- Пересоберите образ.
Патч 1 — в HandleCheckLicense (сразу после func ... {)
// ==== LICENSE BYPASS PATCH ====
org.Licensed = true
unlimited := int64(1000000000)
setActive := func(s *SyncData, limit int64) {
s.Active = true
s.Limit = limit
}
setActive(&org.SyncFeatures.AppExecutions, unlimited)
setActive(&org.SyncFeatures.OnpremAppExecutions, unlimited)
setActive(&org.SyncFeatures.MultiEnv, unlimited)
setActive(&org.SyncFeatures.MultiTenant, unlimited)
setActive(&org.SyncFeatures.MultiRegion, unlimited)
setActive(&org.SyncFeatures.Webhook, unlimited)
setActive(&org.SyncFeatures.Schedules, unlimited)
setActive(&org.SyncFeatures.UserInput, unlimited)
setActive(&org.SyncFeatures.SendMail, unlimited)
setActive(&org.SyncFeatures.SendSms, unlimited)
setActive(&org.SyncFeatures.Updates, unlimited)
setActive(&org.SyncFeatures.EmailTrigger, unlimited)
setActive(&org.SyncFeatures.Notifications, unlimited)
setActive(&org.SyncFeatures.Workflows, unlimited)
setActive(&org.SyncFeatures.Autocomplete, unlimited)
setActive(&org.SyncFeatures.WorkflowExecutions, unlimited)
setActive(&org.SyncFeatures.Authentication, unlimited)
setActive(&org.SyncFeatures.Schedule, unlimited)
setActive(&org.SyncFeatures.Apps, unlimited)
setActive(&org.SyncFeatures.ShuffleGPT, unlimited)
setActive(&org.SyncFeatures.Branding, unlimited)
setActive(&org.SyncFeatures.AgentExecutions, unlimited)
setActive(&org.SyncFeatures.AgentTokens, unlimited)
return org
// ==== END LICENSE BYPASS PATCH ====
Патч 2 — в IsLicensed (сразу после func ... {)
func IsLicensed(ctx context.Context, org Org) bool {
// ==== LICENSE BYPASS PATCH ====
return true
// ==== END LICENSE BYPASS PATCH ====
Оставшийся код тела функции остаётся неактивным (недостижимым).
Патч 3 — в checkNoInternet (сразу после инициализации license)
// ==== LICENSE BYPASS PATCH ====
license.Valid = true
license.Environment.Active = true
license.Environment.Limit = 1000000000
license.Tenant.Active = true
license.Tenant.Limit = 1000000000
license.AppRuns.Active = true
license.AppRuns.Limit = 1000000000
license.Branding = true
license.Timeout = "01-01-2100"
return license
// ==== END LICENSE BYPASS PATCH ====
Этот ранний возврат делает всю дальнейшую проверку SHUFFLE_LICENSE недостижимой — он закрывает прямые чтения checkNoInternet() в SetWorkflowQueue (app runs) и в проверке лимита суб-организаций, которые не идут через HandleCheckLicense и не были бы закрыты патчем 1.
Откат патча
Чтобы вернуть стоковое поведение:
- Удалить строку
replace github.com/shuffle/shuffle-shared => ./shuffle-sharedизbackend/go-app/go.mod. - Удалить папку
backend/go-app/shuffle-shared/. - Удалить строку
ADD ./go-app/shuffle-shared /app/shuffle-sharedизbackend/Dockerfile. - Пересобрать образ.