Skip to content

feat: configure API keys from the admin panel (Developer Settings) - #87

Open
jsnapoli1 wants to merge 1 commit into
AJFrio:mainfrom
jsnapoli1:feat/developer-settings
Open

feat: configure API keys from the admin panel (Developer Settings)#87
jsnapoli1 wants to merge 1 commit into
AJFrio:mainfrom
jsnapoli1:feat/developer-settings

Conversation

@jsnapoli1

Copy link
Copy Markdown

Problem

Setting up a store means running wrangler secret put for every key. That's a poor handoff for a shop owner who just wants to turn on AI images or connect Stripe — it requires a terminal, the Wrangler CLI, and knowing the key names.

Approach

A Developer Settings page for STRIPE_SECRET_KEY, GEMINI_API_KEY, OPENROUTER_API_KEY, OPENROUTER_MODEL, SITE_URL, and the admin password.

A Worker cannot write its own secrets. env is injected per request and is read-only; changing a Cloudflare secret needs an account-scoped API token that has no business living inside a store. So values go to KV, and every runtime read goes through resolveSetting(), which checks KV first, env second.

That ordering is deliberate. If env won, a value typed into the UI would be silently shadowed by a stale binding — the setting would appear to save and do nothing.

Clearing a field deletes the KV entry and restores the env value. That's also the recovery path if a bad value is ever saved:

wrangler kv key delete developer:settings --namespace-id <id> --remote

The admin password

This one needs justifying, because 295ee74 ("authenticate admin login against ADMIN_PASSWORD secret") deliberately moved auth onto the binding and away from KV. Re-opening that door isn't something to do casually, so it's constrained:

  • Stored salted and hashed (never recoverable plaintext), unlike the old KV password_hash this replaces
  • Changing it requires the current password, so an unattended admin session can't be used to take the store over
  • DELETE /password drops the override and falls back to the binding
  • It can't be set through the generic settings route (returns 400)
  • Login falls back to env whenever no KV password is stored, so existing deploys are untouched

If you'd still rather the password stay binding-only, I'm happy to drop that part and keep the rest — the API keys are the bulk of the UX win.

Security notes

  • Secret values are write-only over the API. Responses report only whether a key is set and where it came from, never the value. A test asserts the raw secret never appears in a response body.
  • This does move secrets from Cloudflare's encrypted secret store into KV, which is a real reduction in protection. It's a deliberate trade for setup usability, and env remains available for anyone who prefers it — a store that never opens the page behaves exactly as today.

Verification

Adds tests/integration/developer-settings.test.js (9 cases): KV-over-env precedence, fallthrough on clear, password change end-to-end (new password works and old env password stops working), the recovery path, and that plaintext is never persisted.

Test Files  24 passed (24)
     Tests  287 passed (287)

npm run lint — 0 errors. Build clean; verified the admin bundle contains the new page and has no unresolved references.

Notes

https://claude.ai/code/session_015XLFFfHWNeazSuz6UcsQ4C

Setting up a store currently means running `wrangler secret put` for
every key. That is a poor handoff for a shop owner who just wants to
turn on AI images or connect Stripe.

Adds a Developer Settings page for STRIPE_SECRET_KEY, GEMINI_API_KEY,
OPENROUTER_API_KEY, OPENROUTER_MODEL, SITE_URL and the admin password.

A Worker cannot write its own secrets — `env` is injected per request and
is read-only, and changing a Cloudflare secret needs an account-scoped
API token that has no business inside a store. So values are stored in
KV, and every runtime read goes through resolveSetting(), which checks
KV before `env`. A value set in the UI therefore genuinely takes effect
rather than being shadowed by a stale binding.

Clearing a field deletes the KV entry and restores the `env` value. That
is also the recovery path if a bad value is saved:

    wrangler kv key delete developer:settings --namespace-id <id> --remote

The admin password gets extra handling, since 295ee74 deliberately moved
auth onto the ADMIN_PASSWORD binding:

- stored salted-and-hashed, never as recoverable plaintext
- changing it requires the current password, so an unattended session
  cannot be used to take the store over
- DELETE drops the override and falls back to the binding
- it cannot be set through the generic settings route

Secret values are write-only over the API: responses report only whether
a key is set and where it came from, never the value.

Adds tests/integration/developer-settings.test.js (9 cases), covering
KV-over-env precedence, fallthrough on clear, the recovery path, and
that plaintext is never persisted. Full suite: 287 passing.

Claude-Session: https://claude.ai/code/session_015XLFFfHWNeazSuz6UcsQ4C
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