tools.manage_api_keys module
Per-user API key management + global/channel API key pools (v4)
Per-user keys follow the user across channels and servers. Pool keys are shared – either globally or scoped to a single channel.
Resolution order: user key -> channel pool -> global pool -> env/config
- Redis key patterns:
stargazer:user_api_keys:{user_id} (per-user hash) stargazer:global_api_pool:{service} (global pool hash) stargazer:channel_api_pool:{channel_id}:{service} (channel pool hash)
API keys are encrypted at rest (AES-256-GCM) with per-user keys stored in SQLite.
- tools.manage_api_keys.missing_api_key_error(service)[source]
Return a verbose, actionable error message for a missing API key.
Intended to be called by consumer tools when
get_user_api_keyreturnsNone. The message includes the service display name, signup URL (when known), and the exactset_user_api_keycommand so the LLM can relay it to the user.
- async tools.manage_api_keys.check_default_key_limit(user_id, tool_name, redis_client, daily_limit=20)[source]
Check whether a user is within the daily shared-key usage limit.
Reads the per-user, per-tool counter at
stargazer:default_key_usage:{user_id}:{tool_name}so tools that fall back to a shared (non-user-supplied) API key can cap free usage. The check is fail-open: any missing Redis client, missing counter, or Redis error is treated as “allowed” so a Redis hiccup never blocks legitimate calls. Reads Redis only;increment_default_key_usageperforms the matching write.Called by many sibling tools that consume shared keys – e.g.
tools/generate_image.py,tools/brave_search.py,tools/research_tool.py,tools/play_music.py,tools/youtube_describe.py– typically guarded bydefault_key_limit_applies.- Parameters:
- Returns:
(allowed, current_count, daily_limit).- Return type:
- async tools.manage_api_keys.increment_default_key_usage(user_id, tool_name, redis_client)[source]
Bump the daily shared-key usage counter after a successful call.
Records one use of a shared (non-user-supplied) API key by incrementing the per-user, per-tool counter at
stargazer:default_key_usage:{user_id}:{tool_name}and (re)setting its TTL to_DEFAULT_KEY_TTL(24 hours), so the limit window rolls forward and the counter self-expires. Issues the INCR and EXPIRE in a single Redis pipeline; a missing client is a no-op and Redis errors are logged but swallowed so counting never breaks the tool that just succeeded. The companion read ischeck_default_key_limit.Called by the same shared-key tools after they complete a call – e.g.
tools/generate_image.py,tools/brave_search.py,tools/research_tool.py,tools/play_music.py,tools/youtube_describe.py.
- tools.manage_api_keys.default_key_limit_error(tool_name, current, limit)[source]
Build the user-facing message shown when the shared-key limit is hit.
Produces a verbose, actionable string a tool returns to the LLM once
check_default_key_limitreports the user is over quota: it states the current count and limit and tells the user to set their own (unlimited) key viaset_user_api_key. Pure string formatting with no I/O.Called by shared-key tools when the daily limit is exceeded – e.g.
tools/generate_veo_video.py,tools/brave_search.py,tools/research_tool.py,tools/play_music.py,tools/youtube_describe.py.
- async tools.manage_api_keys.default_key_limit_applies(ctx, using_own_key=False)[source]
Return True if the shared default-key daily limit should be enforced.
Returns False (i.e. limit is not enforced) when any of these hold: -
using_own_keyis True (caller supplied their own API key) -ctxis None, orctx.user_id/ctx.redisare missing - The calling user is a bot admin (config.admin_user_ids) - The calling user has theBYPASS_RATELIMITprivilege bit setAll other callers are subject to the limit. Safe to call multiple times per request (one lightweight Redis GET per call).
- async tools.manage_api_keys.get_gitea_credentials(user_id, *, redis_client=None, channel_id=None, fallback_to_pool=True, config=None)[source]
Return (token, base_url) for Gitea, or None.
Resolution order: user key -> channel pool -> global pool -> GITEA_TOKEN -> GITEA_TOKEN_FILE (default
GITEA_TOKEN_FILE_DEFAULT).Parses plain token or JSON {“token”: “…”, “base_url”: “…”}. Env/file fallbacks use
GITEA_BASE_URL(default https://git.neko.li) when the raw value is a plain token; JSON may override withbase_url.
- async tools.manage_api_keys.get_pool_api_key(service, *, redis_client=None)[source]
Return a random shared key from the global pool for a service.
Public helper used during key resolution: it names the global pool hash via
_pool_key(service)and draws a decrypted key through_get_pool_api_key_impl. ReturnsNone(rather than raising) when no Redis client is supplied or anything fails, logging the failure so a degraded pool never breaks the calling tool. Reads Redis only.Called by
get_user_api_keyin this module as the final fallback in the resolution chain; not invoked directly by other modules.
- async tools.manage_api_keys.get_channel_pool_api_key(channel_id, service, *, redis_client=None)[source]
Return a random shared key from a channel’s pool for a service.
Public helper used during key resolution: it names the channel-scoped pool hash via
_channel_pool_key(channel_id, service)and draws a decrypted key through_get_pool_api_key_impl. ReturnsNone(rather than raising) when no Redis client or channel id is supplied or anything fails, logging the failure so a degraded pool never breaks the calling tool. Reads Redis only.Called by
get_user_api_keyin this module, preferred over the global pool when a channel id is in scope; not invoked directly by other modules.