Skip to content

feat(projects): mark environment variables as secrets at project creation - #581

Open
dviejokfs wants to merge 2 commits into
mainfrom
feat/project-create-env-var-secret-flag
Open

feat(projects): mark environment variables as secrets at project creation#581
dviejokfs wants to merge 2 commits into
mainfrom
feat/project-create-env-var-secret-flag

Conversation

@dviejokfs

Copy link
Copy Markdown
Contributor

What

Adds a Secret checkbox to the environment-variable rows in the project-creation flow, and makes it actually do something.

Why the backend had to change

The checkbox was half-present already: ManualProjectConfigurator rendered one, but the create-project API took environment_variables: Vec<(String, String)> and EnvVarService::create_environment_variable hard-coded is_secret: false. The flag was collected in the form and silently dropped on the way to the database. The git-import configurator (the one in the screenshot this came from) had no checkbox at all.

Backend

  • CreateProjectEnvVar replaces the tuple. A hand-written Deserialize accepts both shapes, so no API client breaks:
    • {"key": "API_KEY", "value": "sk-...", "is_secret": true}
    • ["API_KEY", "sk-..."] (legacy, implies is_secret: false)
      ProjectEnvVarInput is a doc-only ToSchema struct so the OpenAPI spec describes the preferred object form.
  • EnvVarService::create_environment_variable takes and persists is_secret, and rejects an empty secret — write-only means it could never be read back or corrected, only deleted.
  • create_project validates env vars before the project row is inserted. Previously a bad env var surfaced as a 500 "Project Creation Failed" from the post-insert finalize step, which then rolled the whole project back; now it's a 400 with an actionable message and nothing is created.
  • Template creation (EnvVarInput) carries the flag as well.

Importers deliberately unchanged. caprover/coolify/dokploy/portainer derive is_secret from a key-name heuristic (looks_like_secret_env_key), not from anything the source platform asserts. Forwarding it would make imported values write-only and hide them from the operator who still needs to verify the import. They now pass CreateProjectEnvVar::plain(...) with a comment explaining the choice. Values are encrypted at rest either way.

Frontend

  • Checkbox added to ProjectConfigurator and the drop flow, with the consequence stated inline ("stored encrypted and never shown again, only replaceable") rather than a bare "Secret" label.
  • All four submit paths send is_secret: ProjectConfigurator, ManualProjectConfigurator, GitImportClone, ImportProject, plus Drop.
  • Zod/validation mirrors the server: a secret with no value is refused client-side too.
  • Both generated clients regenerated from a live server per CLAUDE.md.

CLI

temps projects create asks per variable on manual entry, and once for the whole batch on .env import. (temps env set --secret already existed for the post-creation path.)

Verification

Ran against a live local server + database:

Request Result
[{"key":"API_KEY","value":"sk-live-123","is_secret":true}, {"key":"LOG_LEVEL","value":"info"}] 200 — API_KEY stored is_secret=t, returned as value: null; LOG_LEVEL is_secret=f
[["PORT","8080"],["DEBUG","1"]] (legacy) 200 — both stored, is_secret=f
[{"key":"TOKEN","value":"","is_secret":true}] 400 with the explanatory message, no orphan project row
  • cargo clippy --all-targets -- -D warnings clean on all 8 touched crates
  • cargo test --lib — 160 pass (includes 8 new tests: both wire formats, mixed lists, malformed entries, empty-secret rejection)
  • tsc --noEmit clean; bun test src — 161 pass
  • No new eslint errors (ImportProject.tsx / ProjectConfigurator.tsx were reformatted by prettier and are now warning-free; they had 123 and 15 pre-existing warnings)

Not verified in a browser: the two new checkboxes render on surfaces I couldn't reach locally — ProjectConfigurator's step needs a connected Git provider, and the drop flow needs a real file drop. They use the same Checkbox/FormField primitives as the existing ManualProjectConfigurator checkbox that ships today, and typecheck/lint pass, but a reviewer should eyeball them.

…tion

Project creation could only ever store env vars as readable values: the
create-project API took `Vec<(String, String)>` tuples and the service
hard-coded `is_secret: false` on every insert. The web configurator that
already rendered a "Secret" checkbox (ManualProjectConfigurator) was
therefore lying — the flag was collected and silently dropped — and the
git-import configurator had no checkbox at all.

Backend:
- `CreateProjectEnvVar` replaces the tuple. It deserializes from either
  `{"key","value","is_secret"}` or the legacy `["KEY","value"]` form, so
  existing API clients keep working unchanged.
- `EnvVarService::create_environment_variable` takes and persists
  `is_secret`, and refuses an empty secret (write-only means it could
  never be filled in or inspected afterwards).
- `create_project` validates env vars before the project row is inserted,
  so a bad request is a 400 instead of a 500 from the post-insert
  finalize step that then rolls the project back.
- Template creation (`EnvVarInput`) carries the flag too.

Importers keep their current behaviour: their `is_secret` is a heuristic
over the key name rather than something the source platform asserts, so
forwarding it would hide values the operator still needs to verify.

Frontend: the checkbox now appears in ProjectConfigurator and the drop
flow alongside the existing ManualProjectConfigurator one, with the
write-only consequence spelled out, and all four submit paths send
`is_secret`. Client-side validation mirrors the server's empty-secret
rejection.

CLI: `temps projects create` asks per variable on manual entry, and once
for the whole batch on `.env` import.
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

📓 Changelog preview

This is what your commits will add to the generated CHANGELOG.md at release time (via git-cliff). Do not edit CHANGELOG.md by hand — it is generated from your Conventional Commit messages.

## [Unreleased]

### Added

- **projects:** Mark environment variables as secrets at project creation

### Styling

- **web:** Drop incidental prettier reformatting from the env-var change

…ange

Running prettier over ImportProject.tsx and ProjectConfigurator.tsx
reformatted code the secret-flag change never touched: 137+/126- and
128+/67- of re-indentation that buried the actual checkbox and made the
frontend look untouched in review.

Both files are restored to their original formatting with only the real
edits re-applied — 5 and 44 lines. The checkbox now uses `md:col-span-2`
inside the existing key/value grid instead of a wrapper div, so no
surrounding JSX needed re-indenting.

No behaviour change: same schema refinement, same submit mapping, same
checkbox. Neither file gains an eslint error (both were already
non-prettier-formatted on main).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant