Latest commit

History

17,828 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Open WebUI + Extra

This is Open WebUI with an Extra assistant embedded in it, wired to act as whoever is signed in rather than through one shared admin key. Everything Extra-specific lives in extra/; the rest is upstream Open WebUI (its own README).

Run it

Open WebUI — two terminals, per the official dev guide:

# terminal 1 — frontend
cp -RPp .env.example .env
npm install
npm run build
npm run dev
# terminal 2 — backendcd backend
cp .env.example .env # dev.sh needs WEBUI_SECRET_KEY set; unlike start.sh it won't generate one
python3 -m venv venv &&source venv/bin/activate
pip install -r requirements.txt -U
sh dev.sh

Frontend at http://localhost:5173, backend at http://localhost:8080.

Extra — set up extra/.env per the Environment section below, then from a clone of extra-org/extra:

make dev AGENTS=<path-to-this-repo>/extra/agents.yml ENV_FILE=<path-to-this-repo>/extra/.env

Or with Docker instead of a local extra checkout:

docker run -d -p 8100:8100 \
-v <path-to-this-repo>/extra:/workspace -w /workspace \
--env-file <path-to-this-repo>/extra/.env \
ghcr.io/extra-org/extra:latest \
agent-manager --config agents.yml --port 8100

Playground at http://localhost:8100/playground. Sign into Open WebUI and the assistant appears there too.

Environment

cp extra/.env.example extra/.env

Everything below is already filled in except the model key — open extra/.env and add that, and you're running:

ANTHROPIC_API_KEY=your-key-here # or see the Ollama comment for a free local model instead
OPEN_WEBUI_URL=http://localhost:8080
AGENT_AUTH_MODE=mint
AGENT_AUTH_SECRET=pLiUxoi+ziwTUIhNhVg/AN1U50UMom00
AGENT_AUTH_CLAIM_USER_ID=id
AGENT_AUTH_CLAIM_ROLES=role
CORS_ORIGINS=http://localhost:5173

What each of these is, and why it has to be that exact value:

KeyValueWhy
ANTHROPIC_API_KEYyour keythe model the agents run on — skip it and use Ollama for free instead, see the comment above
OPEN_WEBUI_URLhttp://localhost:8080the backend, where tool calls actually land
AGENT_AUTH_SECRETpLiUxoi+ziwTUIhNhVg/AN1U50UMom00 — same fixed value as backend/.env.example's WEBUI_SECRET_KEYExtra verifies Open WebUI's session JWT itself; HMAC means one shared secret signs and verifies on both sides
AGENT_AUTH_MODEminttoken-url points at Open WebUI's dedicated /auths/agent-chat/token, not the user's regular session — mint is for exactly that: a short-lived token minted specifically for us, with its lifetime capped (AGENT_AUTH_MAX_TTL_SECONDS, 1h default) rather than trusted at whatever the token claims
AGENT_AUTH_CLAIM_USER_IDidOpen WebUI's token carries the user id under id, not the usual sub
AGENT_AUTH_CLAIM_ROLESrolewhich claim plugins/access.py reads to decide who can reach the protected admin_management node
CORS_ORIGINShttp://localhost:5173where the browser loads the page from, not where the backend answers — the two are different ports in dev

AGENT_AUTH_SECRET/WEBUI_SECRET_KEY is a fixed, publicly-known dev value — the same one ships in both .env.example files, on purpose. That's fine only because this all runs on your machine with no real users and nothing else trusts it; change both together to something private (openssl rand -base64 24) the moment this runs anywhere else reachable.

Get the rest wrong and the failure points back here: a mismatched AGENT_AUTH_SECRET fails every request with a signature error, a wrong CORS_ORIGINS shows up as a browser console error before any request lands at all.

What's in extra/

A sequence of commits, each a step in the same story: a naive assistant sharing one admin API key between every user, then made to act as the caller instead, then made to survive runs longer than a session token's lifetime, then made to hide the whole admin path from anyone who isn't actually an Open WebUI admin, and finally made to stop describing that path to people who cannot use it. Read them in order — git log extra/ — to see why each change was needed, not just what it does.

Those last two are the pair worth studying together, because either alone leaves the assistant inconsistent:

  • protected: true (agents.yml) plus plugins/access.py decides what the router can reach. A node marked protected is invisible to the router, not just refused, unless the access plugin allows it.
  • {{admin_routing}} in the router's prompt, filled by plugins/resolvers/openwebui.py, decides what the router is told exists. Without it the prompt named a destination a non-admin would never be offered, so the assistant announced admin help it could not deliver.

Both are general engine features, not specific to this example — see access control and resolvers. Both read the caller's role, and plugins/_identity.py is the one place that decides what "admin" means so the two can never drift apart.

backend/open_webui/routers/auths.py and the four files under src/ are the Open WebUI side of the wiring: one endpoint that mints a short-lived token for the signed-in user — carrying their role, which is what the access plugin checks — and the widget embed that calls it.

About

This repo show how easy integrate extra to any app. This is 3 commit and OpenWebUI have ai-assistance.

Resources

Code of conduct

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

17,828 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Open WebUI + Extra

This is Open WebUI with an Extra assistant embedded in it, wired to act as whoever is signed in rather than through one shared admin key. Everything Extra-specific lives in extra/; the rest is upstream Open WebUI (its own README).

Run it

Open WebUI — two terminals, per the official dev guide:

# terminal 1 — frontend
cp -RPp .env.example .env
npm install
npm run build
npm run dev
# terminal 2 — backendcd backend
cp .env.example .env # dev.sh needs WEBUI_SECRET_KEY set; unlike start.sh it won't generate one
python3 -m venv venv &&source venv/bin/activate
pip install -r requirements.txt -U
sh dev.sh

Frontend at http://localhost:5173, backend at http://localhost:8080.

Extra — set up extra/.env per the Environment section below, then from a clone of extra-org/extra:

make dev AGENTS=<path-to-this-repo>/extra/agents.yml ENV_FILE=<path-to-this-repo>/extra/.env

Or with Docker instead of a local extra checkout:

docker run -d -p 8100:8100 \
-v <path-to-this-repo>/extra:/workspace -w /workspace \
--env-file <path-to-this-repo>/extra/.env \
ghcr.io/extra-org/extra:latest \
agent-manager --config agents.yml --port 8100

Playground at http://localhost:8100/playground. Sign into Open WebUI and the assistant appears there too.

Environment

cp extra/.env.example extra/.env

Everything below is already filled in except the model key — open extra/.env and add that, and you're running:

ANTHROPIC_API_KEY=your-key-here # or see the Ollama comment for a free local model instead
OPEN_WEBUI_URL=http://localhost:8080
AGENT_AUTH_MODE=mint
AGENT_AUTH_SECRET=pLiUxoi+ziwTUIhNhVg/AN1U50UMom00
AGENT_AUTH_CLAIM_USER_ID=id
AGENT_AUTH_CLAIM_ROLES=role
CORS_ORIGINS=http://localhost:5173

What each of these is, and why it has to be that exact value:

KeyValueWhy
ANTHROPIC_API_KEYyour keythe model the agents run on — skip it and use Ollama for free instead, see the comment above
OPEN_WEBUI_URLhttp://localhost:8080the backend, where tool calls actually land
AGENT_AUTH_SECRETpLiUxoi+ziwTUIhNhVg/AN1U50UMom00 — same fixed value as backend/.env.example's WEBUI_SECRET_KEYExtra verifies Open WebUI's session JWT itself; HMAC means one shared secret signs and verifies on both sides
AGENT_AUTH_MODEminttoken-url points at Open WebUI's dedicated /auths/agent-chat/token, not the user's regular session — mint is for exactly that: a short-lived token minted specifically for us, with its lifetime capped (AGENT_AUTH_MAX_TTL_SECONDS, 1h default) rather than trusted at whatever the token claims
AGENT_AUTH_CLAIM_USER_IDidOpen WebUI's token carries the user id under id, not the usual sub
AGENT_AUTH_CLAIM_ROLESrolewhich claim plugins/access.py reads to decide who can reach the protected admin_management node
CORS_ORIGINShttp://localhost:5173where the browser loads the page from, not where the backend answers — the two are different ports in dev

AGENT_AUTH_SECRET/WEBUI_SECRET_KEY is a fixed, publicly-known dev value — the same one ships in both .env.example files, on purpose. That's fine only because this all runs on your machine with no real users and nothing else trusts it; change both together to something private (openssl rand -base64 24) the moment this runs anywhere else reachable.

Get the rest wrong and the failure points back here: a mismatched AGENT_AUTH_SECRET fails every request with a signature error, a wrong CORS_ORIGINS shows up as a browser console error before any request lands at all.

What's in extra/

A sequence of commits, each a step in the same story: a naive assistant sharing one admin API key between every user, then made to act as the caller instead, then made to survive runs longer than a session token's lifetime, then made to hide the whole admin path from anyone who isn't actually an Open WebUI admin, and finally made to stop describing that path to people who cannot use it. Read them in order — git log extra/ — to see why each change was needed, not just what it does.

Those last two are the pair worth studying together, because either alone leaves the assistant inconsistent:

  • protected: true (agents.yml) plus plugins/access.py decides what the router can reach. A node marked protected is invisible to the router, not just refused, unless the access plugin allows it.
  • {{admin_routing}} in the router's prompt, filled by plugins/resolvers/openwebui.py, decides what the router is told exists. Without it the prompt named a destination a non-admin would never be offered, so the assistant announced admin help it could not deliver.

Both are general engine features, not specific to this example — see access control and resolvers. Both read the caller's role, and plugins/_identity.py is the one place that decides what "admin" means so the two can never drift apart.

backend/open_webui/routers/auths.py and the four files under src/ are the Open WebUI side of the wiring: one endpoint that mints a short-lived token for the signed-in user — carrying their role, which is what the access plugin checks — and the widget embed that calls it.

About

This repo show how easy integrate extra to any app. This is 3 commit and OpenWebUI have ai-assistance.

Resources

