| name | accessible-key-setup |
| description | Provide an accessible, form-based local key setup flow that writes to .env without command-line editing, with safety locks and zero secret leakage |
| source | auto-skill |
| extracted_at | 2026-05-30T06:14:09.148Z |
Accessible Key Setup
When a project requires API keys but the user cannot safely edit tiny command-line values, provide a local form-based key setup flow with large text fields, high contrast, and a separate PowerShell script fallback. Keys are written to .env only and are never committed, logged, or exposed via API.
Core Rules
- Keys are written to local
.env only — never hardcoded, never committed
- Saving keys does NOT enable live mode — live mode requires separate operator confirmation
- Keys never appear in logs, API responses, or console output — only
key_present: true/false
- Mock mode remains default after saving keys
.env is in .gitignore
Procedure
1. Create the key management module
SECRET_KEYS = {"NVIDIA_NIM_KEY_ADAM", "NVIDIA_NIM_KEY_EVE"}
def get_env_path() -> Path:
return Path(__file__).resolve().parent.parent / ".env"
def read_env() -> dict[str, str]:
"""Read existing .env file into a dict."""
env_path = get_env_path()
if not env_path.exists():
return {}
env_vars = {}
with open(env_path, "r") as f:
for line in f:
line = line.strip()
if not line or line.startswith("#"):
continue
if "=" in line:
key, _, value = line.partition("=")
env_vars[key.strip()] = value.strip()
return env_vars
def write_env(env_vars: dict[str, str]) -> None:
"""Write env vars to .env file. Preserves existing comments."""
env_path = get_env_path()
existing = read_env()
existing.update(env_vars)
with open(env_path, "w") as f:
f.write("# Genesis Kernel — Local Environment\n")
f.write("# NEVER commit this file to version control.\n\n")
for key in sorted(existing.keys()):
value = existing[key]
if key in SECRET_KEYS:
f.write("# SECRET — never commit\n")
f.write(f"{key}={value}\n")
def save_keys(adam_key: str, eve_key: str) -> dict:
"""Save keys to .env. Does NOT enable live mode."""
env_vars = {}
if adam_key and adam_key.strip():
env_vars["NVIDIA_NIM_KEY_ADAM"] = adam_key.strip()
if eve_key and eve_key.strip():
env_vars["NVIDIA_NIM_KEY_EVE"] = eve_key.strip()
if not env_vars:
return {"status": "error", "message": "No keys provided"}
write_env(env_vars)
for key, value in env_vars.items():
os.environ[key] = value
return {
"status": "ok",
"message": "Keys saved to local .env only. Live mode NOT enabled.",
"adam_key_present": bool(adam_key),
"eve_key_present": bool(eve_key),
"provider_mode": "mock",
"live_mode_enabled": False,
}
def clear_keys() -> dict:
"""Clear keys from .env and environment."""
env_vars = read_env()
for key in SECRET_KEYS:
env_vars.pop(key, None)
os.environ.pop(key, None)
env_path = get_env_path()
with open(env_path, "w") as f:
f.write("# Genesis Kernel — Local Environment\n")
f.write("# NEVER commit this file to version control.\n\n")
for key in sorted(env_vars.keys()):
f.write(f"{key}={env_vars[key]}\n")
return {"status": "ok", "message": "Keys cleared.", "adam_key_present": False, "eve_key_present": False}
def get_key_status() -> dict:
"""Return key presence only — never key values."""
return {
"adam_key_present": bool(os.environ.get("NVIDIA_NIM_KEY_ADAM", "")),
"eve_key_present": bool(os.environ.get("NVIDIA_NIM_KEY_EVE", "")),
}
2. Add API endpoints
@app.get("/setup-keys")
def serve_setup_keys():
return FileResponse(PROJECT_ROOT / "frontend" / "setup-keys.html")
@app.get("/api/setup-keys")
def get_setup_keys_status():
"""Return key presence only — never key values."""
return get_key_status()
@app.post("/api/setup-keys")
def post_setup_keys(body: dict):
"""Save keys to .env. Does NOT enable live mode."""
return save_keys(body.get("adam_key", ""), body.get("eve_key", ""))
@app.post("/api/setup-keys/clear")
def post_clear_keys():
return clear_keys()
@app.get("/api/setup-keys/test")
def get_setup_keys_test():
"""Test dry-run with configured keys."""
return test_dry_run()
3. Create the accessible web form
frontend/setup-keys.html — requirements:
- Minimum font size 18px for all text
- High contrast — white text on dark background, bright accent colors
- Large text fields —
min-height: 60px, font-size: 1.2rem, monospace font for keys
- Large buttons —
min-height: 56px, font-size: 1.2rem, padding: 1rem 2rem
- Password input type — keys are masked in the form
- Clear security warning at the top
- Key status display — shows "Configured ✓" or "Not configured" for each agent
- Safety note — "Saving keys does NOT enable live mode"
- Three buttons: Save Keys Locally, Test Dry-Run, Clear Keys
4. Create the PowerShell fallback script
scripts/setup_keys_accessible.ps1:
# Large prompts — user pastes keys, presses Enter
Write-Host "Adam NVIDIA NIM Key: (paste or press Enter to skip)" -ForegroundColor White
$adamKey = Read-Host " "
Write-Host "Eve NVIDIA NIM Key: (paste or press Enter to skip)" -ForegroundColor White
$eveKey = Read-Host " "
# Confirmation before saving
Write-Host "Save these keys to local .env?" -ForegroundColor White
$null = Read-Host "Press Enter to save, or Ctrl+C to cancel"
# Write to .env
Write-EnvFile -AdamKey $adamKey -EveKey $eveKey
5. Verify /api/providers never leaks keys
The /api/providers endpoint must only return:
{
"agents": {
"Adam": {
"provider": "mock",
"model": "meta/llama-3.1-8b-instruct",
"key_present": true
}
}
}
Never return: key value, key preview, key prefix, key suffix.
6. Test key security
def test_keys_never_in_logs():
log_capture.clear()
test_key = "sk-test-key-log-leak-abcde"
save_keys(adam_key=test_key, eve_key="")
for log_entry in log_capture:
assert test_key not in log_entry
assert "abcde" not in log_entry
assert "sk-test" not in log_entry
clear_keys()
def test_keys_never_in_api():
save_keys(adam_key="sk-test-key-api-leak", eve_key="")
status = get_key_status()
for key, value in status.items():
assert "sk-test" not in str(value)
assert "api-leak" not in str(value)
clear_keys()
def test_mock_mode_stays_default():
result = save_keys(adam_key="sk-test", eve_key="")
assert result["provider_mode"] == "mock"
assert result["live_mode_enabled"] is False
clear_keys()
Key Rules
- Form-based, not CLI — large text fields, no command-line editing
- Password input type — keys masked in browser
- PowerShell fallback — for users who prefer terminal but need large prompts
- Safety lock — saving keys never enables live mode
- Presence-only API —
/api/setup-keys returns true/false, never key values
- No key previews — not even last 4 chars in the setup API
.env gitignored — confirmed in .gitignore
- Clear works — keys removed from both
.env and os.environ
- Dry-run test available — verify keys work without live calls
Anti-Patterns
- ❌ Requiring command-line editing of tiny values
- ❌ Showing key values or previews in any API response
- ❌ Enabling live mode when keys are saved
- ❌ Logging key values (even partially)
- ❌ Storing keys anywhere other than
.env
- ✅ Always use
key_present: true/false in API responses
- ✅ Always confirm before clearing keys
- ✅ Always show security warning on the setup page
- ✅ Provide both web form and PowerShell script options