nollagluiz/AI_forSE
GitHub: nollagluiz/AI_forSE
分布式 Chromium 浏览器 Agent 集群,通过模拟真实用户访问周期来持续监控 Web 资产的 TLS 1.3 合规性与 HTTPS 安全态势,并配备实时可视化控制台。
Stars: 1 | Forks: 0
# Web Agent 集群
[](https://github.com/nollagluiz/AI_forSE/actions/workflows/ci.yml)
[](https://github.com/nollagluiz/AI_forSE/actions/workflows/codeql.yml)
[](https://github.com/nollagluiz/AI_forSE/actions/workflows/image-scan.yml)
[](https://scorecard.dev/viewer/?uri=github.com/nollagluiz/AI_forSE)
[](./LICENSE)
[](https://github.com/nollagluiz/AI_forSE/releases)
[](https://github.com/nollagluiz/AI_forSE/commits/main)
作者:**André Luiz Gallon** · 联系方式:[agallon@Cisco.com](mailto:agallon@Cisco.com) · 许可证:Elastic-2.0
最多包含 **300** 个 Linux + 浏览器 Agent 的集群,可执行逼真的全页 Web 访问周期,搭配 Apple 风格的 Next.js 编排控制台(TypeScript、Tailwind、Drizzle、PostgreSQL)。
## 仓库布局
```
.
├── agent/ # Node.js + Playwright agent (Docker image)
├── webserver/ # Caddy 2.8 image: HTTP/2 + HTTP/3 demo fleet (1..20)
├── dashboard/ # Next.js (App Router) + TS + Tailwind + Drizzle UI/API
├── k8s/ # Kubernetes manifests for production deployment
├── docker-compose.yml # DEFAULT: 3-stack split via Compose include:
├── docker-compose.all-in-one.yml# Legacy 1-stack flavour (no webserver fleet)
├── docker-compose.control.yml # SPLIT: postgres + dashboard only
├── docker-compose.fleet.yml # SPLIT: agents only
├── docker-compose.webserver.yml # SPLIT: 1..20 Caddy HTTP/2 + HTTP/3 webservers
├── docker-compose.ghcr.yml # Override: pull signed GHCR images instead of building
├── docker-compose.no-scaler.yml # Override: opt out of the local autoscaler
├── scripts/stack-up.sh # Convenience wrapper for the split topology
├── docs/SPLIT_STACKS.md # When/why/how to use the split topology
├── .env.example # Required environment variables for compose
└── README.md
```
## 工作原理
1. **Agent** 作为容器化的 Linux 客户端运行。每个 Agent 通过 Playwright 启动一个真实的 Chromium 实例,导航到配置的目标 URL 之一,下载整个页面(HTML、CSS、JS、图像、字体、第三方资源),记录每个资源的指标(大小、类型、状态、TLS 版本),然后关闭浏览器上下文,擦除用户数据配置文件,并等待配置的周期间隔,然后开始新的导航。因为每个周期都使用全新的 BrowserContext **以及**新的磁盘配置文件,所以不会有 Cookie、缓存、DNS 记录或临时数据在周期之间泄漏。
2. **Dashboard** 暴露了以下接口:
- `/api/agents/*` – 经过身份验证的 Agent API(注册、心跳、配置轮询)。
- `/api/runs/*` – 运行生命周期(开始/完成 + 每个资源的指标)。
- `/api/logs` – 结构化 Agent 日志摄取。
- `/api/dashboard/*` – UI 的只读数据源。
- `/api/admin/*` – 受 Basic Auth 保护的端点,用于添加/编辑目标以及切换集群状态。
3. **PostgreSQL** 存储 Agent、目标、运行、运行资源、结构化日志以及驱动实时图表的 60 秒时间序列桶。
4. **Kubernetes** 清单提供了生产环境的部署姿态:非 root 容器、只读根文件系统、丢弃权限、NetworkPolicy 出站白名单、1 到 100 个副本的 HPA、PodDisruptionBudget、TLS 入口(仅限 TLS 1.3)。
## 安全亮点
- **强制执行 TLS 1.3**:启动的 Chromium 使用 `--ssl-version-min=tls1.3` 参数启动,检查导航响应,任何非 TLS 1.3 的协商都会中止该周期。`REQUIRE_TLS13`、`HTTPS_ONLY` 和 `REJECT_INVALID_CERTS` 默认设置为 `true`。
- **拒绝自签名/无效证书**(`ignoreHTTPSErrors=false`)。
- **基于 Bearer Token 认证的 Agent API**,使用恒定时间比较以及按来源的令牌桶限流。
- **Admin 端点**需要 Basic Auth 和 SSRF 风格的验证:只接受公共 HTTPS URL 作为目标;拒绝私有/回环节点。
- 每个仪表板响应均带有**强化的 HTTP 头**(CSP、HSTS、no-referrer、frame-ancestors none、X-Content-Type-Options、Permissions-Policy、COOP/CORP)。
- **日志清理**会去除 CR/LF,隐去已知的敏感键,并持久化为结构化的 JSON。
- **容器**以非 root UID 运行,具有只读根文件系统,丢弃了 Linux capabilities,设置了 `no-new-privileges`,并使用 `RuntimeDefault` seccomp 配置文件。
- **绝不提交 Secrets。** `k8s/*.yaml` 和 `docker-compose.yml` 使用占位符字符串和显式注释,指示操作员将其替换为通过外部方式(Vault/KMS/`kubectl create secret`)创建的真实 Secrets。
## 实时更新
仪表板订阅了 `/api/dashboard/stream`(text/event-stream)。服务器每 1.5 秒轮询一次 `runs` 表,并在发生任何更改时推送 `tick` 事件;客户端会在每次 tick 时使 React Query 缓存失效,以便图表立即刷新。如果流被中间代理切断,5 秒轮询回退机制仍会每 30 秒触发一次。
## TimescaleDB(可选)
迁移 `0004_timescale_optional.sql` 将 `metrics_buckets` 转换为 TimescaleDB 超级表,并创建一个 1 分钟的连续聚合(`metrics_buckets_1m`)以及保留策略(原始数据 7 天,聚合数据 90 天)。这在原生的 postgres 镜像上是一个空操作。要启用它:部署一个编译了 TimescaleDB 的 Postgres 容器(例如 `timescale/timescaledb-ha:pg16`),添加 `shared_preload_libraries = 'timescaledb'` 并运行迁移。
## 管理目标站点
仪表板在任何时刻最多接受 **20 个已启用的 HTTPS 目标**。
在 `/agents/playwright/sites` 管理它们(旧版 `/targets` URL 仍可通过 308 重定向使用):
- 创建新目标,包含 URL、可选标签、权重(1–100)和周期间隔(`1 s`、`3 s`、`10 s`、`20 s`、`60 s`、`5 min` 或 1 秒到 1 小时之间的任何自定义值,或 "Padrão global" 以继承 `cluster_config`)。
- 内联编辑每个目标的标签、权重、频率和启用/禁用状态。
- 永久删除目标(外键级联会清理其运行和资源)。
- 20 个目标的上限在服务器端的 POST 请求以及将 `enabled` 从 `false` 改为 `true` 的 PATCH 请求中强制执行。
Agent 循环是**按目标调度**的:它总是触发下一次运行时间最早的目标。因此混合间隔(例如,一个站点为 1 秒,另一个为 60 秒)会按预期工作——1 秒的站点将主导循环,而 60 秒的站点每分钟触发一次。
## 扩展集群
两个控制面协同工作:
1. **逻辑目标** — `cluster_config.desired_agent_count`(0–300)。从 `/dashboard` 的 **Escala da frota** 卡片(滑块 + 预设值 1/10/50/100/200/300)进行设置,或者通过 API:
ADMIN=$(grep ^ADMIN_BASIC_AUTH .env | cut -d= -f2-)
curl -u "$ADMIN" -X PUT -H 'content-type: application/json' \
-d '{"enabled":true,"cycleIntervalMs":30000,"jitterMs":3000,"navigationTimeoutMs":45000,"desiredAgentCount":50}' \
http://localhost:3000/api/admin/config
2. **物理副本** — 编排器实际启动的数量。
### 本地(`docker compose`)
`agent` 服务是一个普通的 compose 服务。项目附带的 `.env` 文件设置了 `COMPOSE_FILE=docker-compose.yml:docker-compose.scaler.yml`,因此一个简单的 `docker compose up -d` 会自动:
- 启动 Postgres、Dashboard 和一个 Agent;
- 赋予 Dashboard 访问 `/var/run/docker.sock` 的权限,这样 **/agents/playwright 中的滑块就能在物理上扩缩 `agent` 服务**,而无需额外的终端操作;
- 每个 Agent 以 `CYCLE_CONCURRENCY=3`(一个 Chromium 内 3 个并行周期)运行,从而提高每个副本的吞吐量。
/agents/playwright 页面显示 "Auto-scaler local ATIVO" 以及一个 **Reconciliar agora** 按钮以强制手动同步。全新安装的默认集群节奏:周期间隔 **10 秒**,2 个所需 Agent。
#### 选项 B — 外部脚本(无套接字挂载)
```
# 在第三个终端中:
./scripts/scale-local.sh # poll the dashboard every 5s and reconcile
./scripts/scale-local.sh once # apply current target and exit
./scripts/scale-local.sh 10 # poll every 10s
```
每当所需数量发生变化时,它就会调用 `docker compose up -d --scale agent=N agent`。每个 Agent 容器都运行自己的无头 Chromium(约 300–500 MB RAM),因此请据此规划容量:**在典型的 16 GB Mac 上,8–15 个 Agent 是一个舒适的上限**。超出此范围,请使用 Kubernetes。
### Kubernetes
包含的 `HorizontalPodAutoscaler` 配置为 `minReplicas: 1, maxReplicas: 300`。可以使用 `kubectl scale deploy/web-agent --replicas=N` 手动扩缩,或者让 HPA 根据CPU和内存利用率将部署增加到最多 300 个 Pod。
## 本地快速启动
### 默认版本(3 个栈,自 v2.1.0 起)
```
# 1) 生成带有随机 secrets 和正确的 DOCKER_GID 的 .env:
./scripts/init-env.sh
# (打印 admin 密码 — 保存好它;你将在 /login 处用到它)
# 2) 创建两个隔离的 Docker 网络 (每台主机一次性操作)
docker network create --driver bridge --internal ai_forse_oobi
docker network create --driver bridge ai_forse_prod
# 3) 启动所有服务 — postgres + dashboard + 1 个 agent + 1 个 webserver
docker compose up -d
# 4) 状态 — 应显示 4 个服务运行正常 (healthy)
docker compose ps
# 5) 扩展 (1..300 个 agent, 1..20 个 webserver)
docker compose up -d --scale agent=20 --scale webserver=5
# 6) 打开 cockpit
open http://127.0.0.1:3000
# 更简单的方法:通过 helper 实现相同结果,它会自动创建网络 +
# 支持按栈的 restart / scale 操作:
# scripts/stack-up.sh up
# scripts/stack-up.sh scale-fleet 25
# scripts/stack-up.sh scale-webserver 12
# scripts/stack-up.sh restart-control
```
### 旧版 — All-in-one 单个项目(无 webserver 池)
```
# 为你的主机生成带有随机 secrets 和正确的 DOCKER_GID 的 .env:
./scripts/init-env.sh
# (它会打印 admin 密码 — 保存好它;你将在 /login 处用到它)
# 构建并仅在 127.0.0.1 上启动 stack。
docker compose -f docker-compose.all-in-one.yml up --build
# 在另一个终端中,推送 schema (一次性操作):
docker compose exec dashboard sh -lc 'cd /app && DATABASE_URL=$DATABASE_URL npm exec --no -- drizzle-kit push --config=drizzle.config.ts || true'
# 通过 admin API 添加目标站点:
curl -u "$ADMIN_BASIC_AUTH" -H 'content-type: application/json' \
-d '{"url":"https://www.g1.globo.com","weight":2}' \
http://127.0.0.1:3000/api/admin/targets
curl -u "$ADMIN_BASIC_AUTH" -H 'content-type: application/json' \
-d '{"url":"https://www.uol.com.br","weight":1}' \
http://127.0.0.1:3000/api/admin/targets
curl -u "$ADMIN_BASIC_AUTH" -H 'content-type: application/json' \
-d '{"url":"https://www.nasa.gov","weight":1}' \
http://127.0.0.1:3000/api/admin/targets
# 为整个 fleet 启用 cycles:
curl -u "$ADMIN_BASIC_AUTH" -X PUT -H 'content-type: application/json' \
-d '{"enabled":true,"cycleIntervalMs":30000,"jitterMs":3000,"navigationTimeoutMs":45000}' \
http://127.0.0.1:3000/api/admin/config
# 打开 cockpit:
open http://127.0.0.1:3000
```
### 每个栈的操作(辅助脚本)
默认的 `docker compose up -d` 会在**一个** Compose 项目中启动所有内容。要进行每个栈的独立重启/扩缩容(3 个独立的 Compose 项目),请使用辅助脚本:
```
scripts/stack-up.sh up # bring all 3 stacks up
scripts/stack-up.sh restart-control # bounce postgres + dashboard
scripts/stack-up.sh restart-fleet # bounce all agents
scripts/stack-up.sh restart-webserver # bounce all webservers
scripts/stack-up.sh scale-fleet 25 # scale agent fleet 1..300
scripts/stack-up.sh scale-webserver 12 # scale webserver pool 1..20
scripts/stack-up.sh ps # status of all 3 stacks
```
## Ubuntu 上使用 k3s 的快速入门 (1–300 副本)
这份完整的分步指南(15 个步骤)安装了 k3s、Nginx Ingress、metrics-server,构建项目镜像,导入到 k3s 的 containerd 中,应用清单,并展示了如何使用 HorizontalPodAutoscaler 扩展到 300 个副本。提供 3 种语言和 3 种格式:
| 语言 | Markdown | Word | PDF |
|---|---|---|---|
| 🇧🇷 葡萄牙语 | [`UBUNTU_K3S_QUICKSTART.pt-BR.md`](./docs/UBUNTU_K3S_QUICKSTART.pt-BR.md) | [`.docx`](./docs/UBUNTU_K3S_QUICKSTART.pt-BR.docx) | [`.pdf`](./docs/UBUNTU_K3S_QUICKSTART.pt-BR.pdf) |
| 🇺🇸 英语 | [`UBUNTU_K3S_QUICKSTART.en.md`](./docs/UBUNTU_K3S_QUICKSTART.en.md) | [`.docx`](./docs/UBUNTU_K3S_QUICKSTART.en.docx) | [`.pdf`](./docs/UBUNTU_K3S_QUICKSTART.en.pdf) |
| 🇪🇸 西班牙语 | [`UBUNTU_K3S_QUICKSTART.es.md`](./docs/UBUNTU_K3S_QUICKSTART.es.md) | [`.docx`](./docs/UBUNTU_K3S_QUICKSTART.es.docx) | [`.pdf`](./docs/UBUNTU_K3S_QUICKSTART.es.pdf) |
`.docx` 和 `.pdf` 格式通过 [`scripts/generate-docs.sh`](./scripts/generate-docs.sh) 从 Markdown 生成——每当你编辑了任何 `.md` 文件时,请重新执行该脚本以保持所有副本同步。
## Kubernetes 上的生产部署
```
# 1. 创建 namespace 和基础资源:
kubectl apply -f k8s/00-namespace.yaml
# 2. 创建真实的 secrets (不要使用 10/40/50 中的占位符 Secret):
kubectl -n web-agents create secret generic web-agent-secrets \
--from-literal=CONTROLLER_TOKEN="$(openssl rand -hex 32)"
kubectl -n web-agents create secret generic postgres-credentials \
--from-literal=POSTGRES_USER=agent_dashboard \
--from-literal=POSTGRES_PASSWORD="$(openssl rand -hex 24)" \
--from-literal=POSTGRES_DB=agent_dashboard
kubectl -n web-agents create secret generic dashboard-secrets \
--from-literal=DATABASE_URL="postgresql://agent_dashboard:@postgres:5432/agent_dashboard?sslmode=require" \
--from-literal=AGENT_API_TOKEN="" \
--from-literal=ADMIN_BASIC_AUTH="admin:" \
--from-literal=SESSION_SECRET="$(openssl rand -hex 32)"
# 3. 应用其余部分,排除 10/40/50 中的占位符 Secret manifests。
kubectl apply -f k8s/10-agent-config.yaml
kubectl apply -f k8s/40-postgres.yaml
kubectl apply -f k8s/50-dashboard.yaml
kubectl apply -f k8s/20-agent-deployment.yaml
kubectl apply -f k8s/30-agent-network-policy.yaml
# 4. 运行一次 database migrations:
kubectl -n web-agents exec deploy/dashboard -- node -e "require('drizzle-kit/node');" \
|| kubectl -n web-agents exec deploy/dashboard -- sh -lc 'npm exec --no -- drizzle-kit push'
# 5. 扩展 agents:
kubectl -n web-agents scale deploy/web-agent --replicas=50
```
HorizontalPodAutoscaler 将 Agent 部署限制在 1 到 100 个副本之间。资源请求/限制、PodDisruptionBudget 和 Pod 反亲和性确保集群分布在各个节点上。
## 配置
### Agent (`agent/`)
| 变量 | 默认值 | 描述 |
|----------|---------|-------------|
| `CONTROLLER_URL` | 必填 | Dashboard 基础 URL。 |
| `CONTROLLER_TOKEN` | 必填 (≥32 字符) | 与 Dashboard 共享的 Bearer Token。 |
| `AGENT_NAME` | 主机名 | UI 中显示的友好名称。 |
| `POLL_INTERVAL_MS` | 5000 | 配置刷新频率。 |
| `HEARTBEAT_INTERVAL_MS` | 10000 | 心跳频率。 |
| `DEFAULT_CYCLE_INTERVAL_MS` | 30000 | 在 Dashboard 响应之前使用的周期间隔。 |
| `NAVIGATION_TIMEOUT_MS` | 45000 | 每次导航的最长时间。 |
| `RESOURCE_QUIET_MS` | 2500 | `networkidle` 静默窗口。 |
| `HTTPS_ONLY` | `true` | 拒绝非 HTTPS 目标。 |
| `REQUIRE_TLS13` | `true` | 如果对等端协商的协议低于 TLS 1.3,则中止周期。 |
| `REJECT_INVALID_CERTS` | `true` | 拒绝自签名/无效证书。 |
| `LOG_LEVEL` | `info` | `debug` / `info` / `warn` / `error`。 |
### Dashboard (`dashboard/`)
| 变量 | 描述 |
|----------|-------------|
| `DATABASE_URL` | `postgresql://...?sslmode=require`。 |
| `AGENT_API_TOKEN` | 为每个 `/api/{agents,runs,logs}` 调用验证的 Bearer Token。 |
| `ADMIN_BASIC_AUTH` | Admin 端点的user:password`。 |
| `SESSION_SECRET` | 保留用于签名状态。 |
| `NODE_ENV` | `production`。 |
## 数据模型
- `agents` – 集群清单、最后活跃时间、当前 URL。
- `targets` – HTTPS URL + 权重 + 启用标志。
- `cluster_config` – 全局开启/关闭、周期间隔、抖动、导航超时。
- `runs` – 每个周期的导航及其汇总指标(持续时间、字节、资源计数、TLS 分布、内容类型直方图、状态码直方图)。
- `run_resources` – 每个资源的详细信息(URL、类型、字节、持续时间、TLS 版本)。
- `metrics_buckets` – 预聚合的 60 秒时间序列行,为实时图表提供数据。
- `logs` – 经过清理的结构化 Agent 日志。
## 操作说明
- Agent 附带了官方的 `mcr.microsoft.com/playwright` 基础镜像,因此 Chromium 构建版本与 Playwright 客户端匹配;这保证了 TLS 1.3 协商可以开箱即用。
- `BrowserRunner` 在每个周期后清除其用户数据配置文件,并且 BrowserContext 会被销毁,因此 cookies、localStorage、IndexedDB、service workers 和 Chromium 的 HTTP 缓存都会在请求之间被丢弃。
- `metrics_buckets` 表在 `runs/complete` 时同步更新;对于非常大的集群,您可以将其替换为 TimescaleDB 中的连续聚合,而无需更改 Dashboard 的 API 契约。
- 所有 admin/agent 端点都明确设置为 `dynamic = 'force-dynamic'` 并发出 `cache-control: no-store`,以防止中间缓存。
## 弹性、可观测性与运维
该集群开箱即包含以下生产级特性:
- **登录 + 会话** — `/login` 使用 HMAC 签名的 HttpOnly Cookie(可通过 `SESSION_SECRET` 轮换)。对于编程式客户端仍接受 Basic Auth。
- **审计日志** — 每次管理员变更(cluster_config、targets、导入、登录尝试)都会记录在 `audit_log` 中,包含操作者、变更前/后的 JSON 和源 IP。
- **后台清理器** — 每 30 秒将过期的 `running` 运行标记为 `error`,清除 `current_run_id` 指针,并老化掉幂等键。
- **幂等性** — Agent 的 `runs/start` 和 `runs/complete` 上的 `Idempotency-Key` 头可去重在不稳定网络上的重试。
- **熔断器** — Agent 内部按目标设置。在连续 5 次失败后,该目标将暂停 30 秒(以指数级增长,最长至 5 分钟)。
- **带指数退避和抖动的重试** — Controller 调用最多重试 4 次,退避时间约为 250 ms → 8 秒。
- **Chromium 哨兵** — Agent 在连续 5 次浏览器级别的失败后自动重新启动无头浏览器。
- **优雅停机** — SIGTERM 会等待正在进行的周期完成(上限为 `navigationTimeoutMs`)后再退出。
- **每个 Agent 的并发** — `CYCLE_CONCURRENCY` 环境变量在一个 Chromium 内运行 N 个并行周期。成倍增加每个 Agent 的吞吐量。
- **地理区域** — 每个副本设置 `AGENT_REGION`,并按区域划分 `/dashboard`。
- **每个目标的活跃时间** — 使用 `activeHoursStart/End` 和 7 位 `activeDays` 掩码来调度目标。
- **突发模式**(即时或定时) — 一次性丢弃 N 个合成周期,或者在 T 秒内分散进行。
- **Prometheus** `/api/metrics` 端点 — 计数器和仪表盘(agents_live、runs_per_minute、runs_completed_total{status}、runs_by_protocol_total、reaper_*)。
- **健康检查** — `/api/health` 会对数据库执行实际的 `SELECT 1` 查询。
- **时间序列** — 可选 TimescaleDB(迁移 `0004`);原生 Postgres 路径在迁移 `0005` 中包含热路径索引。
- **备份** — `k8s/60-postgres-backup.yaml` 每天于 UTC 时间 03:00 运行 `pg_dump`,保留 14 天。
- **告警** — `k8s/70-prometheus-rules.yaml` 提供了针对低于预期的 Agent 数量、无存活的 Agent、错误率 > 20%、慢周期、清理器变动的 PrometheusRules。
- **Dashboard PDB + 拓扑分布** — `k8s/55-dashboard-pdb.yaml` 保持至少有 1 个 Pod 可用;拓扑分布将其分散到各个区域。
- **CI/CD** — GitHub Actions 工作流在每次推送时运行类型检查 + 代码检查 + 测试 + 生产构建 + 两个服务的 Docker 镜像构建。
- **测试** — Vitest 包含 14 个单元测试(调度、格式化、熔断器),包括 i18n 跨午夜窗口的边缘情况。
## 作者
参见 [`AUTHORS`](./AUTHORS) 和 [`NOTICE`](./NOTICE)。
- **André Luiz Gallon** — 初始设计与实现。联系方式:[agallon@Cisco.com](mailto:agallon@Cisco.com)。
## 许可证
本项目基于 **Elastic License 2.0** (ELv2) 授权。完整文本请参见 [`LICENSE`](./LICENSE)。
### ELv2 的通俗解释
| 您**可以** | 您**不可以** |
|---|---|
| 出于任何目的(无论是商业还是其他)在内部使用该软件 | 将本软件作为其功能与本软件基本重叠的托管或托管服务提供给第三方 |
| 阅读、研究和修改源代码 | 移动、更改、禁用或规避任何许可证密钥功能 |
| 分发它(保持这些声明完整) | 移除或模糊化版权、许可证或商标声明 |
| 将其嵌入到您自己的产品中,内部使用或销售给您的客户 | 未经许可,使用 **Web Agent Cluster** 名称或 André Luiz Gallon 的标志用于衍生产品 |
大多数用户(为其自身监控运行的公司、个人、学术界)都**完全涵盖在内**。该许可证仅阻止同类产品的竞争性托管/管理服务。如果您的用例需要不同的条款,请联系作者获取单独的商业许可证:[agallon@Cisco.com](mailto:agallon@Cisco.com)。
### 商标声明
名称 **"Web Agent Cluster"**、相关标志以及控制台独特的 Apple 风格视觉识别是 **André Luiz Gallon** 的商标(™ — 正在注册中)。在衍生作品、分支或竞争性服务中使用这些标志需要书面许可。ELv2 许可证**不**授予商标权。
### 历史
早期的提交(在 2026 年 5 月引入此许可证更改的提交 [`d219e2f`](https://github.com/nollagluiz/AI_forSE/commit/d219e2f) 之前)是在 Apache License 2.0 下发布的。这些提交将永远受 Apache-2.0 管辖——但在许可证更改时或之后的每个提交均适用 ELv2。
标签:App Router, Caddy, CodeQL, DNS解析, Docker Compose, Drizzle, Elastic-2.0, GNU通用公共许可证, Helm, MITM代理, Node.js, Playwright, PostgreSQL, Tailwind CSS, TLS 1.3, TypeScript, Web代理集群, 仪表盘, 全页面访问, 分布式系统, 响应大小分析, 子域名突变, 安全扫描, 安全插件, 安全评估工具, 容器化部署, 开源项目, 提示注入, 时序注入, 流量模拟, 测试用例, 浏览器自动化, 熔断器, 特征检测, 用户行为模拟, 监控指标, 网络安全, 自动化攻击, 自定义请求头, 请求拦截, 隐私保护, 集群管理