Skip to content

Add CharaVault provider; optional gateway base URL for ChubAI/CharacterTavern - #18

Open
AdamsGH wants to merge 7 commits into
Sillyanonymous:mainfrom
AdamsGH:charavault-provider
Open

Add CharaVault provider; optional gateway base URL for ChubAI/CharacterTavern#18
AdamsGH wants to merge 7 commits into
Sillyanonymous:mainfrom
AdamsGH:charavault-provider

Conversation

@AdamsGH

@AdamsGH AdamsGH commented May 9, 2026

Copy link
Copy Markdown

What

Adds a new online provider (CharaVault) and lets the existing ChubAI / CharacterTavern providers route through a self-hosted gateway when the user opts in.

Six commits, each self-contained:

  1. provider-utils: fall back to /proxy/ on 401/403/451 - widens fetchWithProxy to retry through ST /proxy/ on origin-shaped rejections, not just network errors. Generic; useful for any provider hitting a hostile upstream. Later commit narrows this back to 403/451 only after testing showed 401 is genuine auth-fail and should propagate.
  2. chub: route writes through fetchWithProxy, use CHUB_AVATAR_BASE - the chub provider already used fetchWithProxy for read paths but not for follow / unfollow / timeline / favorites / import. Behind a regional block those calls 401/403/451 and there is no retry. Routes every chub.ai fetch through fetchWithProxy so the new fallback applies uniformly. Drive-by: replace two inline https://avatars.charhub.io/avatars/ strings with the existing CHUB_AVATAR_BASE constant they already paralleled.
  3. Add CharaVault provider - new modules/providers/charavault/ provider for charavault.net. Supports browse + search (NSFW + lorebook filters), tag filtering, the visually-similar widget the site backs with image hashes, linked lorebooks, and full V2-card import. Endpoints default to charavault.net direct. Two optional settings (charavaultGatewayUrl, charavaultGatewayKey) let users proxy CharaVault. App-password is its own optional setting (charavaultAppPassword); the public anonymous tier works for read paths.
  4. chub, chartavern: optional gateway base URL and key - ChubAI and CharacterTavern each split their service across multiple upstream hosts (api.chub.ai + gateway.chub.ai + avatars.charhub.io; character-tavern.com/api + the public site + cards.character-tavern.com). Replaces the module-load constants with runtime helpers that read from settings each call. Defaults still point at the real hosts; out-of-the-box behaviour is unchanged. For users who proxy through a self-hosted gateway, a single setting (chubGatewayBaseUrl / chartavernGatewayBaseUrl) is enough; helpers append the conventional path (/v1/chub, /v1/ct, etc.) for each host. Per-host overrides remain available for non-standard topologies. Settings UI exposes Base URL + Bearer Key by default with the per-host overrides tucked into an "Advanced" details element.
  5. chartavern: send Authorization on gateway-routed fetch; restore CharaVault favicon - small follow-up after exercising the integration end-to-end: pass the gateway Bearer key as Authorization on chartavern's ctFetch / fetchTopTags, restore the iconUrl getter on the CharaVault provider (built from getCvCdnBase() so it follows the gateway override), and stop the provider-utils fallback from retrying on 401 (genuine auth-fail).
  6. link reference proxy implementation in gateway settings hints - one-line addition to the three new "Gateway (optional)" sections pointing at AdamsGH/charlib-proxy, a small Node + Express passthrough that already exposes the path conventions baked into each provider's helper. Optional reading material for users who want to actually run a proxy.

API quirks worth knowing (CharaVault)

Discovered while writing the provider; preserved as comments in the code:

  • /api/cards/similar takes path= as the absolute server-side disk path stored in entry.path, not the folder/file form the public docs suggest. The folder/file form returns 404 for every card.
  • The search-list entry carries has_lorebook; the per-card detail entry does not. The import path threads the search row through buildCvCharacterCard so the embedded extension metadata preserves the flag.

Testing

Built and exercised the new provider end-to-end against both the direct upstreams and a self-hosted proxy (charlib-proxy):

  • ChubAI search / import / favorites / follow - through both direct and gateway.
  • CharacterTavern search / detail / import - through both direct and gateway.
  • CharaVault browse / preview / find-similar / linked-lorebooks / import.
  • CORS preflight + auth-bearer enforcement on the proxy side.

No changes to any other provider's behaviour.

Settings added (defaults are all empty / null)

charavaultAppPassword
charavaultGatewayUrl
charavaultGatewayKey

chubGatewayBaseUrl
chubGatewayApiUrl       # advanced
chubGatewayGatewayUrl   # advanced
chubGatewayAvatarUrl    # advanced
chubGatewayKey

chartavernGatewayBaseUrl
chartavernGatewayApiUrl  # advanced
chartavernGatewaySiteUrl # advanced
chartavernGatewayCdnUrl  # advanced
chartavernGatewayKey

AdamsGH added 7 commits May 9, 2026 18:11
fetchWithProxy currently only retries through ST's /proxy/ on a thrown
network error. Some upstreams (chub.ai is one example) accept the CORS
preflight but reject the browser Origin/Referer with 401, 403 or 451 on
the actual request. Treat those statuses the same way as a network
failure: remember the origin and retry through /proxy/, which strips
Origin and Referer.