Code of conduct

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

17,828 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Open WebUI + Extra

This is Open WebUI with an Extra assistant embedded in it, wired to act as whoever is signed in rather than through one shared admin key. Everything Extra-specific lives in extra/; the rest is upstream Open WebUI (its own README).

Run it

Open WebUI — two terminals, per the official dev guide:

# terminal 1 — frontend
cp -RPp .env.example .env
npm install
npm run build
npm run dev
# terminal 2 — backendcd backend
cp .env.example .env # dev.sh needs WEBUI_SECRET_KEY set; unlike start.sh it won't generate one
python3 -m venv venv &&source venv/bin/activate
pip install -r requirements.txt -U
sh dev.sh

Frontend at http://localhost:5173, backend at http://localhost:8080.

Extra — set up extra/.env per the Environment section below, then from a clone of extra-org/extra:

make dev AGENTS=<path-to-this-repo>/extra/agents.yml ENV_FILE=<path-to-this-repo>/extra/.env

Or with Docker instead of a local extra checkout:

docker run -d -p 8100:8100 \
-v <path-to-this-repo>/extra:/workspace -w /workspace \
--env-file <path-to-this-repo>/extra/.env \
ghcr.io/extra-org/extra:latest \
agent-manager --config agents.yml --port 8100

Playground at http://localhost:8100/playground. Sign into Open WebUI and the assistant appears there too.

Environment

cp extra/.env.example extra/.env

Everything below is already filled in except the model key — open extra/.env and add that, and you're running:

ANTHROPIC_API_KEY=your-key-here # or see the Ollama comment for a free local model instead
OPEN_WEBUI_URL=http://localhost:8080
AGENT_AUTH_MODE=mint
AGENT_AUTH_SECRET=pLiUxoi+ziwTUIhNhVg/AN1U50UMom00
AGENT_AUTH_CLAIM_USER_ID=id
AGENT_AUTH_CLAIM_ROLES=role
CORS_ORIGINS=http://localhost:5173

What each of these is, and why it has to be that exact value:

KeyValueWhy
ANTHROPIC_API_KEYyour keythe model the agents run on — skip it and use Ollama for free instead, see the comment above
OPEN_WEBUI_URLhttp://localhost:8080the backend, where tool calls actually land
AGENT_AUTH_SECRETpLiUxoi+ziwTUIhNhVg/AN1U50UMom00 — same fixed value as backend/.env.example's WEBUI_SECRET_KEYExtra verifies Open WebUI's session JWT itself; HMAC means one shared secret signs and verifies on both sides
AGENT_AUTH_MODEminttoken-url points at Open WebUI's dedicated /auths/agent-chat/token, not the user's regular session — mint is for exactly that: a short-lived token minted specifically for us, with its lifetime capped (AGENT_AUTH_MAX_TTL_SECONDS, 1h default) rather than trusted at whatever the token claims
AGENT_AUTH_CLAIM_USER_IDidOpen WebUI's token carries the user id under id, not the usual sub
AGENT_AUTH_CLAIM_ROLESrolewhich claim plugins/access.py reads to decide who can reach the protected admin_management node
CORS_ORIGINShttp://localhost:5173where the browser loads the page from, not where the backend answers — the two are different ports in dev

AGENT_AUTH_SECRET/WEBUI_SECRET_KEY is a fixed, publicly-known dev value — the same one ships in both .env.example files, on purpose. That's fine only because this all runs on your machine with no real users and nothing else trusts it; change both together to something private (openssl rand -base64 24) the moment this runs anywhere else reachable.

Get the rest wrong and the failure points back here: a mismatched AGENT_AUTH_SECRET fails every request with a signature error, a wrong CORS_ORIGINS shows up as a browser console error before any request lands at all.

What's in extra/

A sequence of commits, each a step in the same story: a naive assistant sharing one admin API key between every user, then made to act as the caller instead, then made to survive runs longer than a session token's lifetime, then made to hide the whole admin path from anyone who isn't actually an Open WebUI admin, and finally made to stop describing that path to people who cannot use it. Read them in order — git log extra/ — to see why each change was needed, not just what it does.

Those last two are the pair worth studying together, because either alone leaves the assistant inconsistent:

  • protected: true (agents.yml) plus plugins/access.py decides what the router can reach. A node marked protected is invisible to the router, not just refused, unless the access plugin allows it.
  • {{admin_routing}} in the router's prompt, filled by plugins/resolvers/openwebui.py, decides what the router is told exists. Without it the prompt named a destination a non-admin would never be offered, so the assistant announced admin help it could not deliver.

Both are general engine features, not specific to this example — see access control and resolvers. Both read the caller's role, and plugins/_identity.py is the one place that decides what "admin" means so the two can never drift apart.

backend/open_webui/routers/auths.py and the four files under src/ are the Open WebUI side of the wiring: one endpoint that mints a short-lived token for the signed-in user — carrying their role, which is what the access plugin checks — and the widget embed that calls it.

About

This repo show how easy integrate extra to any app. This is 3 commit and OpenWebUI have ai-assistance.

Resources

