AnythingLLM extension: clarify API key vs nginx bearer, normalize Bearer prefix

**Motivations:**
- 403 No valid api key when users paste Ollama nginx secret into extension

**Root causes:**
- AnythingLLM validates keys only from its DB; nginx Bearer is unrelated

**Correctifs:**
- README and fixKnowledge doc; strip optional Bearer prefix in client

**Evolutions:**
- Extension version 0.1.1

**Pages affectées:**
- extensions/anythingllm-workspaces/*
- docs/fixKnowledge/anythingllm-extension-403-api-key.md
- docs/README.md
This commit is contained in:
Nicolas Cantu 2026-03-23 14:23:09 +01:00
parent b8f073ebf9
commit 564b9d5576
5 changed files with 55 additions and 3 deletions

View File

@ -14,6 +14,7 @@ Operational, architectural, and UX-design notes for the local-AI IDE initiative
| [../deploy/nginx/README-ia-enso.md](../deploy/nginx/README-ia-enso.md) | Proxy HTTPS `ia.enso.4nkweb.com` → Ollama / AnythingLLM (Bearer, script SSH, dépannage) | | [../deploy/nginx/README-ia-enso.md](../deploy/nginx/README-ia-enso.md) | Proxy HTTPS `ia.enso.4nkweb.com` → Ollama / AnythingLLM (Bearer, script SSH, dépannage) |
| [../extensions/anythingllm-workspaces/README.md](../extensions/anythingllm-workspaces/README.md) | Extension VS Code / Cursor : lister les workspaces AnythingLLM (API) et ouvrir lUI | | [../extensions/anythingllm-workspaces/README.md](../extensions/anythingllm-workspaces/README.md) | Extension VS Code / Cursor : lister les workspaces AnythingLLM (API) et ouvrir lUI |
| [features/anythingllm-vscode-extension.md](./features/anythingllm-vscode-extension.md) | Fiche évolution : extension AnythingLLM, impacts, modalités | | [features/anythingllm-vscode-extension.md](./features/anythingllm-vscode-extension.md) | Fiche évolution : extension AnythingLLM, impacts, modalités |
| [fixKnowledge/anythingllm-extension-403-api-key.md](./fixKnowledge/anythingllm-extension-403-api-key.md) | 403 API AnythingLLM : clé nginx Ollama vs clé UI API Keys |
| [features/ia-enso-nginx-proxy-ollama-anythingllm.md](./features/ia-enso-nginx-proxy-ollama-anythingllm.md) | Fiche évolution : objectifs, impacts, modalités du reverse proxy ia.enso | | [features/ia-enso-nginx-proxy-ollama-anythingllm.md](./features/ia-enso-nginx-proxy-ollama-anythingllm.md) | Fiche évolution : objectifs, impacts, modalités du reverse proxy ia.enso |
| [anythingllm-workspaces.md](./anythingllm-workspaces.md) | One AnythingLLM workspace per project; sync pipeline | | [anythingllm-workspaces.md](./anythingllm-workspaces.md) | One AnythingLLM workspace per project; sync pipeline |
| [ux-navigation-model.md](./ux-navigation-model.md) | Beyond file explorer: intentions, graph, palette, risks, expert mode | | [ux-navigation-model.md](./ux-navigation-model.md) | Beyond file explorer: intentions, graph, palette, risks, expert mode |

View File

@ -0,0 +1,43 @@
# AnythingLLM extension — 403 « No valid api key found »
**Author:** 4NK
## Symptôme
Commande **AnythingLLM: List workspaces** → erreur du type :
`AnythingLLM API 403: {"error":"No valid api key found."}`
## Cause
Le middleware amont `validApiKey` lit `Authorization`, extrait le jeton après `Bearer `, puis appelle `ApiKey.get({ secret })` sur la base AnythingLLM. Toute valeur absente de cette base produit la même réponse 403.
## Root cause fréquente
Confusion entre :
- le **secret Bearer nginx** utilisé pour `https://ia.enso.4nkweb.com/ollama/…` (documenté dans `deploy/nginx/README-ia-enso.md`) ;
- une **clé API AnythingLLM** créée dans lUI : **Settings → API Keys**.
Ce sont deux mécanismes indépendants. Le secret nginx nest **pas** enregistré comme clé API dans AnythingLLM.
## Correctifs côté utilisateur
1. Ouvrir lUI AnythingLLM (`anythingllm.baseUrl`).
2. **Settings → API Keys** : créer une clé si besoin, copier le secret affiché.
3. Coller ce secret dans `anythingllm.apiKey` (réglages **Utilisateur** de léditeur).
## Correctifs côté code / doc
- README de lextension : rappel explicite nginx vs clé AnythingLLM.
- Client : normalisation `normalizeApiSecret` — si lutilisateur a collé `Bearer <secret>`, le préfixe est retiré avant lenvoi (évite un jeton parsé comme `Bearer` par erreur).
## Modalités danalyse
- Vérifier la réponse HTTP brute (403 + corps JSON).
- Comparer la valeur configurée avec lorigine (fichier map nginx vs écran API Keys).
- Tester avec `curl` : `curl -sS -H "Authorization: Bearer <secret AnythingLLM>" "<baseUrl>/api/v1/workspaces"`.
## Modalités de déploiement
Redéployer / réinstaller lextension après modification du client (`npm run compile` ou nouveau `.vsix`). Aucun changement nginx requis pour ce diagnostic.

View File

@ -7,12 +7,14 @@ Minimal extension to call the **AnythingLLM developer API** and open a workspace
- AnythingLLM reachable at your public base URL (e.g. `https://ia.enso.4nkweb.com/anythingllm`). - AnythingLLM reachable at your public base URL (e.g. `https://ia.enso.4nkweb.com/anythingllm`).
- An **API key** created in AnythingLLM: **Settings → API Keys**. - An **API key** created in AnythingLLM: **Settings → API Keys**.
**Do not** use the **nginx Bearer secret** for `/ollama/` (see `deploy/nginx/README-ia-enso.md`). That value is only for the Ollama reverse proxy. AnythingLLM validates API keys against its **own** database; a wrong secret yields `403` with `{"error":"No valid api key found."}`.
## Configuration ## Configuration
| Setting | Description | | Setting | Description |
|--------|-------------| |--------|-------------|
| `anythingllm.baseUrl` | Base URL without trailing slash (default matches `deploy/nginx/README-ia-enso.md`). | | `anythingllm.baseUrl` | Base URL without trailing slash (default matches `deploy/nginx/README-ia-enso.md`). |
| `anythingllm.apiKey` | Bearer token for `GET /api/v1/workspaces`. Use **User** settings to avoid committing secrets. | | `anythingllm.apiKey` | Secret from AnythingLLM **Settings → API Keys** (optional leading `Bearer ` is stripped). Use **User** settings to avoid committing secrets. |
## Commands ## Commands

View File

@ -2,7 +2,7 @@
"name": "anythingllm-workspaces", "name": "anythingllm-workspaces",
"displayName": "AnythingLLM Workspaces (ia.enso)", "displayName": "AnythingLLM Workspaces (ia.enso)",
"description": "List AnythingLLM workspaces via your proxied instance (e.g. ia.enso.4nkweb.com/anythingllm).", "description": "List AnythingLLM workspaces via your proxied instance (e.g. ia.enso.4nkweb.com/anythingllm).",
"version": "0.1.0", "version": "0.1.1",
"publisher": "4nk", "publisher": "4nk",
"license": "MIT", "license": "MIT",
"engines": { "engines": {

View File

@ -49,12 +49,18 @@ const parseListWorkspaces = (payload: unknown): readonly AnythingWorkspace[] =>
return workspaces; return workspaces;
}; };
const normalizeApiSecret = (raw: string): string => {
const trimmed = raw.trim();
const bearerPrefix = /^Bearer\s+/i;
return bearerPrefix.test(trimmed) ? trimmed.replace(bearerPrefix, "").trim() : trimmed;
};
export const listWorkspaces = async ( export const listWorkspaces = async (
baseUrl: string, baseUrl: string,
apiKey: string, apiKey: string,
): Promise<readonly AnythingWorkspace[]> => { ): Promise<readonly AnythingWorkspace[]> => {
const normalized = normalizeAnythingLlmBaseUrl(baseUrl); const normalized = normalizeAnythingLlmBaseUrl(baseUrl);
const key = apiKey.trim(); const key = normalizeApiSecret(apiKey);
if (key.length === 0) { if (key.length === 0) {
throw new Error("anythingllm.apiKey is empty"); throw new Error("anythingllm.apiKey is empty");
} }