Skip to content

IvanLi-CN/tavily-hikari

Repository files navigation

Tavily Hikari

Release CI Pipeline Rust Frontend Docs-zh

Tavily Hikari is a Rust + Axum proxy for Tavily's MCP endpoint. It multiplexes multiple API keys, anonymizes upstream traffic, stores full audit logs in SQLite, and ships with a React + Vite web console for realtime visibility.

Looking for the Chinese documentation? Check README.zh-CN.md.

Why Tavily Hikari

  • Key pool with fairness – SQLite keeps last-used timestamps and assigns each access token a short‑lived “home” key; new or expired affinities are resolved via least‑recently‑used selection across active keys to keep wear balanced.
  • Short IDs and secret isolation – every Tavily key receives a 4-char nanoid. The real token is only retrievable via admin APIs/UI.
  • Health-aware routing – status code 432 automatically marks keys as exhausted until the next UTC month or manual recovery.
  • High-anonymity forwarding – only /mcp traffic is tunneled upstream; sensitive headers are stripped or rewritten. See docs/high-anonymity-proxy.md.
  • Full audit trailrequest_logs persists method/path/query, upstream responses, error payloads, and the list of forwarded/dropped headers.
  • Operator UI – the SPA in web/ visualizes key health, request logs, and admin actions (soft delete, restore, reveal real keys).
  • CI + Release – GitHub Actions runs lint/tests; releases are driven by PR intent labels and publish ghcr.io/ivanli-cn/tavily-hikari:<tag> with prebuilt web assets.

Architecture Snapshot

Client → Tavily Hikari (Axum) ──┬─> Tavily upstream (/mcp)
                                ├─> SQLite (api_keys, request_logs)
                                └─> Web SPA (React/Vite)
  • Backend: Rust 2024 edition, Axum, SQLx, Tokio, Clap.
  • Data: SQLite single-file DB with api_keys + request_logs.
  • Frontend: React 18, TanStack Router, Tailwind CSS, shadcn/ui (Radix), Vite 5 (served from web/dist or via Vite dev server).

Quick Start

Local dev

# Start backend (high port recommended during dev)
cargo run -- --bind 127.0.0.1 --port 58087

# Optional: start SPA dev server
cd web && bun install --frozen-lockfile && bun run dev -- --host 127.0.0.1 --port 55173

# Register Tavily keys via admin API (ForwardAuth headers depend on your setup)
curl -X POST http://127.0.0.1:58087/api/keys \
  -H "X-Forwarded-User: admin@example.com" \
  -H "X-Forwarded-Admin: true" \
  -H "Content-Type: application/json" \
  -d '{"api_key":"key_a"}'

Visit http://127.0.0.1:58087/health for a health check or http://127.0.0.1:55173 for the console. Keys should be managed via the admin API or SPA instead of environment variables.

Docker

docker run --rm \
  -p 8787:8787 \
  -v $(pwd)/data:/srv/app/data \
  ghcr.io/ivanli-cn/tavily-hikari:latest

The container listens on 0.0.0.0:8787, serves web/dist, and persists data in /srv/app/data/tavily_proxy.db. Once it is up, register keys via the admin API/console.

Docker Compose

docker compose up -d

# Seed initial keys (requires ForwardAuth headers)
curl -X POST http://127.0.0.1:8787/api/keys \
  -H "X-Forwarded-User: admin@example.com" \
  -H "X-Forwarded-Admin: true" \
  -H "Content-Type: application/json" \
  -d '{"api_key":"key_a"}'

The stock docker-compose.yml exposes port 8787 and mounts a tavily-hikari-data volume. Override any CLI flag with additional environment variables if needed.

CLI Flags & Environment Variables

