Repository navigation
feat(env): manage env variables without returning their values - #59
Merged
Merged
Conversation
Что: - Новый tRPC-роутер env. env.keys (GET /env.keys) отдаёт только имена переменных с флагами secret/empty и ревизией. env.applyChanges (POST /env.apply, operationId env-apply) задаёт, удаляет, генерирует на сервере и копирует переменные и возвращает только имя, действие и источник. - Области: application, compose, postgres, mysql, mariadb, mongo, redis, libsql, project, environment. Для application дополнительно доступны поля buildArgs и buildSecrets. - applyEnvChanges правит текст по грамматике dotenv: многострочное значение в кавычках заменяется и удаляется целиком, комментарии, пустые строки, порядок, export и завершающий перевод строки сохраняются. После правки текст заново разбирается и сверяется с ожидаемым набором переменных. - Значение записывается в форме, которую dotenv читает обратно символ в символ (JSON, обратные слэши, кавычки, многострочные блоки). - Чтение, проверка ревизии и запись идут в одной транзакции с блокировкой строки. Источник copy читается в той же транзакции. - Добавлен общий хелпер ревизии getEnvFieldRevision. Для application/env и compose используются существующие ревизии, поэтому они совместимы с application.envUpsert и compose.env.upsert. - Регулярное выражение имени переменной вынесено в одно место, им пользуются и старые схемы upsert. - Тестовый fake-drizzle-db теперь умеет select ... for update и вложенные with. Зачем: - ИИ-агенту с API-ключом нужно управлять переменными окружения, не получая их значений: увидеть имена, задать значение, сгенерировать его на сервере, скопировать между сервисами, переименовать и удалить. Риски: - tRPC резервирует имя apply, поэтому процедура называется env.applyChanges. REST-путь /env.apply закреплён через meta.openapi, в procedure-access процедура записана как env.applyChanges. - Действие unchanged, в том числе на dryRun, показывает, совпало ли переданное или скопированное значение с текущим. Так же ведут себя существующие upsert-методы. - Имена берутся из разбора dotenv. В повреждённом файле, где секрет занимает несколько строк без кавычек, часть значения может оказаться именем переменной. - Значение, которое нельзя записать без искажения, и правка, меняющая разбор соседних строк, отклоняются с BAD_REQUEST и не сохраняются. - Поле, которое не удалось расшифровать, не читается и не правится (PRECONDITION_FAILED). - upsertEnvVariables и старые upsert-методы не менялись и остаются построчными. Проверки: - Команды и результаты: bun run typecheck в packages/server и в apps/dokploy — без ошибок; bun run test -- --run по пяти новым и изменённым файлам тестов — 5 файлов, 76 тестов прошли; bun run test -- --run __test__/security __test__/env — 38 файлов, 413 тестов прошли; bun run test -- --run целиком — 285 файлов прошли, 1 пропущен, 1 упал; biome check по 15 затронутым файлам — без замечаний. - Ограничения: упавший файл __test__/deploy/application.real.test.ts (4 теста) требует запущенного Docker, в этой среде демон недоступен (ECONNREFUSED на сокете Docker). Транзакция и блокировка строки проверены только на тестовой базе в памяти, на настоящем PostgreSQL и на живой панели ничего не запускалось. openapi.json в корне не перегенерирован. What: - New tRPC router env. env.keys (GET /env.keys) returns only variable names with secret/empty flags and a revision. env.applyChanges (POST /env.apply, operationId env-apply) sets, removes, generates server-side and copies variables and returns only the name, action and source. - Scopes: application, compose, postgres, mysql, mariadb, mongo, redis, libsql, project, environment. The application scope also takes the buildArgs and buildSecrets fields. - applyEnvChanges edits the text by the dotenv grammar: a multi-line quoted value is replaced and removed as a whole, while comments, blank lines, ordering, export and the trailing newline are kept. After the edit the text is parsed again and compared with the expected set of variables. - A value is written in a form that dotenv reads back character for character (JSON, backslashes, quotes, multi-line blocks). - The read, the revision check and the write run in one transaction with a row lock. A copy source is read in the same transaction. - Added the generic revision helper getEnvFieldRevision. application/env and compose reuse the existing revisions, so they interoperate with application.envUpsert and compose.env.upsert. - The variable name pattern now lives in one place and the existing upsert schemas use it. - The fake-drizzle-db test helper now supports select ... for update and nested with. Why: - An AI agent holding an API key has to manage environment variables without receiving their values: see the names, set a value, generate one on the server, copy between services, rename and remove. Risks: - tRPC reserves the name apply, so the procedure is env.applyChanges. The REST path /env.apply is pinned through meta.openapi, and procedure-access lists the procedure as env.applyChanges. - The unchanged action, also on a dry run, tells whether a sent or copied value equals the current one. The existing upsert endpoints behave the same way. - Names come from the dotenv parse. In a damaged file, where a secret spans several lines without quotes, a part of the value can show up as a variable name. - A value that cannot be stored without distortion, and an edit that would change how neighbouring lines parse, are rejected with BAD_REQUEST and not saved. - A field that could not be decrypted is neither listed nor edited (PRECONDITION_FAILED). - upsertEnvVariables and the existing upsert endpoints are untouched and stay line-based. Checks: - Commands and results: bun run typecheck in packages/server and in apps/dokploy — no errors; bun run test -- --run on the five new and changed test files — 5 files, 76 tests passed; bun run test -- --run __test__/security __test__/env — 38 files, 413 tests passed; the full bun run test -- --run — 285 files passed, 1 skipped, 1 failed; biome check on the 15 touched files — clean. - Limitations: the failing file __test__/deploy/application.real.test.ts (4 tests) needs a running Docker daemon, which is not available here (ECONNREFUSED on the Docker socket). The transaction and the row lock were only exercised against the in-memory test database; nothing ran against a real PostgreSQL or a live panel. The root openapi.json was not regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e listing Что: - `env.keys` не возвращает «имена», похожие на кусок закодированных данных: от 20 символов, без подчёркивания, со строчными, заглавными буквами и цифрами. - Тест супер-сессии для слепых env-процедур подменяет сервис известных секретов: ответы по API-ключу проходят фильтр, который читает секреты из таблиц, отсутствующих в фальшивой базе этого теста. Зачем: - Если блок ключа вставлен в env без кавычек, dotenv читает его base64-строки с `=` на конце как присваивания, и строка секрета попала бы в список имён переменных, единственный ответ, где имена отдаются открыто. - Тест должен проходить и вместе с фильтром ответов для API-ключей, и без него. Риски: - Настоящая переменная с таким необычным именем (20+ символов, смешанный регистр, цифры, без подчёркивания) не попадёт в список; менять её по имени по-прежнему можно. - Хвост блока короче 20 символов этим правилом не отсекается. Проверки: - Команды и результаты: в apps/dokploy `bun run typecheck` - без ошибок; `bun run test -- --run __test__/security/super-session-blind-env.test.ts __test__/env` - 250 passed; `biome check` по изменённым файлам - 0 ошибок. - Ограничения: на живой панели не проверялось. What: - `env.keys` does not return "names" that look like a piece of encoded data: 20 characters or more, no underscore, with lowercase letters, uppercase letters and digits. - The super session test of the blind env procedures stubs the known secrets service: API key responses go through a filter that reads secrets from tables this test's fake database does not have. Why: - When a key block is pasted into env without quotes, dotenv reads its base64 lines ending in `=` as assignments, and a line of the secret would land in the list of variable names, the one response where names are returned in clear. - The test has to pass both with the API key response filter and without it. Risks: - A real variable with such an unusual name (20+ characters, mixed case, digits, no underscore) is left out of the listing; it can still be changed by name. - A tail of the block shorter than 20 characters is not caught by this rule. Checks: - Commands and results: in apps/dokploy `bun run typecheck` - no errors; `bun run test -- --run __test__/security/super-session-blind-env.test.ts __test__/env` - 250 passed; `biome check` on the changed files - 0 errors. - Limitations: not exercised against a live panel. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Что это
Способ управлять переменными окружения «вслепую»: ИИ-агент или скрипт с API-ключом видит имена переменных и меняет их, но ни один ответ не содержит значения.
Новые методы
GET /env.keys— список имён с флагамиsecretиemptyи ревизией. Значений нет.POST /env.apply(tRPC-процедураenv.applyChanges: имяapplyв tRPC зарезервировано) — одна операция, в которой можно:set— задать значение;remove— удалить переменную;generate— создать случайный секрет на стороне панели (длина 16–128, форматыbase64url,hex,alphanumeric); значение никуда не возвращается;copy— скопировать значение из другой переменной или другого сервиса, не раскрывая его; так же делается переименование (copy, затем remove).В ответе только имя, действие (
created,updated,unchanged,removed,missing) и источник. ЕстьdryRunиexpectedRevision.Области:
application,compose,postgres,mysql,mariadb,mongo,redis,libsql,project,environment. Для приложения доступны ещёbuildArgsиbuildSecrets.Как это устроено
exportи завершающий перевод строки сохраняются. После правки текст разбирается заново и сверяется с ожидаемым набором — если не сошлось, ничего не сохраняется.envVarsread/write, для проектаprojectEnvVars, для окруженияenvironmentEnvVars, плюс проверка организации. Источникcopyтребует права чтения в своей области.env.keys— чтение,env.applyChanges— обычная запись (по API-ключу работает, пока открыт доступ).application.envUpsertиcompose.env.upsertне тронуты; ревизии совместимы.env.keysне показывает «имена», похожие на кусок закодированных данных (блок ключа, вставленный без кавычек).Ограничения
unchangedпоказывает, совпало ли переданное значение с текущим. Так же ведут себя существующие upsert-методы.Проверки
bun run typecheckвapps/dokployиpackages/server— без ошибок.bun run test -- --run __test__/security __test__/env— проходит; полный набор без*.real.test.ts— проходит.biome checkпо изменённым файлам — 0 ошибок.FOR UPDATEпроверены на тестовой базе в памяти.🤖 Generated with Claude Code