Other non-2xx statuses still propagate as before.
The chub provider already used fetchWithProxy for read paths (search,
metadata) but not for the follow/unfollow/timeline/follows-list/import
flows. Behind a regional block these calls fail because the direct fetch
returns 401/403/451 and there is no retry. Route every chub.ai fetch
through fetchWithProxy so the new fallback in provider-utils.js applies
uniformly.

Drive-by: replace the two inline `https://avatars.charhub.io/avatars/`
strings with the existing CHUB_AVATAR_BASE constant they were already
parallelling.
Adds charavault.net as an Online tab provider. Supports browsing and
search (with NSFW and lorebook filters), tag filtering, the visually
similar widget that the site backs with image hashes, linked lorebooks,
and full V2-card import.

Endpoints default to charavault.net direct. Two optional settings exist
for users who proxy CharaVault through a self-hosted gateway:

  charavaultGatewayUrl  - replaces the API base
  charavaultGatewayKey  - sent as Authorization: Bearer

There is also a charavaultAppPassword setting (X-App-Password header)
for users with their own app password; without it the provider uses the
public anonymous tier (lower rate limit, downloads disabled for some
cards). Empty by default, no built-in fallback.

Notes on two API quirks worth knowing:

  - /api/cards/similar takes path= as the absolute server-side disk
    path stored in entry.path, not the folder/file form the public docs
    suggest. The folder/file form returns 404 for every card.
  - The search-list entry carries has_lorebook; the per-card detail
    entry does not. The import path threads the search row through so
    the embedded extension metadata preserves the flag.
ChubAI and CharacterTavern each split their service across multiple
upstream hosts:

  chub        api.chub.ai, gateway.chub.ai, avatars.charhub.io
  chartavern  character-tavern.com (api + site), cards.character-tavern.com

The previous code hardcoded these as module-load constants. Replace them
with runtime helpers that read from settings each call. Defaults still
point at the real hosts, so out-of-the-box behaviour is unchanged.

For users who proxy through a self-hosted gateway, a single setting per
provider is enough: chubGatewayBaseUrl / chartavernGatewayBaseUrl. The
helpers append the conventional path (/v1/chub, /v1/ct, etc.) for each
host. Per-host overrides remain available for non-standard topologies.

  chubGateway{Base,Api,Gateway,Avatar}Url
  chartavernGateway{Base,Api,Site,Cdn}Url
  chubGatewayKey, chartavernGatewayKey

When chubGatewayKey is set, getChubHeaders() uses it as Authorization
and forwards the user's chub.ai token via X-Chub-Token so the gateway
can re-attach it upstream.

Settings UI exposes Base URL and Bearer Key by default, with the
per-host overrides tucked into an "Advanced" details element.
…Vault favicon

Three small fixes after exercising the gateway integration:

1. chartavern-api: ctFetch and fetchTopTags now pass the gateway Bearer
   key as Authorization. Without this, every search through a self-hosted
   gateway hit the gateway's auth wall and returned 401.

2. charavault-provider: restore the iconUrl getter (charavault.net does
   serve a favicon.ico, and the provider dropdown looks broken without
   one). Built from getCvCdnBase() so it follows the gateway override.

3. provider-utils.fetchWithProxy: stop retrying through ST /proxy/ on
   401. 401 is a genuine auth failure and should propagate to the
   caller. Only 403 and 451, which signal origin-shaped rejection by
   the upstream, still trigger the proxy fallback.
The three providers that grew "Gateway (optional)" sections in the
previous commits leave it to the user to figure out what kind of
proxy goes in there. Add a one-liner pointing at charlib-proxy
(github.com/AdamsGH/charlib-proxy), a small Node + Express passthrough
that already exposes the path conventions baked into each provider's
helper.
The CV grid card was using its own minimal layout (info+meta) that
none of the shared CSS in browse-shared.css actually targeted, so the
card looked anaemic next to chub's: no NSFW badge, no tag chips, no
footer row of stats, no indexed date.

Switch to the same shape every other provider uses: image-with-badges
on top, body with name + creator-link + up to three tag chips, footer
row with rating / tokens / downloads / date. The new fields all come
from the existing search result (tags, nsfw, avg_rating, rating_count,
download_count, indexed_at, description_preview) so no API changes.
@Sillyanonymous

Copy link
Copy Markdown
Owner

Hey thanks for the PR, clearly a lot of work in here. I have to be upfront though, I don't have capacity to review something this size properly rigth now. Got a lot of uncommitted stuff locally and more in the pipeline.

Not closing it, happy to come back to it when things settle. Appreciate you putting it together.

@AdamsGH

AdamsGH commented May 12, 2026

Copy link
Copy Markdown
Author

Sure, take your time. Will be glad if it help sometime. Btw, if needed - you can fully take proxy code, i wrote it for example only

@DJLegends1011

Copy link
Copy Markdown

dam i wanted to do this as well but had issues nice to see that it works!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants