Skip to content

feat(dashboard): list & add arbitrary custom .env keys on the Keys page - #54552

Merged
teknium1 merged 2 commits into
mainfrom
feat/dashboard-custom-env-keys
Jun 29, 2026
Merged

feat(dashboard): list & add arbitrary custom .env keys on the Keys page#54552
teknium1 merged 2 commits into
mainfrom
feat/dashboard-custom-env-keys

Conversation

@benbarclay

@benbarclay benbarclay commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Infographic

custom-keys

What

The dashboard Keys page only rendered env vars present in a catalog
(OPTIONAL_ENV_VARS or the provider catalog). Two gaps fall out of that:

  1. Any key a user set directly in .env that Hermes doesn't recognise was
    completely invisible on the page.
  2. There was no way to add an arbitrary env var from the GUI — e.g. to
    inject a var a skill, MCP server, or your own tooling needs.

Change

A new Custom Keys section on the Keys page that both lists unrecognised
on-disk keys and carries an add-a-key form.

Backend (GET /api/env): after emitting catalogued rows, also emit a row
for every on-disk .env key not in any catalog, flagged category="custom" +
custom=true. Custom rows are password-masked (is_password=true) — an
unrecognised key could hold anything, so it's redacted in the listing and its
value is only retrievable through the existing token-gated, rate-limited,
audit-logged POST /api/env/reveal. Channel-managed credentials stay excluded
(they belong to the Channels page).

No new write surface, no new secret-exposure surface: the save
(PUT /api/env) and reveal paths already accept arbitrary keys, and the
existing server-side env-name guard + denylist (PATH, LD_PRELOAD,
PYTHONPATH, LD_AUDIT, …) still reject anything that could escalate. The
listing never ships plaintext — same redact/reveal model as every other key.

Frontend (EnvPage.tsx): a CustomKeysCard lists the custom rows and an
add-key form takes a variable name (client-side validation mirroring the
backend ^[A-Za-z_][A-Za-z0-9_]*$ regex), opens a normal editable row, and
saves through the existing PUT /api/env flow — so on save the key round-trips
back from the backend as a durable custom row. i18n added for en, zh, and
types.ts.

Why a section, not a raw .env textarea

A bulk raw-.env editor would hand the browser every secret in plaintext in
one GET
, bypassing the redact-by-default + per-key reveal gate + reveal rate
limit + audit log the page deliberately enforces. The add-key form gets the
"inject arbitrary env vars" capability with zero new exposure — values stay
redacted and reveal stays gated.

Tests

tests/hermes_cli/test_env_custom_keys.py — behavior contracts against the real
FastAPI app: an unknown .env key surfaces as a custom, set, password-masked
row whose value never appears in the listing payload; a catalogued key
(HONCHO_API_KEY) is not mislabelled custom; every row carries the custom
flag. Verified to fail on the pre-fix backend (3/4 fail) and pass with this
change. The frontend typechecks (tsc -p web --noEmit), and an E2E round-trip
against a temp HERMES_HOME confirms add → list-as-custom → reveal works.

Infographic (merge)

custom-keys-akira

The Keys page only rendered env vars present in a catalog (OPTIONAL_ENV_VARS
or the provider catalog); any other key a user set in .env was invisible, and
there was no way to add an arbitrary env var from the GUI (e.g. to inject a
var a skill or MCP server needs).

Backend: GET /api/env now also emits a row for every on-disk .env key that
isn't in any catalog, flagged category="custom" + custom=true and
password-masked (an unrecognised key could hold anything, so it's redacted and
reveal-gated like any secret). Channel-managed credentials stay excluded. The
write (PUT /api/env) and reveal (POST /api/env/reveal) paths already handle
arbitrary keys, with the existing env-name guard + denylist (PATH, LD_PRELOAD,
PYTHONPATH, …) enforced server-side — no new write surface.

Frontend: a new "Custom Keys" section lists those custom rows and carries an
add-a-key form (client-side name validation mirroring the backend regex; the
new row reuses the normal edit/save flow, so on save it round-trips back from
the backend as a durable custom row). i18n added for en + zh + types.

Tests: behavior-contract coverage that an unknown .env key surfaces as a
masked custom row and a catalogued key does not — verified to fail on the
pre-fix backend.
@benbarclay
benbarclay requested a review from teknium1 June 29, 2026 01:21
@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

🔎 Lint report: feat/dashboard-custom-env-keys vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 11697 on HEAD, 11696 on base (🆕 +1)

🆕 New issues (1):

Rule Count
unresolved-import 1
First entries
tests/hermes_cli/test_env_custom_keys.py:11: [unresolved-import] unresolved-import: Cannot resolve imported module `fastapi.testclient`

✅ Fixed issues: none

Unchanged: 6141 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/dashboard Web dashboard / control panel UI (dashboard/, landing) comp/cli CLI entry point, hermes_cli/, setup wizard area/config Config system, migrations, profiles duplicate This issue or pull request already exists labels Jun 29, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Duplicate of #20808 (earliest open PR for this feature). Both surface unrecognised on-disk .env keys on the dashboard Keys page and add an arbitrary-key add form through the existing GET/PUT /api/env flow (same files: hermes_cli/web_server.py, web/src/pages/EnvPage.tsx, web/src/lib/api.ts). #49288 was already marked a duplicate of #20808; this is the third entrant in the same cluster. This PR's password-masking of custom rows is a nice refinement a maintainer may want to fold into the canonical PR. CI Docker-image build is currently failing on this branch.

The env translation block is type-checked across every locale (tsc -b), so
the 8 new customKeys strings must exist in all of them, not just en/zh. Add
translated entries to the remaining 14 locales (de, es, fr, it, ja, ko, pt,
ru, tr, uk, hu, ga, af, zh-hant).

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

Verdict: LGTM

Well-structured feature that adds custom .env key management to the dashboard Keys page.

Key observations:

  • Backend: _row() in get_env_vars() now accepts a custom flag and surfaces arbitrary .env keys as custom rows with is_password=True for security.
  • Frontend: New UI section for custom keys with add/search functionality.
  • i18n: 16 language files updated with consistent translations for the new feature.
  • Tests: Dedicated test file (test_env_custom_keys.py) covers custom key surfacing, password masking, catalogued key exclusion, and custom flag presence.
  • Security: Custom keys are treated as secrets by default (is_password=True, redacted, reveal-gated). Channel-managed credentials are excluded.
  • File count: 21 files, but 16 are i18n translations (mechanical additions). Core changes are well-scoped to 5 files.

No security concerns. Clean implementation with good test coverage.


Reviewed by Hermes Agent

@teknium1
teknium1 merged commit 20b03d9 into main Jun 29, 2026
35 checks passed
@teknium1
teknium1 deleted the feat/dashboard-custom-env-keys branch June 29, 2026 05:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles comp/cli CLI entry point, hermes_cli/, setup wizard comp/dashboard Web dashboard / control panel UI (dashboard/, landing) duplicate This issue or pull request already exists P3 Low — cosmetic, nice to have type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants