Files
shuffle-cracked/CRACKED.md
T
Marat Kharitonov 747fd15176
dockerbuild / main (backend, true, backend) (push) Failing after 29m15s
dockerbuild / main (frontend, true, frontend) (push) Failing after 26m52s
dockerbuild / sync-orborus (push) Has been cancelled
dockerbuild / main (worker, true, functions/onprem/worker) (push) Has been cancelled
Fix: also patch checkNoInternet and IsLicensed to close all limit gates
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.
2026-08-12 03:26:33 +03:00

9.2 KiB
Raw Blame History

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:

  1. Узнайте актуальную версию, которую требует новый backend/go-app/go.mod (поле require).
  2. Скачайте исходники этой версии:
    curl -L -o shuffle-shared.zip \
      "https://proxy.golang.org/github.com/shuffle/shuffle-shared/@v/<VERSION>.zip"
    
  3. Замените backend/go-app/shuffle-shared/ новым содержимым.
  4. Повторите патчи в трёх местах (см. блоки ниже).
  5. Пересоберите образ.

Патч 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.


Откат патча

Чтобы вернуть стоковое поведение:

  1. Удалить строку replace github.com/shuffle/shuffle-shared => ./shuffle-shared из backend/go-app/go.mod.
  2. Удалить папку backend/go-app/shuffle-shared/.
  3. Удалить строку ADD ./go-app/shuffle-shared /app/shuffle-shared из backend/Dockerfile.
  4. Пересобрать образ.