feat(web): in-page API key settings (RFC-0029 revision, loopback-gated) - #153
Merged
Conversation
Maintainer-approved plan B for 0.3.0: the console previously accepted
only env:VAR references in wire.rs resolve_api_key, forcing a shell
export before first use. This adds a Settings page with provider
selection and a key entry point.
Backend:
- settings.rs KeyStore: per-provider keys in memory by default,
explicit remember=true persists to ~/.config/aimux-web/keys.json
(%APPDATA% on Windows) with 0600 perms, loaded at startup
- api/settings.rs: GET /api/settings/keys (masked hints only, last 4
chars), PUT /api/settings/keys {provider, key, remember?},
DELETE /api/settings/keys/{provider}
- loopback gating: bind host stored in AppState; non-loopback binds
get 403 on PUT/DELETE and plaintext_entry:false on GET
- resolve_api_key now takes the provider name + KeyStore from all
three callers (calls/cache_probe/replay): explicit spec (env: ref,
or plaintext on loopback only) > stored key > provider env var
Frontend: Settings view (route /settings, nav entry) with provider
combobox, masked key list, remember switch; key-field hints in
Playground/Replay/CacheProbe point at Settings.
Docs: RFC-0029 decision table D4 + new §5.5 (threat model, priority);
README quick start makes env export optional.
No plaintext in logs, responses, or recordings; no new dependencies.
- empty/whitespace key spec resolves as unset (stored key / provider env fallback) instead of an empty key - mask threshold raised to >8 chars so a short key never leaks in full through its own tail - disk persistence writes via temp + rename — concurrent PUT(remember) snapshots interleave outside the lock and could tear the JSON in place - DELETE trims the provider path segment, matching PUT
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.
动机
原设计要求用户先在 shell
export再启动控制台——对下载即用的本地工具是最/frustrating 的第一步。同类产品(OpenWebUI/LM Studio/各官方 Playground)均为网页内设置。设计(安全模型)
~/.config/aimux-web/keys.json(0600,temp+rename 原子写,同目录无 EXDEV);启动加载,损坏文件静默忽略--host 0.0.0.0/LAN)时 PUT/DELETE 403、请求内明文维持拒绝、GET 返回plaintext_entry:false供前端禁用输入框——原决策担心的远程录入面被精确挡住env:VAR> 请求明文(仅回环)> Settings 已存 key > provider 注册默认 env varAPI
GET /api/settings/keys(掩码列表 + plaintext_entry)·PUT /api/settings/keys {provider,key,remember?}·DELETE /api/settings/keys/{provider}文档
RFC-0029 决策表 D4 修订 + §5.5 Settings 与凭据(威胁模型);README 快速开始改为双路径(网页内 or env)。
评审
两轮独立评审:R1 安全核心全过(无明文泄漏面/回环判定 fail-closed/三调用方一致),4 条低危(空串视同 unset、掩码阈值 8、原子写、DELETE trim)已修;R2 CLEAN 的 3 个小尾巴(空分支测试、过时注释、tmp 并发碰撞)亦已修。22 测试全过,clippy -D warnings 干净,前端 build+vue-tsc 过,curl 真机冒烟(掩码/403/重启加载/0600)通过。