Code of conduct

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

17,828 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Open WebUI + Extra

This is Open WebUI with an Extra assistant embedded in it, wired to act as whoever is signed in rather than through one shared admin key. Everything Extra-specific lives in extra/; the rest is upstream Open WebUI (its own README).

Run it

Open WebUI — two terminals, per the official dev guide:

# terminal 1 — frontend
cp -RPp .env.example .env
npm install
npm run build
npm run dev
# terminal 2 — backendcd backend
cp .env.example .env # dev.sh needs WEBUI_SECRET_KEY set; unlike start.sh it won't generate one
python3 -m venv venv &&source venv/bin/activate
pip install -r requirements.txt -U
sh dev.sh

Frontend at http://localhost:5173, backend at http://localhost:8080.

Extra — set up extra/.env per the Environment section below, then from a clone of extra-org/extra:

make dev AGENTS=<path-to-this-repo>/extra/agents.yml ENV_FILE=<path-to-this-repo>/extra/.env

Or with Docker instead of a local extra checkout:

docker run -d -p 8100:8100 \
-v <path-to-this-repo>/extra:/workspace -w /workspace \
--env-file <path-to-this-repo>/extra/.env \
ghcr.io/extra-org/extra:latest \
agent-manager --config agents.yml --port 8100

Playground at http://localhost:8100/playground. Sign into Open WebUI and the assistant appears there too.

Environment

cp extra/.env.example extra/.env

Everything below is already filled in except the model key — open extra/.env and add that, and you're running:

ANTHROPIC_API_KEY=your-key-here # or see the Ollama comment for a free local model instead
OPEN_WEBUI_URL=http://localhost:8080
AGENT_AUTH_MODE=mint
AGENT_AUTH_SECRET=pLiUxoi+ziwTUIhNhVg/AN1U50UMom00
AGENT_AUTH_CLAIM_USER_ID=id
AGENT_AUTH_CLAIM_ROLES=role
CORS_ORIGINS=http://localhost:5173

What each of these is, and why it has to be that exact value:

KeyValueWhy
ANTHROPIC_API_KEYyour keythe model the agents run on — skip it and use Ollama for free instead, see the comment above
OPEN_WEBUI_URLhttp://localhost:8080the backend, where tool calls actually land
AGENT_AUTH_SECRETpLiUxoi+ziwTUIhNhVg/AN1U50UMom00 — same fixed value as backend/.env.example's WEBUI_SECRET_KEYExtra verifies Open WebUI's session JWT itself; HMAC means one shared secret signs and verifies on both sides
AGENT_AUTH_MODEminttoken-url points at Open WebUI's dedicated /auths/agent-chat/token, not the user's regular session — mint is for exactly that: a short-lived token minted specifically for us, with its lifetime capped (AGENT_AUTH_MAX_TTL_SECONDS, 1h default) rather than trusted at whatever the token claims
AGENT_AUTH_CLAIM_USER_IDidOpen WebUI's token carries the user id under id, not the usual sub
AGENT_AUTH_CLAIM_ROLESrolewhich claim plugins/access.py reads to decide who can reach the protected admin_management node
CORS_ORIGINShttp://localhost:5173where the browser loads the page from, not where the backend answers — the two are different ports in dev

AGENT_AUTH_SECRET/WEBUI_SECRET_KEY is a fixed, publicly-known dev value — the same one ships in both .env.example files, on purpose. That's fine only because this all runs on your machine with no real users and nothing else trusts it; change both together to something private (openssl rand -base64 24) the moment this runs anywhere else reachable.

Get the rest wrong and the failure points back here: a mismatched AGENT_AUTH_SECRET fails every request with a signature error, a wrong CORS_ORIGINS shows up as a browser console error before any request lands at all.

What's in extra/

A sequence of commits, each a step in the same story: a naive assistant sharing one admin API key between every user, then made to act as the caller instead, then made to survive runs longer than a session token's lifetime, then made to hide the whole admin path from anyone who isn't actually an Open WebUI admin, and finally made to stop describing that path to people who cannot use it. Read them in order — git log extra/ — to see why each change was needed, not just what it does.

Those last two are the pair worth studying together, because either alone leaves the assistant inconsistent:

  • protected: true (agents.yml) plus plugins/access.py decides what the router can reach. A node marked protected is invisible to the router, not just refused, unless the access plugin allows it.
  • {{admin_routing}} in the router's prompt, filled by plugins/resolvers/openwebui.py, decides what the router is told exists. Without it the prompt named a destination a non-admin would never be offered, so the assistant announced admin help it could not deliver.

Both are general engine features, not specific to this example — see access control and resolvers. Both read the caller's role, and plugins/_identity.py is the one place that decides what "admin" means so the two can never drift apart.

backend/open_webui/routers/auths.py and the four files under src/ are the Open WebUI side of the wiring: one endpoint that mints a short-lived token for the signed-in user — carrying their role, which is what the access plugin checks — and the widget embed that calls it.

About

This repo show how easy integrate extra to any app. This is 3 commit and OpenWebUI have ai-assistance.

Resources

Code of conduct

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

17,828 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Open WebUI + Extra

This is Open WebUI with an Extra assistant embedded in it, wired to act as whoever is signed in rather than through one shared admin key. Everything Extra-specific lives in extra/; the rest is upstream Open WebUI (its own README).

Run it

Open WebUI — two terminals, per the official dev guide:

# terminal 1 — frontend
cp -RPp .env.example .env
npm install
npm run build
npm run dev
# terminal 2 — backendcd backend
cp .env.example .env # dev.sh needs WEBUI_SECRET_KEY set; unlike start.sh it won't generate one
python3 -m venv venv &&source venv/bin/activate
pip install -r requirements.txt -U
sh dev.sh

Frontend at http://localhost:5173, backend at http://localhost:8080.

Extra — set up extra/.env per the Environment section below, then from a clone of extra-org/extra:

make dev AGENTS=<path-to-this-repo>/extra/agents.yml ENV_FILE=<path-to-this-repo>/extra/.env

Or with Docker instead of a local extra checkout:

docker run -d -p 8100:8100 \
-v <path-to-this-repo>/extra:/workspace -w /workspace \
--env-file <path-to-this-repo>/extra/.env \
ghcr.io/extra-org/extra:latest \
agent-manager --config agents.yml --port 8100

Playground at http://localhost:8100/playground. Sign into Open WebUI and the assistant appears there too.

Environment

cp extra/.env.example extra/.env

Everything below is already filled in except the model key — open extra/.env and add that, and you're running:

ANTHROPIC_API_KEY=your-key-here # or see the Ollama comment for a free local model instead
OPEN_WEBUI_URL=http://localhost:8080
AGENT_AUTH_MODE=mint
AGENT_AUTH_SECRET=pLiUxoi+ziwTUIhNhVg/AN1U50UMom00
AGENT_AUTH_CLAIM_USER_ID=id
AGENT_AUTH_CLAIM_ROLES=role
CORS_ORIGINS=http://localhost:5173

What each of these is, and why it has to be that exact value:

KeyValueWhy
ANTHROPIC_API_KEYyour keythe model the agents run on — skip it and use Ollama for free instead, see the comment above
OPEN_WEBUI_URLhttp://localhost:8080the backend, where tool calls actually land
AGENT_AUTH_SECRETpLiUxoi+ziwTUIhNhVg/AN1U50UMom00 — same fixed value as backend/.env.example's WEBUI_SECRET_KEYExtra verifies Open WebUI's session JWT itself; HMAC means one shared secret signs and verifies on both sides
AGENT_AUTH_MODEminttoken-url points at Open WebUI's dedicated /auths/agent-chat/token, not the user's regular session — mint is for exactly that: a short-lived token minted specifically for us, with its lifetime capped (AGENT_AUTH_MAX_TTL_SECONDS, 1h default) rather than trusted at whatever the token claims
AGENT_AUTH_CLAIM_USER_IDidOpen WebUI's token carries the user id under id, not the usual sub
AGENT_AUTH_CLAIM_ROLESrolewhich claim plugins/access.py reads to decide who can reach the protected admin_management node
CORS_ORIGINShttp://localhost:5173where the browser loads the page from, not where the backend answers — the two are different ports in dev

AGENT_AUTH_SECRET/WEBUI_SECRET_KEY is a fixed, publicly-known dev value — the same one ships in both .env.example files, on purpose. That's fine only because this all runs on your machine with no real users and nothing else trusts it; change both together to something private (openssl rand -base64 24) the moment this runs anywhere else reachable.

Get the rest wrong and the failure points back here: a mismatched AGENT_AUTH_SECRET fails every request with a signature error, a wrong CORS_ORIGINS shows up as a browser console error before any request lands at all.

What's in extra/

A sequence of commits, each a step in the same story: a naive assistant sharing one admin API key between every user, then made to act as the caller instead, then made to survive runs longer than a session token's lifetime, then made to hide the whole admin path from anyone who isn't actually an Open WebUI admin, and finally made to stop describing that path to people who cannot use it. Read them in order — git log extra/ — to see why each change was needed, not just what it does.

Those last two are the pair worth studying together, because either alone leaves the assistant inconsistent:

  • protected: true (agents.yml) plus plugins/access.py decides what the router can reach. A node marked protected is invisible to the router, not just refused, unless the access plugin allows it.
  • {{admin_routing}} in the router's prompt, filled by plugins/resolvers/openwebui.py, decides what the router is told exists. Without it the prompt named a destination a non-admin would never be offered, so the assistant announced admin help it could not deliver.

Both are general engine features, not specific to this example — see access control and resolvers. Both read the caller's role, and plugins/_identity.py is the one place that decides what "admin" means so the two can never drift apart.

backend/open_webui/routers/auths.py and the four files under src/ are the Open WebUI side of the wiring: one endpoint that mints a short-lived token for the signed-in user — carrying their role, which is what the access plugin checks — and the widget embed that calls it.

About

This repo show how easy integrate extra to any app. This is 3 commit and OpenWebUI have ai-assistance.

Resources

Code of conduct

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

