From ceac8c8203c1c749d12cd906ece2af748431e5b9 Mon Sep 17 00:00:00 2001 From: "fangyaozheng@bytedance.com" Date: Mon, 15 Jun 2026 13:51:41 +0800 Subject: [PATCH 1/5] feat(examples): add sso_frontend_on_agentkit deploy example Deploy the VeADK web UI together with VeIdentity SSO to an AgentKit runtime. The self-contained app.py serves the bundled UI + agent API behind VeIdentity OAuth2, with two adaptations for the AgentKit gateway: - strips the gateway key from the Authorization header before the SSO middleware runs, so SSO falls back to its session cookie instead of failing with "Invalid JWT format" (the gateway forwards its key in Authorization, which the middleware would otherwise try to decode as a user JWT); - forwards the page querystring onto /assets/* URLs, for gateways configured to take the key from the query string. Runs on stock veadk-python>=0.5.39. Bilingual README with paste-able deploy commands (config -> launch -> set callback -> deploy -> add header in browser). --- .../sso_frontend_on_agentkit/.dockerignore | 28 +++ .../sso_frontend_on_agentkit/.env.example | 28 +++ examples/sso_frontend_on_agentkit/README.md | 121 +++++++++++++ .../sso_frontend_on_agentkit/README.zh.md | 110 ++++++++++++ .../agents/sso_demo_agent/__init__.py | 17 ++ .../agents/sso_demo_agent/agent.py | 30 ++++ examples/sso_frontend_on_agentkit/app.py | 167 ++++++++++++++++++ .../sso_frontend_on_agentkit/requirements.txt | 7 + 8 files changed, 508 insertions(+) create mode 100644 examples/sso_frontend_on_agentkit/.dockerignore create mode 100644 examples/sso_frontend_on_agentkit/.env.example create mode 100644 examples/sso_frontend_on_agentkit/README.md create mode 100644 examples/sso_frontend_on_agentkit/README.zh.md create mode 100644 examples/sso_frontend_on_agentkit/agents/sso_demo_agent/__init__.py create mode 100644 examples/sso_frontend_on_agentkit/agents/sso_demo_agent/agent.py create mode 100644 examples/sso_frontend_on_agentkit/app.py create mode 100644 examples/sso_frontend_on_agentkit/requirements.txt diff --git a/examples/sso_frontend_on_agentkit/.dockerignore b/examples/sso_frontend_on_agentkit/.dockerignore new file mode 100644 index 00000000..02a634c3 --- /dev/null +++ b/examples/sso_frontend_on_agentkit/.dockerignore @@ -0,0 +1,28 @@ +# AgentKit configuration +agentkit.yaml +agentkit*.yaml +.agentkit/ + +# Local secrets — never ship into the image (envs are injected by the runtime) +.env + +# Python cache +__pycache__/ +*.py[cod] +*$py.class + +# Virtual environments +.venv/ +venv/ +ENV/ +env/ + +# IDE / git +.vscode/ +.idea/ +.git/ +.gitignore + +# Docker +Dockerfile* +.dockerignore diff --git a/examples/sso_frontend_on_agentkit/.env.example b/examples/sso_frontend_on_agentkit/.env.example new file mode 100644 index 00000000..6126d56f --- /dev/null +++ b/examples/sso_frontend_on_agentkit/.env.example @@ -0,0 +1,28 @@ +# Copy this file to `.env` and fill in your values. +# `veadk agentkit config` reads these into the runtime environment variables. + +# --- Agent model (Volcengine Ark) --- +MODEL_AGENT_PROVIDER=openai +MODEL_AGENT_NAME=deepseek-v4-flash-260425 +MODEL_AGENT_API_BASE=https://ark.cn-beijing.volces.com/api/v3/ +MODEL_AGENT_API_KEY=your-ark-api-key-here + +# --- Volcengine AK/SK --- +# Used by `veadk agentkit` to build & deploy, AND injected into the runtime so the +# app can talk to the VeIdentity API (resolve the user pool, register the callback). +# Create access keys at https://console.volcengine.com/iam/keymanage +VOLCENGINE_ACCESS_KEY=your-volcengine-access-key +VOLCENGINE_SECRET_KEY=your-volcengine-secret-key + +# --- SSO: VeIdentity user pool (https://console.volcengine.com/veidentity) --- +# UID of an existing user pool and one of its WEB_APPLICATION clients. +OAUTH2_USER_POOL_ID=your-user-pool-uid +OAUTH2_USER_POOL_CLIENT_ID=your-user-pool-client-uid + +# OAuth2 callback. Leave blank for the first deploy; after `veadk agentkit launch` +# prints the endpoint, set this to /oauth2/callback and redeploy. +OAUTH2_REDIRECT_URI= + +# --- Misc --- +OTEL_SDK_DISABLED=true +VEADK_DISABLE_EXPIRE_AT=true diff --git a/examples/sso_frontend_on_agentkit/README.md b/examples/sso_frontend_on_agentkit/README.md new file mode 100644 index 00000000..9f500ce7 --- /dev/null +++ b/examples/sso_frontend_on_agentkit/README.md @@ -0,0 +1,121 @@ +# sso_frontend_on_agentkit · VeADK frontend with SSO on AgentKit + +Deploy the VeADK web UI (A2UI) together with **VeIdentity single sign-on** to +a [Volcengine AgentKit](https://www.volcengine.com/) runtime. Unauthenticated +browsers see a login page; after sign-in the UI and backend agent run as the +logged-in user. + +> 中文版:[README.zh.md](./README.zh.md) + +## Layout + +```text +sso_frontend_on_agentkit/ +├── app.py # entry: web UI + agent API + SSO +├── agents/ +│ └── sso_demo_agent/ # a minimal agent +├── requirements.txt # veadk-python>=0.5.39 +├── .env.example +└── .dockerignore +``` + +## How it works + +The UI, agent API, VeIdentity OAuth2 middleware and bundled web UI all come +from `veadk-python` on PyPI. SSO is configured entirely through runtime +environment variables — no code changes. + +`app.py` adds two adaptations for the AgentKit gateway, which authenticates +every request with the runtime key in the `Authorization: Bearer ` header +and forwards that header to the container: + +- **Strip the gateway key**: the SSO middleware treats any `Authorization` + header as the user's access token and tries to decode it as a JWT — the + opaque gateway key fails with `Invalid JWT format`. `app.py` removes that + non-JWT header before the middleware runs, so SSO falls back to its session + cookie. A real user JWT is kept. +- **Forward the querystring onto assets**: if the gateway is configured to take + the key from the query string, the served `index.html` appends the page's + querystring to its `/assets/*` URLs so subresource requests carry it too. + +## 1. Prerequisites + +- A VeIdentity user pool and one of its `WEB_APPLICATION` clients + () — note both **UIDs**. +- A Volcengine Ark model API key and your account AK/SK. + +```bash +cd examples/sso_frontend_on_agentkit +cp .env.example .env +# Fill: MODEL_AGENT_API_KEY, VOLCENGINE_ACCESS_KEY/SECRET_KEY, +# OAUTH2_USER_POOL_ID, OAUTH2_USER_POOL_CLIENT_ID +set -a && source .env && set +a +``` + +## 2. Configure + +Generate `agentkit.yaml` interactively (region, container registry, runtime +role, …): + +```bash +veadk agentkit config +``` + +Then add these to `common.runtime_envs` in the generated `agentkit.yaml` +(leave `OAUTH2_REDIRECT_URI` until after the first deploy): + +```yaml + runtime_envs: + MODEL_AGENT_PROVIDER: openai + MODEL_AGENT_NAME: deepseek-v4-flash-260425 + MODEL_AGENT_API_BASE: https://ark.cn-beijing.volces.com/api/v3/ + MODEL_AGENT_API_KEY: + OAUTH2_USER_POOL_ID: + OAUTH2_USER_POOL_CLIENT_ID: + VOLCENGINE_ACCESS_KEY: + VOLCENGINE_SECRET_KEY: + OTEL_SDK_DISABLED: 'true' + VEADK_DISABLE_EXPIRE_AT: 'true' +``` + +## 3. Deploy + +```bash +# Build the image and create the runtime; prints the endpoint and API key. +veadk agentkit launch +``` + +Set the callback to the printed endpoint and update the runtime once more: + +```bash +# Add to runtime_envs in agentkit.yaml: +# OAUTH2_REDIRECT_URI: https:///oauth2/callback +veadk agentkit deploy +``` + +The callback URL is registered with the user pool client automatically. + +## 4. Access + +The AgentKit gateway requires the runtime key on every request, and the runtime +key currently only supports the header location (`CreateRuntime`'s +`ApiKeyLocation` accepts only `header`). A browser's top-level navigation cannot +set a header, so use a browser extension (e.g. ModHeader) to add, for this +domain, globally: + +```text +Authorization: Bearer +``` + +Then open the endpoint: the UI loads → redirects to VeIdentity login → callback +(the extension adds the header to pass the gateway) → the session lives in a +cookie and the UI and agent API work. + +## Notes + +- **Model**: any Volcengine Ark chat model for `MODEL_AGENT_*`. +- **AK/SK**: used by `veadk agentkit` to build and deploy, and injected into the + runtime so the app can call the VeIdentity API (resolve the pool, register the + callback). +- **Redeploy**: after editing `runtime_envs`, re-run `veadk agentkit deploy` + (image layers are reused). Tear down with `veadk agentkit destroy`. diff --git a/examples/sso_frontend_on_agentkit/README.zh.md b/examples/sso_frontend_on_agentkit/README.zh.md new file mode 100644 index 00000000..177f671c --- /dev/null +++ b/examples/sso_frontend_on_agentkit/README.zh.md @@ -0,0 +1,110 @@ +# sso_frontend_on_agentkit · 把带 SSO 的 VeADK 前端部署到 AgentKit + +将 VeADK 的 Web 界面(A2UI)连同 **VeIdentity 单点登录** 一起部署到 +[火山引擎 AgentKit](https://www.volcengine.com/) 运行时。未登录的浏览器看到登录页, +登录后以登录用户的身份使用界面与后端 Agent。 + +> English version: [README.md](./README.md) + +## 目录结构 + +```text +sso_frontend_on_agentkit/ +├── app.py # 部署入口:Web 界面 + Agent API + SSO +├── agents/ +│ └── sso_demo_agent/ # 一个最简 Agent +├── requirements.txt # veadk-python>=0.5.39 +├── .env.example +└── .dockerignore +``` + +## 工作原理 + +界面、Agent API、VeIdentity OAuth2 中间件、内置 Web 界面都来自 PyPI 上的 +`veadk-python`。SSO 全部通过运行时环境变量配置,无需改代码。 + +`app.py` 针对 AgentKit 网关做了两处适配。网关对每个请求都用运行时 key 鉴权, +key 放在 `Authorization: Bearer ` 请求头里,并把该头透传给容器: + +- **剥离网关 key**:SSO 中间件会把 `Authorization` 头当成用户的访问令牌去解析 JWT, + 而网关 key 不是 JWT,会报 `Invalid JWT format`。`app.py` 在中间件之前移除这个非 JWT + 的头,使 SSO 回退到会话 cookie;合法的用户 JWT 保持不变。 +- **静态资源透传 querystring**:若网关改为从查询串取 key,浏览器加载 `/assets/*` + 也需带上 key。返回的 `index.html` 会把页面的查询串拼到各静态资源 URL 上。 + +## 1. 前置准备 + +- 一个 VeIdentity 用户池及其下的一个 `WEB_APPLICATION` 客户端 + (控制台:),记下两者的 **UID**。 +- 一个火山引擎 Ark 模型 API Key,以及账号的 AK/SK。 + +```bash +cd examples/sso_frontend_on_agentkit +cp .env.example .env +# 编辑 .env:MODEL_AGENT_API_KEY、VOLCENGINE_ACCESS_KEY/SECRET_KEY、 +# OAUTH2_USER_POOL_ID、OAUTH2_USER_POOL_CLIENT_ID +set -a && source .env && set +a +``` + +## 2. 生成配置 + +交互式生成 `agentkit.yaml`(按提示填写区域、镜像仓库、运行时角色等账号相关字段): + +```bash +veadk agentkit config +``` + +随后在生成的 `agentkit.yaml` 的 `common.runtime_envs` 下补齐以下环境变量 +(`OAUTH2_REDIRECT_URI` 先留空,部署拿到地址后再填): + +```yaml + runtime_envs: + MODEL_AGENT_PROVIDER: openai + MODEL_AGENT_NAME: deepseek-v4-flash-260425 + MODEL_AGENT_API_BASE: https://ark.cn-beijing.volces.com/api/v3/ + MODEL_AGENT_API_KEY: + OAUTH2_USER_POOL_ID: + OAUTH2_USER_POOL_CLIENT_ID: + VOLCENGINE_ACCESS_KEY: + VOLCENGINE_SECRET_KEY: + OTEL_SDK_DISABLED: 'true' + VEADK_DISABLE_EXPIRE_AT: 'true' +``` + +## 3. 部署 + +```bash +# 构建镜像并创建运行时,输出 endpoint 与 API key +veadk agentkit launch +``` + +把上一步输出的 endpoint 写回回调地址,再更新一次运行时: + +```bash +# 在 agentkit.yaml 的 runtime_envs 里加一行: +# OAUTH2_REDIRECT_URI: https:///oauth2/callback +veadk agentkit deploy +``` + +回调地址会自动注册到用户池客户端,云端无需手动添加。 + +## 4. 访问 + +AgentKit 网关要求每个请求都带运行时 key。当前运行时 key 只支持放在请求头里 +(`CreateRuntime` 的 `ApiKeyLocation` 仅接受 `header`),而浏览器的顶层导航无法自带请求头, +因此用浏览器扩展(如 ModHeader)对该域名**全局**添加请求头: + +```text +Authorization: Bearer +``` + +随后访问 endpoint 即可:界面加载 → 跳转 VeIdentity 登录 → 回调(扩展会带上请求头过网关) +→ 登录态走会话 cookie,界面与 Agent API 正常工作。 + +## 注意 + +- **模型**:`MODEL_AGENT_*` 用普通的火山引擎 Ark chat 模型即可。 +- **AK/SK**:既供 `veadk agentkit` 构建部署使用,也注入运行时,供应用调用 VeIdentity API + (解析用户池、注册回调)。 +- **重新部署**:改动 `agentkit.yaml` 的 `runtime_envs` 后重跑 `veadk agentkit deploy` + 即可,镜像层会复用,速度很快。用 `veadk agentkit destroy` 拆除。 diff --git a/examples/sso_frontend_on_agentkit/agents/sso_demo_agent/__init__.py b/examples/sso_frontend_on_agentkit/agents/sso_demo_agent/__init__.py new file mode 100644 index 00000000..e1f5efff --- /dev/null +++ b/examples/sso_frontend_on_agentkit/agents/sso_demo_agent/__init__.py @@ -0,0 +1,17 @@ +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from . import agent + +__all__ = ["agent"] diff --git a/examples/sso_frontend_on_agentkit/agents/sso_demo_agent/agent.py b/examples/sso_frontend_on_agentkit/agents/sso_demo_agent/agent.py new file mode 100644 index 00000000..30357895 --- /dev/null +++ b/examples/sso_frontend_on_agentkit/agents/sso_demo_agent/agent.py @@ -0,0 +1,30 @@ +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Minimal backend agent for the SSO-frontend-on-AgentKit demo. + +A plain VeADK ``Agent`` (no callbacks). The model comes from the MODEL_AGENT_* +runtime environment variables. The Google ADK agent loader picks up ``root_agent``. +""" + +from veadk import Agent + +agent = Agent( + name="sso_demo_agent", + description="Demo agent served behind an SSO login page.", + instruction="You are a helpful assistant. Answer concisely.", +) + +# Required by the Google ADK agent loader. +root_agent = agent diff --git a/examples/sso_frontend_on_agentkit/app.py b/examples/sso_frontend_on_agentkit/app.py new file mode 100644 index 00000000..43ddc654 --- /dev/null +++ b/examples/sso_frontend_on_agentkit/app.py @@ -0,0 +1,167 @@ +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Deployable entry point: VeADK web UI (A2UI) behind VeIdentity SSO, on AgentKit. + +Serves the bundled web UI plus the ADK agent API for the agents under ``./agents`` +and protects them with VeIdentity OAuth2 single sign-on. SSO is configured purely +through runtime environment variables (see README). AgentKit runs this container +with ``python -m app`` on port 8000. + +Two adaptations make it work behind the AgentKit gateway (which authenticates every +request with the runtime key in the ``Authorization`` header and forwards that header +to the container): + +1. ``_StripGatewayAuth`` removes the gateway key from ``Authorization`` before the SSO + middleware runs, so SSO falls back to its session cookie instead of trying to decode + the opaque key as a user JWT ("Invalid JWT format"). A genuine user JWT is preserved. +2. The served ``index.html`` forwards the page querystring onto its ``/assets/*`` URLs, + so that if the gateway is configured to take the key from the query string, the + browser's subresource requests carry it too. + +Everything else comes from the pip-installed ``veadk-python`` (>= 0.5.39). +""" + +import os +import re +from pathlib import Path +from urllib.parse import urlsplit + +import uvicorn +import veadk +from fastapi import Request +from fastapi.responses import FileResponse, HTMLResponse +from fastapi.staticfiles import StaticFiles +from google.adk.cli.fast_api import get_fast_api_app +from veadk.auth.middleware.oauth2_auth import OAuth2Config, setup_oauth2 + +AGENTS_DIR = os.path.abspath(str(Path(__file__).resolve().parent / "agents")) +HOST = os.getenv("HOST", "0.0.0.0") +PORT = int(os.getenv("PORT", "8000")) +WEBUI = Path(veadk.__file__).resolve().parent / "webui" + +app = get_fast_api_app(agents_dir=AGENTS_DIR, allow_origins=[], web=False) + + +@app.get("/ping") +def ping() -> dict[str, str]: + return {"status": "ok"} + + +# ---- SSO: VeIdentity user pool (config from runtime env) ---- +redirect_uri = ( + os.getenv("OAUTH2_REDIRECT_URI") or f"http://{HOST}:{PORT}/oauth2/callback" +) +oauth2_config = OAuth2Config.from_veidentity( + user_pool_uid=os.environ["OAUTH2_USER_POOL_ID"], + client_uid=os.environ["OAUTH2_USER_POOL_CLIENT_ID"], + redirect_uri=redirect_uri, +) +# Secure cookies over HTTPS (runtime), plain over local HTTP. +oauth2_config.cookie_secure = redirect_uri.lower().startswith("https://") +origin = urlsplit(redirect_uri) +oauth2_config.logout_redirect_url = f"{origin.scheme}://{origin.netloc}/" +oauth2_config.end_session_url = None + +providers = [ + {"id": "veidentity", "label": "火山引擎 Identity", "loginUrl": "/oauth2/login"} +] + + +@app.get("/web/auth-config") +async def _web_auth_config() -> dict: + return {"providers": providers} + + +# Protect the API; exempt the SPA shell, assets, and the login-config endpoint so +# the app can load and render its own login page when unauthenticated. +setup_oauth2( + app, + oauth2_config, + exempt_paths={"/", "/index.html", "/favicon.ico", "/web/auth-config", "/ping"}, + exempt_prefixes={"/assets", "/skillhub"}, +) + +# ---- Serving with querystring injection ---- +_index_html = (WEBUI / "index.html").read_text(encoding="utf-8") +_ASSET_REF = re.compile(r'((?:src|href)=")(/[^"?]+)(")') + + +def _render_index(request: Request) -> HTMLResponse: + qs = request.url.query + if not qs: + return HTMLResponse(_index_html) + html = _ASSET_REF.sub( + lambda m: f"{m.group(1)}{m.group(2)}?{qs}{m.group(3)}", _index_html + ) + return HTMLResponse(html) + + +app.mount("/assets", StaticFiles(directory=str(WEBUI / "assets")), name="assets") + + +@app.get("/") +async def _spa_root(request: Request): + return _render_index(request) + + +# SPA fallback: real static files as-is, otherwise the injected HTML shell. +# Registered last so it never shadows the API routes above. +@app.get("/{path:path}") +async def _spa_fallback(path: str, request: Request): + candidate = WEBUI / path + if path and candidate.is_file(): + return FileResponse(str(candidate)) + return _render_index(request) + + +class _StripGatewayAuth: + """Drop a non-JWT ``Authorization`` header before the SSO middleware sees it. + + Behind the AgentKit gateway the runtime key rides in ``Authorization: Bearer + `` and the gateway forwards that header to this container. The SSO + middleware treats any ``Authorization`` header as the user's access token and + tries to decode it as a JWT — the opaque gateway key fails with "Invalid JWT + format", and the session cookie is never consulted. + + The gateway has already authenticated the request upstream, so the key is of + no use here. Remove it when it is not a well-formed JWT (3 dot-separated + parts), letting the SSO middleware fall back to the session cookie. A genuine + user JWT (e.g. from a programmatic client) is left untouched. + """ + + def __init__(self, app): + self.app = app + + async def __call__(self, scope, receive, send): + if scope["type"] == "http": + headers = scope.get("headers", []) + auth = next((v for k, v in headers if k == b"authorization"), None) + if auth is not None: + value = auth.split(b" ", 1)[1] if b" " in auth else auth + if len(value.split(b".")) != 3: # not a JWT -> gateway key + scope = { + **scope, + "headers": [ + (k, v) for k, v in headers if k != b"authorization" + ], + } + await self.app(scope, receive, send) + + +asgi_app = _StripGatewayAuth(app) + + +if __name__ == "__main__": + uvicorn.run(asgi_app, host=HOST, port=PORT) diff --git a/examples/sso_frontend_on_agentkit/requirements.txt b/examples/sso_frontend_on_agentkit/requirements.txt new file mode 100644 index 00000000..d9ec61e9 --- /dev/null +++ b/examples/sso_frontend_on_agentkit/requirements.txt @@ -0,0 +1,7 @@ +# Installed in the image by AgentKit's default `uv pip install -r requirements.txt`. +# +# veadk-python >= 0.5.39 ships the web UI (veadk/webui) and the VeIdentity OAuth2 +# middleware (veadk.auth.middleware.oauth2_auth). The gateway-specific handling +# (querystring-into-assets, stripping the gateway key from Authorization) lives in +# app.py, so no patched veadk build is needed. +veadk-python>=0.5.39 From 6becdbd04f2cb66e03d3f4e4f8db966515e5947d Mon Sep 17 00:00:00 2001 From: "fangyaozheng@bytedance.com" Date: Mon, 15 Jun 2026 14:09:16 +0800 Subject: [PATCH 2/5] feat(frontend,auth): serve the web UI behind a key-auth API gateway Two changes so `veadk frontend` works behind a gateway (e.g. an AgentKit runtime) that authenticates every request with a key and forwards the Authorization header to the app: - cli_frontend: when serving the built UI, forward the page's querystring onto the /assets/* URLs, so a browser behind a query-string gateway carries the key on subresource requests too. Assets are mounted explicitly and a SPA fallback renders the injected shell, registered after the API routes. - oauth2 middleware: ignore a non-JWT Authorization bearer (unless token introspection is enabled) so authentication falls back to the session cookie instead of failing with "Invalid JWT format" when the gateway puts its opaque key in Authorization. A genuine user JWT is left untouched. --- veadk/auth/middleware/oauth2_auth.py | 10 +++++++ veadk/cli/cli_frontend.py | 43 ++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/veadk/auth/middleware/oauth2_auth.py b/veadk/auth/middleware/oauth2_auth.py index 88ab10aa..6c7f716b 100644 --- a/veadk/auth/middleware/oauth2_auth.py +++ b/veadk/auth/middleware/oauth2_auth.py @@ -1688,6 +1688,16 @@ async def oauth2_middleware(request: Request, call_next): return await call_next(request) authorization = request.headers.get("authorization") + # Behind an API gateway that authenticates with a key in the Authorization + # header (e.g. an AgentKit runtime) the opaque gateway key arrives here. It + # is not the user's access token, and unless token introspection is enabled + # it cannot be a valid one (access tokens are JWTs). Ignore a non-JWT bearer + # so authentication falls back to the session cookie instead of failing with + # "Invalid JWT format"; a genuine user JWT is left untouched. + if authorization and not config.use_introspection: + _bearer = _extract_bearer_token(authorization) + if _bearer and len(_bearer.split(".")) != 3: + authorization = None if authorization: token = _extract_bearer_token(authorization) if not token: diff --git a/veadk/cli/cli_frontend.py b/veadk/cli/cli_frontend.py index ef8b0c6a..e1c09864 100644 --- a/veadk/cli/cli_frontend.py +++ b/veadk/cli/cli_frontend.py @@ -972,6 +972,9 @@ async def _web_auth_config(): f"run `cd frontend && npm run dev` and open {DEV_SERVER_ORIGIN}" ) else: + import re as _re + + from fastapi.responses import FileResponse, HTMLResponse from fastapi.staticfiles import StaticFiles webui = _resolve_frontend_dir(frontend_dir) @@ -981,8 +984,44 @@ async def _web_auth_config(): "cd frontend && npm install && npm run build " "(or use --dev for the Vite dev server)." ) - # Mount last so it doesn't shadow the API routes registered above. - app.mount("/", StaticFiles(directory=str(webui), html=True), name="frontend") + + _index_html = (webui / "index.html").read_text(encoding="utf-8") + _ASSET_REF = _re.compile(r'((?:src|href)=")(/[^"?]+)(")') + + def _render_index(request: Request) -> HTMLResponse: + # When behind a query-string API gateway (e.g. an AgentKit runtime + # with the key in the query string), the browser's subresource + # requests for /assets/* must also carry the key. The key arrives as + # the page's querystring; forward it onto every same-origin asset URL + # in the served HTML so those requests pass the gateway too. (The + # app's own API/navigation requests already forward it via auth.ts.) + qs = request.url.query + if not qs: + return HTMLResponse(_index_html) + html = _ASSET_REF.sub( + lambda m: f"{m.group(1)}{m.group(2)}?{qs}{m.group(3)}", _index_html + ) + return HTMLResponse(html) + + # Built assets (the gateway has already authorized the request). + app.mount( + "/assets", StaticFiles(directory=str(webui / "assets")), name="assets" + ) + + @app.get("/") + async def _spa_root(request: Request): + return _render_index(request) + + # SPA fallback: serve real static files as-is, otherwise return the + # (querystring-injected) HTML shell. Registered last so it never shadows + # the API routes above. + @app.get("/{path:path}") + async def _spa_fallback(path: str, request: Request): + candidate = webui / path + if path and candidate.is_file(): + return FileResponse(str(candidate)) + return _render_index(request) + logger.info( f"A2UI UI + API serving on http://{host}:{port} (UI: {webui}, agents: {agents_dir})" ) From 7adaa53cdfa60dd667898a5ce6999619f7446d4e Mon Sep 17 00:00:00 2001 From: "fangyaozheng@bytedance.com" Date: Mon, 15 Jun 2026 14:09:17 +0800 Subject: [PATCH 3/5] docs(examples): make sso_frontend_on_agentkit deploy non-interactive Replace the interactive `veadk agentkit config` step with the non-interactive flag form (account-specific fields auto-create when omitted; runtime_envs are merged), so the whole deploy is copy-paste: config -> launch -> set the callback -> deploy. Note that the gateway adaptations are built into `veadk frontend` from the next release. --- examples/sso_frontend_on_agentkit/README.md | 56 ++++++++++--------- .../sso_frontend_on_agentkit/README.zh.md | 54 +++++++++--------- 2 files changed, 58 insertions(+), 52 deletions(-) diff --git a/examples/sso_frontend_on_agentkit/README.md b/examples/sso_frontend_on_agentkit/README.md index 9f500ce7..2a9c9d46 100644 --- a/examples/sso_frontend_on_agentkit/README.md +++ b/examples/sso_frontend_on_agentkit/README.md @@ -3,7 +3,7 @@ Deploy the VeADK web UI (A2UI) together with **VeIdentity single sign-on** to a [Volcengine AgentKit](https://www.volcengine.com/) runtime. Unauthenticated browsers see a login page; after sign-in the UI and backend agent run as the -logged-in user. +logged-in user. Fully non-interactive — copy the commands to deploy. > 中文版:[README.zh.md](./README.zh.md) @@ -38,6 +38,9 @@ and forwards that header to the container: the key from the query string, the served `index.html` appends the page's querystring to its `/assets/*` URLs so subresource requests carry it too. +> Both adaptations are built into `veadk frontend` from the next release; this +> example self-contains them so it runs on the current release. + ## 1. Prerequisites - A VeIdentity user pool and one of its `WEB_APPLICATION` clients @@ -52,30 +55,29 @@ cp .env.example .env set -a && source .env && set +a ``` -## 2. Configure +## 2. Configure (non-interactive) -Generate `agentkit.yaml` interactively (region, container registry, runtime -role, …): +Account-specific fields (container registry, runtime role, …) are auto-created +when omitted, so there is nothing to fill in by hand: ```bash -veadk agentkit config -``` - -Then add these to `common.runtime_envs` in the generated `agentkit.yaml` -(leave `OAUTH2_REDIRECT_URI` until after the first deploy): - -```yaml - runtime_envs: - MODEL_AGENT_PROVIDER: openai - MODEL_AGENT_NAME: deepseek-v4-flash-260425 - MODEL_AGENT_API_BASE: https://ark.cn-beijing.volces.com/api/v3/ - MODEL_AGENT_API_KEY: - OAUTH2_USER_POOL_ID: - OAUTH2_USER_POOL_CLIENT_ID: - VOLCENGINE_ACCESS_KEY: - VOLCENGINE_SECRET_KEY: - OTEL_SDK_DISABLED: 'true' - VEADK_DISABLE_EXPIRE_AT: 'true' +veadk agentkit config \ + --agent_name sso-frontend-demo \ + --entry_point app.py \ + --language Python --language_version 3.12 \ + --launch_type cloud --region cn-beijing \ + --runtime_name sso-frontend-demo \ + --runtime_auth_type key_auth \ + --runtime_envs MODEL_AGENT_PROVIDER="$MODEL_AGENT_PROVIDER" \ + --runtime_envs MODEL_AGENT_NAME="$MODEL_AGENT_NAME" \ + --runtime_envs MODEL_AGENT_API_BASE="$MODEL_AGENT_API_BASE" \ + --runtime_envs MODEL_AGENT_API_KEY="$MODEL_AGENT_API_KEY" \ + --runtime_envs OAUTH2_USER_POOL_ID="$OAUTH2_USER_POOL_ID" \ + --runtime_envs OAUTH2_USER_POOL_CLIENT_ID="$OAUTH2_USER_POOL_CLIENT_ID" \ + --runtime_envs VOLCENGINE_ACCESS_KEY="$VOLCENGINE_ACCESS_KEY" \ + --runtime_envs VOLCENGINE_SECRET_KEY="$VOLCENGINE_SECRET_KEY" \ + --runtime_envs OTEL_SDK_DISABLED=true \ + --runtime_envs VEADK_DISABLE_EXPIRE_AT=true ``` ## 3. Deploy @@ -85,11 +87,12 @@ Then add these to `common.runtime_envs` in the generated `agentkit.yaml` veadk agentkit launch ``` -Set the callback to the printed endpoint and update the runtime once more: +Set the callback to the printed endpoint (merged into the existing +`runtime_envs`) and update the runtime once more: ```bash -# Add to runtime_envs in agentkit.yaml: -# OAUTH2_REDIRECT_URI: https:///oauth2/callback +veadk agentkit config \ + --runtime_envs OAUTH2_REDIRECT_URI=https:///oauth2/callback veadk agentkit deploy ``` @@ -117,5 +120,6 @@ cookie and the UI and agent API work. - **AK/SK**: used by `veadk agentkit` to build and deploy, and injected into the runtime so the app can call the VeIdentity API (resolve the pool, register the callback). -- **Redeploy**: after editing `runtime_envs`, re-run `veadk agentkit deploy` +- **Redeploy**: after changing an env var, merge it with + `veadk agentkit config --runtime_envs K=V` and re-run `veadk agentkit deploy` (image layers are reused). Tear down with `veadk agentkit destroy`. diff --git a/examples/sso_frontend_on_agentkit/README.zh.md b/examples/sso_frontend_on_agentkit/README.zh.md index 177f671c..3bd813dc 100644 --- a/examples/sso_frontend_on_agentkit/README.zh.md +++ b/examples/sso_frontend_on_agentkit/README.zh.md @@ -2,7 +2,7 @@ 将 VeADK 的 Web 界面(A2UI)连同 **VeIdentity 单点登录** 一起部署到 [火山引擎 AgentKit](https://www.volcengine.com/) 运行时。未登录的浏览器看到登录页, -登录后以登录用户的身份使用界面与后端 Agent。 +登录后以登录用户的身份使用界面与后端 Agent。全程非交互,复制命令即可部署。 > English version: [README.md](./README.md) @@ -32,6 +32,9 @@ key 放在 `Authorization: Bearer ` 请求头里,并把该头透传给容 - **静态资源透传 querystring**:若网关改为从查询串取 key,浏览器加载 `/assets/*` 也需带上 key。返回的 `index.html` 会把页面的查询串拼到各静态资源 URL 上。 +> 这两处适配自下一个版本起已内置进 `veadk frontend`,本示例自带它们以便在当前 +> 发布版上直接运行。 + ## 1. 前置准备 - 一个 VeIdentity 用户池及其下的一个 `WEB_APPLICATION` 客户端 @@ -46,29 +49,28 @@ cp .env.example .env set -a && source .env && set +a ``` -## 2. 生成配置 +## 2. 生成配置(非交互) -交互式生成 `agentkit.yaml`(按提示填写区域、镜像仓库、运行时角色等账号相关字段): +账号相关字段(镜像仓库、运行时角色等)省略即自动创建,无需逐项填写: ```bash -veadk agentkit config -``` - -随后在生成的 `agentkit.yaml` 的 `common.runtime_envs` 下补齐以下环境变量 -(`OAUTH2_REDIRECT_URI` 先留空,部署拿到地址后再填): - -```yaml - runtime_envs: - MODEL_AGENT_PROVIDER: openai - MODEL_AGENT_NAME: deepseek-v4-flash-260425 - MODEL_AGENT_API_BASE: https://ark.cn-beijing.volces.com/api/v3/ - MODEL_AGENT_API_KEY: - OAUTH2_USER_POOL_ID: - OAUTH2_USER_POOL_CLIENT_ID: - VOLCENGINE_ACCESS_KEY: - VOLCENGINE_SECRET_KEY: - OTEL_SDK_DISABLED: 'true' - VEADK_DISABLE_EXPIRE_AT: 'true' +veadk agentkit config \ + --agent_name sso-frontend-demo \ + --entry_point app.py \ + --language Python --language_version 3.12 \ + --launch_type cloud --region cn-beijing \ + --runtime_name sso-frontend-demo \ + --runtime_auth_type key_auth \ + --runtime_envs MODEL_AGENT_PROVIDER="$MODEL_AGENT_PROVIDER" \ + --runtime_envs MODEL_AGENT_NAME="$MODEL_AGENT_NAME" \ + --runtime_envs MODEL_AGENT_API_BASE="$MODEL_AGENT_API_BASE" \ + --runtime_envs MODEL_AGENT_API_KEY="$MODEL_AGENT_API_KEY" \ + --runtime_envs OAUTH2_USER_POOL_ID="$OAUTH2_USER_POOL_ID" \ + --runtime_envs OAUTH2_USER_POOL_CLIENT_ID="$OAUTH2_USER_POOL_CLIENT_ID" \ + --runtime_envs VOLCENGINE_ACCESS_KEY="$VOLCENGINE_ACCESS_KEY" \ + --runtime_envs VOLCENGINE_SECRET_KEY="$VOLCENGINE_SECRET_KEY" \ + --runtime_envs OTEL_SDK_DISABLED=true \ + --runtime_envs VEADK_DISABLE_EXPIRE_AT=true ``` ## 3. 部署 @@ -78,11 +80,11 @@ veadk agentkit config veadk agentkit launch ``` -把上一步输出的 endpoint 写回回调地址,再更新一次运行时: +把上一步输出的 endpoint 填入回调地址(会合并进现有 `runtime_envs`),再更新运行时: ```bash -# 在 agentkit.yaml 的 runtime_envs 里加一行: -# OAUTH2_REDIRECT_URI: https:///oauth2/callback +veadk agentkit config \ + --runtime_envs OAUTH2_REDIRECT_URI=https:///oauth2/callback veadk agentkit deploy ``` @@ -106,5 +108,5 @@ Authorization: Bearer - **模型**:`MODEL_AGENT_*` 用普通的火山引擎 Ark chat 模型即可。 - **AK/SK**:既供 `veadk agentkit` 构建部署使用,也注入运行时,供应用调用 VeIdentity API (解析用户池、注册回调)。 -- **重新部署**:改动 `agentkit.yaml` 的 `runtime_envs` 后重跑 `veadk agentkit deploy` - 即可,镜像层会复用,速度很快。用 `veadk agentkit destroy` 拆除。 +- **重新部署**:改动环境变量后,`veadk agentkit config --runtime_envs K=V` 合并后重跑 + `veadk agentkit deploy` 即可,镜像层会复用。用 `veadk agentkit destroy` 拆除。 From 645b35b75f61c6e6355d7508172456eb0c14e428 Mon Sep 17 00:00:00 2001 From: "fangyaozheng@bytedance.com" Date: Mon, 15 Jun 2026 14:22:56 +0800 Subject: [PATCH 4/5] docs(examples): slim the SSO deploy command to runtime-required envs Only the two VeIdentity UIDs (OAUTH2_USER_POOL_ID / OAUTH2_USER_POOL_CLIENT_ID, which hold the pool/client UIDs) go into --runtime_envs. Drop MODEL_AGENT_* (the model is provided by the runtime) and the Volcengine AK/SK (the runtime uses its service-role credentials for the VeIdentity API; AK/SK stay in .env only for the local build & deploy). Also explain why OAUTH2_REDIRECT_URI is needed on the runtime but not locally. --- .../sso_frontend_on_agentkit/.env.example | 26 ++++------------ examples/sso_frontend_on_agentkit/README.md | 31 ++++++++++--------- .../sso_frontend_on_agentkit/README.zh.md | 28 ++++++++--------- 3 files changed, 36 insertions(+), 49 deletions(-) diff --git a/examples/sso_frontend_on_agentkit/.env.example b/examples/sso_frontend_on_agentkit/.env.example index 6126d56f..48829efb 100644 --- a/examples/sso_frontend_on_agentkit/.env.example +++ b/examples/sso_frontend_on_agentkit/.env.example @@ -1,28 +1,14 @@ -# Copy this file to `.env` and fill in your values. -# `veadk agentkit config` reads these into the runtime environment variables. - -# --- Agent model (Volcengine Ark) --- -MODEL_AGENT_PROVIDER=openai -MODEL_AGENT_NAME=deepseek-v4-flash-260425 -MODEL_AGENT_API_BASE=https://ark.cn-beijing.volces.com/api/v3/ -MODEL_AGENT_API_KEY=your-ark-api-key-here +# Copy this file to `.env`, fill in your values, then: +# set -a && source .env && set +a # --- Volcengine AK/SK --- -# Used by `veadk agentkit` to build & deploy, AND injected into the runtime so the -# app can talk to the VeIdentity API (resolve the user pool, register the callback). -# Create access keys at https://console.volcengine.com/iam/keymanage +# Used locally by `veadk agentkit` to authenticate the build & deploy. NOT +# injected into the runtime (it calls the VeIdentity API with its service-role +# credentials). Create access keys at https://console.volcengine.com/iam/keymanage VOLCENGINE_ACCESS_KEY=your-volcengine-access-key VOLCENGINE_SECRET_KEY=your-volcengine-secret-key # --- SSO: VeIdentity user pool (https://console.volcengine.com/veidentity) --- -# UID of an existing user pool and one of its WEB_APPLICATION clients. +# The UID of an existing user pool and one of its WEB_APPLICATION clients. OAUTH2_USER_POOL_ID=your-user-pool-uid OAUTH2_USER_POOL_CLIENT_ID=your-user-pool-client-uid - -# OAuth2 callback. Leave blank for the first deploy; after `veadk agentkit launch` -# prints the endpoint, set this to /oauth2/callback and redeploy. -OAUTH2_REDIRECT_URI= - -# --- Misc --- -OTEL_SDK_DISABLED=true -VEADK_DISABLE_EXPIRE_AT=true diff --git a/examples/sso_frontend_on_agentkit/README.md b/examples/sso_frontend_on_agentkit/README.md index 2a9c9d46..2c5833c0 100644 --- a/examples/sso_frontend_on_agentkit/README.md +++ b/examples/sso_frontend_on_agentkit/README.md @@ -45,20 +45,23 @@ and forwards that header to the container: - A VeIdentity user pool and one of its `WEB_APPLICATION` clients () — note both **UIDs**. -- A Volcengine Ark model API key and your account AK/SK. +- Your account AK/SK, for the local `veadk agentkit` build & deploy. The model + and runtime credentials are provided by the AgentKit runtime — nothing to set. ```bash cd examples/sso_frontend_on_agentkit cp .env.example .env -# Fill: MODEL_AGENT_API_KEY, VOLCENGINE_ACCESS_KEY/SECRET_KEY, -# OAUTH2_USER_POOL_ID, OAUTH2_USER_POOL_CLIENT_ID +# Edit .env: +# VOLCENGINE_ACCESS_KEY / VOLCENGINE_SECRET_KEY (local deploy auth) +# OAUTH2_USER_POOL_ID / OAUTH2_USER_POOL_CLIENT_ID (pool & client UIDs) set -a && source .env && set +a ``` ## 2. Configure (non-interactive) Account-specific fields (container registry, runtime role, …) are auto-created -when omitted, so there is nothing to fill in by hand: +when omitted. The runtime only needs the two UIDs — the model and access +credentials are provided by the runtime, so they are not in `--runtime_envs`: ```bash veadk agentkit config \ @@ -68,14 +71,8 @@ veadk agentkit config \ --launch_type cloud --region cn-beijing \ --runtime_name sso-frontend-demo \ --runtime_auth_type key_auth \ - --runtime_envs MODEL_AGENT_PROVIDER="$MODEL_AGENT_PROVIDER" \ - --runtime_envs MODEL_AGENT_NAME="$MODEL_AGENT_NAME" \ - --runtime_envs MODEL_AGENT_API_BASE="$MODEL_AGENT_API_BASE" \ - --runtime_envs MODEL_AGENT_API_KEY="$MODEL_AGENT_API_KEY" \ --runtime_envs OAUTH2_USER_POOL_ID="$OAUTH2_USER_POOL_ID" \ --runtime_envs OAUTH2_USER_POOL_CLIENT_ID="$OAUTH2_USER_POOL_CLIENT_ID" \ - --runtime_envs VOLCENGINE_ACCESS_KEY="$VOLCENGINE_ACCESS_KEY" \ - --runtime_envs VOLCENGINE_SECRET_KEY="$VOLCENGINE_SECRET_KEY" \ --runtime_envs OTEL_SDK_DISABLED=true \ --runtime_envs VEADK_DISABLE_EXPIRE_AT=true ``` @@ -96,7 +93,10 @@ veadk agentkit config \ veadk agentkit deploy ``` -The callback URL is registered with the user pool client automatically. +Locally the callback defaults to `http://127.0.0.1:8000/oauth2/callback`, so no +env var is needed; once deployed the browser hits the public endpoint, which is +only known after the runtime is created — hence this separate step. The callback +is registered with the user pool client automatically. ## 4. Access @@ -116,10 +116,11 @@ cookie and the UI and agent API work. ## Notes -- **Model**: any Volcengine Ark chat model for `MODEL_AGENT_*`. -- **AK/SK**: used by `veadk agentkit` to build and deploy, and injected into the - runtime so the app can call the VeIdentity API (resolve the pool, register the - callback). +- **Model**: provided by the AgentKit runtime; to pin one, add + `--runtime_envs MODEL_AGENT_NAME=... --runtime_envs MODEL_AGENT_API_KEY=...`. +- **AK/SK**: used only by the local `veadk agentkit` build & deploy; on the + runtime the VeIdentity API calls (resolve the pool, register the callback) use + the runtime's service-role credentials, so AK/SK are not injected. - **Redeploy**: after changing an env var, merge it with `veadk agentkit config --runtime_envs K=V` and re-run `veadk agentkit deploy` (image layers are reused). Tear down with `veadk agentkit destroy`. diff --git a/examples/sso_frontend_on_agentkit/README.zh.md b/examples/sso_frontend_on_agentkit/README.zh.md index 3bd813dc..eba46f7d 100644 --- a/examples/sso_frontend_on_agentkit/README.zh.md +++ b/examples/sso_frontend_on_agentkit/README.zh.md @@ -39,19 +39,22 @@ key 放在 `Authorization: Bearer ` 请求头里,并把该头透传给容 - 一个 VeIdentity 用户池及其下的一个 `WEB_APPLICATION` 客户端 (控制台:),记下两者的 **UID**。 -- 一个火山引擎 Ark 模型 API Key,以及账号的 AK/SK。 +- 账号的 AK/SK,供本地 `veadk agentkit` 构建与部署鉴权。模型与运行时凭证由 + AgentKit 运行时提供,无需手动设置。 ```bash cd examples/sso_frontend_on_agentkit cp .env.example .env -# 编辑 .env:MODEL_AGENT_API_KEY、VOLCENGINE_ACCESS_KEY/SECRET_KEY、 -# OAUTH2_USER_POOL_ID、OAUTH2_USER_POOL_CLIENT_ID +# 编辑 .env: +# VOLCENGINE_ACCESS_KEY / VOLCENGINE_SECRET_KEY (本地部署鉴权用) +# OAUTH2_USER_POOL_ID / OAUTH2_USER_POOL_CLIENT_ID (用户池、客户端的 UID) set -a && source .env && set +a ``` ## 2. 生成配置(非交互) -账号相关字段(镜像仓库、运行时角色等)省略即自动创建,无需逐项填写: +账号相关字段(镜像仓库、运行时角色等)省略即自动创建。运行时只需两个 UID +——模型和访问凭证由运行时提供,无需写进 `--runtime_envs`: ```bash veadk agentkit config \ @@ -61,14 +64,8 @@ veadk agentkit config \ --launch_type cloud --region cn-beijing \ --runtime_name sso-frontend-demo \ --runtime_auth_type key_auth \ - --runtime_envs MODEL_AGENT_PROVIDER="$MODEL_AGENT_PROVIDER" \ - --runtime_envs MODEL_AGENT_NAME="$MODEL_AGENT_NAME" \ - --runtime_envs MODEL_AGENT_API_BASE="$MODEL_AGENT_API_BASE" \ - --runtime_envs MODEL_AGENT_API_KEY="$MODEL_AGENT_API_KEY" \ --runtime_envs OAUTH2_USER_POOL_ID="$OAUTH2_USER_POOL_ID" \ --runtime_envs OAUTH2_USER_POOL_CLIENT_ID="$OAUTH2_USER_POOL_CLIENT_ID" \ - --runtime_envs VOLCENGINE_ACCESS_KEY="$VOLCENGINE_ACCESS_KEY" \ - --runtime_envs VOLCENGINE_SECRET_KEY="$VOLCENGINE_SECRET_KEY" \ --runtime_envs OTEL_SDK_DISABLED=true \ --runtime_envs VEADK_DISABLE_EXPIRE_AT=true ``` @@ -88,7 +85,9 @@ veadk agentkit config \ veadk agentkit deploy ``` -回调地址会自动注册到用户池客户端,云端无需手动添加。 +本地启动时默认回调即 `http://127.0.0.1:8000/oauth2/callback`,无需设置;部署后浏览器 +访问的是公网 endpoint,而它只在运行时创建后才知道,因此这一步单独设置——回调会自动 +注册到用户池客户端,云端无需手动添加。 ## 4. 访问 @@ -105,8 +104,9 @@ Authorization: Bearer ## 注意 -- **模型**:`MODEL_AGENT_*` 用普通的火山引擎 Ark chat 模型即可。 -- **AK/SK**:既供 `veadk agentkit` 构建部署使用,也注入运行时,供应用调用 VeIdentity API - (解析用户池、注册回调)。 +- **模型**:由 AgentKit 运行时提供;如需指定,可自行加 + `--runtime_envs MODEL_AGENT_NAME=... --runtime_envs MODEL_AGENT_API_KEY=...`。 +- **AK/SK**:仅本地 `veadk agentkit` 构建部署使用;运行时调用 VeIdentity API + (解析用户池、注册回调)走运行时角色凭证,无需注入。 - **重新部署**:改动环境变量后,`veadk agentkit config --runtime_envs K=V` 合并后重跑 `veadk agentkit deploy` 即可,镜像层会复用。用 `veadk agentkit destroy` 拆除。 From a297a8532a006ce3eb1259cca8ddc4250abfe81b Mon Sep 17 00:00:00 2001 From: "fangyaozheng@bytedance.com" Date: Mon, 15 Jun 2026 14:44:41 +0800 Subject: [PATCH 5/5] docs(examples): require AK/SK in runtime_envs for the SSO deploy Verified end-to-end against an AgentKit runtime: the runtime has no usable role credentials (the instance-metadata endpoint times out), so VeIdentity API calls need AK/SK explicitly. Without them the container crashes on startup with "User pool ... not found". Put VOLCENGINE_ACCESS_KEY/SECRET_KEY back into the config command and fix the surrounding notes. The model stays out of runtime_envs (the UI serves without it; the agent loads lazily). --- .../sso_frontend_on_agentkit/.env.example | 6 +++--- examples/sso_frontend_on_agentkit/README.md | 19 ++++++++++++------- .../sso_frontend_on_agentkit/README.zh.md | 15 +++++++++------ 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/examples/sso_frontend_on_agentkit/.env.example b/examples/sso_frontend_on_agentkit/.env.example index 48829efb..12f454e5 100644 --- a/examples/sso_frontend_on_agentkit/.env.example +++ b/examples/sso_frontend_on_agentkit/.env.example @@ -2,9 +2,9 @@ # set -a && source .env && set +a # --- Volcengine AK/SK --- -# Used locally by `veadk agentkit` to authenticate the build & deploy. NOT -# injected into the runtime (it calls the VeIdentity API with its service-role -# credentials). Create access keys at https://console.volcengine.com/iam/keymanage +# Used by `veadk agentkit` to authenticate the build & deploy, and injected into +# the runtime so the app can call the VeIdentity API (the runtime has no usable +# role credentials). Create access keys at https://console.volcengine.com/iam/keymanage VOLCENGINE_ACCESS_KEY=your-volcengine-access-key VOLCENGINE_SECRET_KEY=your-volcengine-secret-key diff --git a/examples/sso_frontend_on_agentkit/README.md b/examples/sso_frontend_on_agentkit/README.md index 2c5833c0..86bdb03b 100644 --- a/examples/sso_frontend_on_agentkit/README.md +++ b/examples/sso_frontend_on_agentkit/README.md @@ -45,8 +45,9 @@ and forwards that header to the container: - A VeIdentity user pool and one of its `WEB_APPLICATION` clients () — note both **UIDs**. -- Your account AK/SK, for the local `veadk agentkit` build & deploy. The model - and runtime credentials are provided by the AgentKit runtime — nothing to set. +- Your account AK/SK — used both by the local `veadk agentkit` build & deploy + and by the runtime to call the VeIdentity API (the runtime has no usable role + credentials, so they must be injected). The model is provided by the runtime. ```bash cd examples/sso_frontend_on_agentkit @@ -60,8 +61,9 @@ set -a && source .env && set +a ## 2. Configure (non-interactive) Account-specific fields (container registry, runtime role, …) are auto-created -when omitted. The runtime only needs the two UIDs — the model and access -credentials are provided by the runtime, so they are not in `--runtime_envs`: +when omitted. The runtime needs the two UIDs and AK/SK — it has no usable role +credentials, so AK/SK must be injected to call the VeIdentity API. The model is +provided by the runtime, so it is not in `--runtime_envs`: ```bash veadk agentkit config \ @@ -73,6 +75,8 @@ veadk agentkit config \ --runtime_auth_type key_auth \ --runtime_envs OAUTH2_USER_POOL_ID="$OAUTH2_USER_POOL_ID" \ --runtime_envs OAUTH2_USER_POOL_CLIENT_ID="$OAUTH2_USER_POOL_CLIENT_ID" \ + --runtime_envs VOLCENGINE_ACCESS_KEY="$VOLCENGINE_ACCESS_KEY" \ + --runtime_envs VOLCENGINE_SECRET_KEY="$VOLCENGINE_SECRET_KEY" \ --runtime_envs OTEL_SDK_DISABLED=true \ --runtime_envs VEADK_DISABLE_EXPIRE_AT=true ``` @@ -118,9 +122,10 @@ cookie and the UI and agent API work. - **Model**: provided by the AgentKit runtime; to pin one, add `--runtime_envs MODEL_AGENT_NAME=... --runtime_envs MODEL_AGENT_API_KEY=...`. -- **AK/SK**: used only by the local `veadk agentkit` build & deploy; on the - runtime the VeIdentity API calls (resolve the pool, register the callback) use - the runtime's service-role credentials, so AK/SK are not injected. +- **AK/SK**: used by the local `veadk agentkit` build & deploy **and** on the + runtime for the VeIdentity API calls (resolve the pool, register the callback). + The runtime has no usable role credentials (IMDS times out), so AK/SK must be + injected or the container crashes on startup. - **Redeploy**: after changing an env var, merge it with `veadk agentkit config --runtime_envs K=V` and re-run `veadk agentkit deploy` (image layers are reused). Tear down with `veadk agentkit destroy`. diff --git a/examples/sso_frontend_on_agentkit/README.zh.md b/examples/sso_frontend_on_agentkit/README.zh.md index eba46f7d..0958cdff 100644 --- a/examples/sso_frontend_on_agentkit/README.zh.md +++ b/examples/sso_frontend_on_agentkit/README.zh.md @@ -39,8 +39,8 @@ key 放在 `Authorization: Bearer ` 请求头里,并把该头透传给容 - 一个 VeIdentity 用户池及其下的一个 `WEB_APPLICATION` 客户端 (控制台:),记下两者的 **UID**。 -- 账号的 AK/SK,供本地 `veadk agentkit` 构建与部署鉴权。模型与运行时凭证由 - AgentKit 运行时提供,无需手动设置。 +- 账号的 AK/SK:本地 `veadk agentkit` 构建部署,以及运行时调用 VeIdentity API + 都要用(运行时取不到角色凭证,必须注入)。模型由 AgentKit 运行时提供。 ```bash cd examples/sso_frontend_on_agentkit @@ -53,8 +53,9 @@ set -a && source .env && set +a ## 2. 生成配置(非交互) -账号相关字段(镜像仓库、运行时角色等)省略即自动创建。运行时只需两个 UID -——模型和访问凭证由运行时提供,无需写进 `--runtime_envs`: +账号相关字段(镜像仓库、运行时角色等)省略即自动创建。运行时需要两个 UID 和 +AK/SK——运行时取不到角色凭证,调用 VeIdentity API 必须注入 AK/SK;模型由运行时 +提供,无需写进 `--runtime_envs`: ```bash veadk agentkit config \ @@ -66,6 +67,8 @@ veadk agentkit config \ --runtime_auth_type key_auth \ --runtime_envs OAUTH2_USER_POOL_ID="$OAUTH2_USER_POOL_ID" \ --runtime_envs OAUTH2_USER_POOL_CLIENT_ID="$OAUTH2_USER_POOL_CLIENT_ID" \ + --runtime_envs VOLCENGINE_ACCESS_KEY="$VOLCENGINE_ACCESS_KEY" \ + --runtime_envs VOLCENGINE_SECRET_KEY="$VOLCENGINE_SECRET_KEY" \ --runtime_envs OTEL_SDK_DISABLED=true \ --runtime_envs VEADK_DISABLE_EXPIRE_AT=true ``` @@ -106,7 +109,7 @@ Authorization: Bearer - **模型**:由 AgentKit 运行时提供;如需指定,可自行加 `--runtime_envs MODEL_AGENT_NAME=... --runtime_envs MODEL_AGENT_API_KEY=...`。 -- **AK/SK**:仅本地 `veadk agentkit` 构建部署使用;运行时调用 VeIdentity API - (解析用户池、注册回调)走运行时角色凭证,无需注入。 +- **AK/SK**:本地构建部署要用;运行时调用 VeIdentity API(解析用户池、注册回调) + 也要用——运行时取不到角色凭证(IMDS 超时),必须注入 `runtime_envs`,否则容器启动即崩。 - **重新部署**:改动环境变量后,`veadk agentkit config --runtime_envs K=V` 合并后重跑 `veadk agentkit deploy` 即可,镜像层会复用。用 `veadk agentkit destroy` 拆除。