Flag / Env Description
--keys / TAVILY_API_KEYS Optional helper for bootstrapping or local experiments. In production, prefer the admin API/UI to manage keys.
--upstream / TAVILY_UPSTREAM Tavily MCP upstream (default https://mcp.tavily.com/mcp).
--bind / PROXY_BIND Listen address (default 127.0.0.1).
--port / PROXY_PORT Listen port (default 8787).
--db-path / PROXY_DB_PATH SQLite file path (default tavily_proxy.db).
--static-dir / WEB_STATIC_DIR Directory for static assets; auto-detected if web/dist exists.
--forward-auth-header / FORWARD_AUTH_HEADER Request header that carries the authenticated user identity (e.g., Remote-Email).
--forward-auth-admin-value / FORWARD_AUTH_ADMIN_VALUE Header value that grants admin privileges; leave empty to disable.
--forward-auth-nickname-header / FORWARD_AUTH_NICKNAME_HEADER Optional header for displaying a friendly name in the UI (e.g., Remote-Name).
--admin-mode-name / ADMIN_MODE_NAME Override nickname when ForwardAuth headers are missing.
--admin-auth-forward-enabled / ADMIN_AUTH_FORWARD_ENABLED Boolean switch to enable ForwardAuth checks (default true).
--admin-auth-builtin-enabled / ADMIN_AUTH_BUILTIN_ENABLED Boolean switch to enable built-in admin login (cookie session) (default false).
--admin-auth-builtin-password-hash / ADMIN_AUTH_BUILTIN_PASSWORD_HASH Built-in admin password hash (PHC string, recommended).
--admin-auth-builtin-password / ADMIN_AUTH_BUILTIN_PASSWORD Built-in admin password (deprecated; prefer password hash).
--dev-open-admin / DEV_OPEN_ADMIN Boolean flag to bypass admin checks in local/dev setups (default false).
--linuxdo-oauth-enabled / LINUXDO_OAUTH_ENABLED Enable Linux DO Connect OAuth2 login for end users (default false).
--linuxdo-oauth-client-id / LINUXDO_OAUTH_CLIENT_ID Linux DO OAuth2 client ID (connect.linux.do app).
--linuxdo-oauth-client-secret / LINUXDO_OAUTH_CLIENT_SECRET Linux DO OAuth2 client secret.
--linuxdo-oauth-authorize-url / LINUXDO_OAUTH_AUTHORIZE_URL OAuth2 authorize endpoint (default https://connect.linux.do/oauth2/authorize).
--linuxdo-oauth-token-url / LINUXDO_OAUTH_TOKEN_URL OAuth2 token endpoint (default https://connect.linux.do/oauth2/token).
--linuxdo-oauth-userinfo-url / LINUXDO_OAUTH_USERINFO_URL OAuth2 user profile endpoint (default https://connect.linux.do/api/user).
--linuxdo-oauth-scope / LINUXDO_OAUTH_SCOPE OAuth scope (default user).
--linuxdo-oauth-redirect-url / LINUXDO_OAUTH_REDIRECT_URL Callback URL on this service (for example https://tavily.ivanli.cc/auth/linuxdo/callback).
--user-session-max-age-secs / USER_SESSION_MAX_AGE_SECS End-user login cookie max age in seconds (default 1209600, 14 days).
--oauth-login-state-ttl-secs / OAUTH_LOGIN_STATE_TTL_SECS One-time OAuth state token TTL in seconds (default 600).

If --keys/TAVILY_API_KEYS is supplied, the database sync logic adds or revives keys listed there and soft deletes the rest. Otherwise, the admin workflow fully controls key state.

HTTP API Cheat Sheet

Method Path Description Auth
GET /health Liveness probe. none
GET /api/summary High-level success/failure stats and last activity. none
GET /api/keys Lists short IDs, status, and counters. Admin
GET /api/logs?page=1 Recent proxy logs (paginated, default 20 per page). Admin
POST /api/tavily/search Tavily /search proxy via Hikari key pool (Cherry Studio, etc.). Hikari token
POST /api/keys Admin: add/restore a key. Body { "api_key": "..." }. Admin
DELETE /api/keys/:id Admin: soft-delete key by short ID. Admin
GET /api/keys/:id/secret Admin: reveal the real Tavily key. Admin

Cherry Studio integration

Tavily Hikari also exposes a Tavily HTTP façade so Cherry Studio and other HTTP clients can talk to Tavily through Hikari’s key pool and per-token quotas instead of calling Tavily directly.

  • Base URL: https://<your Hikari host>/api/tavily
  • API key: Hikari access token th-<id>-<secret> created from the user dashboard

Cherry Studio setup:

  1. Create an access token (for example th-xxxx-xxxxxxxxxxxx) from the Tavily Hikari user dashboard and copy it.
  2. In Cherry Studio, open Settings → Web Search.
  3. Choose the provider Tavily (API key).
  4. Set API URL to https://<your Hikari host>/api/tavily (for local dev it is usually http://127.0.0.1:58087/api/tavily).
  5. Set API key to the Hikari access token from step 1 (the full th-xxxx-xxxxxxxxxxxx value), not your Tavily official API key.
  6. Optionally tune result count, answer/date options, etc.; Cherry Studio will pass these fields through to Tavily while Hikari rotates Tavily keys and enforces token quotas.

Do not put your Tavily API key directly into Cherry Studio. Always route traffic through Hikari by using its access token.

For the full HTTP proxy design and acceptance criteria, see docs/tavily-http-api-proxy.md.

Key Lifecycle & Observability

  • exhausted status is triggered automatically when upstream returns 432; scheduler skips those keys until UTC month rollover or manual recovery.
  • Each access token maintains a soft affinity to a single API key for a short time window. Within that window, the proxy prefers the same key when it remains active; when affinity expires or the key becomes exhausted/disabled, the next key is chosen by a global least‑recently‑used scheduler to keep load balanced across healthy keys. If all are disabled, the proxy falls back to the oldest disabled entries.
  • request_logs captures request metadata, upstream payloads, and dropped/forwarded header sets for postmortem analysis.
  • High-anonymity behavior (header allowlist, origin rewrite, etc.) is detailed in docs/high-anonymity-proxy.md.

ForwardAuth Integration

Tavily Hikari relies on a zero-trust/ForwardAuth proxy to decide who can operate admin APIs. Configure the following environment variables (or CLI flags) to match your identity provider:

export ADMIN_AUTH_FORWARD_ENABLED=true
export FORWARD_AUTH_HEADER=Remote-Email
export FORWARD_AUTH_ADMIN_VALUE=admin@example.com
export FORWARD_AUTH_NICKNAME_HEADER=Remote-Name
  • Requests must include the header defined by FORWARD_AUTH_HEADER. If its value equals FORWARD_AUTH_ADMIN_VALUE, the caller is treated as an admin and can hit /api/keys/* privileged endpoints.
  • FORWARD_AUTH_NICKNAME_HEADER (optional) is surfaced in the UI to show who is operating the console. When absent, the backend falls back to ADMIN_MODE_NAME (if provided) or hides the nickname.
  • For purely local experiments you can set DEV_OPEN_ADMIN=true, but never enable it in production.

Built-in Admin Login

If you cannot (or do not want to) run a ForwardAuth gateway, Tavily Hikari can expose a built-in admin login page backed by an HttpOnly cookie session.

export ADMIN_AUTH_BUILTIN_ENABLED=true
echo -n 'change-me' | cargo run --quiet --bin admin_password_hash
export ADMIN_AUTH_BUILTIN_PASSWORD_HASH='<phc-string>'
# Optional: disable ForwardAuth entirely if you are not using it.
export ADMIN_AUTH_FORWARD_ENABLED=false
  • When built-in login is enabled and the browser is not signed in, the public homepage shows an Admin Login button.
  • Successful login sets an HttpOnly cookie (hikari_admin_session) and unlocks admin-only APIs + /admin.
  • For production, prefer ForwardAuth. Built-in login is intended for small/self-hosted deployments.
    • Avoid storing plaintext passwords in env vars. Prefer ADMIN_AUTH_BUILTIN_PASSWORD_HASH (PHC string) and use a strong password.
    • Sessions are stored in-memory and expire server-side (aligned with cookie Max-Age, default 14 days). Restarting the process logs users out.
    • The in-memory session store is bounded (evicts oldest sessions when the cap is exceeded) to avoid unbounded growth.
    • If you terminate TLS at a reverse proxy, set X-Forwarded-Proto: https (or Forwarded: proto=https) so the backend can mark the session cookie as Secure.

Deployment example (Caddy as gateway): see examples/forwardauth-caddy/.

Linux DO OAuth Login (User Flow)

Tavily Hikari can expose Linux DO Connect OAuth2 login for regular users, independent from admin auth.

export LINUXDO_OAUTH_ENABLED=true
export LINUXDO_OAUTH_CLIENT_ID='<your-linuxdo-client-id>'
export LINUXDO_OAUTH_CLIENT_SECRET='<your-linuxdo-client-secret>'
export LINUXDO_OAUTH_REDIRECT_URL='https://tavily.ivanli.cc/auth/linuxdo/callback'
  • Homepage behavior:
    • When not logged in, area ① shows a Sign in with Linux DO button.
    • After login, area ① is hidden and area ② auto-fills the user's bound th-... token.
  • Token policy:
    • First Linux DO login automatically creates and binds one Hikari access token.
    • Later logins reuse the same binding; no extra token is created.
    • If the bound token is disabled/deleted, /api/user/token returns an error (404 or 409) and does not auto-regenerate.
  • New endpoints:
    • GET /auth/linuxdo
    • GET /auth/linuxdo/callback
    • GET /api/user/token
    • POST /api/user/logout

Frontend Highlights

  • Built with React 18, TanStack Router, shadcn/ui (Radix), Tailwind, Iconify.
  • Displays live key table, request log stream, and admin-only actions (copy real key, restore, delete).
  • scripts/write-version.mjs stamps the build version into the UI during CI releases.
  • bun run dev proxies /api, /mcp, and /health to the backend to avoid CORS hassle during development.

Screenshots

Operator and integration views of Tavily Hikari.

MCP Client Setup

MCP client setup in Codex CLI

Admin Dashboard

Admin overview with key table and metrics

User Dashboard

User dashboard showing monthly success, today count, key pool status, and recent requests

MCP Clients

Tavily Hikari speaks standard MCP over HTTP and works with popular clients:

Example (Codex CLI — ~/.codex/config.toml):

experimental_use_rmcp_client = true

[mcp_servers.tavily_hikari]
url = "https://<your-host>/mcp"
bearer_token_env_var = "TAVILY_HIKARI_TOKEN"

Then set the token and verify:

export TAVILY_HIKARI_TOKEN="<token>"
codex mcp list | grep tavily_hikari

Development

  • Rust toolchain pinned to 1.91.0 via rust-toolchain.toml.
  • Common commands: cargo fmt, cargo clippy -- -D warnings, cargo test --locked --all-features, cargo run -- --help.
  • Frontend (Bun, pinned via .bun-version): bun install --frozen-lockfile, bun run dev, bun run build (runs tsc -b + vite build).
  • Hooks: run lefthook install to enable automatic cargo fmt, cargo clippy, bunx dprint fmt, and bunx commitlint --edit on every commit.
  • CI: .github/workflows/ci.yml runs lint/tests/build.
  • Release: .github/workflows/release.yml runs after main CI succeeds and publishes tags, GitHub Releases, and GHCR images.

Release (PR labels)

Releases are label-driven:

  • Every PR must have exactly one intent label: type:patch, type:minor, type:major, type:docs, or type:skip.
  • Every PR must have exactly one channel label: channel:stable or channel:rc.
  • When a PR is merged into main and CI passes, the release workflow computes the next stable semver (X.Y.Z) and publishes:
    • Git tag + GitHub Release
    • GHCR image tags:
      • stable (channel:stable): latest, vX.Y.Z
      • prerelease (channel:rc): vX.Y.Z-rc.<sha7> (no latest)
  • If a commit cannot be mapped to exactly one PR, release is skipped (conservative default).

Deployment Notes

  1. Only expose /mcp, /api/*, and static assets; everything else returns 404.
  2. Protect admin APIs/UI via ForwardAuth or another zero-trust proxy so regular users never see real keys.
  3. Follow the header sanitization guidance in docs/high-anonymity-proxy.md when operating in high-anonymity environments.
  4. Persist tavily_proxy.db via volumes or external storage and export request_logs for compliance if needed.

License

Distributed under the MIT License. Keep the license notice intact when copying or distributing the software.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors