Files
beaver_project/README.md
steven_li ebfa242862 feat(outlook): 添加Outlook集成功能支持
添加完整的Outlook MCP集成,包括邮件和日历功能,通过AuthZ模式进行认证和权限管理,
支持邮箱连接、断开、状态检查和数据同步等功能。

fix(config): 统一配置文件路径从.nanobot到.beaver

将配置文件路径从/root/.nanobot统一更改为/root/.beaver,更新Dockerfile中的环境变量定义,
确保所有组件使用一致的配置目录结构。

feat(agent): 添加代理删除功能和助手身份提示

为代理注册表添加delete_agent方法,实现代理的动态删除功能;同时添加海狸助手身份提示,
确保AI助手在交互中保持一致的身份认知。

feat(engine): 增强引擎循环并添加意图决策快照

扩展AgentLoop类,添加intent_agent_decision参数用于意图驱动的代理决策,并在会话中记录
决策快照,便于后续分析和调试。

feat(authz): 扩展认证客户端功能

为AuthzClient添加设置权限、用户注册、后端注册和Outlook设置管理等新方法,增强系统
的认证和授权能力。
2026-05-14 16:01:46 +08:00

417 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

https://d3qpg7p2n3hazf.cloudfront.net/api/v1/client/subscribe?token=2185761c5925a800c2d2c1ec44449b65
# nano_project
单机部署版运行结构:
- `auth-portal`
- 用户入口页,提供登录、注册、跳转。
- `authz-service`
- AuthZ 服务。
- 现在负责注册编排:`auth-portal -> authz-service -> deploy-control -> app-instance -> authz-service`
- `deploy-control`
- 部署机控制面。
- 负责创建实例、解析实例、刷新反向代理。
- `router-proxy`
- 统一入口代理。
- 按 Host 把 `<slug>.<base_domain>` 转发到对应实例容器。
- `app-instance`
- 真正的单用户实例。
- 一个容器里同时包含前端、后端和 Nginx。
## 请求链路
注册:
```text
Browser
-> auth-portal
-> authz-service POST /portal/register
-> deploy-control POST /api/instances/register
-> create-instance.sh
-> app-instance POST /api/auth/register
-> authz-service /oauth/register or /backends/register
```
登录:
```text
Browser
-> auth-portal
-> deploy-control POST /api/instances/resolve
-> app-instance POST /api/auth/login
```
## 这份部署指南的前提
这份 README 只覆盖一套基准方案:
- 一台 Linux 服务器
- 用 Docker 运行 `auth-portal``authz-service``deploy-control``router-proxy`
- `deploy-control` 通过 Docker socket 在同一台机器上创建 `app-instance`
- 所有容器共用一个 Docker network`nano-instance-edge`
- 测试域名先用 `nip.io`
如果你后面要接 HTTPS、外部 LB、Kubernetes、真实 DNS这份流程仍然适合作为最小可运行基线。
可直接参考这些模板文件:
- [`.env.example`](/home/ivan/xuan/nano_project/.env.example)
- [`auth-portal/src/.env.example`](/home/ivan/xuan/nano_project/auth-portal/src/.env.example)
- [`authz-service/.env.example`](/home/ivan/xuan/nano_project/authz-service/.env.example)
- [`deploy-control/.env.example`](/home/ivan/xuan/nano_project/deploy-control/.env.example)
- [`router-proxy/.env.example`](/home/ivan/xuan/nano_project/router-proxy/.env.example)
注意:
- 这些文件是模板,不会被现有脚本自动加载
- 你可以手动 `export`,或者在 `docker run` 时使用 `--env-file`
## 部署前必须先定好的值
先准备这些值:
- `PROJECT_ROOT`
- 仓库根目录。
- `NANO_NET`
- Docker network 名。
- 推荐固定成 `nano-instance-edge`
- `NANO_DEPLOY_TOKEN`
- `auth-portal` / `authz-service``deploy-control` 时用的 Bearer token。
- `NANO_AUTHZ_INTERNAL_TOKEN`
- AuthZ 内部接口 token。
- `NANO_SERVER_IP`
- 服务器公网 IP`nip.io` 测试使用。
- `NANO_BASE_DOMAIN`
- 实例基域名。
- 测试环境推荐 `<SERVER_IP>.nip.io`
- `NANO_PROVIDER`
- 默认 provider例如 `openai`
- `NANO_MODEL`
- 默认模型,例如 `openai/gpt-5`
- `NANO_API_KEY`
- 默认分发给新实例的 provider API key
- `NANO_API_BASE`
- 可空,自定义 provider base URL
- `NANO_AUTHZ_URL`
- 这个值必须是 `app-instance` 容器能访问到的 AuthZ 地址
- `NANO_OUTLOOK_MCP_URL`
- 可空。
- 如果配置了,所有新创建的 `app-instance` 都会默认带一个 Outlook MCP HTTP 工具配置。
- `NANO_OUTLOOK_MCP_SERVER_ID`
- Outlook MCP 默认 server id。
- 推荐固定成 `outlook_mcp`
- `NANO_DEPLOY_URL`
- `auth-portal``authz-service` 在容器网络里访问 deploy-control 的地址
直接导出一套最小配置:
```bash
export PROJECT_ROOT=/home/ivan/xuan/nano_project
export NANO_NET=nano-instance-edge
export NANO_DEPLOY_TOKEN="$(openssl rand -hex 32)"
export NANO_AUTHZ_INTERNAL_TOKEN="$(openssl rand -hex 32)"
export NANO_SERVER_IP=203.0.113.10
export NANO_BASE_DOMAIN="${NANO_SERVER_IP}.nip.io"
export NANO_PROVIDER=openai
export NANO_MODEL=openai/gpt-5
export NANO_API_KEY='sk-xxxxxxxx'
export NANO_API_BASE=''
export NANO_AUTHZ_URL='http://nano-authz-service:19090'
export NANO_OUTLOOK_MCP_URL=''
export NANO_OUTLOOK_MCP_SERVER_ID='outlook_mcp'
export NANO_DEPLOY_URL='http://nano-deploy-control:8090'
```
## 变量到底是什么
最容易混淆的是下面这几组:
- `DEPLOY_API_TOKEN`
- 调用方带出去的 token。
- `auth-portal``authz-service` 会用它请求 `deploy-control`
- `DEPLOY_CONTROL_API_TOKEN`
- `deploy-control` 服务端校验的 token。
- 它必须和 `DEPLOY_API_TOKEN` 相等。
- `AUTHZ_ISSUER`
- 当前实现里它既是 AuthZ 的 issuer也是新实例要访问的 AuthZ base URL。
- 所以不要乱写 `127.0.0.1`,要写成实例容器能访问到的地址。
- `APP_INSTANCE_PROVIDER`
- 新实例默认 provider。
- `APP_INSTANCE_MODEL`
- 新实例默认模型。
- `APP_INSTANCE_API_KEY`
- 新实例默认 API key。
- `APP_INSTANCE_API_BASE`
- 新实例默认 API base。
- `DEFAULT_AUTHZ_BASE_URL`
- `deploy-control` 在没收到显式 `authz_base_url` 时,给新实例写入的兜底 AuthZ 地址。
当前版本里provider 配置不是从 AuthZ setting 下发的。
它是在创建实例时由 `deploy-control` 写入 `app-instance``config.json`
## 目录持久化
至少保留这几个目录:
- `authz-service/runtime/data`
- `app-instance/runtime`
- `router-proxy/runtime`
建议先创建:
```bash
mkdir -p \
"$PROJECT_ROOT/authz-service/runtime/data" \
"$PROJECT_ROOT/app-instance/runtime/instances" \
"$PROJECT_ROOT/app-instance/runtime/registry" \
"$PROJECT_ROOT/router-proxy/runtime/conf.d"
```
## 1. 构建镜像
先把四个镜像都构建好。虽然 `deploy-control` 在镜像缺失时也能触发构建,但上线前先显式构建更容易排错。
```bash
cd "$PROJECT_ROOT"
docker build -t nano/app-instance:latest app-instance
docker build -t nano/authz-service:latest authz-service
docker build -t nano/deploy-control:latest deploy-control
docker build -t nano/auth-portal:latest auth-portal/src
```
## 2. 创建共享网络
```bash
docker network inspect "$NANO_NET" >/dev/null 2>&1 || docker network create "$NANO_NET"
```
## 3. 启动 router-proxy
```bash
cd "$PROJECT_ROOT"
PROXY_NETWORK_NAME="$NANO_NET" \
PROXY_HTTP_PORT=8088 \
./router-proxy/start-proxy.sh --replace
```
默认对外入口:
- `router-proxy`: `http://<slug>.<base_domain>:8088`
## 4. 启动 authz-service
```bash
docker rm -f nano-authz-service >/dev/null 2>&1 || true
docker run -d \
--name nano-authz-service \
--restart unless-stopped \
--network "$NANO_NET" \
-p 19090:19090 \
-v "$PROJECT_ROOT/authz-service/runtime/data:/var/lib/authz-service/data" \
-e AUTHZ_ISSUER="$NANO_AUTHZ_URL" \
-e AUTHZ_INTERNAL_TOKEN="$NANO_AUTHZ_INTERNAL_TOKEN" \
-e DEPLOY_API_BASE_URL="$NANO_DEPLOY_URL" \
-e DEPLOY_API_TOKEN="$NANO_DEPLOY_TOKEN" \
nano/authz-service:latest
```
这里最重要的是:
- `AUTHZ_ISSUER` 现在不能只按“外部访问地址”理解
- 它必须是后续 `app-instance` 容器也能访问到的地址
- 这套单机 Docker 方案里直接用 `http://nano-authz-service:19090`
## 5. 启动 deploy-control
`deploy-control` 需要高权限:
- 读写 Docker socket
- 访问 `app-instance/`
- 访问 `router-proxy/`
```bash
docker rm -f nano-deploy-control >/dev/null 2>&1 || true
docker run -d \
--name nano-deploy-control \
--restart unless-stopped \
--network "$NANO_NET" \
-p 8090:8090 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "$PROJECT_ROOT/app-instance:$PROJECT_ROOT/app-instance" \
-v "$PROJECT_ROOT/router-proxy:$PROJECT_ROOT/router-proxy" \
-e APP_INSTANCE_DIR="$PROJECT_ROOT/app-instance" \
-e ROUTER_PROXY_DIR="$PROJECT_ROOT/router-proxy" \
-e DEPLOY_CONTROL_API_TOKEN="$NANO_DEPLOY_TOKEN" \
-e APP_INSTANCE_IMAGE="nano/app-instance:latest" \
-e APP_INSTANCE_NETWORK_NAME="$NANO_NET" \
-e APP_INSTANCE_PROVIDER="$NANO_PROVIDER" \
-e APP_INSTANCE_MODEL="$NANO_MODEL" \
-e APP_INSTANCE_API_KEY="$NANO_API_KEY" \
-e APP_INSTANCE_API_BASE="$NANO_API_BASE" \
-e DEFAULT_AUTHZ_BASE_URL="$NANO_AUTHZ_URL" \
-e DEFAULT_AUTHZ_OUTLOOK_MCP_URL="$NANO_OUTLOOK_MCP_URL" \
-e DEFAULT_OUTLOOK_MCP_SERVER_ID="$NANO_OUTLOOK_MCP_SERVER_ID" \
-e DEPLOY_PUBLIC_SCHEME="http" \
-e DEPLOY_PUBLIC_BASE_DOMAIN="$NANO_BASE_DOMAIN" \
-e DEPLOY_PUBLIC_PORT="8088" \
-e DEPLOY_AUTO_START_PROXY="1" \
nano/deploy-control:latest
```
这里不要把宿主机目录挂到容器内的另一个短路径,比如 `/app-instance`
原因是 `deploy-control` 会通过挂载进来的 Docker socket 再去创建 `app-instance` 容器;这时传给 Docker 的 bind mount 源路径必须是宿主机真实路径。如果你把宿主机目录映射成容器内短路径,`create-instance.sh` 生成的挂载源就会变成错误路径,最终表现为:
- 注册接口超时
- `app-instance` 容器反复重启
- 日志里出现 `Missing Boardware Genius config: /root/.beaver/config.json`
当前版本里,新实例的默认大模型配置就是从这里分发的:
- `APP_INSTANCE_PROVIDER`
- `APP_INSTANCE_MODEL`
- `APP_INSTANCE_API_KEY`
- `APP_INSTANCE_API_BASE`
如果 `APP_INSTANCE_API_KEY` 没配,新用户注册时创建实例会直接失败。
## 6. 启动 auth-portal
```bash
docker rm -f nano-auth-portal >/dev/null 2>&1 || true
docker run -d \
--name nano-auth-portal \
--restart unless-stopped \
--network "$NANO_NET" \
-p 3081:3081 \
-e AUTHZ_API_BASE_URL="$NANO_AUTHZ_URL" \
-e DEPLOY_API_BASE_URL="$NANO_DEPLOY_URL" \
-e DEPLOY_API_TOKEN="$NANO_DEPLOY_TOKEN" \
nano/auth-portal:latest
```
当前页面入口:
- `http://<server-ip>:3081`
## 7. 上线前健康检查
先确认四个基础组件都起来了:
```bash
curl http://127.0.0.1:19090/healthz
curl http://127.0.0.1:8090/healthz
curl -I http://127.0.0.1:3081
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
```
再确认 `router-proxy` 在跑:
```bash
docker logs --tail=50 nano-router-proxy
```
## 8. 首次注册验收
打开:
```text
http://<server-ip>:3081/register
```
注册一个新用户后,预期结果是:
1. `auth-portal``authz-service /portal/register`
2. `authz-service``deploy-control /api/instances/register`
3. `deploy-control` 创建一个新的 `app-instance`
4. `app-instance` 回调 AuthZ 完成 backend 身份初始化
5. 浏览器被跳转到该实例自己的 URL
同时你应该能看到:
```bash
cd "$PROJECT_ROOT"
./app-instance/list-instances.sh --json
```
新实例 URL 形如:
```text
http://<instance-slug>.<base-domain>:8088
```
如果你前面用的是:
```text
NANO_BASE_DOMAIN=<server-ip>.nip.io
```
那么实例地址会像:
```text
http://alice.203.0.113.10.nip.io:8088
```
## 9. 常见问题
### 1. 为什么不要把 `AUTHZ_ISSUER` 写成 `http://127.0.0.1:19090`
因为 `app-instance` 容器里访问 `127.0.0.1` 只会打到它自己,不会打到 AuthZ 容器。
在当前实现里,`AUTHZ_ISSUER` 会被直接传给新实例当作 `authz_base_url`
### 2. `DEPLOY_API_TOKEN` 和 `DEPLOY_CONTROL_API_TOKEN` 为什么要一样
因为一个是客户端发出去的 token一个是服务端拿来校验的 token。
### 3. 这些 provider 配置是写到哪里
写到每个实例自己的:
```text
app-instance/runtime/instances/<slug>/nanobot-home/config.json
```
不是写在 AuthZ 的某个 setting 里。
### 4. 现在支持每个用户注册时填自己的 API key 吗
后端请求模型已经支持 `provider/model/api_key/api_base` 字段,但当前 `auth-portal` 页面没有把这些字段暴露出来。
当前上线流程默认是:所有新实例先继承 `deploy-control` 上配置的默认 provider 凭证。
### 5. 现在内置 HTTPS 吗
没有。
当前内置 `router-proxy` 是 HTTP 入口。如果你要公网 HTTPS
- 在外面再放一层 Nginx / Caddy / LB 做 TLS 终止
- 再把流量转给 `router-proxy:8088``auth-portal:3081`
## 10. 仓库结构
```text
/home/ivan/xuan/nano_project
├── README.md
├── app-instance/
├── auth-portal/
├── authz-service/
├── deploy-control/
└── router-proxy/
```
各子目录更细的实现说明见:
- [`app-instance/README.md`](/home/ivan/xuan/nano_project/app-instance/README.md)
- [`auth-portal/src/README.md`](/home/ivan/xuan/nano_project/auth-portal/src/README.md)
- [`authz-service/README.md`](/home/ivan/xuan/nano_project/authz-service/README.md)
- [`deploy-control/README.md`](/home/ivan/xuan/nano_project/deploy-control/README.md)
- [`router-proxy/README.md`](/home/ivan/xuan/nano_project/router-proxy/README.md)