17,828 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Open WebUI + Extra

This is Open WebUI with an Extra assistant embedded in it, wired to act as whoever is signed in rather than through one shared admin key. Everything Extra-specific lives in extra/; the rest is upstream Open WebUI (its own README).

Run it

Open WebUI — two terminals, per the official dev guide:

# terminal 1 — frontend
cp -RPp .env.example .env
npm install
npm run build
npm run dev
# terminal 2 — backendcd backend
cp .env.example .env # dev.sh needs WEBUI_SECRET_KEY set; unlike start.sh it won't generate one
python3 -m venv venv &&source venv/bin/activate
pip install -r requirements.txt -U
sh dev.sh

Frontend at http://localhost:5173, backend at http://localhost:8080.

Extra — set up extra/.env per the Environment section below, then from a clone of extra-org/extra:

make dev AGENTS=<path-to-this-repo>/extra/agents.yml ENV_FILE=<path-to-this-repo>/extra/.env

Or with Docker instead of a local extra checkout:

docker run -d -p 8100:8100 \
-v <path-to-this-repo>/extra:/workspace -w /workspace \
--env-file <path-to-this-repo>/extra/.env \
ghcr.io/extra-org/extra:latest \
agent-manager --config agents.yml --port 8100

Playground at http://localhost:8100/playground. Sign into Open WebUI and the assistant appears there too.

Environment

cp extra/.env.example extra/.env

Everything below is already filled in except the model key — open extra/.env and add that, and you're running:

ANTHROPIC_API_KEY=your-key-here # or see the Ollama comment for a free local model instead
OPEN_WEBUI_URL=http://localhost:8080
AGENT_AUTH_MODE=mint
AGENT_AUTH_SECRET=pLiUxoi+ziwTUIhNhVg/AN1U50UMom00
AGENT_AUTH_CLAIM_USER_ID=id
AGENT_AUTH_CLAIM_ROLES=role
CORS_ORIGINS=http://localhost:5173

What each of these is, and why it has to be that exact value:

KeyValueWhy
ANTHROPIC_API_KEYyour keythe model the agents run on — skip it and use Ollama for free instead, see the comment above
OPEN_WEBUI_URLhttp://localhost:8080the backend, where tool calls actually land
AGENT_AUTH_SECRETpLiUxoi+ziwTUIhNhVg/AN1U50UMom00 — same fixed value as backend/.env.example's WEBUI_SECRET_KEYExtra verifies Open WebUI's session JWT itself; HMAC means one shared secret signs and verifies on both sides
AGENT_AUTH_MODEminttoken-url points at Open WebUI's dedicated /auths/agent-chat/token, not the user's regular session — mint is for exactly that: a short-lived token minted specifically for us, with its lifetime capped (AGENT_AUTH_MAX_TTL_SECONDS, 1h default) rather than trusted at whatever the token claims
AGENT_AUTH_CLAIM_USER_IDidOpen WebUI's token carries the user id under id, not the usual sub
AGENT_AUTH_CLAIM_ROLESrolewhich claim plugins/access.py reads to decide who can reach the protected admin_management node
CORS_ORIGINShttp://localhost:5173where the browser loads the page from, not where the backend answers — the two are different ports in dev

AGENT_AUTH_SECRET/WEBUI_SECRET_KEY is a fixed, publicly-known dev value — the same one ships in both .env.example files, on purpose. That's fine only because this all runs on your machine with no real users and nothing else trusts it; change both together to something private (openssl rand -base64 24) the moment this runs anywhere else reachable.

Get the rest wrong and the failure points back here: a mismatched AGENT_AUTH_SECRET fails every request with a signature error, a wrong CORS_ORIGINS shows up as a browser console error before any request lands at all.

What's in extra/

A sequence of commits, each a step in the same story: a naive assistant sharing one admin API key between every user, then made to act as the caller instead, then made to survive runs longer than a session token's lifetime, then made to hide the whole admin path from anyone who isn't actually an Open WebUI admin, and finally made to stop describing that path to people who cannot use it. Read them in order — git log extra/ — to see why each change was needed, not just what it does.

Those last two are the pair worth studying together, because either alone leaves the assistant inconsistent:

  • protected: true (agents.yml) plus plugins/access.py decides what the router can reach. A node marked protected is invisible to the router, not just refused, unless the access plugin allows it.
  • {{admin_routing}} in the router's prompt, filled by plugins/resolvers/openwebui.py, decides what the router is told exists. Without it the prompt named a destination a non-admin would never be offered, so the assistant announced admin help it could not deliver.

Both are general engine features, not specific to this example — see access control and resolvers. Both read the caller's role, and plugins/_identity.py is the one place that decides what "admin" means so the two can never drift apart.

backend/open_webui/routers/auths.py and the four files under src/ are the Open WebUI side of the wiring: one endpoint that mints a short-lived token for the signed-in user — carrying their role, which is what the access plugin checks — and the widget embed that calls it.

About

This repo show how easy integrate extra to any app. This is 3 commit and OpenWebUI have ai-assistance.

Resources

Code of conduct

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

