Let a sign-in reach a containerised MCP server - #92
Merged
Conversation
Under ToolHive the server read its credentials from FIBER_SECRETS once at startup, so a container held whatever was true when it began: you signed in, the keychain got the new token, and the server went on presenting the expired one until someone re-exported the secrets and replaced the workload. Credentials can now travel through the collections directory the container already mounts. The app rewrites that file whenever a credential changes and the server re-reads it, so the next call picks the new value up. The file is sealed with XChaCha20-Poly1305 and the key stays out of the mount — keychain on the app's side, ToolHive's encrypted store on the container's — so a copy of it alone is inert. Its existence is the opt-in: toolhive.sh creates it, the app writes to it only if it is already there, and deleting it opts back out. Bearer collections needed a second fix to benefit. A static token cannot be refreshed by replaying a request, so a 401 never dropped it, and a zero-TTL cache entry has nothing else to expire it — a container would have presented its startup token for the life of the workload even with a live file. A rejected credential is now dropped whenever it came from a source that can change underneath the process. The desktop app is unaffected: it has no such source, and the same line there would cost a keychain prompt per 401.
Merged
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.
Under ToolHive the MCP server read its credentials from
FIBER_SECRETSonce at startup, so a container held whatever was true when it began — you signed in, the keychain got the new token, and the server went on presenting the expired one until someone re-exported the secrets and replaced the workload.Credentials now travel through the collections directory the container already mounts: the app rewrites that file whenever a credential changes, the server re-reads it, and the next call picks the new value up. The file is sealed with XChaCha20-Poly1305 and the key stays out of the mount — keychain on the app's side, ToolHive's encrypted store on the container's — so a copy of it alone is inert; its existence is the opt-in, so a desktop-only install never has credentials on disk.
Bearer collections needed a second fix to benefit, since a static token cannot be refreshed by replaying a request and so a 401 never dropped it — a rejected credential is now dropped whenever it came from a source that can change underneath the process, which leaves the desktop app untouched. Adds
fiber mcp file-keyandfiber mcp export-secrets --to <path>, wired up byscripts/toolhive.sh.Verified with 146 passing tests (7 new, including an end-to-end one that fails with a 401 when the fix is reverted), clippy and fmt clean on both the GUI and
--no-default-featuresbuilds; a real ToolHive container was not run, since that needs Docker plusthvand writes a key to the developer's actual keychain.