mirror of
https://github.com/rehan-remade/universal-modder.git
synced 2026-11-05 01:37:40 +00:00
[PR #281] [MERGED] fal: um fal key stores the API key in the OS keyring (Linux) or an owner-only file #280
Labels
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
rehan-remade/universal-modder#280
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/rehan-remade/universal-modder/pull/281
Author: @Selene0623
Created: 10/13/2026
Status: ✅ Merged
Merged: 10/14/2026
Merged by: @rehan-remade
Base:
main← Head:fal-keyring📝 Commits (8)
1f62a8bdocs(agents): note render3d needs Blender on PATH15e9f78skills: add game-research-websearch645fdf7fal: keep the API key in the OS keyring8d41344fal: review fixes for the keyring key storeb6850c8Merge remote-tracking branch 'origin/main' into merge/pr-28137c235cfal: read secret-tool output as UTF-8 (text_utf8 from #276)ca542ffSECURITY.md: um fal also reads the keyum fal key setstored37f9e96Merge remote-tracking branch 'origin/main' into land/pr-281📊 Changes
14 files changed (+441 additions, -52 deletions)
View changed files
📝
.agents/skills/fal-assets/SKILL.md(+19 -8)📝
.claude/skills/fal-assets/SKILL.md(+19 -8)📝
.codex/config.toml(+2 -0)📝
AGENTS.md(+4 -1)📝
CLAUDE.md(+4 -0)📝
README.md(+9 -2)📝
SECURITY.md(+2 -1)📝
examples/aoe2-de-civ/README.md(+4 -4)📝
examples/aoe2-de-civ/assets/gen.sh(+2 -2)📝
examples/terraria-tmodloader/README.md(+1 -1)📝
examples/terraria-tmodloader/assets/make_art.sh(+1 -1)📝
skills/fal-assets/SKILL.md(+19 -8)➕
tests/test_fal_key.py(+146 -0)📝
um/fal.py(+209 -16)📄 Description
fal: keep the API key in the OS keyring, authenticate MCP with OAuth
um fallooked for FAL_KEY in the environment, then a plaintext~/.secrets/fal-key, then a .env — and the fal MCP server that ships with
the repo was configured the same way, with
Authorization: Bearer $FAL_KEYin every agent config. That pushes a long-lived secret into ashell profile or a dotfile that agents read, which is the leak this
change removes.
Now:
um fal key set|status|migrate|clearstores the key with libsecret(KWallet / gnome-keyring) and only writes a plaintext file when asked
(
--file-only, mode 600). Resolution order is FAL_KEY (CI) → OSkeyring → plaintext fallback → .env, and a fallback prints a warning.
OAuth flow instead (
"oauth": true), so no key is stored anywhere.Each client authenticates once:
opencode mcp auth fal,/mcp→Authenticate (Claude Code),
codex mcp login fal.export FAL_KEY=....Why this shape, and not the environment variable the docs suggest:
initializereturnswww-authenticate: Bearer realm="OAuth", resource_metadata="https://mcp.fal.ai/.well-known/ oauth-protected-resource/mcp", withauthorization_servers: ["https://auth.fal.ai"]. Sentry, Linear and Notion answer the sameway, so OAuth is what remote MCP servers use, and the client ends up
holding a rotating refresh token instead of a pasted bearer.
OAuth by default, credentials kept outside the project config
(
opencode mcp auth <name>), with{env:NAME}as the substitutionmechanism rather than a shell
$NAME(https://opencode.ai/v2/docs/mcp-servers).
somewhere. The desktop keyring is the freedesktop way to do it:
secret-toolis the standard libsecret front end, the same storegit-credential-libsecretand other CLI tools use. It keeps thesecret out of the repo, out of
.env, out of shell history and outof any file an agent can read, and it survives reboots without a
daemon of our own.
Nothing here changes the fal MCP server or the models;
um falbehavesexactly as before once a key is present.
Tested:
uv run --with pytest pytest -q tests(63 passed, 1 skipped),./bin/um publish check .(0 failures), and a keyring store/read-backround-trip plus
--file-onlypermission check on Linux/KWallet.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.