17,828 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Open WebUI + Extra

This is Open WebUI with an Extra assistant embedded in it, wired to act as whoever is signed in rather than through one shared admin key. Everything Extra-specific lives in extra/; the rest is upstream Open WebUI (its own README).

Run it

Open WebUI — two terminals, per the official dev guide:

# terminal 1 — frontend
cp -RPp .env.example .env
npm install
npm run build
npm run dev
# terminal 2 — backendcd backend
cp .env.example .env # dev.sh needs WEBUI_SECRET_KEY set; unlike start.sh it won't generate one
python3 -m venv venv &&source venv/bin/activate
pip install -r requirements.txt -U
sh dev.sh

Frontend at http://localhost:5173, backend at http://localhost:8080.

Extra — set up extra/.env per the Environment section below, then from a clone of extra-org/extra:

make dev AGENTS=<path-to-this-repo>/extra/agents.yml ENV_FILE=<path-to-this-repo>/extra/.env

Or with Docker instead of a local extra checkout:

docker run -d -p 8100:8100 \
-v <path-to-this-repo>/extra:/workspace -w /workspace \
--env-file <path-to-this-repo>/extra/.env \
ghcr.io/extra-org/extra:latest \
agent-manager --config agents.yml --port 8100

Playground at http://localhost:8100/playground. Sign into Open WebUI and the assistant appears there too.

Environment

cp extra/.env.example extra/.env

Everything below is already filled in except the model key — open extra/.env and add that, and you're running:

ANTHROPIC_API_KEY=your-key-here # or see the Ollama comment for a free local model instead
OPEN_WEBUI_URL=http://localhost:8080
AGENT_AUTH_MODE=mint
AGENT_AUTH_SECRET=pLiUxoi+ziwTUIhNhVg/AN1U50UMom00
AGENT_AUTH_CLAIM_USER_ID=id
AGENT_AUTH_CLAIM_ROLES=role
CORS_ORIGINS=http://localhost:5173

What each of these is, and why it has to be that exact value:

KeyValueWhy
ANTHROPIC_API_KEYyour keythe model the agents run on — skip it and use Ollama for free instead, see the comment above
OPEN_WEBUI_URLhttp://localhost:8080the backend, where tool calls actually land
AGENT_AUTH_SECRETpLiUxoi+ziwTUIhNhVg/AN1U50UMom00 — same fixed value as backend/.env.example's WEBUI_SECRET_KEYExtra verifies Open WebUI's session JWT itself; HMAC means one shared secret signs and verifies on both sides
AGENT_AUTH_MODEminttoken-url points at Open WebUI's dedicated /auths/agent-chat/token, not the user's regular session — mint is for exactly that: a short-lived token minted specifically for us, with its lifetime capped (AGENT_AUTH_MAX_TTL_SECONDS, 1h default) rather than trusted at whatever the token claims
AGENT_AUTH_CLAIM_USER_IDidOpen WebUI's token carries the user id under id, not the usual sub
AGENT_AUTH_CLAIM_ROLESrolewhich claim plugins/access.py reads to decide who can reach the protected admin_management node
CORS_ORIGINShttp://localhost:5173where the browser loads the page from, not where the backend answers — the two are different ports in dev

AGENT_AUTH_SECRET/WEBUI_SECRET_KEY is a fixed, publicly-known dev value — the same one ships in both .env.example files, on purpose. That's fine only because this all runs on your machine with no real users and nothing else trusts it; change both together to something private (openssl rand -base64 24) the moment this runs anywhere else reachable.

Get the rest wrong and the failure points back here: a mismatched AGENT_AUTH_SECRET fails every request with a signature error, a wrong CORS_ORIGINS shows up as a browser console error before any request lands at all.

What's in extra/

A sequence of commits, each a step in the same story: a naive assistant sharing one admin API key between every user, then made to act as the caller instead, then made to survive runs longer than a session token's lifetime, then made to hide the whole admin path from anyone who isn't actually an Open WebUI admin, and finally made to stop describing that path to people who cannot use it. Read them in order — git log extra/ — to see why each change was needed, not just what it does.

Those last two are the pair worth studying together, because either alone leaves the assistant inconsistent:

  • protected: true (agents.yml) plus plugins/access.py decides what the router can reach. A node marked protected is invisible to the router, not just refused, unless the access plugin allows it.
  • {{admin_routing}} in the router's prompt, filled by plugins/resolvers/openwebui.py, decides what the router is told exists. Without it the prompt named a destination a non-admin would never be offered, so the assistant announced admin help it could not deliver.

Both are general engine features, not specific to this example — see access control and resolvers. Both read the caller's role, and plugins/_identity.py is the one place that decides what "admin" means so the two can never drift apart.

backend/open_webui/routers/auths.py and the four files under src/ are the Open WebUI side of the wiring: one endpoint that mints a short-lived token for the signed-in user — carrying their role, which is what the access plugin checks — and the widget embed that calls it.

About

This repo show how easy integrate extra to any app. This is 3 commit and OpenWebUI have ai-assistance.

Resources

Code of conduct

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

17,828 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Open WebUI + Extra

This is Open WebUI with an Extra assistant embedded in it, wired to act as whoever is signed in rather than through one shared admin key. Everything Extra-specific lives in extra/; the rest is upstream Open WebUI (its own README).

Run it

Open WebUI — two terminals, per the official dev guide:

# terminal 1 — frontend
cp -RPp .env.example .env
npm install
npm run build
npm run dev
# terminal 2 — backendcd backend
cp .env.example .env # dev.sh needs WEBUI_SECRET_KEY set; unlike start.sh it won't generate one
python3 -m venv venv &&source venv/bin/activate
pip install -r requirements.txt -U
sh dev.sh

Frontend at http://localhost:5173, backend at http://localhost:8080.

Extra — set up extra/.env per the Environment section below, then from a clone of extra-org/extra:

make dev AGENTS=<path-to-this-repo>/extra/agents.yml ENV_FILE=<path-to-this-repo>/extra/.env

Or with Docker instead of a local extra checkout:

docker run -d -p 8100:8100 \
-v <path-to-this-repo>/extra:/workspace -w /workspace \
--env-file <path-to-this-repo>/extra/.env \
ghcr.io/extra-org/extra:latest \
agent-manager --config agents.yml --port 8100

Playground at http://localhost:8100/playground. Sign into Open WebUI and the assistant appears there too.

Environment

cp extra/.env.example extra/.env

Everything below is already filled in except the model key — open extra/.env and add that, and you're running:

ANTHROPIC_API_KEY=your-key-here # or see the Ollama comment for a free local model instead
OPEN_WEBUI_URL=http://localhost:8080
AGENT_AUTH_MODE=mint
AGENT_AUTH_SECRET=pLiUxoi+ziwTUIhNhVg/AN1U50UMom00
AGENT_AUTH_CLAIM_USER_ID=id
AGENT_AUTH_CLAIM_ROLES=role
CORS_ORIGINS=http://localhost:5173

What each of these is, and why it has to be that exact value:

KeyValueWhy
ANTHROPIC_API_KEYyour keythe model the agents run on — skip it and use Ollama for free instead, see the comment above
OPEN_WEBUI_URLhttp://localhost:8080the backend, where tool calls actually land
AGENT_AUTH_SECRETpLiUxoi+ziwTUIhNhVg/AN1U50UMom00 — same fixed value as backend/.env.example's WEBUI_SECRET_KEYExtra verifies Open WebUI's session JWT itself; HMAC means one shared secret signs and verifies on both sides
AGENT_AUTH_MODEminttoken-url points at Open WebUI's dedicated /auths/agent-chat/token, not the user's regular session — mint is for exactly that: a short-lived token minted specifically for us, with its lifetime capped (AGENT_AUTH_MAX_TTL_SECONDS, 1h default) rather than trusted at whatever the token claims
AGENT_AUTH_CLAIM_USER_IDidOpen WebUI's token carries the user id under id, not the usual sub
AGENT_AUTH_CLAIM_ROLESrolewhich claim plugins/access.py reads to decide who can reach the protected admin_management node
CORS_ORIGINShttp://localhost:5173where the browser loads the page from, not where the backend answers — the two are different ports in dev

AGENT_AUTH_SECRET/WEBUI_SECRET_KEY is a fixed, publicly-known dev value — the same one ships in both .env.example files, on purpose. That's fine only because this all runs on your machine with no real users and nothing else trusts it; change both together to something private (openssl rand -base64 24) the moment this runs anywhere else reachable.

Get the rest wrong and the failure points back here: a mismatched AGENT_AUTH_SECRET fails every request with a signature error, a wrong CORS_ORIGINS shows up as a browser console error before any request lands at all.

What's in extra/

A sequence of commits, each a step in the same story: a naive assistant sharing one admin API key between every user, then made to act as the caller instead, then made to survive runs longer than a session token's lifetime, then made to hide the whole admin path from anyone who isn't actually an Open WebUI admin, and finally made to stop describing that path to people who cannot use it. Read them in order — git log extra/ — to see why each change was needed, not just what it does.

Those last two are the pair worth studying together, because either alone leaves the assistant inconsistent:

  • protected: true (agents.yml) plus plugins/access.py decides what the router can reach. A node marked protected is invisible to the router, not just refused, unless the access plugin allows it.
  • {{admin_routing}} in the router's prompt, filled by plugins/resolvers/openwebui.py, decides what the router is told exists. Without it the prompt named a destination a non-admin would never be offered, so the assistant announced admin help it could not deliver.

Both are general engine features, not specific to this example — see access control and resolvers. Both read the caller's role, and plugins/_identity.py is the one place that decides what "admin" means so the two can never drift apart.

backend/open_webui/routers/auths.py and the four files under src/ are the Open WebUI side of the wiring: one endpoint that mints a short-lived token for the signed-in user — carrying their role, which is what the access plugin checks — and the widget embed that calls it.

About

This repo show how easy integrate extra to any app. This is 3 commit and OpenWebUI have ai-assistance.

Resources

Code of conduct

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages