Repository: apache/dubbo-go-pixiu

Issue:
Trie前缀树优化
<!-- Please only use this template for submitting enhancement requests -->

**What would you like to be added**:

<img width="810" height="816" alt="Image" src="https://github.com/user-attachments/assets/eb917754-a564-431d-9196-00ff9d6c7c7f" />

from https://dubbo-go-pixiu.github.io/zh-cn/docs/dev/trie.html

**Why is this needed**:

优化性能


Hints from issue comments:
@1kasa: I want to be responsible for this job@Alanxtl: [Pixiu路由原理简介.pdf](https://github.com/user-attachments/files/21966418/Pixiu.pdf)@zeusro: 那我掏出超长正则匹配，阁下如何应对
```
(?s)^(?:(?:(?:(?:(?:(?:(?:(?:(?:(?:(?:\Q
    .-""""-.
   /  -  -  \
  |  o    o  |
  |    ^     |
  |  \___/   |
   \_______/
  /  /   \  \
 |__/     \__|
\E))))))))))){1}$
```@Alanxtl: > 那我掏出超长正则匹配，阁下如何应对

Hi @zeusro 
和正则表达式的复杂程度没关系啊，我们缓存的是正则表达式编译之后的数据，为的是减少正则表达式编译过程的耗时
@zeusro: > > 那我掏出超长正则匹配，阁下如何应对
> 
> Hi [@zeusro](https://github.com/zeusro) 和正则表达式的复杂程度没关系啊，我们缓存的是正则表达式编译之后的数据，为的是减少正则表达式编译过程的耗时

与C#的正则表达式超时相比，目前golang应该没有正则的超时匹配概念。

```c#
public Regex (string pattern, System.Text.RegularExpressions.RegexOptions options, TimeSpan matchTimeout);
```
https://learn.microsoft.com/zh-tw/dotnet/api/system.text.regularexpressions.regex.-ctor?view=net-9.0#system-text-regularexpressions-regex-ctor(system-string-system-text-regularexpressions-regexoptions-system-timespan)@Alanxtl: > > > 那我掏出超长正则匹配，阁下如何应对
> > 
> > 
> > Hi [@zeusro](https://github.com/zeusro) 和正则表达式的复杂程度没关系啊，我们缓存的是正则表达式编译之后的数据，为的是减少正则表达式编译过程的耗时
> 
> 与C#的正则表达式超时相比，目前golang应该没有正则的超时匹配概念。
> 
> public Regex (string pattern, System.Text.RegularExpressions.RegexOptions options, TimeSpan matchTimeout);
> https://learn.microsoft.com/zh-tw/dotnet/api/system.text.regularexpressions.regex.-ctor?view=net-9.0#system-text-regularexpressions-regex-ctor(system-string-system-text-regularexpressions-regexoptions-system-timespan)

感谢补充，不过我们这里缓存的是正则编译结果，目的仅是避免在路由构建阶段重复调用 regexp.Compile，与正则匹配阶段的性能或超时无关。
Go 使用的 RE2 引擎本身不会出现灾难性回溯，因此缓存行为不影响匹配安全性，也不会引入正则超时相关的问题 :)

Solution patch:
diff --git a/Dockerfile b/Dockerfile
index 7d5682f4..caf0a4c9 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -18,7 +18,7 @@
 #
 
 ### builder
-FROM golang:1.23.4-bullseye AS builder
+FROM golang:1.25.4-bookworm AS builder
 LABEL MAINTAINER="dev@dubbo.apache.org"
 
 RUN apt-get update && apt-get install -y --no-install-recommends gcc
@@ -41,7 +41,7 @@ RUN go build -ldflags '-r ./lib -s -w' -tags="wasmer" -trimpath -o /app/dubbo-go
 RUN find /go/pkg/mod -name "libwasmer.so" -exec cp {} /app/libwasmer.so \;
 
 
-FROM pingcap/alpine-glibc:alpine-3.14.6
+FROM oowy/glibc:2.39-alpine3.20
 LABEL MAINTAINER="dev@dubbo.apache.org"
 
 RUN addgroup -S nonroot \
diff --git a/README.md b/README.md
index 8dd7800b..07f3a1cb 100644
--- a/README.md
+++ b/README.md
@@ -117,6 +117,8 @@ After starting, you can access the management plane by opening `http://localhost
 
 ![pixiu-admin.png](./docs/images/pixiu-admin.png)
 
+👉 **More Details：** please visit [pixiu-admin](https://github.com/apache/dubbo-go-pixiu/tree/develop/admin)
+
 ## Other Projects in the Dubbo-Go-Pixiu Ecosystem
 
 * **[pixiu-samples](https://github.com/apache/dubbo-go-pixiu-samples)**
diff --git a/README_CN.md b/README_CN.md
index 1de416a5..c596b7f2 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -115,6 +115,8 @@ docker-compose up -d
 
 ![pixiu-admin.png](./docs/images/pixiu-admin.png)
 
+👉 **更多细节：** 请访问 [pixiu-admin](https://github.com/apache/dubbo-go-pixiu/tree/develop/admin)
+
 ## Dubbo-go-pixiu 生态系统的其他项目
 
 -   **[pixiu-samples](https://github.com/apache/dubbo-go-pixiu-samples)**
diff --git a/admin/API.md b/admin/API.md
new file mode 100644
index 00000000..0a365bb3
--- /dev/null
+++ b/admin/API.md
@@ -0,0 +1,376 @@
+# Backend API Documentation
+
+**English** | [中文](API_CN.md)
+
+This API documentation describes the backend operations of the Pixiu management platform, including the APIs for retrieving, creating, modifying, and deleting resources (Resource), methods (Method), and plugin groups (PluginGroup). Pixiu provides a complete set of APIs to help users manage API gateway resource mappings, plugin configurations, and request handling. The examples in this document cover common request and response formats and show how to test the APIs using Postman.
+
+Whether you are creating new resources, modifying existing configurations, or managing plugin groups, this document provides clear steps and necessary API details, making it easier for developers to get started and integrate quickly.
+
+More detailed API descriptions can be found in the [Swagger documentation](./doc/swagger.json).
+
+## Response Codes
+
+* **code**:
+
+    * `10001`: Success
+    * `10002`: Data not found
+    * `10003`: Concurrent operation, please refresh the page and try again
+
+* **data**: Typically, data will be in YAML format.
+
+## I. Basic Information
+
+### 1.1 Get Basic Information
+
+**Request**:
+
+```http
+GET /config/api/base HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+```
+
+**Response**:
+
+```json
+{
+  "code": "10001",
+  "data": "name: pixiu\ndescription: pixiu111 sample\npluginFilePath: \"\"\n"
+}
+```
+
+### 1.2 Create or Modify Basic Information
+
+**Request**:
+
+```http
+POST /config/api/base HTTP/1.1
+Host: 127.0.0.1:8080
+Content-Type: multipart/form-data; boundary=-WebKitFormBoundary7MA4YWxkTrZu0gW
+cache-control: no-cache
+```
+
+**Form Data**:
+
+```text
+Content-Disposition: form-data; name="content"
+name: pixiu
+description: pixiu111 sample
+```
+
+## II. Resource
+
+### 2.1 Get Resource List
+
+**Request**:
+
+```http
+GET /config/api/resource/list HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+```
+
+### 2.2 Get Resource Details
+
+**Request**:
+
+```http
+GET /config/api/resource/detail?resourceId=1 HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+```
+
+### 2.3 Create Resource
+
+**Request**:
+
+```http
+POST /config/api/resource/ HTTP/1.1
+Host: 127.0.0.1:8080
+Content-Type: multipart/form-data; boundary=-WebKitFormBoundary7MA4YWxkTrZu0gW
+cache-control: no-cache
+```
+
+**Form Data**:
+
+```text
+Content-Disposition: form-data; name="content"
+path: '/api/v1/test-dubbo/friend2'
+type: restful
+description: user
+timeout: 100ms
+plugins:
+  pre:
+    pluginNames:
+      - rate limit
+      - access
+  post:
+    groupNames:
+      - group2
+methods:
+  - httpVerb: GET
+    resourcePath: '/api/v1/test-dubbo/friend2'
+    onAir: true
+    timeout: 1000ms
+    inboundRequest:
+      requestType: http
+      queryStrings:
+        - name: name
+          required: true
+    integrationRequest:
+      requestType: http
+      host: 127.0.0.1:8889
+      path: /UserProvider/GetUserByName
+      mappingParams:
+        - name: queryStrings.name
+          mapTo: queryStrings.name
+      group: "test"
+      version: 1.0.0
+```
+
+### 2.4 Modify Resource
+
+**Request**:
+
+```http
+PUT /config/api/resource? HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+Content-Type: multipart/form-data; boundary=-WebKitFormBoundary7MA4YWxkTrZu0gW
+```
+
+**Form Data**:
+
+```text
+Content-Disposition: form-data; name="content"
+id: 1
+path: '/api/v1/test-dubbo/friend'
+type: restful
+description: update
+timeout: 1000ms
+plugins:
+  pre:
+    pluginNames:
+      - rate limit
+      - access
+  post:
+    groupNames:
+      - group2
+methods:
+  - httpVerb: GET
+    onAir: true
+    timeout: 1000ms
+    inboundRequest:
+      requestType: http
+      queryStrings:
+        - name: name
+          required: true
+    integrationRequest:
+      requestType: http
+      host: 127.0.0.1:8889
+      path: /UserProvider/GetUserByName
+      mappingParams:
+        - name: queryStrings.name
+          mapTo: queryStrings.name
+      group: "test"
+      version: 1.0.0
+```
+
+### 2.5 Delete Resource
+
+**Request**:
+
+```http
+DELETE /config/api/resource/?resourceId=2 HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+```
+
+## III. Method Related
+
+### 3.1 Get Method List for a Resource
+
+**Request**:
+
+```http
+GET /config/api/resource/method/list?resourceId=1 HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+```
+
+### 3.2 Get Method Details
+
+**Request**:
+
+```http
+GET /config/api/resource/method/detail?resourceId=1&methodId=2 HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+```
+
+### 3.3 Create Method
+
+**Request**:
+
+```http
+POST /config/api/resource/method/?resourceId=1 HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+Content-Type: multipart/form-data; boundary=-WebKitFormBoundary7MA4YWxkTrZu0gW
+```
+
+**Form Data**:
+
+```text
+Content-Disposition: form-data; name="content"
+httpVerb: PUT
+resourcePath: '/api/v1/test-dubbo/friend'
+onAir: true
+timeout: 1000ms
+inboundRequest:
+  requestType: http
+  queryStrings:
+    - name: name
+      required: true
+integrationRequest:
+  requestType: http
+  host: 127.0.0.1:8889
+  path: /UserProvider/GetUserByName
+  mappingParams:
+    - name: queryStrings.name
+      mapTo: queryStrings.name
+  group: "test"
+  version: 1.0.0
+```
+
+### 3.4 Modify Method
+
+**Request**:
+
+```http
+PUT /config/api/resource/method/?resourceId=1 HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+Content-Type: multipart/form-data; boundary=-WebKitFormBoundary7MA4YWxkTrZu0gW
+```
+
+**Form Data**:
+
+```text
+Content-Disposition: form-data; name="content"
+id: 2
+httpVerb: PUT
+resourcePath: '/api/v1/test-dubbo/friend'
+onAir: true
+timeout: 300ms
+inboundRequest:
+  requestType: http
+  queryStrings:
+    - name: name
+      required: true
+integrationRequest:
+  requestType: http
+  host: 127.0.0.1:8889
+  path: /UserProvider/GetUserByName
+  mappingParams:
+    - name: queryStrings.name
+      mapTo: queryStrings.name
+  group: "test"
+  version: 1.0.0
+```
+
+### 3.5 Delete Method
+
+**Request**:
+
+```http
+DELETE /config/api/resource/method/?resourceId=1&methodId=2 HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+```
+
+## IV. PluginGroup and Plugin Related
+
+### 4.1 Get PluginGroup List
+
+**Request**:
+
+```http
+GET /config/api/plugin_group/list HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+```
+
+### 4.2 Get PluginGroup Details
+
+**Request**:
+
+```http
+GET /config/api/plugin_group/list HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+```
+
+### 4.3 Create PluginGroup
+
+**Request**:
+
+```http
+POST /config/api/plugin_group/ HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+Content-Type: multipart/form-data; boundary=-WebKitFormBoundary7MA4YWxkTrZu0gW
+```
+
+**Form Data**:
+
+```text
+Content-Disposition: form-data; name="content"
+groupName: "group1"
+plugins:
+  - name: "rate limit"
+    version: "0.0.1"
+    priority: 1000
+    externalLookupName: "ExternalPluginRateLimit"
+  - name: "access"
+    version: "0.0.1"
+    priority: 1000
+    externalLookupName: "ExternalPluginAccess"
+```
+
+### 4.4 Modify PluginGroup
+
+**Request**:
+
+```http
+PUT /config/api/plugin_group/ HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+Content-Type: multipart/form-data; boundary=-WebKitFormBoundary7MA4YWxkTrZu0gW
+```
+
+**Form Data**:
+
+```text
+Content-Disposition: form-data; name="content"
+groupName: "group1"
+plugins:
+  - name: "rate limit"
+    version: "0.0.2"
+    priority: 1000
+    externalLookupName: "ExternalPluginRateLimit"
+  - name: "access"
+    version: "0.0.1"
+    priority: 1000
+    externalLookupName: "ExternalPluginAccess"
+```
+
+### 4.5 Delete PluginGroup
+
+**Request**:
+
+```http
+DELETE /config/api/plugin_group/?name=group1 HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+```
diff --git a/admin/API_CN.md b/admin/API_CN.md
new file mode 100644
index 00000000..a4c02f11
--- /dev/null
+++ b/admin/API_CN.md
@@ -0,0 +1,378 @@
+# 后端API接口文档
+
+[English](API.md) | **中文**
+
+本接口文档详细描述了 Pixiu 管理平台的后端 API 操作，包括获取、创建、修改、删除资源（Resource）、方法（Method）及插件组（PluginGroup）的接口。Pixiu
+平台提供了一整套 API 来帮助用户管理 API 网关的资源映射、插件配置以及请求处理。文档中的示例涵盖了常见的请求与响应格式，并介绍了如何使用
+Postman 进行接口测试。
+
+无论是创建新资源、修改现有配置，还是管理插件组，本文档都提供了清晰的步骤和必要的 API 细节，方便开发者快速上手并进行集成。
+
+更多的 API 具体介绍请参考 [Swagger 文档](./doc/swagger.json)
+
+## 返回值说明
+
+* **code**：
+
+    * `10001`: 成功
+    * `10002`: 未找到对应数据
+    * `10003`: 并发操作，请刷新页面重试
+
+* **data**：一般为 YAML 格式的数据
+
+## 一、基础信息
+
+### 1.1 获取基础信息
+
+**请求**：
+
+```http
+GET /config/api/base HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+```
+
+**返回值**：
+
+```json
+{
+  "code": "10001",
+  "data": "name: pixiu\ndescription: pixiu111 sample\npluginFilePath: \"\"\n"
+}
+```
+
+### 1.2 创建或修改基础信息
+
+**请求**：
+
+```http
+POST /config/api/base HTTP/1.1
+Host: 127.0.0.1:8080
+Content-Type: multipart/form-data; boundary=-WebKitFormBoundary7MA4YWxkTrZu0gW
+cache-control: no-cache
+```
+
+**表单数据**：
+
+```text
+Content-Disposition: form-data; name="content"
+name: pixiu
+description: pixiu111 sample
+```
+
+## 二、Resource
+
+### 2.1 获取 Resource 列表
+
+**请求**：
+
+```http
+GET /config/api/resource/list HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+```
+
+### 2.2 获取 Resource 详情
+
+**请求**：
+
+```http
+GET /config/api/resource/detail?resourceId=1 HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+```
+
+### 2.3 创建 Resource
+
+**请求**：
+
+```http
+POST /config/api/resource/ HTTP/1.1
+Host: 127.0.0.1:8080
+Content-Type: multipart/form-data; boundary=-WebKitFormBoundary7MA4YWxkTrZu0gW
+cache-control: no-cache
+```
+
+**表单数据**：
+
+```text
+Content-Disposition: form-data; name="content"
+path: '/api/v1/test-dubbo/friend2'
+type: restful
+description: user
+timeout: 100ms
+plugins:
+  pre:
+    pluginNames:
+      - rate limit
+      - access
+  post:
+    groupNames:
+      - group2
+methods:
+  - httpVerb: GET
+    resourcePath: '/api/v1/test-dubbo/friend2'
+    onAir: true
+    timeout: 1000ms
+    inboundRequest:
+      requestType: http
+      queryStrings:
+        - name: name
+          required: true
+    integrationRequest:
+      requestType: http
+      host: 127.0.0.1:8889
+      path: /UserProvider/GetUserByName
+      mappingParams:
+        - name: queryStrings.name
+          mapTo: queryStrings.name
+      group: "test"
+      version: 1.0.0
+```
+
+### 2.4 修改 Resource
+
+**请求**：
+
+```http
+PUT /config/api/resource? HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+Content-Type: multipart/form-data; boundary=-WebKitFormBoundary7MA4YWxkTrZu0gW
+```
+
+**表单数据**：
+
+```text
+Content-Disposition: form-data; name="content"
+id: 1
+path: '/api/v1/test-dubbo/friend'
+type: restful
+description: update
+timeout: 1000ms
+plugins:
+  pre:
+    pluginNames:
+      - rate limit
+      - access
+  post:
+    groupNames:
+      - group2
+methods:
+  - httpVerb: GET
+    onAir: true
+    timeout: 1000ms
+    inboundRequest:
+      requestType: http
+      queryStrings:
+        - name: name
+          required: true
+    integrationRequest:
+      requestType: http
+      host: 127.0.0.1:8889
+      path: /UserProvider/GetUserByName
+      mappingParams:
+        - name: queryStrings.name
+          mapTo: queryStrings.name
+      group: "test"
+      version: 1.0.0
+```
+
+### 2.5 删除 Resource
+
+**请求**：
+
+```http
+DELETE /config/api/resource/?resourceId=2 HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+```
+
+## 三、Method 相关
+
+### 3.1 查询某个 Resource 下的 Method 列表
+
+**请求**：
+
+```http
+GET /config/api/resource/method/list?resourceId=1 HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+```
+
+### 3.2 查询 Method 详情
+
+**请求**：
+
+```http
+GET /config/api/resource/method/detail?resourceId=1&methodId=2 HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+```
+
+### 3.3 创建 Method
+
+**请求**：
+
+```http
+POST /config/api/resource/method/?resourceId=1 HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+Content-Type: multipart/form-data; boundary=-WebKitFormBoundary7MA4YWxkTrZu0gW
+```
+
+**表单数据**：
+
+```text
+Content-Disposition: form-data; name="content"
+httpVerb: PUT
+resourcePath: '/api/v1/test-dubbo/friend'
+onAir: true
+timeout: 1000ms
+inboundRequest:
+  requestType: http
+  queryStrings:
+    - name: name
+      required: true
+integrationRequest:
+  requestType: http
+  host: 127.0.0.1:8889
+  path: /UserProvider/GetUserByName
+  mappingParams:
+    - name: queryStrings.name
+      mapTo: queryStrings.name
+  group: "test"
+  version: 1.0.0
+```
+
+### 3.4 修改 Method
+
+**请求**：
+
+```http
+PUT /config/api/resource/method/?resourceId=1 HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+Content-Type: multipart/form-data; boundary=-WebKitFormBoundary7MA4YWxkTrZu0gW
+```
+
+**表单数据**：
+
+```text
+Content-Disposition: form-data; name="content"
+id: 2
+httpVerb: PUT
+resourcePath: '/api/v1/test-dubbo/friend'
+onAir: true
+timeout: 300ms
+inboundRequest:
+  requestType: http
+  queryStrings:
+    - name: name
+      required: true
+integrationRequest:
+  requestType: http
+  host: 127.0.0.1:8889
+  path: /UserProvider/GetUserByName
+  mappingParams:
+    - name: queryStrings.name
+      mapTo: queryStrings.name
+  group: "test"
+  version: 1.0.0
+```
+
+### 3.5 删除 Method
+
+**请求**：
+
+```http
+DELETE /config/api/resource/method/?resourceId=1&methodId=2 HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+```
+
+## 四、PluginGroup 和 Plugin 相关
+
+### 4.1 查看 PluginGroup 列表
+
+**请求**：
+
+```http
+GET /config/api/plugin_group/list HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+```
+
+### 4.2 查看 PluginGroup 详情
+
+**请求**：
+
+```http
+GET /config/api/plugin_group/list HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+```
+
+### 4.3 创建 PluginGroup
+
+**请求**：
+
+```http
+POST /config/api/plugin_group/ HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+Content-Type: multipart/form-data; boundary=-WebKitFormBoundary7MA4YWxkTrZu0gW
+```
+
+**表单数据**：
+
+```text
+Content-Disposition: form-data; name="content"
+groupName: "group1"
+plugins:
+  - name: "rate limit"
+    version: "0.0.1"
+    priority: 1000
+    externalLookupName: "ExternalPluginRateLimit"
+  - name: "access"
+    version: "0.0.1"
+    priority: 1000
+    externalLookupName: "ExternalPluginAccess"
+```
+
+### 4.4 修改 PluginGroup
+
+**请求**：
+
+```http
+PUT /config/api/plugin_group/ HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+Content-Type: multipart/form-data; boundary=-WebKitFormBoundary7MA4YWxkTrZu0gW
+```
+
+**表单数据**：
+
+```text
+Content-Disposition: form-data; name="content"
+groupName: "group1"
+plugins:
+  - name: "rate limit"
+    version: "0.0.2"
+    priority: 1000
+    externalLookupName: "ExternalPluginRateLimit"
+  - name: "access"
+    version: "0.0.1"
+    priority: 1000
+    externalLookupName: "ExternalPluginAccess"
+```
+
+### 4.5 删除 PluginGroup
+
+**请求**：
+
+```http
+DELETE /config/api/plugin_group/?name=group1 HTTP/1.1
+Host: 127.0.0.1:8080
+cache-control: no-cache
+```
diff --git a/admin/README.md b/admin/README.md
new file mode 100644
index 00000000..232399d3
--- /dev/null
+++ b/admin/README.md
@@ -0,0 +1,305 @@
+# Pixiu-Admin Deployment and Configuration Guide
+
+**English** | [中文](README_CN.md)
+
+**Pixiu-Admin** is a management platform based on the **Pixiu** ecosystem, primarily used for configuring, monitoring, and managing Pixiu gateway resources. It provides centralized management functionality via a web user interface and RESTful API. This document explains how to deploy and configure Pixiu-Admin in a Linux environment.
+
+For backend API documentation, please refer to [API.md](../admin/API.md).
+
+## Deployment Documentation
+
+### Start Using Docker
+
+First, ensure you are in the root directory of the project (the directory containing the Dockerfile). Then, start instantly with Docker Compose:
+
+```bash
+docker-compose up -d
+```
+
+### Deploy from Source Code
+
+If you prefer not to use Docker, you can download the source code by running:
+
+```bash
+git clone https://github.com/apache/dubbo-go-pixiu
+```
+
+### Deploy etcd
+
+To manually deploy the etcd service, run the following command:
+
+```bash
+docker run -d -p 2379:2379 --env ALLOW_NONE_AUTHENTICATION=yes --name etcd bitnami/etcd
+```
+
+For M1/M1 Pro users, use the following command:
+
+```bash
+docker run -d -p 2379:2379 --platform linux/amd64 --env ALLOW_NONE_AUTHENTICATION=yes --name etcd bitnami/etcd:3.5.1
+```
+
+### Run Admin
+
+#### Run from Source Code
+
+Go to the project directory and run:
+
+```bash
+cd dubbo-go-pixiu
+# Run directly
+go run ./cmd/admin/admin.go -c /your/local/path/conf.yaml
+# Run in the background
+nohup go run ./cmd/admin/admin.go -c /your/local/path/conf.yaml &
+```
+
+#### Run Pixiu
+
+Default config can refer to [pixiu_with_admin_config.yaml](../configs/pixiu_with_admin_config.yaml)
+
+```bash
+go run ./cmd/pixiu/pixiu.go gateway start -c ./configs/pixiu_with_admin_config.yaml
+```
+
+### Test Running Admin Web
+
+Go to the `web` directory and install dependencies:
+
+```bash
+cd ./admin/web/
+yarn install  # Install dependencies
+yarn run serve  # Test run
+```
+
+#### Admin Web Configuration
+
+Edit the `vue.config.js` file in the `web` directory to configure the backend service address:
+
+```
+devServer: {
+    host: '0.0.0.0',
+    port: 8080,  // Web app address
+    hot: true,
+    https: false,
+    open: false,
+    disableHostCheck: true,
+    proxy: {
+        "/config": {
+            target: "http://127.0.0.1:8081",  // Backend service address
+            ws: true,  // Enable websockets
+            changeOrigin: true,  // Enable proxy
+        }
+    }
+}
+```
+
+After running successfully, you can access the Admin web interface in the browser at [http://127.0.0.1:8081/login.html#/Overview](http://127.0.0.1:8081/login.html#/Overview).
+
+## II. Related Operations
+
+### Resource Management (Mapping)
+
+#### Create Mapping Configuration
+
+1. Click "Mapping Configuration" to enter the mapping configuration list interface.
+2. Click the "Add" button in the top right to create a new mapping configuration.
+
+![1.png](../docs/images/admin/1.png)
+
+Type the mapping configuration in the code editor and click "Confirm" to create it.
+
+![2.png](../docs/images/admin/2.png)
+
+#### Mapping Configuration Example
+
+```yaml
+path: '/api/v1/test-dubbo/user'
+type: restful
+description: user
+filters:
+  - filter0
+methods:
+  - httpVerb: GET
+    onAir: true
+    timeout: 1000ms
+    inboundRequest:
+      requestType: http
+      queryStrings:
+        - name: name
+          required: true
+    integrationRequest:
+      requestType: dubbo
+      mappingParams:
+        - name: queryStrings.name
+          mapTo: 0
+          mapType: "java.lang.String"
+      applicationName: "UserProvider"
+      interface: "com.ic.user.UserProvider"
+      method: "GetUserByName"
+      group: "test"
+      version: 1.0.0
+      clusterName: "test_dubbo"
+  - httpVerb: POST
+    onAir: true
+    timeout: 10s
+    inboundRequest:
+      requestType: http
+    integrationRequest:
+      requestType: dubbo
+      mappingParams:
+        - name: requestBody._all
+          mapTo: 0
+          mapType: "object"
+      applicationName: "UserProvider"
+      interface: "com.ic.user.UserProvider"
+      method: "CreateUser"
+      group: "test"
+      version: 1.0.0
+      clusterName: "test_dubbo"
+```
+
+#### View and Delete Mappings
+
+1. After refreshing the mapping list, you can view and delete configurations.
+
+![3.png](../docs/images/admin/3.png)
+
+2. Clicking "Delete" will remove the mapping configuration, while "View" will take you to the details page.
+
+![4.png](../docs/images/admin/4.png)
+
+#### Edit Mapping
+
+You can modify the mapping configuration in the edit area and click "Save" to apply changes.
+
+![5.png](../docs/images/admin/5.png)
+
+#### Method Mapping
+
+Click the "Add" button to add a new method mapping. Example:
+
+```yaml
+httpVerb: PUT
+onAir: true
+timeout: 10s
+inboundRequest:
+  requestType: http
+integrationRequest:
+  requestType: dubbo
+  mappingParams:
+    - name: requestBody._all
+      mapTo: 0
+      mapType: "object"
+  applicationName: "UserProvider"
+  interface: "com.ic.user.UserProvider"
+  method: "CreateUser"
+  group: "test"
+  version: 1.0.0
+  clusterName: "test_dubbo"
+```
+
+After confirming, the method mapping will appear in the list.
+
+![6.png](../docs/images/admin/6.png)
+
+#### View and Delete Method Mappings
+
+You can view detailed information of method mappings or delete them.
+
+For example, change the `httpVerb` of the second method mapping from POST to DELETE.
+
+![7.png](../docs/images/admin/7.png)
+
+Note: The ID cannot be modified, and even after saving changes, it will revert to the old value. Click "Confirm" to update the method mapping.
+
+![8.png](../docs/images/admin/8.png)
+
+### Manage Plugin Groups
+
+#### Create Plugin Group
+
+Click the "Plugin Configuration" menu on the left to view plugin-related configurations.
+
+![9.png](../docs/images/admin/9.png)
+
+Click the "Add" button in the top right to create a new plugin group.
+
+![10.png](../docs/images/admin/10.png)
+
+Plugin group configuration example:
+
+```yaml
+groupName: "group2"
+plugins:
+  - name: "rate limit"
+    version: "0.1.0"
+    priority: 1000
+    externalLookupName: "ExternalPluginRateLimit"
+  - name: "log"
+    version: "0.2.0"
+    priority: 2000
+    externalLookupName: "ExternalPluginLog"
+```
+
+After saving, the list will refresh to show the newly created plugin group.
+
+![11.png](../docs/images/admin/11.png)
+
+Similar to mapping configurations, you can click "View" to edit the plugin group configuration, or click "Delete" to remove the entire plugin group configuration.
+
+#### View and Delete Plugin Groups
+
+Click "View" to edit the plugin group configuration, or "Delete" to remove the plugin group.
+
+### Manage Rate Limiting Configuration
+
+#### Configure Rate Limiting
+
+Click the "Rate Limiting Configuration" menu to configure the rate-limiting components. Example configuration:
+
+![12.png](../docs/images/admin/12.png)
+
+```yaml
+resources:
+  - name: test-http
+    items:
+      - pattern: /api/v1/test-dubbo/user
+      - matchStrategy: 1
+        pattern: /api/*/test-dubbo/user
+rules:
+  - flowRule:
+      resource: ""
+      tokencalculatestrategy: 0
+      threshold: 100
+      enable: true
+```
+
+After saving, the rate-limiting configuration will take effect.
+
+![13.png](../docs/images/admin/13.png)
+
+## III. Pixiu Remote Configuration
+
+### Start and Configure
+
+Start Pixiu and specify the configuration file. In the configuration file, define the etcd address and configuration path:
+
+```yaml
+api_meta_config:
+  address: "127.0.0.1:2379"
+  api_config_path: "/pixiu/config/api"
+```
+
+### Test
+
+Create resource configurations in the admin panel and test Pixiu forwarding functionality using `curl`:
+
+```bash
+curl "http://127.0.0.1:8888/api/v1/test-dubbo/user?name=tc"
+curl -X POST "http://127.0.0.1:8888/api/v1/test-dubbo/user?name=tc"
+```
+
+If no matching service is found, an error message will be returned; if the configuration is correct, the service response will be returned.
+
+## License
+
+This project is licensed under the Apache License 2.0.
diff --git a/admin/README_CN.md b/admin/README_CN.md
new file mode 100644
index 00000000..ed2c1843
--- /dev/null
+++ b/admin/README_CN.md
@@ -0,0 +1,307 @@
+# Pixiu-Admin 部署与配置文档
+
+[English](README.md) | **中文**
+
+**Pixiu-Admin** 是基于 **Pixiu** 生态的管理平台，主要用于配置、监控和管理 Pixiu 网关资源。通过 Web 用户界面和 RESTful API
+提供集中式管理功能。本文档介绍了如何在 Linux 环境下部署和配置 Pixiu-Admin。
+
+后端API接口文档请参考 [API.md](../admin/API_CN.md)。
+
+## 部署文档
+
+### 使用 Docker 启动
+
+首先，确保您在项目的根目录（包含 Dockerfile 的目录）下，使用以下命令启动 Pixiu-Admin：
+
+```bash
+docker-compose up -d
+```
+
+### 使用源码部署
+
+如果不使用 Docker，可以通过以下命令下载源代码：
+
+```bash
+git clone https://github.com/apache/dubbo-go-pixiu
+```
+
+### 部署 etcd
+
+手动部署 etcd 服务，使用以下命令：
+
+```bash
+docker run -d -p2379:2379 --env ALLOW_NONE_AUTHENTICATION=yes --name etcd bitnami/etcd
+```
+
+对于 M1/M1 Pro 用户，使用以下命令：
+
+```bash
+docker run -d -p2379:2379 --platform linux/amd64 --env ALLOW_NONE_AUTHENTICATION=yes --name etcd bitnami/etcd:3.5.1
+```
+
+### 运行 Admin
+
+#### 源代码运行
+
+进入项目目录并运行：
+
+```bash
+cd dubbo-go-pixiu
+# 直接运行
+go run ./cmd/admin/admin.go -c /your/local/path/conf.yaml
+# 后台运行
+nohup go run ./cmd/admin/admin.go -c /your/local/path/conf.yaml &
+```
+
+#### 运行 Pixiu
+
+默认配置见 [pixiu_with_admin_config.yaml](../configs/pixiu_with_admin_config.yaml)
+
+```bash
+go run ./cmd/pixiu/pixiu.go gateway start -c ./configs/pixiu_with_admin_config.yaml
+```
+
+### 测试运行 admin-web
+
+进入 `web` 目录并安装依赖：
+
+```bash
+cd ./admin/web/
+yarn install  # 安装依赖
+yarn run serve  # 测试运行
+```
+
+#### admin-web 配置
+
+编辑 `web` 目录下的 `vue.config.js`，配置后端服务地址：
+
+```
+devServer: {
+    host: '0.0.0.0',
+        port: 8080,  // Web app address
+        hot: true,
+        https: false,
+        open: false,
+        disableHostCheck: true,
+        proxy: {
+        "/config": {
+            target: "http://127.0.0.1:8081",  // Backend service address
+                ws: true,  // Enable websockets
+                changeOrigin: true,  // Enable proxy
+        }
+    }
+}
+```
+
+运行成功后，可以在浏览器访问 [http://127.0.0.1:8081/login.html#/Overview](http://127.0.0.1:8081/login.html#/Overview)。
+
+## 二、相关操作
+
+### 管理映射（Resource）
+
+#### 创建映射配置
+
+1. 点击 "映射配置"，进入映射配置列表界面。
+2. 点击右上角的新增按钮，创建新的映射配置。
+
+![1.png](../docs/images/admin/1.png)
+
+在代码编辑器中键入映射配置，点击确认创建。
+
+![2.png](../docs/images/admin/2.png)
+
+#### 映射配置示例
+
+```yaml
+path: '/api/v1/test-dubbo/user'
+type: restful
+description: user
+filters:
+  - filter0
+methods:
+  - httpVerb: GET
+    onAir: true
+    timeout: 1000ms
+    inboundRequest:
+      requestType: http
+      queryStrings:
+        - name: name
+          required: true
+    integrationRequest:
+      requestType: dubbo
+      mappingParams:
+        - name: queryStrings.name
+          mapTo: 0
+          mapType: "java.lang.String"
+      applicationName: "UserProvider"
+      interface: "com.ic.user.UserProvider"
+      method: "GetUserByName"
+      group: "test"
+      version: 1.0.0
+      clusterName: "test_dubbo"
+  - httpVerb: POST
+    onAir: true
+    timeout: 10s
+    inboundRequest:
+      requestType: http
+    integrationRequest:
+      requestType: dubbo
+      mappingParams:
+        - name: requestBody._all
+          mapTo: 0
+          mapType: "object"
+      applicationName: "UserProvider"
+      interface: "com.ic.user.UserProvider"
+      method: "CreateUser"
+      group: "test"
+      version: 1.0.0
+      clusterName: "test_dubbo"
+```
+
+#### 查看和删除映射
+
+1. 映射列表刷新后，可以查看和删除配置。
+
+![3.png](../docs/images/admin/3.png)
+
+2. 点击删除会删除该映射配置，点击查看跳转至配置详情界面。
+
+![4.png](../docs/images/admin/4.png)
+
+#### 编辑映射
+
+可以在编辑区修改映射配置，点击 "修改" 保存修改。
+
+![5.png](../docs/images/admin/5.png)
+
+#### 方法映射
+
+点击新增按钮，添加新的方法映射。编辑示例如下：
+
+```yaml
+httpVerb: PUT
+onAir: true
+timeout: 10s
+inboundRequest:
+  requestType: http
+integrationRequest:
+  requestType: dubbo
+  mappingParams:
+    - name: requestBody._all
+      mapTo: 0
+      mapType: "object"
+  applicationName: "UserProvider"
+  interface: "com.ic.user.UserProvider"
+  method: "CreateUser"
+  group: "test"
+  version: 1.0.0
+  clusterName: "test_dubbo"
+```
+
+点击确认后，方法映射会出现在列表中。
+
+![6.png](../docs/images/admin/6.png)
+
+#### 查看和删除方法映射
+
+可以查看方法映射的详细信息或删除该映射。
+
+比如，将第二个方法映射的 httpVerb 从 POST 修改为 DELETE。
+
+![7.png](../docs/images/admin/7.png)
+
+注意：id不能进行修改，即使修改保存后也会改变为旧值。点击确定后，会更新该方法映射。
+
+![8.png](../docs/images/admin/8.png)
+
+### 管理插件组
+
+#### 创建插件组
+
+点击左侧插件配置菜单项，可以查看插件相关配置。
+
+![9.png](../docs/images/admin/9.png)
+
+点击右上方新增，可以创建新的插件组。
+
+![10.png](../docs/images/admin/10.png)
+
+插件组配置示例：
+
+```yaml
+groupName: "group2"
+plugins:
+  - name: "rate limit"
+    version: "0.1.0"
+    priority: 1000
+    externalLookupName: "ExternalPluginRateLimit"
+  - name: "log"
+    version: "0.2.0"
+    priority: 2000
+    externalLookupName: "ExternalPluginLog"
+```
+
+保存后，列表刷新展示新创建的插件组。
+
+![11.png](../docs/images/admin/11.png)
+
+和映射配置类似，点击查看会弹出编辑框，可以对插件组配置进行修改；点击删除会删除整个插件组配置。
+
+#### 查看和删除插件组
+
+点击查看可以编辑插件组配置，点击删除删除插件组。
+
+### 管理限流配置
+
+#### 配置限流
+
+点击 "限流配置" 菜单，进行限流组件的配置。具体配置示例：
+
+![12.png](../docs/images/admin/12.png)
+
+```yaml
+resources:
+  - name: test-http
+    items:
+      - pattern: /api/v1/test-dubbo/user
+      - matchStrategy: 1
+        pattern: /api/*/test-dubbo/user
+rules:
+  - flowRule:
+      resource: ""
+      tokencalculatestrategy: 0
+      threshold: 100
+      enable: true
+```
+
+点击保存后，限流配置生效。
+
+![13.png](../docs/images/admin/13.png)
+
+## 三、Pixiu 远程配置
+
+### 启动和配置
+
+启动 Pixiu 并指定配置文件，在配置文件中，定义 etcd 地址和配置路径：
+
+```yaml
+api_meta_config:
+  address: "127.0.0.1:2379"
+  api_config_path: "/pixiu/config/api"
+```
+
+### 测试
+
+在 admin 中创建资源配置，并使用 `curl` 测试 Pixiu 转发功能：
+
+```bash
+curl "http://127.0.0.1:8888/api/v1/test-dubbo/user?name=tc"
+curl -X POST "http://127.0.0.1:8888/api/v1/test-dubbo/user?name=tc"
+```
+
+如果请求未找到对应服务，返回错误信息；若配置正确，则返回服务响应。
+
+## 许可证
+
+本项目采用 Apache License 2.0 开源许可。
+
diff --git a/admin/controller/account/guest.go b/admin/controller/account/guest.go
index 1fc06f21..126f446d 100644
--- a/admin/controller/account/guest.go
+++ b/admin/controller/account/guest.go
@@ -39,10 +39,10 @@ import (
 )
 
 // @Tags Register
-// @Summary 用户注册
+// @Summary user registration
 // @Produce  application/json
-// @Param username formData string true "用户名"
-// @Param password formData string true "密码"
+// @Param username formData string true "username"
+// @Param password formData string true "password"
 // @Success 200 {string} string "{"code":"","data":""}"
 // @Router /register [post]
 func Register(c *gin.Context) {
diff --git a/admin/controller/account/user.go b/admin/controller/account/user.go
index 8cfabd56..06582c4f 100644
--- a/admin/controller/account/user.go
+++ b/admin/controller/account/user.go
@@ -34,6 +34,12 @@ import (
 	"github.com/apache/dubbo-go-pixiu/admin/logic/account"
 )
 
+// @Tags Account
+// @Summary user logout
+// @Description log out the current user (invalidate the token)
+// @Produce application/json
+// @Success 200 {object} string
+// @Router /user/logout [post]
 // Logout user logout
 func Logout(c *gin.Context) {
 	// Invalid setting token
@@ -54,6 +60,16 @@ func Logout(c *gin.Context) {
 	c.JSON(http.StatusOK, config.WithRet(token))
 }
 
+// @Tags Account
+// @Summary modify account password
+// @Description to modify the current user's password, the username must be included in the header.
+// @Accept application/x-www-form-urlencoded
+// @Produce application/json
+// @Param oldPassword formData string true "old password"
+// @Param newPassword formData string true "new password"
+// @Success 200 {object} string
+// @Failure 200 {object} string
+// @Router /user/password/edit [post]
 // EditPassword modify account password
 func EditPassword(c *gin.Context) {
 
@@ -88,6 +104,13 @@ func EditPassword(c *gin.Context) {
 	//generateToken(c, username)
 }
 
+// @Tags Account
+// @Summary get user information
+// @Description return the current user's detailed information. The header must include the username.
+// @Produce application/json
+// @Success 200 {object} string
+// @Failure 200 {object} string
+// @Router /user/getInfo [post]
 // GetUserInfo get user information
 func GetUserInfo(c *gin.Context) {
 	username := c.Request.Header.Get("username")
@@ -99,6 +122,13 @@ func GetUserInfo(c *gin.Context) {
 	c.JSON(http.StatusOK, config.WithRet(userInfo))
 }
 
+// @Tags Account
+// @Summary get user role
+// @Description return the current user's role information. The header must include the username.
+// @Produce application/json
+// @Success 200 {object} string
+// @Failure 200 {object} string
+// @Router /user/getUserRole [post]
 // GetUserRole get user role
 func GetUserRole(c *gin.Context) {
 	username := c.Request.Header.Get("username")
@@ -110,6 +140,13 @@ func GetUserRole(c *gin.Context) {
 	c.JSON(http.StatusOK, config.WithRet(result))
 }
 
+// @Tags Account
+// @Summary determine whether you are an administrator
+// @Description to determine whether the current user is an administrator, the header must include the username.
+// @Produce application/json
+// @Success 200 {object} string
+// @Failure 200 {object} string
+// @Router /user/checkIsAdmin [post]
 // CheckUserIsAdmin determine whether you are an administrator
 func CheckUserIsAdmin(c *gin.Context) {
 	username := c.Request.Header.Get("username")
diff --git a/admin/controller/auth/auth.go b/admin/controller/auth/auth.go
index 880912e8..fa3a49ef 100644
--- a/admin/controller/auth/auth.go
+++ b/admin/controller/auth/auth.go
@@ -18,7 +18,6 @@
 package auth
 
 import (
-	"log"
 	"net/http"
 	"time"
 )
@@ -35,7 +34,13 @@ import (
 	"github.com/apache/dubbo-go-pixiu/admin/config"
 )
 
-// Check token
+// @Tags Auth
+// @Summary JWT check midware
+// @Description Validate the token field in the request header, parse and verify the JWT. If verification fails, the request will be terminated.
+// @Produce application/json
+// @Success 200 {object} string
+// Note: this is a middleware, not a direct API endpoint
+// JWTAuth Check token
 func JWTAuth() gin.HandlerFunc {
 	return func(c *gin.Context) {
 		token := c.Request.Header.Get("token")
@@ -44,7 +49,7 @@ func JWTAuth() gin.HandlerFunc {
 			c.Abort()
 			return
 		}
-		log.Print("get token: ", token)
+		//log.Print("get token: ", token)
 		j := NewJWT()
 		// Parse the information contained in the token
 		claims, err := j.ParseToken(token)
diff --git a/admin/controller/configInfo/cluster.go b/admin/controller/configInfo/cluster.go
index cf759f63..3f27043a 100644
--- a/admin/controller/configInfo/cluster.go
+++ b/admin/controller/configInfo/cluster.go
@@ -34,6 +34,12 @@ import (
 	"github.com/apache/dubbo-go-pixiu/pkg/logger"
 )
 
+// @Tags Config
+// @Summary get cluster list
+// @Description get all clusters' info
+// @Produce application/json
+// @Success 200 {object} string
+// @Router /config/api/cluster/list [get]
 // GetClusterList get all cluster list
 func GetClusterList(c *gin.Context) {
 	rst, err := logic.BizGetClusters()
@@ -43,6 +49,15 @@ func GetClusterList(c *gin.Context) {
 	c.JSON(http.StatusOK, config.WithRet(rst))
 }
 
+// @Tags Config
+// @Summary create cluster
+// @Description Create a cluster by passing the YAML/JSON configuration for the cluster through the form's content field.
+// @Accept application/x-www-form-urlencoded
+// @Produce application/json
+// @Param content formData string true "Cluster content"
+// @Success 200 {object} string
+// @Failure 200 {object} string
+// @Router /config/api/cluster [put]
 // CreateCluster create a cluster
 func CreateCluster(c *gin.Context) {
 	body := c.PostForm("content")
@@ -61,6 +76,13 @@ func CreateCluster(c *gin.Context) {
 	c.JSON(http.StatusOK, config.WithRet("create cluster success!"))
 }
 
+// @Tags Config
+// @Summary delete cluster
+// @Description delete cluster according to cluster ID
+// @Produce application/json
+// @Param id query string true "Cluster ID"
+// @Success 200 {object} string
+// @Router /config/api/cluster [delete]
 // DeleteCluster delete resource
 func DeleteCluster(c *gin.Context) {
 	id := c.Query(logic.ClusterID)
@@ -73,6 +95,13 @@ func DeleteCluster(c *gin.Context) {
 	c.JSON(http.StatusOK, config.WithRet("delete cluster success!"))
 }
 
+// @Tags Config
+// @Summary get cluster detail
+// @Description get cluster detail according to cluster ID
+// @Produce application/json
+// @Param id query string true "Cluster ID"
+// @Success 200 {object} string
+// @Router /config/api/cluster/detail [get]
 // DetailCluster get cluster detail
 func DetailCluster(c *gin.Context) {
 	id := c.Query(logic.ClusterID)
@@ -84,6 +113,15 @@ func DetailCluster(c *gin.Context) {
 	c.JSON(http.StatusOK, config.WithRet(res))
 }
 
+// @Tags Config
+// @Summary update cluster
+// @Description pass the Cluster's YAML/JSON via the form's content field to update the cluster.
+// @Accept application/x-www-form-urlencoded
+// @Produce application/json
+// @Param content formData string true "Cluster content"
+// @Success 200 {object} string
+// @Failure 200 {object} string
+// @Router /config/api/cluster [post]
 // UpdateCluster update cluster
 func UpdateCluster(c *gin.Context) {
 	body := c.PostForm("content")
diff --git a/admin/controller/configInfo/configInfo.go b/admin/controller/configInfo/configInfo.go
index 60cf4066..80713e3a 100644
--- a/admin/controller/configInfo/configInfo.go
+++ b/admin/controller/configInfo/configInfo.go
@@ -39,6 +39,12 @@ import (
 	"github.com/apache/dubbo-go-pixiu/pkg/logger"
 )
 
+// @Tags Config
+// @Summary get basic configuration of Pixiu
+// @Description get pixiu base info such as name,desc
+// @Produce application/json
+// @Success 200 {string} string "YAML content"
+// @Router /config/api/base [get]
 // GetBaseInfo get pixiu base info such as name,desc
 func GetBaseInfo(c *gin.Context) {
 	conf, err := logic.BizGetBaseInfo()
@@ -50,6 +56,16 @@ func GetBaseInfo(c *gin.Context) {
 	c.JSON(http.StatusOK, config2.WithRet(string(data)))
 }
 
+// @Tags Config
+// @Summary modify pixiu base info such as name,desc
+// @Description Pass YAML content through the form's content field to set basic information.
+// @Accept application/x-www-form-urlencoded
+// @Produce application/json
+// @Param content formData string true "YAML content"
+// @Success 200 {object} string
+// @Failure 200 {object} string
+// @Router /config/api/base/ [post]
+// @Router /config/api/base/ [put]
 // SetBaseInfo modify pixiu base info such as name,desc
 func SetBaseInfo(c *gin.Context) {
 	body := c.PostForm("content")
@@ -70,6 +86,13 @@ func SetBaseInfo(c *gin.Context) {
 	c.JSON(http.StatusOK, config2.WithRet("success"))
 }
 
+// @Tags Config
+// @Summary get all resource list
+// @Description Retrieve the list of resources. Use the unpublished parameter to control whether to include unpublished or published resources.
+// @Produce application/json
+// @Param unpublished formData string false "1: unpublished; 0 or empty: published"
+// @Success 200 {string} string "JSON array"
+// @Router /config/api/resource/list [get]
 // GetResourceList get all resource list
 func GetResourceList(c *gin.Context) {
 	unpublished := getUnpublishedVal(c)
@@ -83,6 +106,14 @@ func GetResourceList(c *gin.Context) {
 	c.JSON(http.StatusOK, config2.WithRet(string(data)))
 }
 
+// @Tags Config
+// @Summary get resource detail with yml
+// @Description get resource details according to resource ID and return the YAML
+// @Produce application/json
+// @Param resourceId query int true "ResourceID"
+// @Param unpublished formData string false "1: unpublished; 0 or empty: published"
+// @Success 200 {string} string "YAML content"
+// @Router /config/api/resource/detail [get]
 // GetResourceDetail get resource detail with yml
 func GetResourceDetail(c *gin.Context) {
 	unpublished := getUnpublishedVal(c)
@@ -95,6 +126,16 @@ func GetResourceDetail(c *gin.Context) {
 	c.JSON(http.StatusOK, config2.WithRet(res))
 }
 
+// @Tags Config
+// @Summary create resource
+// @Description create a resource by passing the Resource's YAML via the form's content field (simultaneously writing to the staging area and production area).
+// @Accept application/x-www-form-urlencoded
+// @Produce application/json
+// @Param content formData string true "Resource YAML"
+// @Param unpublished formData string false "1: unpublished; 0 or empty: published"
+// @Success 200 {object} string
+// @Failure 200 {object} string
+// @Router /config/api/resource [post]
 // CreateResourceInfo create resource
 func CreateResourceInfo(c *gin.Context) {
 	body := c.PostForm("content")
@@ -128,6 +169,16 @@ func CreateResourceInfo(c *gin.Context) {
 	c.JSON(http.StatusOK, config2.WithRet("Success"))
 }
 
+// @Tags Config
+// @Summary modify resource
+// @Description modify resource content, where content is the YAML of the Resource. Use resourceId to specify the resource to be modified.
+// @Accept application/x-www-form-urlencoded
+// @Produce application/json
+// @Param resourceId query int false "resource ID"
+// @Param content formData string true "Resource YAML"
+// @Param unpublished formData string false "1: unpublished; 0 or empty: published"
+// @Success 200 {object} string
+// @Router /config/api/resource [put]
 // ModifyResourceInfo modify resource
 func ModifyResourceInfo(c *gin.Context) {
 	id := c.Query(logic.ResourceID)
@@ -184,6 +235,14 @@ func afterResourcePathChange(resourceId, path string, unpublished bool) {
 	}
 }
 
+// @Tags Config
+// @Summary delete resource
+// @Description delete resources by ID. When unpublished is 1, this indicates deleting configurations for unpublished spaces (requires checking published spaces).
+// @Produce application/json
+// @Param resourceId query int true "resource ID"
+// @Param unpublished formData string false "1: unpublished; 0 or empty: published"
+// @Success 200 {object} string
+// @Router /config/api/resource [delete]
 // DeleteResourceInfo delete resource
 func DeleteResourceInfo(c *gin.Context) {
 	id := c.Query(logic.ResourceID)
@@ -209,6 +268,14 @@ func DeleteResourceInfo(c *gin.Context) {
 	c.JSON(http.StatusOK, config2.WithRet("Success"))
 }
 
+// @Tags Config
+// @Summary get all method list below one resource
+// @Description get the list of methods under the specified resource
+// @Produce application/json
+// @Param resourceId query int true "resource ID"
+// @Param unpublished formData string false "1: unpublished; 0 or empty: published"
+// @Success 200 {string} string "JSON array"
+// @Router /config/api/resource/method/list [get]
 // GetMethodList get all method list below one resource
 func GetMethodList(c *gin.Context) {
 	resourceId := c.Query(logic.ResourceID) // unique id
@@ -223,6 +290,15 @@ func GetMethodList(c *gin.Context) {
 	c.JSON(http.StatusOK, config2.WithRet(string(data)))
 }
 
+// @Tags Config
+// @Summary get method detail with yml
+// @Description get method details based on resourceId and methodId, returning YAML.
+// @Produce application/json
+// @Param resourceId query int true "resource ID"
+// @Param methodId query int true "method ID"
+// @Param unpublished formData string false "1: unpublished; 0 or empty: published"
+// @Success 200 {string} string "YAML content"
+// @Router /config/api/resource/method/detail [get]
 // GetMethodDetail get method detail with yml
 func GetMethodDetail(c *gin.Context) {
 	resourceId := c.Query(logic.ResourceID)
@@ -236,6 +312,15 @@ func GetMethodDetail(c *gin.Context) {
 	c.JSON(http.StatusOK, config2.WithRet(res))
 }
 
+// @Tags Config
+// @Summary delete method
+// @Description Deleting a method under a resource, where `unpublished` equals 1 indicates removing the configuration for unpublished spaces (requires checking published spaces).
+// @Produce application/json
+// @Param resourceId query int true "ResourceID"
+// @Param methodId query int true "MethodID"
+// @Param unpublished formData string false "1: unpublished; 0 or empty: published"
+// @Success 200 {object} string
+// @Router /config/api/resource/method [delete]
 // DeleteResourceInfo delete method
 func DeleteMethodInfo(c *gin.Context) {
 	resourceId := c.Query(logic.ResourceID)
@@ -261,6 +346,16 @@ func DeleteMethodInfo(c *gin.Context) {
 	c.JSON(http.StatusOK, config2.WithRet("Success"))
 }
 
+// @Tags Config
+// @Summary create method
+// @Description create a method under the specified resource, where content is the YAML for Method.
+// @Accept application/x-www-form-urlencoded
+// @Produce application/json
+// @Param resourceId query int true "ResourceID"
+// @Param content formData string true "Method YAML"
+// @Param unpublished formData string false "1: unpublished; 0 or empty: published"
+// @Success 200 {object} string
+// @Router /config/api/resource/method [post]
 // CreateMethodInfo create method
 func CreateMethodInfo(c *gin.Context) {
 	body := c.PostForm("content")
@@ -307,6 +402,17 @@ func getResourceDetail(id string, unpublished bool) (*fc.Resource, error) {
 	return resource, nil
 }
 
+// @Tags Config
+// @Summary modify method
+// @Description Modify the specified method, where content is the YAML for Method.
+// @Accept application/x-www-form-urlencoded
+// @Produce application/json
+// @Param resourceId query int true "ResourceID"
+// @Param methodId query int false "MethodID"
+// @Param content formData string true "Method YAML"
+// @Param unpublished formData string false "1: unpublished; 0 or empty: published"
+// @Success 200 {object} string
+// @Router /config/api/resource/method [put]
 // ModifyMethodInfo modify method
 func ModifyMethodInfo(c *gin.Context) {
 	body := c.PostForm("content")
@@ -347,6 +453,9 @@ func ModifyMethodInfo(c *gin.Context) {
 	c.JSON(http.StatusOK, config2.WithRet("Success"))
 }
 
+// @Tags Config
+// @Summary determine the configuration type of the current operation
+// @Description internal function, no external documentation required
 // getUnpublishedVal Determine the configuration type of the current operation
 func getUnpublishedVal(c *gin.Context) bool {
 	// The front-end request carries the unpublished field to determine which configuration is currently operating
@@ -359,6 +468,12 @@ func getUnpublishedVal(c *gin.Context) bool {
 	}
 }
 
+// @Tags Config
+// @Summary publish all configuration information
+// @Description publish resources from unpublished spaces to published spaces
+// @Produce application/json
+// @Success 200 {object} string
+// @Router /config/api/resource/publish [put]
 // BatchReleaseResource Publish all configuration information
 func BatchReleaseResource(c *gin.Context) {
 	fromKList, fromVList, fromErr := logic.BRGetResourceList(true) // from represent unpublished space
@@ -401,11 +516,23 @@ func BatchReleaseResource(c *gin.Context) {
 	}
 }
 
+// @Tags Config
+// @Summary batch Release Method Config
+// @Description TODO: unimplemented
+// @Produce application/json
+// @Success 200 {object} string
+// @Router /config/api/resource/method/publish [put]
 // BatchReleaseMethod Batch Release Method Config
 func BatchReleaseMethod(c *gin.Context) {
 	// todo
 }
 
+// @Tags Config
+// @Summary batch Release PluginGroup Config
+// @Description publish the PluginGroup from the unpublished space to the published space.
+// @Produce application/json
+// @Success 200 {object} string
+// @Router /config/api/plugin_group/publish [put]
 // BatchReleasePluginGroup Batch Release PluginGroup Config
 func BatchReleasePluginGroup(c *gin.Context) {
 	fromKList, fromVList, fromErr := logic.BRGetPluginGroupList(true) // from represent unpublished space
diff --git a/admin/controller/configInfo/listener.go b/admin/controller/configInfo/listener.go
index 2750775e..82a7bd23 100644
--- a/admin/controller/configInfo/listener.go
+++ b/admin/controller/configInfo/listener.go
@@ -34,6 +34,12 @@ import (
 	"github.com/apache/dubbo-go-pixiu/pkg/logger"
 )
 
+// @Tags Config
+// @Summary get all Listener list
+// @Description get all Listener list
+// @Produce application/json
+// @Success 200 {object} string
+// @Router /config/api/listener/list [get]
 // GetListenerList get all Listener list
 func GetListenerList(c *gin.Context) {
 	rst, err := logic.BizGetListeners()
@@ -44,6 +50,15 @@ func GetListenerList(c *gin.Context) {
 	c.JSON(http.StatusOK, config.WithRet(rst))
 }
 
+// @Tags Config
+// @Summary create a Listener
+// @Description pass the YAML/JSON for the Listener through the form's content field to create the Listener.
+// @Accept application/x-www-form-urlencoded
+// @Produce application/json
+// @Param content formData string true "Listener content"
+// @Success 200 {object} string
+// @Failure 200 {object} string
+// @Router /config/api/listener [put]
 // CreateListener create a Listener
 func CreateListener(c *gin.Context) {
 	body := c.PostForm("content")
@@ -62,7 +77,14 @@ func CreateListener(c *gin.Context) {
 	c.JSON(http.StatusOK, config.WithRet("create Listener success!"))
 }
 
-// DeleteListener delete resource
+// @Tags Config
+// @Summary delete Listener
+// @Description delete the Listener based on the name
+// @Produce application/json
+// @Param name query string true "Listener id"
+// @Success 200 {object} string
+// @Router /config/api/listener [delete]
+// DeleteListener delete Listener
 func DeleteListener(c *gin.Context) {
 	id := c.Query(logic.Listener)
 	err := logic.BizDeleteListener(id)
@@ -74,6 +96,13 @@ func DeleteListener(c *gin.Context) {
 	c.JSON(http.StatusOK, config.WithRet("delete Listener success!"))
 }
 
+// @Tags Config
+// @Summary get Listener detail
+// @Description get Listener details based on name
+// @Produce application/json
+// @Param name query string true "Listener name"
+// @Success 200 {object} string
+// @Router /config/api/listener/detail [get]
 // DetailListener get Listener detail
 func DetailListener(c *gin.Context) {
 	name := c.Query(logic.Listener)
@@ -85,6 +114,15 @@ func DetailListener(c *gin.Context) {
 	c.JSON(http.StatusOK, config.WithRet(res))
 }
 
+// @Tags Config
+// @Summary update Listener
+// @Description pass the YAML/JSON for the Listener through the form's content field to update the Listener.
+// @Accept application/x-www-form-urlencoded
+// @Produce application/json
+// @Param content formData string true "Listener content"
+// @Success 200 {object} string
+// @Failure 200 {object} string
+// @Router /config/api/listener [post]
 // UpdateListener update Listener
 func UpdateListener(c *gin.Context) {
 	body := c.PostForm("content")
diff --git a/admin/doc/docs.go b/admin/doc/docs.go
new file mode 100644
index 00000000..87fb91ae
--- /dev/null
+++ b/admin/doc/docs.go
@@ -0,0 +1,1013 @@
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You 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.
+//
+
+// Package doc Code generated by swaggo/swag. DO NOT EDIT
+package doc
+
+import "github.com/swaggo/swag"
+
+const docTemplate = `{
+    "schemes": {{ marshal .Schemes }},
+    "swagger": "2.0",
+    "info": {
+        "description": "{{escape .Description}}",
+        "title": "{{.Title}}",
+        "contact": {},
+        "version": "{{.Version}}"
+    },
+    "host": "{{.Host}}",
+    "basePath": "{{.BasePath}}",
+    "paths": {
+        "/config/api/base": {
+            "get": {
+                "description": "get pixiu base info such as name,desc",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "get basic configuration of Pixiu",
+                "responses": {
+                    "200": {
+                        "description": "YAML content",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/base/": {
+            "put": {
+                "description": "Pass YAML content through the form's content field to set basic information.",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "modify pixiu base info such as name,desc",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "YAML content",
+                        "name": "content",
+                        "in": "formData",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            },
+            "post": {
+                "description": "Pass YAML content through the form's content field to set basic information.",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "modify pixiu base info such as name,desc",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "YAML content",
+                        "name": "content",
+                        "in": "formData",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/cluster": {
+            "put": {
+                "description": "Create a cluster by passing the YAML/JSON configuration for the cluster through the form's content field.",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "create cluster",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Cluster content",
+                        "name": "content",
+                        "in": "formData",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            },
+            "post": {
+                "description": "pass the Cluster's YAML/JSON via the form's content field to update the cluster.",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "update cluster",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Cluster content",
+                        "name": "content",
+                        "in": "formData",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            },
+            "delete": {
+                "description": "delete cluster according to cluster ID",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "delete cluster",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Cluster ID",
+                        "name": "id",
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/cluster/detail": {
+            "get": {
+                "description": "get cluster detail according to cluster ID",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "get cluster detail",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Cluster ID",
+                        "name": "id",
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/cluster/list": {
+            "get": {
+                "description": "get all clusters' info",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "get cluster list",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/listener": {
+            "put": {
+                "description": "pass the YAML/JSON for the Listener through the form's content field to create the Listener.",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "create a Listener",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Listener content",
+                        "name": "content",
+                        "in": "formData",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            },
+            "post": {
+                "description": "pass the YAML/JSON for the Listener through the form's content field to update the Listener.",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "update Listener",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Listener content",
+                        "name": "content",
+                        "in": "formData",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            },
+            "delete": {
+                "description": "delete the Listener based on the name",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "delete Listener",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Listener id",
+                        "name": "name",
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/listener/detail": {
+            "get": {
+                "description": "get Listener details based on name",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "get Listener detail",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Listener name",
+                        "name": "name",
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/listener/list": {
+            "get": {
+                "description": "get all Listener list",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "get all Listener list",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/plugin_group/publish": {
+            "put": {
+                "description": "publish the PluginGroup from the unpublished space to the published space.",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "batch Release PluginGroup Config",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/resource": {
+            "put": {
+                "description": "modify resource content, where content is the YAML of the Resource. Use resourceId to specify the resource to be modified.",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "modify resource",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "resource ID",
+                        "name": "resourceId",
+                        "in": "query"
+                    },
+                    {
+                        "type": "string",
+                        "description": "Resource YAML",
+                        "name": "content",
+                        "in": "formData",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "1: unpublished; 0 or empty: published",
+                        "name": "unpublished",
+                        "in": "formData"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            },
+            "post": {
+                "description": "create a resource by passing the Resource's YAML via the form's content field (simultaneously writing to the staging area and production area).",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "create resource",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Resource YAML",
+                        "name": "content",
+                        "in": "formData",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "1: unpublished; 0 or empty: published",
+                        "name": "unpublished",
+                        "in": "formData"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            },
+            "delete": {
+                "description": "delete resources by ID. When unpublished is 1, this indicates deleting configurations for unpublished spaces (requires checking published spaces).",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "delete resource",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "resource ID",
+                        "name": "resourceId",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "1: unpublished; 0 or empty: published",
+                        "name": "unpublished",
+                        "in": "formData"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/resource/detail": {
+            "get": {
+                "description": "get resource details according to resource ID and return the YAML",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "get resource detail with yml",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "ResourceID",
+                        "name": "resourceId",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "1: unpublished; 0 or empty: published",
+                        "name": "unpublished",
+                        "in": "formData"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "YAML content",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/resource/list": {
+            "get": {
+                "description": "Retrieve the list of resources. Use the unpublished parameter to control whether to include unpublished or published resources.",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "get all resource list",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "1: unpublished; 0 or empty: published",
+                        "name": "unpublished",
+                        "in": "formData"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "JSON array",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/resource/method": {
+            "put": {
+                "description": "Modify the specified method, where content is the YAML for Method.",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "modify method",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "ResourceID",
+                        "name": "resourceId",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "integer",
+                        "description": "MethodID",
+                        "name": "methodId",
+                        "in": "query"
+                    },
+                    {
+                        "type": "string",
+                        "description": "Method YAML",
+                        "name": "content",
+                        "in": "formData",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "1: unpublished; 0 or empty: published",
+                        "name": "unpublished",
+                        "in": "formData"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            },
+            "post": {
+                "description": "create a method under the specified resource, where content is the YAML for Method.",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "create method",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "ResourceID",
+                        "name": "resourceId",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "Method YAML",
+                        "name": "content",
+                        "in": "formData",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "1: unpublished; 0 or empty: published",
+                        "name": "unpublished",
+                        "in": "formData"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            },
+            "delete": {
+                "description": "Deleting a method under a resource, where ` + "`" + `unpublished` + "`" + ` equals 1 indicates removing the configuration for unpublished spaces (requires checking published spaces).",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "delete method",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "ResourceID",
+                        "name": "resourceId",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "integer",
+                        "description": "MethodID",
+                        "name": "methodId",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "1: unpublished; 0 or empty: published",
+                        "name": "unpublished",
+                        "in": "formData"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/resource/method/detail": {
+            "get": {
+                "description": "get method details based on resourceId and methodId, returning YAML.",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "get method detail with yml",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "resource ID",
+                        "name": "resourceId",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "integer",
+                        "description": "method ID",
+                        "name": "methodId",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "1: unpublished; 0 or empty: published",
+                        "name": "unpublished",
+                        "in": "formData"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "YAML content",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/resource/method/list": {
+            "get": {
+                "description": "get the list of methods under the specified resource",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "get all method list below one resource",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "resource ID",
+                        "name": "resourceId",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "1: unpublished; 0 or empty: published",
+                        "name": "unpublished",
+                        "in": "formData"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "JSON array",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/resource/method/publish": {
+            "put": {
+                "description": "TODO: unimplemented",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "batch Release Method Config",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/resource/publish": {
+            "put": {
+                "description": "publish resources from unpublished spaces to published spaces",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "publish all configuration information",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/register": {
+            "post": {
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Register"
+                ],
+                "summary": "user registration",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "username",
+                        "name": "username",
+                        "in": "formData",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "password",
+                        "name": "password",
+                        "in": "formData",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "{\"code\":\"\",\"data\":\"\"}",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/user/checkIsAdmin": {
+            "post": {
+                "description": "to determine whether the current user is an administrator, the header must include the username.",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Account"
+                ],
+                "summary": "determine whether you are an administrator",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/user/getInfo": {
+            "post": {
+                "description": "return the current user's detailed information. The header must include the username.",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Account"
+                ],
+                "summary": "get user information",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/user/getUserRole": {
+            "post": {
+                "description": "return the current user's role information. The header must include the username.",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Account"
+                ],
+                "summary": "get user role",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/user/logout": {
+            "post": {
+                "description": "log out the current user (invalidate the token)",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Account"
+                ],
+                "summary": "user logout",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/user/password/edit": {
+            "post": {
+                "description": "to modify the current user's password, the username must be included in the header.",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Account"
+                ],
+                "summary": "modify account password",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "old password",
+                        "name": "oldPassword",
+                        "in": "formData",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "new password",
+                        "name": "newPassword",
+                        "in": "formData",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        }
+    }
+}`
+
+// SwaggerInfo holds exported Swagger Info so clients can modify it
+var SwaggerInfo = &swag.Spec{
+	Version:          "",
+	Host:             "",
+	BasePath:         "",
+	Schemes:          []string{},
+	Title:            "",
+	Description:      "",
+	InfoInstanceName: "swagger",
+	SwaggerTemplate:  docTemplate,
+	LeftDelim:        "{{",
+	RightDelim:       "}}",
+}
+
+func init() {
+	swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
+}
diff --git a/admin/doc/swagger.json b/admin/doc/swagger.json
new file mode 100644
index 00000000..7b1b085e
--- /dev/null
+++ b/admin/doc/swagger.json
@@ -0,0 +1,967 @@
+{
+    "swagger": "2.0",
+    "info": {
+        "contact": {}
+    },
+    "paths": {
+        "/config/api/base": {
+            "get": {
+                "description": "get pixiu base info such as name,desc",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "get basic configuration of Pixiu",
+                "responses": {
+                    "200": {
+                        "description": "YAML content",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/base/": {
+            "put": {
+                "description": "Pass YAML content through the form's content field to set basic information.",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "modify pixiu base info such as name,desc",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "YAML content",
+                        "name": "content",
+                        "in": "formData",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            },
+            "post": {
+                "description": "Pass YAML content through the form's content field to set basic information.",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "modify pixiu base info such as name,desc",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "YAML content",
+                        "name": "content",
+                        "in": "formData",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/cluster": {
+            "put": {
+                "description": "Create a cluster by passing the YAML/JSON configuration for the cluster through the form's content field.",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "create cluster",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Cluster content",
+                        "name": "content",
+                        "in": "formData",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            },
+            "post": {
+                "description": "pass the Cluster's YAML/JSON via the form's content field to update the cluster.",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "update cluster",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Cluster content",
+                        "name": "content",
+                        "in": "formData",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            },
+            "delete": {
+                "description": "delete cluster according to cluster ID",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "delete cluster",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Cluster ID",
+                        "name": "id",
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/cluster/detail": {
+            "get": {
+                "description": "get cluster detail according to cluster ID",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "get cluster detail",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Cluster ID",
+                        "name": "id",
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/cluster/list": {
+            "get": {
+                "description": "get all clusters' info",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "get cluster list",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/listener": {
+            "put": {
+                "description": "pass the YAML/JSON for the Listener through the form's content field to create the Listener.",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "create a Listener",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Listener content",
+                        "name": "content",
+                        "in": "formData",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            },
+            "post": {
+                "description": "pass the YAML/JSON for the Listener through the form's content field to update the Listener.",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "update Listener",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Listener content",
+                        "name": "content",
+                        "in": "formData",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            },
+            "delete": {
+                "description": "delete the Listener based on the name",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "delete Listener",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Listener id",
+                        "name": "name",
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/listener/detail": {
+            "get": {
+                "description": "get Listener details based on name",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "get Listener detail",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Listener name",
+                        "name": "name",
+                        "in": "query",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/listener/list": {
+            "get": {
+                "description": "get all Listener list",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "get all Listener list",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/plugin_group/publish": {
+            "put": {
+                "description": "publish the PluginGroup from the unpublished space to the published space.",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "batch Release PluginGroup Config",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/resource": {
+            "put": {
+                "description": "modify resource content, where content is the YAML of the Resource. Use resourceId to specify the resource to be modified.",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "modify resource",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "resource ID",
+                        "name": "resourceId",
+                        "in": "query"
+                    },
+                    {
+                        "type": "string",
+                        "description": "Resource YAML",
+                        "name": "content",
+                        "in": "formData",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "1: unpublished; 0 or empty: published",
+                        "name": "unpublished",
+                        "in": "formData"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            },
+            "post": {
+                "description": "create a resource by passing the Resource's YAML via the form's content field (simultaneously writing to the staging area and production area).",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "create resource",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "Resource YAML",
+                        "name": "content",
+                        "in": "formData",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "1: unpublished; 0 or empty: published",
+                        "name": "unpublished",
+                        "in": "formData"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            },
+            "delete": {
+                "description": "delete resources by ID. When unpublished is 1, this indicates deleting configurations for unpublished spaces (requires checking published spaces).",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "delete resource",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "resource ID",
+                        "name": "resourceId",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "1: unpublished; 0 or empty: published",
+                        "name": "unpublished",
+                        "in": "formData"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/resource/detail": {
+            "get": {
+                "description": "get resource details according to resource ID and return the YAML",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "get resource detail with yml",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "ResourceID",
+                        "name": "resourceId",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "1: unpublished; 0 or empty: published",
+                        "name": "unpublished",
+                        "in": "formData"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "YAML content",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/resource/list": {
+            "get": {
+                "description": "Retrieve the list of resources. Use the unpublished parameter to control whether to include unpublished or published resources.",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "get all resource list",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "1: unpublished; 0 or empty: published",
+                        "name": "unpublished",
+                        "in": "formData"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "JSON array",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/resource/method": {
+            "put": {
+                "description": "Modify the specified method, where content is the YAML for Method.",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "modify method",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "ResourceID",
+                        "name": "resourceId",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "integer",
+                        "description": "MethodID",
+                        "name": "methodId",
+                        "in": "query"
+                    },
+                    {
+                        "type": "string",
+                        "description": "Method YAML",
+                        "name": "content",
+                        "in": "formData",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "1: unpublished; 0 or empty: published",
+                        "name": "unpublished",
+                        "in": "formData"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            },
+            "post": {
+                "description": "create a method under the specified resource, where content is the YAML for Method.",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "create method",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "ResourceID",
+                        "name": "resourceId",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "Method YAML",
+                        "name": "content",
+                        "in": "formData",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "1: unpublished; 0 or empty: published",
+                        "name": "unpublished",
+                        "in": "formData"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            },
+            "delete": {
+                "description": "Deleting a method under a resource, where `unpublished` equals 1 indicates removing the configuration for unpublished spaces (requires checking published spaces).",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "delete method",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "ResourceID",
+                        "name": "resourceId",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "integer",
+                        "description": "MethodID",
+                        "name": "methodId",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "1: unpublished; 0 or empty: published",
+                        "name": "unpublished",
+                        "in": "formData"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/resource/method/detail": {
+            "get": {
+                "description": "get method details based on resourceId and methodId, returning YAML.",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "get method detail with yml",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "resource ID",
+                        "name": "resourceId",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "integer",
+                        "description": "method ID",
+                        "name": "methodId",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "1: unpublished; 0 or empty: published",
+                        "name": "unpublished",
+                        "in": "formData"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "YAML content",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/resource/method/list": {
+            "get": {
+                "description": "get the list of methods under the specified resource",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "get all method list below one resource",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "resource ID",
+                        "name": "resourceId",
+                        "in": "query",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "1: unpublished; 0 or empty: published",
+                        "name": "unpublished",
+                        "in": "formData"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "JSON array",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/resource/method/publish": {
+            "put": {
+                "description": "TODO: unimplemented",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "batch Release Method Config",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/config/api/resource/publish": {
+            "put": {
+                "description": "publish resources from unpublished spaces to published spaces",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Config"
+                ],
+                "summary": "publish all configuration information",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/register": {
+            "post": {
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Register"
+                ],
+                "summary": "user registration",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "username",
+                        "name": "username",
+                        "in": "formData",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "password",
+                        "name": "password",
+                        "in": "formData",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "{\"code\":\"\",\"data\":\"\"}",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/user/checkIsAdmin": {
+            "post": {
+                "description": "to determine whether the current user is an administrator, the header must include the username.",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Account"
+                ],
+                "summary": "determine whether you are an administrator",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/user/getInfo": {
+            "post": {
+                "description": "return the current user's detailed information. The header must include the username.",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Account"
+                ],
+                "summary": "get user information",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/user/getUserRole": {
+            "post": {
+                "description": "return the current user's role information. The header must include the username.",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Account"
+                ],
+                "summary": "get user role",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/user/logout": {
+            "post": {
+                "description": "log out the current user (invalidate the token)",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Account"
+                ],
+                "summary": "user logout",
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        },
+        "/user/password/edit": {
+            "post": {
+                "description": "to modify the current user's password, the username must be included in the header.",
+                "consumes": [
+                    "application/x-www-form-urlencoded"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "Account"
+                ],
+                "summary": "modify account password",
+                "parameters": [
+                    {
+                        "type": "string",
+                        "description": "old password",
+                        "name": "oldPassword",
+                        "in": "formData",
+                        "required": true
+                    },
+                    {
+                        "type": "string",
+                        "description": "new password",
+                        "name": "newPassword",
+                        "in": "formData",
+                        "required": true
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "type": "string"
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/admin/doc/swagger.yaml b/admin/doc/swagger.yaml
new file mode 100644
index 00000000..3848f685
--- /dev/null
+++ b/admin/doc/swagger.yaml
@@ -0,0 +1,656 @@
+info:
+  contact: {}
+paths:
+  /config/api/base:
+    get:
+      description: get pixiu base info such as name,desc
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: YAML content
+          schema:
+            type: string
+      summary: get basic configuration of Pixiu
+      tags:
+      - Config
+  /config/api/base/:
+    post:
+      consumes:
+      - application/x-www-form-urlencoded
+      description: Pass YAML content through the form's content field to set basic
+        information.
+      parameters:
+      - description: YAML content
+        in: formData
+        name: content
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: modify pixiu base info such as name,desc
+      tags:
+      - Config
+    put:
+      consumes:
+      - application/x-www-form-urlencoded
+      description: Pass YAML content through the form's content field to set basic
+        information.
+      parameters:
+      - description: YAML content
+        in: formData
+        name: content
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: modify pixiu base info such as name,desc
+      tags:
+      - Config
+  /config/api/cluster:
+    delete:
+      description: delete cluster according to cluster ID
+      parameters:
+      - description: Cluster ID
+        in: query
+        name: id
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: delete cluster
+      tags:
+      - Config
+    post:
+      consumes:
+      - application/x-www-form-urlencoded
+      description: pass the Cluster's YAML/JSON via the form's content field to update
+        the cluster.
+      parameters:
+      - description: Cluster content
+        in: formData
+        name: content
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: update cluster
+      tags:
+      - Config
+    put:
+      consumes:
+      - application/x-www-form-urlencoded
+      description: Create a cluster by passing the YAML/JSON configuration for the
+        cluster through the form's content field.
+      parameters:
+      - description: Cluster content
+        in: formData
+        name: content
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: create cluster
+      tags:
+      - Config
+  /config/api/cluster/detail:
+    get:
+      description: get cluster detail according to cluster ID
+      parameters:
+      - description: Cluster ID
+        in: query
+        name: id
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: get cluster detail
+      tags:
+      - Config
+  /config/api/cluster/list:
+    get:
+      description: get all clusters' info
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: get cluster list
+      tags:
+      - Config
+  /config/api/listener:
+    delete:
+      description: delete the Listener based on the name
+      parameters:
+      - description: Listener id
+        in: query
+        name: name
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: delete Listener
+      tags:
+      - Config
+    post:
+      consumes:
+      - application/x-www-form-urlencoded
+      description: pass the YAML/JSON for the Listener through the form's content
+        field to update the Listener.
+      parameters:
+      - description: Listener content
+        in: formData
+        name: content
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: update Listener
+      tags:
+      - Config
+    put:
+      consumes:
+      - application/x-www-form-urlencoded
+      description: pass the YAML/JSON for the Listener through the form's content
+        field to create the Listener.
+      parameters:
+      - description: Listener content
+        in: formData
+        name: content
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: create a Listener
+      tags:
+      - Config
+  /config/api/listener/detail:
+    get:
+      description: get Listener details based on name
+      parameters:
+      - description: Listener name
+        in: query
+        name: name
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: get Listener detail
+      tags:
+      - Config
+  /config/api/listener/list:
+    get:
+      description: get all Listener list
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: get all Listener list
+      tags:
+      - Config
+  /config/api/plugin_group/publish:
+    put:
+      description: publish the PluginGroup from the unpublished space to the published
+        space.
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: batch Release PluginGroup Config
+      tags:
+      - Config
+  /config/api/resource:
+    delete:
+      description: delete resources by ID. When unpublished is 1, this indicates deleting
+        configurations for unpublished spaces (requires checking published spaces).
+      parameters:
+      - description: resource ID
+        in: query
+        name: resourceId
+        required: true
+        type: integer
+      - description: '1: unpublished; 0 or empty: published'
+        in: formData
+        name: unpublished
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: delete resource
+      tags:
+      - Config
+    post:
+      consumes:
+      - application/x-www-form-urlencoded
+      description: create a resource by passing the Resource's YAML via the form's
+        content field (simultaneously writing to the staging area and production area).
+      parameters:
+      - description: Resource YAML
+        in: formData
+        name: content
+        required: true
+        type: string
+      - description: '1: unpublished; 0 or empty: published'
+        in: formData
+        name: unpublished
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: create resource
+      tags:
+      - Config
+    put:
+      consumes:
+      - application/x-www-form-urlencoded
+      description: modify resource content, where content is the YAML of the Resource.
+        Use resourceId to specify the resource to be modified.
+      parameters:
+      - description: resource ID
+        in: query
+        name: resourceId
+        type: integer
+      - description: Resource YAML
+        in: formData
+        name: content
+        required: true
+        type: string
+      - description: '1: unpublished; 0 or empty: published'
+        in: formData
+        name: unpublished
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: modify resource
+      tags:
+      - Config
+  /config/api/resource/detail:
+    get:
+      description: get resource details according to resource ID and return the YAML
+      parameters:
+      - description: ResourceID
+        in: query
+        name: resourceId
+        required: true
+        type: integer
+      - description: '1: unpublished; 0 or empty: published'
+        in: formData
+        name: unpublished
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: YAML content
+          schema:
+            type: string
+      summary: get resource detail with yml
+      tags:
+      - Config
+  /config/api/resource/list:
+    get:
+      description: Retrieve the list of resources. Use the unpublished parameter to
+        control whether to include unpublished or published resources.
+      parameters:
+      - description: '1: unpublished; 0 or empty: published'
+        in: formData
+        name: unpublished
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: JSON array
+          schema:
+            type: string
+      summary: get all resource list
+      tags:
+      - Config
+  /config/api/resource/method:
+    delete:
+      description: Deleting a method under a resource, where `unpublished` equals
+        1 indicates removing the configuration for unpublished spaces (requires checking
+        published spaces).
+      parameters:
+      - description: ResourceID
+        in: query
+        name: resourceId
+        required: true
+        type: integer
+      - description: MethodID
+        in: query
+        name: methodId
+        required: true
+        type: integer
+      - description: '1: unpublished; 0 or empty: published'
+        in: formData
+        name: unpublished
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: delete method
+      tags:
+      - Config
+    post:
+      consumes:
+      - application/x-www-form-urlencoded
+      description: create a method under the specified resource, where content is
+        the YAML for Method.
+      parameters:
+      - description: ResourceID
+        in: query
+        name: resourceId
+        required: true
+        type: integer
+      - description: Method YAML
+        in: formData
+        name: content
+        required: true
+        type: string
+      - description: '1: unpublished; 0 or empty: published'
+        in: formData
+        name: unpublished
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: create method
+      tags:
+      - Config
+    put:
+      consumes:
+      - application/x-www-form-urlencoded
+      description: Modify the specified method, where content is the YAML for Method.
+      parameters:
+      - description: ResourceID
+        in: query
+        name: resourceId
+        required: true
+        type: integer
+      - description: MethodID
+        in: query
+        name: methodId
+        type: integer
+      - description: Method YAML
+        in: formData
+        name: content
+        required: true
+        type: string
+      - description: '1: unpublished; 0 or empty: published'
+        in: formData
+        name: unpublished
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: modify method
+      tags:
+      - Config
+  /config/api/resource/method/detail:
+    get:
+      description: get method details based on resourceId and methodId, returning
+        YAML.
+      parameters:
+      - description: resource ID
+        in: query
+        name: resourceId
+        required: true
+        type: integer
+      - description: method ID
+        in: query
+        name: methodId
+        required: true
+        type: integer
+      - description: '1: unpublished; 0 or empty: published'
+        in: formData
+        name: unpublished
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: YAML content
+          schema:
+            type: string
+      summary: get method detail with yml
+      tags:
+      - Config
+  /config/api/resource/method/list:
+    get:
+      description: get the list of methods under the specified resource
+      parameters:
+      - description: resource ID
+        in: query
+        name: resourceId
+        required: true
+        type: integer
+      - description: '1: unpublished; 0 or empty: published'
+        in: formData
+        name: unpublished
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: JSON array
+          schema:
+            type: string
+      summary: get all method list below one resource
+      tags:
+      - Config
+  /config/api/resource/method/publish:
+    put:
+      description: 'TODO: unimplemented'
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: batch Release Method Config
+      tags:
+      - Config
+  /config/api/resource/publish:
+    put:
+      description: publish resources from unpublished spaces to published spaces
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: publish all configuration information
+      tags:
+      - Config
+  /register:
+    post:
+      parameters:
+      - description: username
+        in: formData
+        name: username
+        required: true
+        type: string
+      - description: password
+        in: formData
+        name: password
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: '{"code":"","data":""}'
+          schema:
+            type: string
+      summary: user registration
+      tags:
+      - Register
+  /user/checkIsAdmin:
+    post:
+      description: to determine whether the current user is an administrator, the
+        header must include the username.
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: determine whether you are an administrator
+      tags:
+      - Account
+  /user/getInfo:
+    post:
+      description: return the current user's detailed information. The header must
+        include the username.
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: get user information
+      tags:
+      - Account
+  /user/getUserRole:
+    post:
+      description: return the current user's role information. The header must include
+        the username.
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: get user role
+      tags:
+      - Account
+  /user/logout:
+    post:
+      description: log out the current user (invalidate the token)
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: user logout
+      tags:
+      - Account
+  /user/password/edit:
+    post:
+      consumes:
+      - application/x-www-form-urlencoded
+      description: to modify the current user's password, the username must be included
+        in the header.
+      parameters:
+      - description: old password
+        in: formData
+        name: oldPassword
+        required: true
+        type: string
+      - description: new password
+        in: formData
+        name: newPassword
+        required: true
+        type: string
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            type: string
+      summary: modify account password
+      tags:
+      - Account
+swagger: "2.0"
diff --git a/admin/initialize/router.go b/admin/initialize/router.go
index 3ef172ee..5374ce02 100644
--- a/admin/initialize/router.go
+++ b/admin/initialize/router.go
@@ -26,10 +26,10 @@ import (
 )
 
 import (
-	account2 "github.com/apache/dubbo-go-pixiu/admin/controller/account"
+	"github.com/apache/dubbo-go-pixiu/admin/controller/account"
 	"github.com/apache/dubbo-go-pixiu/admin/controller/auth"
-	configInfo2 "github.com/apache/dubbo-go-pixiu/admin/controller/configInfo"
-	_ "github.com/apache/dubbo-go-pixiu/docs"
+	"github.com/apache/dubbo-go-pixiu/admin/controller/configInfo"
+	_ "github.com/apache/dubbo-go-pixiu/admin/doc"
 )
 
 // Routers init router
@@ -39,8 +39,8 @@ func Routers() *gin.Engine {
 	router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
 
 	// Guest router
-	router.POST("/login", account2.Login)
-	router.POST("/register", account2.Register)
+	router.POST("/login", account.Login)
+	router.POST("/register", account.Register)
 
 	// auth router
 	taR := router.Group("/", auth.JWTAuth())
@@ -48,44 +48,44 @@ func Routers() *gin.Engine {
 	// The following router needs to check the token
 	{
 		// user router
-		taR.POST("/user/logout", account2.Logout)
-		taR.POST("/user/password/edit", account2.EditPassword)
-		taR.POST("/user/getInfo", account2.GetUserInfo)
-		taR.POST("/user/getUserRole", account2.GetUserRole)
-		taR.POST("/user/checkIsAdmin", account2.CheckUserIsAdmin)
-
-		taR.GET("/config/api/base", configInfo2.GetBaseInfo)
-		taR.POST("/config/api/base/", configInfo2.SetBaseInfo)
-		taR.PUT("/config/api/base/", configInfo2.SetBaseInfo)
-
-		taR.GET("/config/api/resource/list", configInfo2.GetResourceList)
-		taR.GET("/config/api/resource/detail", configInfo2.GetResourceDetail)
-		taR.POST("/config/api/resource", configInfo2.CreateResourceInfo)
-		taR.PUT("/config/api/resource", configInfo2.ModifyResourceInfo)
-		taR.DELETE("/config/api/resource", configInfo2.DeleteResourceInfo)
-
-		taR.GET("/config/api/cluster/list", configInfo2.GetClusterList)
-		taR.GET("/config/api/cluster/detail", configInfo2.DetailCluster)
-		taR.POST("/config/api/cluster", configInfo2.UpdateCluster)
-		taR.PUT("/config/api/cluster", configInfo2.CreateCluster)
-		taR.DELETE("/config/api/cluster", configInfo2.DeleteCluster)
-
-		taR.GET("/config/api/listener/list", configInfo2.GetListenerList)
-		taR.GET("/config/api/listener/detail", configInfo2.DetailListener)
-		taR.POST("/config/api/listener", configInfo2.UpdateListener)
-		taR.PUT("/config/api/listener", configInfo2.CreateListener)
-		taR.DELETE("/config/api/listener", configInfo2.DeleteListener)
-
-		taR.GET("/config/api/resource/method/list", configInfo2.GetMethodList)
-		taR.GET("/config/api/resource/method/detail", configInfo2.GetMethodDetail)
-		taR.POST("/config/api/resource/method", configInfo2.CreateMethodInfo)
-		taR.PUT("/config/api/resource/method", configInfo2.ModifyMethodInfo)
-		taR.DELETE("/config/api/resource/method", configInfo2.DeleteMethodInfo)
+		taR.POST("/user/logout", account.Logout)
+		taR.POST("/user/password/edit", account.EditPassword)
+		taR.POST("/user/getInfo", account.GetUserInfo)
+		taR.POST("/user/getUserRole", account.GetUserRole)
+		taR.POST("/user/checkIsAdmin", account.CheckUserIsAdmin)
+
+		taR.GET("/config/api/base", configInfo.GetBaseInfo)
+		taR.POST("/config/api/base/", configInfo.SetBaseInfo)
+		taR.PUT("/config/api/base/", configInfo.SetBaseInfo)
+
+		taR.GET("/config/api/resource/list", configInfo.GetResourceList)
+		taR.GET("/config/api/resource/detail", configInfo.GetResourceDetail)
+		taR.POST("/config/api/resource", configInfo.CreateResourceInfo)
+		taR.PUT("/config/api/resource", configInfo.ModifyResourceInfo)
+		taR.DELETE("/config/api/resource", configInfo.DeleteResourceInfo)
+
+		taR.GET("/config/api/cluster/list", configInfo.GetClusterList)
+		taR.GET("/config/api/cluster/detail", configInfo.DetailCluster)
+		taR.POST("/config/api/cluster", configInfo.UpdateCluster)
+		taR.PUT("/config/api/cluster", configInfo.CreateCluster)
+		taR.DELETE("/config/api/cluster", configInfo.DeleteCluster)
+
+		taR.GET("/config/api/listener/list", configInfo.GetListenerList)
+		taR.GET("/config/api/listener/detail", configInfo.DetailListener)
+		taR.POST("/config/api/listener", configInfo.UpdateListener)
+		taR.PUT("/config/api/listener", configInfo.CreateListener)
+		taR.DELETE("/config/api/listener", configInfo.DeleteListener)
+
+		taR.GET("/config/api/resource/method/list", configInfo.GetMethodList)
+		taR.GET("/config/api/resource/method/detail", configInfo.GetMethodDetail)
+		taR.POST("/config/api/resource/method", configInfo.CreateMethodInfo)
+		taR.PUT("/config/api/resource/method", configInfo.ModifyMethodInfo)
+		taR.DELETE("/config/api/resource/method", configInfo.DeleteMethodInfo)
 
 		// Which request method to choose, Temporarily choose put method
-		taR.PUT("/config/api/resource/publish", configInfo2.BatchReleaseResource)
-		taR.PUT("/config/api/resource/method/publish", configInfo2.BatchReleaseMethod)
-		taR.PUT("/config/api/plugin_group/publish", configInfo2.BatchReleasePluginGroup)
+		taR.PUT("/config/api/resource/publish", configInfo.BatchReleaseResource)
+		taR.PUT("/config/api/resource/method/publish", configInfo.BatchReleaseMethod)
+		taR.PUT("/config/api/plugin_group/publish", configInfo.BatchReleasePluginGroup)
 	}
 
 	return router
diff --git a/admin/web/package-lock.json b/admin/web/package-lock.json
index dc4e3117..88bcffcc 100644
--- a/admin/web/package-lock.json
+++ b/admin/web/package-lock.json
@@ -20150,9 +20150,9 @@
       "dev": true
     },
     "node_modules/js-yaml": {
-      "version": "3.14.1",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
-      "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+      "version": "3.14.2",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
+      "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
diff --git a/admin/web/package.json b/admin/web/package.json
index 626b37aa..b8dd22f1 100644
--- a/admin/web/package.json
+++ b/admin/web/package.json
@@ -10,7 +10,7 @@
   },
   "dependencies": {
     "axios": "^1.12.0",
-    "element-ui": "^2.4.5",
+    "element-ui": "^2.15.14",
     "js-cookie": "^2.2.0",
     "jsencrypt": "^3.0.0-rc.1",
     "moment": "^2.24.0",
@@ -19,7 +19,7 @@
     "three": "^0.127.0",
     "three-css2drender": "^1.0.0",
     "three-obj-mtl-loader": "^1.0.3",
-    "vue": "^3.0.0",
+    "vue": "^2.6.14",
     "vue-cropper": "^0.5.7",
     "vue-router": "^3.0.1",
     "vuex": "^3.0.1",
diff --git a/admin/web/src/views/dashboard/manage/Listener.vue b/admin/web/src/views/dashboard/manage/Listener.vue
index c422467e..2b301b8c 100644
--- a/admin/web/src/views/dashboard/manage/Listener.vue
+++ b/admin/web/src/views/dashboard/manage/Listener.vue
@@ -59,7 +59,6 @@
             label="端口"
           >
           </el-table-column>
-          </el-table-column>
           <el-table-column
             class-name="custom-popper--overflow"
             label="路由匹配"
diff --git a/admin/web/vue.config.js b/admin/web/vue.config.js
index 4e39dde7..5a77a22e 100644
--- a/admin/web/vue.config.js
+++ b/admin/web/vue.config.js
@@ -24,7 +24,6 @@ function resolve(dir) {
 }
 
 module.exports = {
-    baseUrl : '/',
     publicPath : '/',
     productionSourceMap: true,
     chainWebpack: config => {
@@ -36,14 +35,14 @@ module.exports = {
         .set('@views',resolve('src/views'))
     },
     devServer:{
-        // 设置代理
+        // set proxy
         // before: require('./src/mock'),
         host: '0.0.0.0',
         port: 8080,
         hot: true,
         https: false,
         open: false,
-        disableHostCheck: true,
+        allowedHosts: 'all',
         proxy: {
             "/config": {
                 target: process.env.VUE_APP_BACKEND_URL,
diff --git a/admin/web/yarn.lock b/admin/web/yarn.lock
index b99e97c7..7534a9c1 100644
--- a/admin/web/yarn.lock
+++ b/admin/web/yarn.lock
@@ -298,6 +298,11 @@
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8"
   integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==
 
+"@babel/helper-validator-identifier@^7.28.5":
+  version "7.28.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4"
+  integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==
+
 "@babel/helper-validator-option@^7.27.1":
   version "7.27.1"
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f"
@@ -327,13 +332,20 @@
   dependencies:
     "@babel/types" "^7.26.0"
 
-"@babel/parser@^7.11.5", "@babel/parser@^7.14.7", "@babel/parser@^7.26.9", "@babel/parser@^7.27.2", "@babel/parser@^7.28.0":
+"@babel/parser@^7.14.7", "@babel/parser@^7.26.9", "@babel/parser@^7.27.2", "@babel/parser@^7.28.0":
   version "7.28.0"
   resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.0.tgz#979829fbab51a29e13901e5a80713dbcb840825e"
   integrity sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==
   dependencies:
     "@babel/types" "^7.28.0"
 
+"@babel/parser@^7.23.5":
+  version "7.28.5"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.5.tgz#0b0225ee90362f030efd644e8034c99468893b08"
+  integrity sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==
+  dependencies:
+    "@babel/types" "^7.28.5"
+
 "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.27.1":
   version "7.27.1"
   resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz#61dd8a8e61f7eb568268d1b5f129da3eee364bf9"
@@ -1101,7 +1113,7 @@
     "@babel/helper-string-parser" "^7.25.9"
     "@babel/helper-validator-identifier" "^7.25.9"
 
-"@babel/types@^7.11.5", "@babel/types@^7.26.9", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.0", "@babel/types@^7.28.2", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
+"@babel/types@^7.26.9", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.0", "@babel/types@^7.28.2", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
   version "7.28.2"
   resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.2.tgz#da9db0856a9a88e0a13b019881d7513588cf712b"
   integrity sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==
@@ -1109,6 +1121,14 @@
     "@babel/helper-string-parser" "^7.27.1"
     "@babel/helper-validator-identifier" "^7.27.1"
 
+"@babel/types@^7.28.5":
+  version "7.28.5"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.5.tgz#10fc405f60897c35f07e85493c932c7b5ca0592b"
+  integrity sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==
+  dependencies:
+    "@babel/helper-string-parser" "^7.27.1"
+    "@babel/helper-validator-identifier" "^7.28.5"
+
 "@bcoe/v8-coverage@^0.2.3":
   version "0.2.3"
   resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
@@ -2282,17 +2302,6 @@
     semver "^7.3.4"
     strip-ansi "^6.0.0"
 
-"@vue/compiler-core@3.0.0":
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.0.0.tgz#25e4f079cf6c39f83bad23700f814c619105a0f2"
-  integrity sha512-XqPC7vdv4rFE77S71oCHmT1K4Ks3WE2Gi6Lr4B5wn0Idmp+NyQQBUHsCNieMDRiEpgtJrw+yOHslrsV0AfAsfQ==
-  dependencies:
-    "@babel/parser" "^7.11.5"
-    "@babel/types" "^7.11.5"
-    "@vue/shared" "3.0.0"
-    estree-walker "^2.0.1"
-    source-map "^0.6.1"
-
 "@vue/compiler-core@3.5.18":
   version "3.5.18"
   resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.18.tgz#521a138cdd970d9bfd27e42168d12f77a04b2074"
@@ -2304,14 +2313,6 @@
     estree-walker "^2.0.2"
     source-map-js "^1.2.1"
 
-"@vue/compiler-dom@3.0.0":
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.0.0.tgz#4cbb48fcf1f852daef2babcf9953b681ac463526"
-  integrity sha512-ukDEGOP8P7lCPyStuM3F2iD5w2QPgUu2xwCW2XNeqPjFKIlR2xMsWjy4raI/cLjN6W16GtlMFaZdK8tLj5PRog==
-  dependencies:
-    "@vue/compiler-core" "3.0.0"
-    "@vue/shared" "3.0.0"
-
 "@vue/compiler-dom@3.5.18":
   version "3.5.18"
   resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz#e13504492c3061ec5bbe6a2e789f15261d4f03a7"
@@ -2320,6 +2321,17 @@
     "@vue/compiler-core" "3.5.18"
     "@vue/shared" "3.5.18"
 
+"@vue/compiler-sfc@2.7.16":
+  version "2.7.16"
+  resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz#ff81711a0fac9c68683d8bb00b63f857de77dc83"
+  integrity sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==
+  dependencies:
+    "@babel/parser" "^7.23.5"
+    postcss "^8.4.14"
+    source-map "^0.6.1"
+  optionalDependencies:
+    prettier "^1.18.2 || ^2.0.0"
+
 "@vue/compiler-sfc@^3.5.13":
   version "3.5.18"
   resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz#ba1e849561337d809937994cdaf900539542eeca"
@@ -2359,35 +2371,6 @@
   optionalDependencies:
     prettier "^1.18.2 || ^2.0.0"
 
-"@vue/reactivity@3.0.0":
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.0.0.tgz#fd15632a608650ce2a969c721787e27e2c80aa6b"
-  integrity sha512-mEGkztGQrAPZRhV7C6PorrpT3+NtuA4dY2QjMzzrW31noKhssWTajRZTwpLF39NBRrF5UU6cp9+1I0FfavMgEQ==
-  dependencies:
-    "@vue/shared" "3.0.0"
-
-"@vue/runtime-core@3.0.0":
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.0.0.tgz#480febf1bfe32798b6abbd71a88f8e8b473a51c2"
-  integrity sha512-3ABMLeA0ZbeVNLbGGLXr+pNUwqXILOqz8WCVGfDWwQb+jW114Cm8djOHVVDoqdvRETQvDf8yHSUmpKHZpQuTkA==
-  dependencies:
-    "@vue/reactivity" "3.0.0"
-    "@vue/shared" "3.0.0"
-
-"@vue/runtime-dom@3.0.0":
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.0.0.tgz#e0d1f7c7e22e1318696014cc3501e06b288c2e11"
-  integrity sha512-f312n5w9gK6mVvkDSj6/Xnot1XjlKXzFBYybmoy6ahAVC8ExbQ+LOWti1IZM/adU8VMNdKaw7Q53Hxz3y5jX8g==
-  dependencies:
-    "@vue/runtime-core" "3.0.0"
-    "@vue/shared" "3.0.0"
-    csstype "^2.6.8"
-
-"@vue/shared@3.0.0":
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.0.0.tgz#ec089236629ecc0f10346b92f101ff4339169f1a"
-  integrity sha512-4XWL/avABGxU2E2ZF1eZq3Tj7fvksCMssDZUHOykBIMmh5d+KcAnQMC5XHMhtnA0NAvktYsA2YpdsVwVmhWzvA==
-
 "@vue/shared@3.5.18", "@vue/shared@^3.5.13":
   version "3.5.18"
   resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.18.tgz#529f24a88d3ed678d50fd5c07455841fbe8ac95e"
@@ -3853,10 +3836,10 @@ cssstyle@^2.3.0:
   dependencies:
     cssom "~0.3.6"
 
-csstype@^2.6.8:
-  version "2.6.21"
-  resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.21.tgz#2efb85b7cc55c80017c66a5ad7cbd931fda3a90e"
-  integrity sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==
+csstype@^3.1.0:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
+  integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
 
 data-urls@^2.0.0:
   version "2.0.0"
@@ -4186,9 +4169,9 @@ electron-to-chromium@^1.5.173:
   resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.194.tgz#05e541c3373ba8d967a65c92bc14d60608908236"
   integrity sha512-SdnWJwSUot04UR51I2oPD8kuP2VI37/CADR1OHsFOUzZIvfWJBO6q11k5P/uKNyTT3cdOsnyjkrZ+DDShqYqJA==
 
-element-ui@^2.4.5:
+element-ui@^2.15.14:
   version "2.15.14"
-  resolved "https://registry.npmmirror.com/element-ui/-/element-ui-2.15.14.tgz#3c34df79467636592812d720d2e6784e7a6ec2ea"
+  resolved "https://registry.yarnpkg.com/element-ui/-/element-ui-2.15.14.tgz#3c34df79467636592812d720d2e6784e7a6ec2ea"
   integrity sha512-2v9fHL0ZGINotOlRIAJD5YuVB8V7WKxrE9Qy7dXhRipa035+kF7WuU/z+tEmLVPBcJ0zt8mOu1DKpWcVzBK8IA==
   dependencies:
     async-validator "~1.8.1"
@@ -4466,7 +4449,7 @@ estraverse@^5.2.0:
   resolved "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
   integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
 
-estree-walker@^2.0.1, estree-walker@^2.0.2:
+estree-walker@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
   integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
@@ -6455,9 +6438,9 @@ js-message@1.0.7:
   integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
 
 js-yaml@^3.13.1:
-  version "3.14.1"
-  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
-  integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
+  version "3.14.2"
+  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.2.tgz#77485ce1dd7f33c061fd1b16ecea23b55fcb04b0"
+  integrity sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==
   dependencies:
     argparse "^1.0.7"
     esprima "^4.0.0"
@@ -7834,7 +7817,7 @@ postcss@^7.0.36:
     picocolors "^0.2.1"
     source-map "^0.6.1"
 
-postcss@^8.2.6, postcss@^8.3.5, postcss@^8.4.33, postcss@^8.5.6:
+postcss@^8.2.6, postcss@^8.3.5, postcss@^8.4.14, postcss@^8.4.33, postcss@^8.5.6:
   version "8.5.6"
   resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c"
   integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==
@@ -9527,14 +9510,13 @@ vue-template-es2015-compiler@^1.9.0:
   resolved "https://registry.npmmirror.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
   integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
 
-vue@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/vue/-/vue-3.0.0.tgz#cfb5df5c34efce319b113a1667d12b74dcfd9c90"
-  integrity sha512-ZMrAARZ32sGIaYKr7Fk2GZEBh/VhulSrGxcGBiAvbN4fhjl3tuJyNFbbbLFqGjndbLoBW66I2ECq8ICdvkKdJw==
+vue@^2.6.14:
+  version "2.7.16"
+  resolved "https://registry.yarnpkg.com/vue/-/vue-2.7.16.tgz#98c60de9def99c0e3da8dae59b304ead43b967c9"
+  integrity sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==
   dependencies:
-    "@vue/compiler-dom" "3.0.0"
-    "@vue/runtime-dom" "3.0.0"
-    "@vue/shared" "3.0.0"
+    "@vue/compiler-sfc" "2.7.16"
+    csstype "^3.1.0"
 
 vuex@^3.0.1:
   version "3.6.2"
diff --git a/configs/admin_docker_config.yaml b/configs/admin_docker_config.yaml
index 1aa38b1c..f42f8bc6 100644
--- a/configs/admin_docker_config.yaml
+++ b/configs/admin_docker_config.yaml
@@ -20,7 +20,7 @@
 server:
   address: 127.0.0.1:8081
 etcd:
-  address: etcd_pixiu:2379
+  address: pixiu_admin_etcd:2379
   path: /pixiu/config/api
 mysql:
   username: root
diff --git a/configs/pixiu_with_admin_config.yaml b/configs/pixiu_with_admin_config.yaml
new file mode 100644
index 00000000..70e4e3c8
--- /dev/null
+++ b/configs/pixiu_with_admin_config.yaml
@@ -0,0 +1,86 @@
+#
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you 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.
+#
+
+static_resources:
+  listeners:
+    - name: "net/http"
+      protocol_type: "HTTP"
+      address:
+        socket_address:
+          address: "0.0.0.0"
+          port: 8888
+      filter_chains:
+        filters:
+          - name: dgp.filter.httpconnectionmanager
+            config:
+              route_config:
+                routes:
+                  - match:
+                      prefix: "/api/v1"
+                    route:
+                      cluster: "test-dubbo"
+                      cluster_not_found_response_code: 505
+              http_filters:
+                - name: dgp.filter.http.apiconfig
+                  config:
+                    api_meta_config:
+                      address: "pixiu_admin_etcd:2379"
+                      api_config_path: "/pixiu/config/api"
+
+              server_name: "test_http_dubbo"
+              generate_request_id: false
+      config:
+        idle_timeout: 5s
+        read_timeout: 5s
+        write_timeout: 5s
+  clusters:
+    - name: "xds-server"
+      type: "Static"
+      endpoints:
+        - socket_address:
+            address: "pixiu_admin_go_backend"
+            port: 18000
+  shutdown_config:
+    timeout: "60s"
+    step_timeout: "10s"
+    reject_policy: "immediacy"
+
+api_meta_config:
+  address: "pixiu_admin_etcd:2379"
+  api_config_path: "/proxy/config/api"
+
+dynamic_resources:
+  lds_config: # config listener
+    cluster_name: ["xds-server"]
+    api_type: "GRPC"
+    refresh_delay: "5s"
+    request_timeout: "10s"
+    grpc_services:
+      - timeout: "5s"
+  cds_config: # config cluster
+    cluster_name: ["xds-server"]
+    api_type: "GRPC"
+    refresh_delay: "5s"
+    request_timeout: "10s"
+    grpc_services:
+      - timeout: "5s"
+
+node:
+  id: "test-id"
+  cluster: "pixiu"
\ No newline at end of file
diff --git a/controllers/Makefile b/controllers/Makefile
new file mode 100644
index 00000000..234fc2fb
--- /dev/null
+++ b/controllers/Makefile
@@ -0,0 +1,267 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+#
+
+GATEAY_API_VERSION ?= v1.3.0
+
+# Image URL to use all building/pushing image targets
+IMG ?= controller:latest
+
+# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
+ifeq (,$(shell go env GOBIN))
+GOBIN=$(shell go env GOPATH)/bin
+else
+GOBIN=$(shell go env GOBIN)
+endif
+
+# CONTAINER_TOOL defines the container tool to be used for building images.
+# Be aware that the target commands are only tested with Docker which is
+# scaffolded by default. However, you might want to replace it to use other
+# tools. (i.e. podman)
+CONTAINER_TOOL ?= docker
+
+# Setting SHELL to bash allows bash commands to be executed by recipes.
+# Options are set to exit when a recipe line exits non-zero or a piped command fails.
+SHELL = /usr/bin/env bash -o pipefail
+.SHELLFLAGS = -ec
+
+.PHONY: all
+all: build
+
+##@ General
+
+# The help target prints out all targets with their descriptions organized
+# beneath their categories. The categories are represented by '##@' and the
+# target descriptions by '##'. The awk command is responsible for reading the
+# entire set of makefiles included in this invocation, looking for lines of the
+# file as xyz: ## something, and then pretty-format the target and help. Then,
+# if there's a line with ##@ something, that gets pretty-printed as a category.
+# More info on the usage of ANSI control characters for terminal formatting:
+# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
+# More info on the awk command:
+# http://linuxcommand.org/lc3_adv_awk.php
+
+.PHONY: help
+help: ## Display this help.
+	@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n  make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf "  \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
+
+##@ Development
+
+.PHONY: manifests
+manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
+	$(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
+
+.PHONY: generate
+generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
+	$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
+
+.PHONY: fmt
+fmt: ## Run go fmt against code.
+	go fmt ./...
+
+.PHONY: vet
+vet: ## Run go vet against code.
+	go vet ./...
+
+.PHONY: test
+test: manifests generate fmt vet setup-envtest ## Run tests.
+	KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out
+
+# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'.
+# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally.
+# CertManager is installed by default; skip with:
+# - CERT_MANAGER_INSTALL_SKIP=true
+KIND_CLUSTER ?= -ingress-controller-test-e2e
+
+.PHONY: setup-test-e2e
+setup-test-e2e: ## Set up a Kind cluster for e2e tests if it does not exist
+	@command -v $(KIND) >/dev/null 2>&1 || { \
+		echo "Kind is not installed. Please install Kind manually."; \
+		exit 1; \
+	}
+	@case "$$($(KIND) get clusters)" in \
+		*"$(KIND_CLUSTER)"*) \
+			echo "Kind cluster '$(KIND_CLUSTER)' already exists. Skipping creation." ;; \
+		*) \
+			echo "Creating Kind cluster '$(KIND_CLUSTER)'..."; \
+			$(KIND) create cluster --name $(KIND_CLUSTER) ;; \
+	esac
+
+.PHONY: test-e2e
+test-e2e: setup-test-e2e manifests generate fmt vet ## Run the e2e tests. Expected an isolated environment using Kind.
+	KIND_CLUSTER=$(KIND_CLUSTER) go test ./test/e2e/ -v -ginkgo.v
+	$(MAKE) cleanup-test-e2e
+
+.PHONY: cleanup-test-e2e
+cleanup-test-e2e: ## Tear down the Kind cluster used for e2e tests
+	@$(KIND) delete cluster --name $(KIND_CLUSTER)
+
+.PHONY: lint
+lint: golangci-lint ## Run golangci-lint linter
+	$(GOLANGCI_LINT) run
+
+.PHONY: lint-fix
+lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes
+	$(GOLANGCI_LINT) run --fix
+
+.PHONY: lint-config
+lint-config: golangci-lint ## Verify golangci-lint linter configuration
+	$(GOLANGCI_LINT) config verify
+
+##@ Build
+
+.PHONY: build
+build: manifests generate fmt vet ## Build manager binary.
+	go build -o bin/manager /cmd/cli/main.go
+
+.PHONY: run
+run: manifests generate fmt vet ## Run a controller from your host.
+	go run ./cmd/cli/main.go
+
+# If you wish to build the manager image targeting other platforms you can use the --platform flag.
+# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it.
+# More info: https://docs.docker.com/develop/develop-images/build_enhancements/
+.PHONY: docker-build
+docker-build: ## Build docker image with the manager.
+	$(CONTAINER_TOOL) build -t ${IMG} .
+
+.PHONY: docker-push
+docker-push: ## Push docker image with the manager.
+	$(CONTAINER_TOOL) push ${IMG}
+
+# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple
+# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to:
+# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/
+# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/
+# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=<myregistry/image:<tag>> then the export will fail)
+# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option.
+PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le
+.PHONY: docker-buildx
+docker-buildx: ## Build and push docker image for the manager for cross-platform support
+	# copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile
+	sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross
+	- $(CONTAINER_TOOL) buildx create --name pixiu-ingress-controller-builder
+	$(CONTAINER_TOOL) buildx use pixiu-ingress-controller-builder
+	- $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross .
+	- $(CONTAINER_TOOL) buildx rm pixiu-ingress-controller-builder
+	rm Dockerfile.cross
+
+.PHONY: build-installer
+build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment.
+	mkdir -p dist
+	cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
+	$(KUSTOMIZE) build config/default > dist/install.yaml
+
+##@ Deployment
+
+ifndef ignore-not-found
+  ignore-not-found = false
+endif
+
+.PHONY: install
+install: manifests kustomize install-gateway-api ## Install CRDs into the K8s cluster specified in ~/.kube/config.
+	$(KUSTOMIZE) build config/crd | $(KUBECTL) apply -f -
+
+.PHONY: uninstall
+uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
+	$(KUSTOMIZE) build config/crd | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f -
+
+.PHONY: install-gateway-api
+install-gateway-api: ## Install Gateway API CRDs into the K8s cluster specified in ~/.kube/config.
+	kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/$(GATEAY_API_VERSION)/standard-install.yaml
+
+.PHONY: uninstall-gateway-api
+uninstall-gateway-api: ## Uninstall Gateway API CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
+	kubectl delete -f https://github.com/kubernetes-sigs/gateway-api/releases/download/$(GATEAY_API_VERSION)/standard-install.yaml
+
+.PHONY: deploy
+deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
+	cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
+	$(KUSTOMIZE) build config/default | $(KUBECTL) apply -f -
+
+.PHONY: undeploy
+undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
+	$(KUSTOMIZE) build config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f -
+
+##@ Dependencies
+
+## Location to install dependencies to
+LOCALBIN ?= $(shell pwd)/bin
+$(LOCALBIN):
+	mkdir -p $(LOCALBIN)
+
+## Tool Binaries
+KUBECTL ?= kubectl
+KIND ?= kind
+KUSTOMIZE ?= $(LOCALBIN)/kustomize
+CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
+ENVTEST ?= $(LOCALBIN)/setup-envtest
+GOLANGCI_LINT = $(LOCALBIN)/golangci-lint
+
+## Tool Versions
+KUSTOMIZE_VERSION ?= v5.6.0
+CONTROLLER_TOOLS_VERSION ?= v0.18.0
+#ENVTEST_VERSION is the version of controller-runtime release branch to fetch the envtest setup script (i.e. release-0.20)
+ENVTEST_VERSION ?= $(shell go list -m -f "{{ .Version }}" sigs.k8s.io/controller-runtime | awk -F'[v.]' '{printf "release-%d.%d", $$2, $$3}')
+#ENVTEST_K8S_VERSION is the version of Kubernetes to use for setting up ENVTEST binaries (i.e. 1.31)
+ENVTEST_K8S_VERSION ?= $(shell go list -m -f "{{ .Version }}" k8s.io/api | awk -F'[v.]' '{printf "1.%d", $$3}')
+GOLANGCI_LINT_VERSION ?= v2.1.6
+
+.PHONY: kustomize
+kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary.
+$(KUSTOMIZE): $(LOCALBIN)
+	$(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION))
+
+.PHONY: controller-gen
+controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary.
+$(CONTROLLER_GEN): $(LOCALBIN)
+	$(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION))
+
+.PHONY: setup-envtest
+setup-envtest: envtest ## Download the binaries required for ENVTEST in the local bin directory.
+	@echo "Setting up envtest binaries for Kubernetes version $(ENVTEST_K8S_VERSION)..."
+	@$(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path || { \
+		echo "Error: Failed to set up envtest binaries for version $(ENVTEST_K8S_VERSION)."; \
+		exit 1; \
+	}
+
+.PHONY: envtest
+envtest: $(ENVTEST) ## Download setup-envtest locally if necessary.
+$(ENVTEST): $(LOCALBIN)
+	$(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION))
+
+.PHONY: golangci-lint
+golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
+$(GOLANGCI_LINT): $(LOCALBIN)
+	$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION))
+
+# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist
+# $1 - target path with name of binary
+# $2 - package url which can be installed
+# $3 - specific version of package
+define go-install-tool
+@[ -f "$(1)-$(3)" ] || { \
+set -e; \
+package=$(2)@$(3) ;\
+echo "Downloading $${package}" ;\
+rm -f $(1) || true ;\
+GOBIN=$(LOCALBIN) go install $${package} ;\
+mv $(1) $(1)-$(3) ;\
+} ;\
+ln -sf $(1)-$(3) $(1)
+endef
diff --git a/controllers/README.md b/controllers/README.md
new file mode 100644
index 00000000..d7ca2143
--- /dev/null
+++ b/controllers/README.md
@@ -0,0 +1,135 @@
+# controllers
+// TODO(user): Add simple overview of use/purpose
+
+## Description
+// TODO(user): An in-depth paragraph about your project and overview of use
+
+## Getting Started
+
+### Prerequisites
+- go version v1.24.0+
+- docker version 17.03+.
+- kubectl version v1.11.3+.
+- Access to a Kubernetes v1.11.3+ cluster.
+
+### To Deploy on the cluster
+**Build and push your image to the location specified by `IMG`:**
+
+```sh
+make docker-build docker-push IMG=<some-registry>/controllers:tag
+```
+
+**NOTE:** This image ought to be published in the personal registry you specified.
+And it is required to have access to pull the image from the working environment.
+Make sure you have the proper permission to the registry if the above commands don’t work.
+
+**Install the CRDs into the cluster:**
+
+```sh
+make install
+```
+
+**Deploy the Manager to the cluster with the image specified by `IMG`:**
+
+```sh
+make deploy IMG=<some-registry>/controllers:tag
+```
+
+> **NOTE**: If you encounter RBAC errors, you may need to grant yourself cluster-admin
+privileges or be logged in as admin.
+
+**Create instances of your solution**
+You can apply the samples (examples) from the config/sample:
+
+```sh
+kubectl apply -k config/samples/
+```
+
+>**NOTE**: Ensure that the samples has default values to test it out.
+
+### To Uninstall
+**Delete the instances (CRs) from the cluster:**
+
+```sh
+kubectl delete -k config/samples/
+```
+
+**Delete the APIs(CRDs) from the cluster:**
+
+```sh
+make uninstall
+```
+
+**UnDeploy the controller from the cluster:**
+
+```sh
+make undeploy
+```
+
+## Project Distribution
+
+Following the options to release and provide this solution to the users.
+
+### By providing a bundle with all YAML files
+
+1. Build the installer for the image built and published in the registry:
+
+```sh
+make build-installer IMG=<some-registry>/controllers:tag
+```
+
+**NOTE:** The makefile target mentioned above generates an 'install.yaml'
+file in the dist directory. This file contains all the resources built
+with Kustomize, which are necessary to install this project without its
+dependencies.
+
+2. Using the installer
+
+Users can just run 'kubectl apply -f <URL for YAML BUNDLE>' to install
+the project, i.e.:
+
+```sh
+kubectl apply -f https://raw.githubusercontent.com/<org>/controllers/<tag or branch>/dist/install.yaml
+```
+
+### By providing a Helm Chart
+
+1. Build the chart using the optional helm plugin
+
+```sh
+kubebuilder edit --plugins=helm/v1-alpha
+```
+
+2. See that a chart was generated under 'dist/chart', and users
+can obtain this solution from there.
+
+**NOTE:** If you change the project, you need to update the Helm Chart
+using the same command above to sync the latest changes. Furthermore,
+if you create webhooks, you need to use the above command with
+the '--force' flag and manually ensure that any custom configuration
+previously added to 'dist/chart/values.yaml' or 'dist/chart/manager/manager.yaml'
+is manually re-applied afterwards.
+
+## Contributing
+// TODO(user): Add detailed information on how you would like others to contribute to this project
+
+**NOTE:** Run `make help` for more information on all potential `make` targets
+
+More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html)
+
+## License
+
+Copyright 2025 dubbo.apache.org.
+
+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.
+
diff --git a/controllers/api/v1alpha1/gatewayproxy_types.go b/controllers/api/v1alpha1/gatewayproxy_types.go
new file mode 100644
index 00000000..a305c312
--- /dev/null
+++ b/controllers/api/v1alpha1/gatewayproxy_types.go
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package v1alpha1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// EDIT THIS FILE!  THIS IS SCAFFOLDING FOR YOU TO OWN!
+// NOTE: json tags are required.  Any new fields you add must have json tags for the fields to be serialized.
+// GatewayProxySpec defines the desired state of GatewayProxy
+type GatewayProxySpec struct{}
+
+// GatewayProxyStatus defines the observed state of GatewayProxy.
+
+type GatewayProxyStatus struct {
+	// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
+	// Important: Run "make" to regenerate code after modifying this file
+}
+
+// +kubebuilder:object:root=true
+// +kubebuilder:subresource:status
+
+// GatewayProxy is the Schema for the gatewayproxies API
+type GatewayProxy struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	// GatewayProxySpec defines configuration of gateway proxy instances,
+	// including networking settings, global plugins, and plugin metadata.
+	Spec GatewayProxySpec `json:"spec,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+
+// GatewayProxyList contains a list of GatewayProxy
+type GatewayProxyList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []GatewayProxy `json:"items"`
+}
+
+func init() {
+	SchemeBuilder.Register(&GatewayProxy{}, &GatewayProxyList{})
+}
diff --git a/controllers/api/v1alpha1/groupversion_info.go b/controllers/api/v1alpha1/groupversion_info.go
new file mode 100644
index 00000000..e1be4b08
--- /dev/null
+++ b/controllers/api/v1alpha1/groupversion_info.go
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+// Package v1alpha1 contains API Schema definitions for the  v1alpha1 API group.
+// +kubebuilder:object:generate=true
+// +groupName=pixiu.apache.org
+package v1alpha1
+
+import (
+	"k8s.io/apimachinery/pkg/runtime/schema"
+
+	"sigs.k8s.io/controller-runtime/pkg/scheme"
+)
+
+var (
+	// GroupVersion is group version used to register these objects.
+	GroupVersion = schema.GroupVersion{Group: "pixiu.apache.org", Version: "v1alpha1"}
+
+	// SchemeBuilder is used to add go types to the GroupVersionKind scheme.
+	SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
+
+	// AddToScheme adds the types in this group-version to the given scheme.
+	AddToScheme = SchemeBuilder.AddToScheme
+)
diff --git a/controllers/api/v1alpha1/zz_generated.deepcopy.go b/controllers/api/v1alpha1/zz_generated.deepcopy.go
new file mode 100644
index 00000000..62726fa7
--- /dev/null
+++ b/controllers/api/v1alpha1/zz_generated.deepcopy.go
@@ -0,0 +1,112 @@
+//go:build !ignore_autogenerated
+// +build !ignore_autogenerated
+
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+// Code generated by controller-gen. DO NOT EDIT.
+package v1alpha1
+
+import (
+	runtime "k8s.io/apimachinery/pkg/runtime"
+)
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *GatewayProxy) DeepCopyInto(out *GatewayProxy) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	out.Spec = in.Spec
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayProxy.
+func (in *GatewayProxy) DeepCopy() *GatewayProxy {
+	if in == nil {
+		return nil
+	}
+	out := new(GatewayProxy)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *GatewayProxy) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *GatewayProxyList) DeepCopyInto(out *GatewayProxyList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]GatewayProxy, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayProxyList.
+func (in *GatewayProxyList) DeepCopy() *GatewayProxyList {
+	if in == nil {
+		return nil
+	}
+	out := new(GatewayProxyList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *GatewayProxyList) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *GatewayProxySpec) DeepCopyInto(out *GatewayProxySpec) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayProxySpec.
+func (in *GatewayProxySpec) DeepCopy() *GatewayProxySpec {
+	if in == nil {
+		return nil
+	}
+	out := new(GatewayProxySpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *GatewayProxyStatus) DeepCopyInto(out *GatewayProxyStatus) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayProxyStatus.
+func (in *GatewayProxyStatus) DeepCopy() *GatewayProxyStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(GatewayProxyStatus)
+	in.DeepCopyInto(out)
+	return out
+}
diff --git a/controllers/cmd/cli/main.go b/controllers/cmd/cli/main.go
new file mode 100644
index 00000000..770a099f
--- /dev/null
+++ b/controllers/cmd/cli/main.go
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package main
+
+import (
+	"controllers/cmd"
+)
+
+func main() {
+	rootCmd := cmd.GetRootCmd()
+	if err := rootCmd.Execute(); err != nil {
+		getExitCode(err)
+	}
+}
+
+func getExitCode(e error) string {
+	return e.Error()
+}
diff --git a/controllers/cmd/root.go b/controllers/cmd/root.go
new file mode 100644
index 00000000..d1c47c46
--- /dev/null
+++ b/controllers/cmd/root.go
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package cmd
+
+import (
+	"os"
+)
+
+import (
+	"controllers/internal/controller/config"
+
+	"controllers/internal/manager"
+
+	"github.com/go-logr/zapr"
+
+	"github.com/spf13/cobra"
+
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
+
+	ctrl "sigs.k8s.io/controller-runtime"
+)
+
+func GetRootCmd() *cobra.Command {
+	root := newPixiuIngressController()
+	return root
+}
+
+func newPixiuIngressController() *cobra.Command {
+	cfg := config.ControllerConfig
+	var configPath string
+	cmd := &cobra.Command{
+		Use:  "pixiu-ingress-controller [command]",
+		Long: "",
+		RunE: func(cmd *cobra.Command, args []string) error {
+			if configPath != "" {
+				c, err := config.GetConfigFromFile(configPath)
+				if err != nil {
+					return err
+				}
+				cfg = c
+				config.SetControllerConfig(c)
+			}
+
+			logLevel, err := zapcore.ParseLevel(cfg.LogLevel)
+			if err != nil {
+				return err
+			}
+
+			// controllers log
+			core := zapcore.NewCore(
+				zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()),
+				zapcore.AddSync(zapcore.Lock(os.Stderr)),
+				logLevel,
+			)
+			logger := zapr.NewLogger(zap.New(core, zap.AddCaller()))
+
+			logger.Info("controller initialized", "configuration", cfg)
+			ctrl.SetLogger(logger.WithName("controller-runtime"))
+
+			ctx := ctrl.LoggerInto(cmd.Context(), logger)
+			return manager.Run(ctx, logger)
+		},
+	}
+
+	cmd.Flags().StringVarP(&configPath, "config-path", "c", "", "configuration file path for pixiu-ingress-controller")
+	cmd.Flags().StringVar(&cfg.MetricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
+		"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
+	cmd.Flags().StringVar(&cfg.ProbeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
+	cmd.Flags().StringVar(&cfg.LogLevel, "log-level", config.DefaultLogLevel, "The log level for pixiu-ingress-controller")
+	cmd.Flags().StringVar(&cfg.ControllerName, "controller-name", config.DefaultControllerName, "The name of the controller")
+
+	return cmd
+}
diff --git a/controllers/config/crd/bases/pixiu.apache.org_gatewayproxies.yaml b/controllers/config/crd/bases/pixiu.apache.org_gatewayproxies.yaml
new file mode 100644
index 00000000..cb4ca3a0
--- /dev/null
+++ b/controllers/config/crd/bases/pixiu.apache.org_gatewayproxies.yaml
@@ -0,0 +1,48 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.18.0
+  name: gatewayproxies.pixiu.apache.org
+spec:
+  group: pixiu.apache.org
+  names:
+    kind: GatewayProxy
+    listKind: GatewayProxyList
+    plural: gatewayproxies
+    singular: gatewayproxy
+  scope: Namespaced
+  versions:
+  - name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: GatewayProxy is the Schema for the gatewayproxies API
+        properties:
+          apiVersion:
+            description: |-
+              APIVersion defines the versioned schema of this representation of an object.
+              Servers should convert recognized schemas to the latest internal value, and
+              may reject unrecognized values.
+              More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+            type: string
+          kind:
+            description: |-
+              Kind is a string value representing the REST resource this object represents.
+              Servers may infer this from the endpoint the client submits requests to.
+              Cannot be updated.
+              In CamelCase.
+              More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: |-
+              GatewayProxySpec defines configuration of gateway proxy instances,
+              including networking settings, global plugins, and plugin metadata.
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
diff --git a/controllers/config/crd/kustomization.yaml b/controllers/config/crd/kustomization.yaml
new file mode 100644
index 00000000..1c339098
--- /dev/null
+++ b/controllers/config/crd/kustomization.yaml
@@ -0,0 +1,16 @@
+# This kustomization.yaml is not intended to be run by itself,
+# since it depends on service name and namespace that are out of this kustomize package.
+# It should be run by config/default
+resources:
+- bases/pixiu.apache.org_gatewayproxies.yaml
+# +kubebuilder:scaffold:crdkustomizeresource
+
+patches:
+# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
+# patches here are for enabling the conversion webhook for each CRD
+# +kubebuilder:scaffold:crdkustomizewebhookpatch
+
+# [WEBHOOK] To enable webhook, uncomment the following section
+# the following config is for teaching kustomize how to do kustomization for CRDs.
+#configurations:
+#- kustomizeconfig.yaml
diff --git a/controllers/config/crd/kustomizeconfig.yaml b/controllers/config/crd/kustomizeconfig.yaml
new file mode 100644
index 00000000..ec5c150a
--- /dev/null
+++ b/controllers/config/crd/kustomizeconfig.yaml
@@ -0,0 +1,19 @@
+# This file is for teaching kustomize how to substitute name and namespace reference in CRD
+nameReference:
+- kind: Service
+  version: v1
+  fieldSpecs:
+  - kind: CustomResourceDefinition
+    version: v1
+    group: apiextensions.k8s.io
+    path: spec/conversion/webhook/clientConfig/service/name
+
+namespace:
+- kind: CustomResourceDefinition
+  version: v1
+  group: apiextensions.k8s.io
+  path: spec/conversion/webhook/clientConfig/service/namespace
+  create: false
+
+varReference:
+- path: metadata/annotations
diff --git a/controllers/config/default/cert_metrics_manager_patch.yaml b/controllers/config/default/cert_metrics_manager_patch.yaml
new file mode 100644
index 00000000..d9750155
--- /dev/null
+++ b/controllers/config/default/cert_metrics_manager_patch.yaml
@@ -0,0 +1,30 @@
+# This patch adds the args, volumes, and ports to allow the manager to use the metrics-server certs.
+
+# Add the volumeMount for the metrics-server certs
+- op: add
+  path: /spec/template/spec/containers/0/volumeMounts/-
+  value:
+    mountPath: /tmp/k8s-metrics-server/metrics-certs
+    name: metrics-certs
+    readOnly: true
+
+# Add the --metrics-cert-path argument for the metrics server
+- op: add
+  path: /spec/template/spec/containers/0/args/-
+  value: --metrics-cert-path=/tmp/k8s-metrics-server/metrics-certs
+
+# Add the metrics-server certs volume configuration
+- op: add
+  path: /spec/template/spec/volumes/-
+  value:
+    name: metrics-certs
+    secret:
+      secretName: metrics-server-cert
+      optional: false
+      items:
+        - key: ca.crt
+          path: ca.crt
+        - key: tls.crt
+          path: tls.crt
+        - key: tls.key
+          path: tls.key
diff --git a/controllers/config/default/kustomization.yaml b/controllers/config/default/kustomization.yaml
new file mode 100644
index 00000000..902309e8
--- /dev/null
+++ b/controllers/config/default/kustomization.yaml
@@ -0,0 +1,234 @@
+# Adds namespace to all resources.
+namespace: controllers-system
+
+# Value of this field is prepended to the
+# names of all resources, e.g. a deployment named
+# "wordpress" becomes "alices-wordpress".
+# Note that it should also match with the prefix (text before '-') of the namespace
+# field above.
+namePrefix: controllers-
+
+# Labels to add to all resources and selectors.
+#labels:
+#- includeSelectors: true
+#  pairs:
+#    someName: someValue
+
+resources:
+- ../crd
+- ../rbac
+- ../manager
+# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
+# crd/kustomization.yaml
+#- ../webhook
+# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.
+#- ../certmanager
+# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
+#- ../prometheus
+# [METRICS] Expose the controller manager metrics service.
+- metrics_service.yaml
+# [NETWORK POLICY] Protect the /metrics endpoint and Webhook Server with NetworkPolicy.
+# Only Pod(s) running a namespace labeled with 'metrics: enabled' will be able to gather the metrics.
+# Only CR(s) which requires webhooks and are applied on namespaces labeled with 'webhooks: enabled' will
+# be able to communicate with the Webhook Server.
+#- ../network-policy
+
+# Uncomment the patches line if you enable Metrics
+patches:
+# [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443.
+# More info: https://book.kubebuilder.io/reference/metrics
+- path: manager_metrics_patch.yaml
+  target:
+    kind: Deployment
+
+# Uncomment the patches line if you enable Metrics and CertManager
+# [METRICS-WITH-CERTS] To enable metrics protected with certManager, uncomment the following line.
+# This patch will protect the metrics with certManager self-signed certs.
+#- path: cert_metrics_manager_patch.yaml
+#  target:
+#    kind: Deployment
+
+# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
+# crd/kustomization.yaml
+#- path: manager_webhook_patch.yaml
+#  target:
+#    kind: Deployment
+
+# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
+# Uncomment the following replacements to add the cert-manager CA injection annotations
+#replacements:
+# - source: # Uncomment the following block to enable certificates for metrics
+#     kind: Service
+#     version: v1
+#     name: controller-manager-metrics-service
+#     fieldPath: metadata.name
+#   targets:
+#     - select:
+#         kind: Certificate
+#         group: cert-manager.io
+#         version: v1
+#         name: metrics-certs
+#       fieldPaths:
+#         - spec.dnsNames.0
+#         - spec.dnsNames.1
+#       options:
+#         delimiter: '.'
+#         index: 0
+#         create: true
+#     - select: # Uncomment the following to set the Service name for TLS config in Prometheus ServiceMonitor
+#         kind: ServiceMonitor
+#         group: monitoring.coreos.com
+#         version: v1
+#         name: controller-manager-metrics-monitor
+#       fieldPaths:
+#         - spec.endpoints.0.tlsConfig.serverName
+#       options:
+#         delimiter: '.'
+#         index: 0
+#         create: true
+
+# - source:
+#     kind: Service
+#     version: v1
+#     name: controller-manager-metrics-service
+#     fieldPath: metadata.namespace
+#   targets:
+#     - select:
+#         kind: Certificate
+#         group: cert-manager.io
+#         version: v1
+#         name: metrics-certs
+#       fieldPaths:
+#         - spec.dnsNames.0
+#         - spec.dnsNames.1
+#       options:
+#         delimiter: '.'
+#         index: 1
+#         create: true
+#     - select: # Uncomment the following to set the Service namespace for TLS in Prometheus ServiceMonitor
+#         kind: ServiceMonitor
+#         group: monitoring.coreos.com
+#         version: v1
+#         name: controller-manager-metrics-monitor
+#       fieldPaths:
+#         - spec.endpoints.0.tlsConfig.serverName
+#       options:
+#         delimiter: '.'
+#         index: 1
+#         create: true
+
+# - source: # Uncomment the following block if you have any webhook
+#     kind: Service
+#     version: v1
+#     name: webhook-service
+#     fieldPath: .metadata.name # Name of the service
+#   targets:
+#     - select:
+#         kind: Certificate
+#         group: cert-manager.io
+#         version: v1
+#         name: serving-cert
+#       fieldPaths:
+#         - .spec.dnsNames.0
+#         - .spec.dnsNames.1
+#       options:
+#         delimiter: '.'
+#         index: 0
+#         create: true
+# - source:
+#     kind: Service
+#     version: v1
+#     name: webhook-service
+#     fieldPath: .metadata.namespace # Namespace of the service
+#   targets:
+#     - select:
+#         kind: Certificate
+#         group: cert-manager.io
+#         version: v1
+#         name: serving-cert
+#       fieldPaths:
+#         - .spec.dnsNames.0
+#         - .spec.dnsNames.1
+#       options:
+#         delimiter: '.'
+#         index: 1
+#         create: true
+
+# - source: # Uncomment the following block if you have a ValidatingWebhook (--programmatic-validation)
+#     kind: Certificate
+#     group: cert-manager.io
+#     version: v1
+#     name: serving-cert # This name should match the one in certificate.yaml
+#     fieldPath: .metadata.namespace # Namespace of the certificate CR
+#   targets:
+#     - select:
+#         kind: ValidatingWebhookConfiguration
+#       fieldPaths:
+#         - .metadata.annotations.[cert-manager.io/inject-ca-from]
+#       options:
+#         delimiter: '/'
+#         index: 0
+#         create: true
+# - source:
+#     kind: Certificate
+#     group: cert-manager.io
+#     version: v1
+#     name: serving-cert
+#     fieldPath: .metadata.name
+#   targets:
+#     - select:
+#         kind: ValidatingWebhookConfiguration
+#       fieldPaths:
+#         - .metadata.annotations.[cert-manager.io/inject-ca-from]
+#       options:
+#         delimiter: '/'
+#         index: 1
+#         create: true
+
+# - source: # Uncomment the following block if you have a DefaultingWebhook (--defaulting )
+#     kind: Certificate
+#     group: cert-manager.io
+#     version: v1
+#     name: serving-cert
+#     fieldPath: .metadata.namespace # Namespace of the certificate CR
+#   targets:
+#     - select:
+#         kind: MutatingWebhookConfiguration
+#       fieldPaths:
+#         - .metadata.annotations.[cert-manager.io/inject-ca-from]
+#       options:
+#         delimiter: '/'
+#         index: 0
+#         create: true
+# - source:
+#     kind: Certificate
+#     group: cert-manager.io
+#     version: v1
+#     name: serving-cert
+#     fieldPath: .metadata.name
+#   targets:
+#     - select:
+#         kind: MutatingWebhookConfiguration
+#       fieldPaths:
+#         - .metadata.annotations.[cert-manager.io/inject-ca-from]
+#       options:
+#         delimiter: '/'
+#         index: 1
+#         create: true
+
+# - source: # Uncomment the following block if you have a ConversionWebhook (--conversion)
+#     kind: Certificate
+#     group: cert-manager.io
+#     version: v1
+#     name: serving-cert
+#     fieldPath: .metadata.namespace # Namespace of the certificate CR
+#   targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD.
+# +kubebuilder:scaffold:crdkustomizecainjectionns
+# - source:
+#     kind: Certificate
+#     group: cert-manager.io
+#     version: v1
+#     name: serving-cert
+#     fieldPath: .metadata.name
+#   targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD.
+# +kubebuilder:scaffold:crdkustomizecainjectionname
diff --git a/controllers/config/default/manager_metrics_patch.yaml b/controllers/config/default/manager_metrics_patch.yaml
new file mode 100644
index 00000000..2aaef653
--- /dev/null
+++ b/controllers/config/default/manager_metrics_patch.yaml
@@ -0,0 +1,4 @@
+# This patch adds the args to allow exposing the metrics endpoint using HTTPS
+- op: add
+  path: /spec/template/spec/containers/0/args/0
+  value: --metrics-bind-address=:8443
diff --git a/controllers/config/default/metrics_service.yaml b/controllers/config/default/metrics_service.yaml
new file mode 100644
index 00000000..cbd92de9
--- /dev/null
+++ b/controllers/config/default/metrics_service.yaml
@@ -0,0 +1,18 @@
+apiVersion: v1
+kind: Service
+metadata:
+  labels:
+    control-plane: controller-manager
+    app.kubernetes.io/name: controllers
+    app.kubernetes.io/managed-by: kustomize
+  name: controller-manager-metrics-service
+  namespace: system
+spec:
+  ports:
+  - name: https
+    port: 8443
+    protocol: TCP
+    targetPort: 8443
+  selector:
+    control-plane: controller-manager
+    app.kubernetes.io/name: controllers
diff --git a/controllers/config/manager/kustomization.yaml b/controllers/config/manager/kustomization.yaml
new file mode 100644
index 00000000..5c5f0b84
--- /dev/null
+++ b/controllers/config/manager/kustomization.yaml
@@ -0,0 +1,2 @@
+resources:
+- manager.yaml
diff --git a/controllers/config/manager/manager.yaml b/controllers/config/manager/manager.yaml
new file mode 100644
index 00000000..e94d0bfb
--- /dev/null
+++ b/controllers/config/manager/manager.yaml
@@ -0,0 +1,99 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+  labels:
+    control-plane: controller-manager
+    app.kubernetes.io/name: controllers
+    app.kubernetes.io/managed-by: kustomize
+  name: system
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: controller-manager
+  namespace: system
+  labels:
+    control-plane: controller-manager
+    app.kubernetes.io/name: controllers
+    app.kubernetes.io/managed-by: kustomize
+spec:
+  selector:
+    matchLabels:
+      control-plane: controller-manager
+      app.kubernetes.io/name: controllers
+  replicas: 1
+  template:
+    metadata:
+      annotations:
+        kubectl.kubernetes.io/default-container: manager
+      labels:
+        control-plane: controller-manager
+        app.kubernetes.io/name: controllers
+    spec:
+      # TODO(user): Uncomment the following code to configure the nodeAffinity expression
+      # according to the platforms which are supported by your solution.
+      # It is considered best practice to support multiple architectures. You can
+      # build your manager image using the makefile target docker-buildx.
+      # affinity:
+      #   nodeAffinity:
+      #     requiredDuringSchedulingIgnoredDuringExecution:
+      #       nodeSelectorTerms:
+      #         - matchExpressions:
+      #           - key: kubernetes.io/arch
+      #             operator: In
+      #             values:
+      #               - amd64
+      #               - arm64
+      #               - ppc64le
+      #               - s390x
+      #           - key: kubernetes.io/os
+      #             operator: In
+      #             values:
+      #               - linux
+      securityContext:
+        # Projects are configured by default to adhere to the "restricted" Pod Security Standards.
+        # This ensures that deployments meet the highest security requirements for Kubernetes.
+        # For more details, see: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted
+        runAsNonRoot: true
+        seccompProfile:
+          type: RuntimeDefault
+      containers:
+      - command:
+        - /manager
+        args:
+          - --leader-elect
+          - --health-probe-bind-address=:8081
+        image: controller:latest
+        name: manager
+        ports: []
+        securityContext:
+          readOnlyRootFilesystem: true
+          allowPrivilegeEscalation: false
+          capabilities:
+            drop:
+            - "ALL"
+        livenessProbe:
+          httpGet:
+            path: /healthz
+            port: 8081
+          initialDelaySeconds: 15
+          periodSeconds: 20
+        readinessProbe:
+          httpGet:
+            path: /readyz
+            port: 8081
+          initialDelaySeconds: 5
+          periodSeconds: 10
+        # TODO(user): Configure the resources accordingly based on the project requirements.
+        # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+        resources:
+          limits:
+            cpu: 500m
+            memory: 128Mi
+          requests:
+            cpu: 10m
+            memory: 64Mi
+        volumeMounts: []
+      volumes: []
+      serviceAccountName: controller-manager
+      terminationGracePeriodSeconds: 10
diff --git a/controllers/config/network-policy/allow-metrics-traffic.yaml b/controllers/config/network-policy/allow-metrics-traffic.yaml
new file mode 100644
index 00000000..d784b901
--- /dev/null
+++ b/controllers/config/network-policy/allow-metrics-traffic.yaml
@@ -0,0 +1,27 @@
+# This NetworkPolicy allows ingress traffic
+# with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those
+# namespaces are able to gather data from the metrics endpoint.
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+  labels:
+    app.kubernetes.io/name: controllers
+    app.kubernetes.io/managed-by: kustomize
+  name: allow-metrics-traffic
+  namespace: system
+spec:
+  podSelector:
+    matchLabels:
+      control-plane: controller-manager
+      app.kubernetes.io/name: controllers
+  policyTypes:
+    - Ingress
+  ingress:
+    # This allows ingress traffic from any namespace with the label metrics: enabled
+    - from:
+      - namespaceSelector:
+          matchLabels:
+            metrics: enabled  # Only from namespaces with this label
+      ports:
+        - port: 8443
+          protocol: TCP
diff --git a/controllers/config/network-policy/kustomization.yaml b/controllers/config/network-policy/kustomization.yaml
new file mode 100644
index 00000000..ec0fb5e5
--- /dev/null
+++ b/controllers/config/network-policy/kustomization.yaml
@@ -0,0 +1,2 @@
+resources:
+- allow-metrics-traffic.yaml
diff --git a/controllers/config/prometheus/kustomization.yaml b/controllers/config/prometheus/kustomization.yaml
new file mode 100644
index 00000000..fdc5481b
--- /dev/null
+++ b/controllers/config/prometheus/kustomization.yaml
@@ -0,0 +1,11 @@
+resources:
+- monitor.yaml
+
+# [PROMETHEUS-WITH-CERTS] The following patch configures the ServiceMonitor in ../prometheus
+# to securely reference certificates created and managed by cert-manager.
+# Additionally, ensure that you uncomment the [METRICS WITH CERTMANAGER] patch under config/default/kustomization.yaml
+# to mount the "metrics-server-cert" secret in the Manager Deployment.
+#patches:
+#  - path: monitor_tls_patch.yaml
+#    target:
+#      kind: ServiceMonitor
diff --git a/controllers/config/prometheus/monitor.yaml b/controllers/config/prometheus/monitor.yaml
new file mode 100644
index 00000000..4779ceb2
--- /dev/null
+++ b/controllers/config/prometheus/monitor.yaml
@@ -0,0 +1,27 @@
+# Prometheus Monitor Service (Metrics)
+apiVersion: monitoring.coreos.com/v1
+kind: ServiceMonitor
+metadata:
+  labels:
+    control-plane: controller-manager
+    app.kubernetes.io/name: controllers
+    app.kubernetes.io/managed-by: kustomize
+  name: controller-manager-metrics-monitor
+  namespace: system
+spec:
+  endpoints:
+    - path: /metrics
+      port: https # Ensure this is the name of the port that exposes HTTPS metrics
+      scheme: https
+      bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
+      tlsConfig:
+        # TODO(user): The option insecureSkipVerify: true is not recommended for production since it disables
+        # certificate verification, exposing the system to potential man-in-the-middle attacks.
+        # For production environments, it is recommended to use cert-manager for automatic TLS certificate management.
+        # To apply this configuration, enable cert-manager and use the patch located at config/prometheus/servicemonitor_tls_patch.yaml,
+        # which securely references the certificate from the 'metrics-server-cert' secret.
+        insecureSkipVerify: true
+  selector:
+    matchLabels:
+      control-plane: controller-manager
+      app.kubernetes.io/name: controllers
diff --git a/controllers/config/prometheus/monitor_tls_patch.yaml b/controllers/config/prometheus/monitor_tls_patch.yaml
new file mode 100644
index 00000000..5bf84ce0
--- /dev/null
+++ b/controllers/config/prometheus/monitor_tls_patch.yaml
@@ -0,0 +1,19 @@
+# Patch for Prometheus ServiceMonitor to enable secure TLS configuration
+# using certificates managed by cert-manager
+- op: replace
+  path: /spec/endpoints/0/tlsConfig
+  value:
+    # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize
+    serverName: SERVICE_NAME.SERVICE_NAMESPACE.svc
+    insecureSkipVerify: false
+    ca:
+      secret:
+        name: metrics-server-cert
+        key: ca.crt
+    cert:
+      secret:
+        name: metrics-server-cert
+        key: tls.crt
+    keySecret:
+      name: metrics-server-cert
+      key: tls.key
diff --git a/controllers/config/rbac/gatewayproxy_admin_role.yaml b/controllers/config/rbac/gatewayproxy_admin_role.yaml
new file mode 100644
index 00000000..79b57562
--- /dev/null
+++ b/controllers/config/rbac/gatewayproxy_admin_role.yaml
@@ -0,0 +1,27 @@
+# This rule is not used by the project controllers itself.
+# It is provided to allow the cluster admin to help manage permissions for users.
+#
+# Grants full permissions ('*') over pixiu.apache.org.
+# This role is intended for users authorized to modify roles and bindings within the cluster,
+# enabling them to delegate specific permissions to other users or groups as needed.
+
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  labels:
+    app.kubernetes.io/name: controllers
+    app.kubernetes.io/managed-by: kustomize
+  name: gatewayproxy-admin-role
+rules:
+- apiGroups:
+  - pixiu.apache.org
+  resources:
+  - gatewayproxies
+  verbs:
+  - '*'
+- apiGroups:
+  - pixiu.apache.org
+  resources:
+  - gatewayproxies/status
+  verbs:
+  - get
diff --git a/controllers/config/rbac/gatewayproxy_editor_role.yaml b/controllers/config/rbac/gatewayproxy_editor_role.yaml
new file mode 100644
index 00000000..5f8afe79
--- /dev/null
+++ b/controllers/config/rbac/gatewayproxy_editor_role.yaml
@@ -0,0 +1,33 @@
+# This rule is not used by the project controllers itself.
+# It is provided to allow the cluster admin to help manage permissions for users.
+#
+# Grants permissions to create, update, and delete resources within the pixiu.apache.org.
+# This role is intended for users who need to manage these resources
+# but should not control RBAC or manage permissions for others.
+
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  labels:
+    app.kubernetes.io/name: controllers
+    app.kubernetes.io/managed-by: kustomize
+  name: gatewayproxy-editor-role
+rules:
+- apiGroups:
+  - pixiu.apache.org
+  resources:
+  - gatewayproxies
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - pixiu.apache.org
+  resources:
+  - gatewayproxies/status
+  verbs:
+  - get
diff --git a/controllers/config/rbac/gatewayproxy_viewer_role.yaml b/controllers/config/rbac/gatewayproxy_viewer_role.yaml
new file mode 100644
index 00000000..0a50c6fa
--- /dev/null
+++ b/controllers/config/rbac/gatewayproxy_viewer_role.yaml
@@ -0,0 +1,29 @@
+# This rule is not used by the project controllers itself.
+# It is provided to allow the cluster admin to help manage permissions for users.
+#
+# Grants read-only access to pixiu.apache.org resources.
+# This role is intended for users who need visibility into these resources
+# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing.
+
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  labels:
+    app.kubernetes.io/name: controllers
+    app.kubernetes.io/managed-by: kustomize
+  name: gatewayproxy-viewer-role
+rules:
+- apiGroups:
+  - pixiu.apache.org
+  resources:
+  - gatewayproxies
+  verbs:
+  - get
+  - list
+  - watch
+- apiGroups:
+  - pixiu.apache.org
+  resources:
+  - gatewayproxies/status
+  verbs:
+  - get
diff --git a/controllers/config/rbac/kustomization.yaml b/controllers/config/rbac/kustomization.yaml
new file mode 100644
index 00000000..10206ddb
--- /dev/null
+++ b/controllers/config/rbac/kustomization.yaml
@@ -0,0 +1,28 @@
+resources:
+# All RBAC will be applied under this service account in
+# the deployment namespace. You may comment out this resource
+# if your manager will use a service account that exists at
+# runtime. Be sure to update RoleBinding and ClusterRoleBinding
+# subjects if changing service account names.
+- service_account.yaml
+- role.yaml
+- role_binding.yaml
+- leader_election_role.yaml
+- leader_election_role_binding.yaml
+# The following RBAC configurations are used to protect
+# the metrics endpoint with authn/authz. These configurations
+# ensure that only authorized users and service accounts
+# can access the metrics endpoint. Comment the following
+# permissions if you want to disable this protection.
+# More info: https://book.kubebuilder.io/reference/metrics.html
+- metrics_auth_role.yaml
+- metrics_auth_role_binding.yaml
+- metrics_reader_role.yaml
+# For each CRD, "Admin", "Editor" and "Viewer" roles are scaffolded by
+# default, aiding admins in cluster management. Those roles are
+# not used by the controllers itself. You can comment the following lines
+# if you do not want those helpers be installed with your Project.
+- gatewayproxy_admin_role.yaml
+- gatewayproxy_editor_role.yaml
+- gatewayproxy_viewer_role.yaml
+
diff --git a/controllers/config/rbac/leader_election_role.yaml b/controllers/config/rbac/leader_election_role.yaml
new file mode 100644
index 00000000..1cc9d1af
--- /dev/null
+++ b/controllers/config/rbac/leader_election_role.yaml
@@ -0,0 +1,40 @@
+# permissions to do leader election.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  labels:
+    app.kubernetes.io/name: controllers
+    app.kubernetes.io/managed-by: kustomize
+  name: leader-election-role
+rules:
+- apiGroups:
+  - ""
+  resources:
+  - configmaps
+  verbs:
+  - get
+  - list
+  - watch
+  - create
+  - update
+  - patch
+  - delete
+- apiGroups:
+  - coordination.k8s.io
+  resources:
+  - leases
+  verbs:
+  - get
+  - list
+  - watch
+  - create
+  - update
+  - patch
+  - delete
+- apiGroups:
+  - ""
+  resources:
+  - events
+  verbs:
+  - create
+  - patch
diff --git a/controllers/config/rbac/leader_election_role_binding.yaml b/controllers/config/rbac/leader_election_role_binding.yaml
new file mode 100644
index 00000000..41b766b6
--- /dev/null
+++ b/controllers/config/rbac/leader_election_role_binding.yaml
@@ -0,0 +1,15 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  labels:
+    app.kubernetes.io/name: controllers
+    app.kubernetes.io/managed-by: kustomize
+  name: leader-election-rolebinding
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: leader-election-role
+subjects:
+- kind: ServiceAccount
+  name: controller-manager
+  namespace: system
diff --git a/controllers/config/rbac/metrics_auth_role.yaml b/controllers/config/rbac/metrics_auth_role.yaml
new file mode 100644
index 00000000..32d2e4ec
--- /dev/null
+++ b/controllers/config/rbac/metrics_auth_role.yaml
@@ -0,0 +1,17 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: metrics-auth-role
+rules:
+- apiGroups:
+  - authentication.k8s.io
+  resources:
+  - tokenreviews
+  verbs:
+  - create
+- apiGroups:
+  - authorization.k8s.io
+  resources:
+  - subjectaccessreviews
+  verbs:
+  - create
diff --git a/controllers/config/rbac/metrics_auth_role_binding.yaml b/controllers/config/rbac/metrics_auth_role_binding.yaml
new file mode 100644
index 00000000..e775d67f
--- /dev/null
+++ b/controllers/config/rbac/metrics_auth_role_binding.yaml
@@ -0,0 +1,12 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: metrics-auth-rolebinding
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: metrics-auth-role
+subjects:
+- kind: ServiceAccount
+  name: controller-manager
+  namespace: system
diff --git a/controllers/config/rbac/metrics_reader_role.yaml b/controllers/config/rbac/metrics_reader_role.yaml
new file mode 100644
index 00000000..51a75db4
--- /dev/null
+++ b/controllers/config/rbac/metrics_reader_role.yaml
@@ -0,0 +1,9 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: metrics-reader
+rules:
+- nonResourceURLs:
+  - "/metrics"
+  verbs:
+  - get
diff --git a/controllers/config/rbac/role.yaml b/controllers/config/rbac/role.yaml
new file mode 100644
index 00000000..8b3db158
--- /dev/null
+++ b/controllers/config/rbac/role.yaml
@@ -0,0 +1,103 @@
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: manager-role
+rules:
+- apiGroups:
+  - ""
+  resources:
+  - events
+  verbs:
+  - create
+  - patch
+- apiGroups:
+  - ""
+  resources:
+  - namespaces
+  - pods
+  - services
+  verbs:
+  - get
+  - list
+  - watch
+- apiGroups:
+  - coordination.k8s.io
+  resources:
+  - leases
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - discovery.k8s.io
+  resources:
+  - endpointslices
+  verbs:
+  - get
+  - list
+  - watch
+- apiGroups:
+  - gateway.networking.k8s.io
+  resources:
+  - gatewayclasses
+  - gateways
+  verbs:
+  - get
+  - list
+  - update
+  - watch
+- apiGroups:
+  - gateway.networking.k8s.io
+  resources:
+  - gatewayclasses/status
+  - gateways/status
+  - httproutes/status
+  - referencegrants/status
+  verbs:
+  - get
+  - update
+- apiGroups:
+  - gateway.networking.k8s.io
+  resources:
+  - httproutes
+  verbs:
+  - get
+  - list
+  - watch
+- apiGroups:
+  - gateway.networking.k8s.io
+  resources:
+  - referencegrants
+  verbs:
+  - list
+  - update
+  - watch
+- apiGroups:
+  - networking.k8s.io
+  resources:
+  - ingressclasses
+  verbs:
+  - get
+  - list
+  - watch
+- apiGroups:
+  - networking.k8s.io
+  resources:
+  - ingresses
+  verbs:
+  - get
+  - list
+  - update
+  - watch
+- apiGroups:
+  - networking.k8s.io
+  resources:
+  - ingresses/status
+  verbs:
+  - get
+  - update
diff --git a/controllers/config/rbac/role_binding.yaml b/controllers/config/rbac/role_binding.yaml
new file mode 100644
index 00000000..506f311e
--- /dev/null
+++ b/controllers/config/rbac/role_binding.yaml
@@ -0,0 +1,15 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  labels:
+    app.kubernetes.io/name: controllers
+    app.kubernetes.io/managed-by: kustomize
+  name: manager-rolebinding
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: manager-role
+subjects:
+- kind: ServiceAccount
+  name: controller-manager
+  namespace: system
diff --git a/controllers/config/rbac/service_account.yaml b/controllers/config/rbac/service_account.yaml
new file mode 100644
index 00000000..c6b102b9
--- /dev/null
+++ b/controllers/config/rbac/service_account.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  labels:
+    app.kubernetes.io/name: controllers
+    app.kubernetes.io/managed-by: kustomize
+  name: controller-manager
+  namespace: system
diff --git a/controllers/config/samples/kustomization.yaml b/controllers/config/samples/kustomization.yaml
new file mode 100644
index 00000000..463a663a
--- /dev/null
+++ b/controllers/config/samples/kustomization.yaml
@@ -0,0 +1,4 @@
+## Append samples of your project ##
+resources:
+- v1alpha1_gatewayproxy.yaml
+# +kubebuilder:scaffold:manifestskustomizesamples
diff --git a/controllers/config/samples/v1alpha1_gatewayproxy.yaml b/controllers/config/samples/v1alpha1_gatewayproxy.yaml
new file mode 100644
index 00000000..b10e1bfc
--- /dev/null
+++ b/controllers/config/samples/v1alpha1_gatewayproxy.yaml
@@ -0,0 +1,9 @@
+apiVersion: pixiu.apache.org/v1alpha1
+kind: GatewayProxy
+metadata:
+  labels:
+    app.kubernetes.io/name: controllers
+    app.kubernetes.io/managed-by: kustomize
+  name: gatewayproxy-sample
+spec:
+  # TODO(user): Add fields here
diff --git a/controllers/go.mod b/controllers/go.mod
new file mode 100644
index 00000000..bd6c1958
--- /dev/null
+++ b/controllers/go.mod
@@ -0,0 +1,101 @@
+module controllers
+
+go 1.24.0
+
+require (
+	github.com/go-logr/logr v1.4.3
+	github.com/go-logr/zapr v1.3.0
+	github.com/google/go-cmp v0.7.0
+	github.com/onsi/ginkgo/v2 v2.22.0
+	github.com/onsi/gomega v1.36.1
+	github.com/samber/lo v1.52.0
+	github.com/spf13/cobra v1.9.1
+	go.uber.org/zap v1.27.0
+	gopkg.in/yaml.v3 v3.0.1
+	k8s.io/api v0.34.1
+	k8s.io/apimachinery v0.34.1
+	k8s.io/client-go v0.34.1
+	k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d
+	sigs.k8s.io/controller-runtime v0.22.1
+	sigs.k8s.io/gateway-api v1.4.0
+)
+
+require (
+	cel.dev/expr v0.24.0 // indirect
+	github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
+	github.com/beorn7/perks v1.0.1 // indirect
+	github.com/blang/semver/v4 v4.0.0 // indirect
+	github.com/cenkalti/backoff/v4 v4.3.0 // indirect
+	github.com/cespare/xxhash/v2 v2.3.0 // indirect
+	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
+	github.com/emicklei/go-restful/v3 v3.13.0 // indirect
+	github.com/evanphx/json-patch/v5 v5.9.11 // indirect
+	github.com/felixge/httpsnoop v1.0.4 // indirect
+	github.com/fsnotify/fsnotify v1.9.0 // indirect
+	github.com/fxamacker/cbor/v2 v2.9.0 // indirect
+	github.com/go-logr/stdr v1.2.2 // indirect
+	github.com/go-openapi/jsonpointer v0.21.2 // indirect
+	github.com/go-openapi/jsonreference v0.21.0 // indirect
+	github.com/go-openapi/swag v0.23.1 // indirect
+	github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
+	github.com/gogo/protobuf v1.3.2 // indirect
+	github.com/google/btree v1.1.3 // indirect
+	github.com/google/cel-go v0.26.0 // indirect
+	github.com/google/gnostic-models v0.7.0 // indirect
+	github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect
+	github.com/google/uuid v1.6.0 // indirect
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
+	github.com/inconshreveable/mousetrap v1.1.0 // indirect
+	github.com/josharian/intern v1.0.0 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/mailru/easyjson v0.9.0 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
+	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+	github.com/prometheus/client_golang v1.23.0 // indirect
+	github.com/prometheus/client_model v0.6.2 // indirect
+	github.com/prometheus/common v0.65.0 // indirect
+	github.com/prometheus/procfs v0.17.0 // indirect
+	github.com/spf13/pflag v1.0.7 // indirect
+	github.com/stoewer/go-strcase v1.3.0 // indirect
+	github.com/x448/float16 v0.8.4 // indirect
+	go.opentelemetry.io/auto/sdk v1.1.0 // indirect
+	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
+	go.opentelemetry.io/otel v1.37.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
+	go.opentelemetry.io/otel/metric v1.37.0 // indirect
+	go.opentelemetry.io/otel/sdk v1.37.0 // indirect
+	go.opentelemetry.io/otel/trace v1.37.0 // indirect
+	go.opentelemetry.io/proto/otlp v1.5.0 // indirect
+	go.uber.org/multierr v1.11.0 // indirect
+	go.yaml.in/yaml/v2 v2.4.2 // indirect
+	go.yaml.in/yaml/v3 v3.0.4 // indirect
+	golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
+	golang.org/x/net v0.43.0 // indirect
+	golang.org/x/oauth2 v0.30.0 // indirect
+	golang.org/x/sync v0.16.0 // indirect
+	golang.org/x/sys v0.35.0 // indirect
+	golang.org/x/term v0.34.0 // indirect
+	golang.org/x/text v0.28.0 // indirect
+	golang.org/x/time v0.12.0 // indirect
+	golang.org/x/tools v0.36.0 // indirect
+	gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 // indirect
+	google.golang.org/grpc v1.75.1 // indirect
+	google.golang.org/protobuf v1.36.8 // indirect
+	gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
+	gopkg.in/inf.v0 v0.9.1 // indirect
+	k8s.io/apiextensions-apiserver v0.34.1 // indirect
+	k8s.io/apiserver v0.34.1 // indirect
+	k8s.io/component-base v0.34.1 // indirect
+	k8s.io/klog/v2 v2.130.1 // indirect
+	k8s.io/kube-openapi v0.0.0-20250814151709-d7b6acb124c3 // indirect
+	sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect
+	sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
+	sigs.k8s.io/randfill v1.0.0 // indirect
+	sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
+	sigs.k8s.io/yaml v1.6.0 // indirect
+)
diff --git a/controllers/go.sum b/controllers/go.sum
new file mode 100644
index 00000000..616751ef
--- /dev/null
+++ b/controllers/go.sum
@@ -0,0 +1,262 @@
+cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
+cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
+github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
+github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
+github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
+github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
+github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
+github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k=
+github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
+github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
+github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
+github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
+github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
+github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
+github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
+github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
+github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
+github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
+github.com/go-openapi/jsonpointer v0.21.2 h1:AqQaNADVwq/VnkCmQg6ogE+M3FOsKTytwges0JdwVuA=
+github.com/go-openapi/jsonpointer v0.21.2/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
+github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
+github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
+github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
+github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
+github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
+github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
+github.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI=
+github.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM=
+github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
+github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
+github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
+github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
+github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
+github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
+github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
+github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
+github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
+github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
+github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
+github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
+github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
+github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
+github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
+github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
+github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
+github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
+github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
+github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
+github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
+github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
+github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8=
+github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
+github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
+github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
+go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
+go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
+go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
+go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
+go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
+go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
+go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
+go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
+go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
+go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
+go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
+go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
+go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
+go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
+go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
+go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
+go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
+golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
+golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
+golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
+golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
+golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
+golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
+golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
+golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
+golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
+golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
+golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
+gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
+gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
+gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
+google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU=
+google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 h1:pmJpJEvT846VzausCQ5d7KreSROcDqmO388w5YbnltA=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og=
+google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
+google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
+google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
+google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
+gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
+gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM=
+k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk=
+k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI=
+k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc=
+k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4=
+k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
+k8s.io/apiserver v0.34.1 h1:U3JBGdgANK3dfFcyknWde1G6X1F4bg7PXuvlqt8lITA=
+k8s.io/apiserver v0.34.1/go.mod h1:eOOc9nrVqlBI1AFCvVzsob0OxtPZUCPiUJL45JOTBG0=
+k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY=
+k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8=
+k8s.io/component-base v0.34.1 h1:v7xFgG+ONhytZNFpIz5/kecwD+sUhVE6HU7qQUiRM4A=
+k8s.io/component-base v0.34.1/go.mod h1:mknCpLlTSKHzAQJJnnHVKqjxR7gBeHRv0rPXA7gdtQ0=
+k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
+k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
+k8s.io/kube-openapi v0.0.0-20250814151709-d7b6acb124c3 h1:liMHz39T5dJO1aOKHLvwaCjDbf07wVh6yaUlTpunnkE=
+k8s.io/kube-openapi v0.0.0-20250814151709-d7b6acb124c3/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
+k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPGPs+Ki1gHw4w1R0=
+k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
+sigs.k8s.io/controller-runtime v0.22.1 h1:Ah1T7I+0A7ize291nJZdS1CabF/lB4E++WizgV24Eqg=
+sigs.k8s.io/controller-runtime v0.22.1/go.mod h1:FwiwRjkRPbiN+zp2QRp7wlTCzbUXxZ/D4OzuQUDwBHY=
+sigs.k8s.io/gateway-api v1.4.0 h1:ZwlNM6zOHq0h3WUX2gfByPs2yAEsy/EenYJB78jpQfQ=
+sigs.k8s.io/gateway-api v1.4.0/go.mod h1:AR5RSqciWP98OPckEjOjh2XJhAe2Na4LHyXD2FUY7Qk=
+sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
+sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
+sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
+sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
+sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
+sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
+sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
+sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
diff --git a/controllers/hack/boilerplate.go.txt b/controllers/hack/boilerplate.go.txt
new file mode 100644
index 00000000..6bf5542c
--- /dev/null
+++ b/controllers/hack/boilerplate.go.txt
@@ -0,0 +1,15 @@
+/*
+Copyright 2025 dubbo.apache.org.
+
+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.
+*/
\ No newline at end of file
diff --git a/controllers/internal/controller/config/config.go b/controllers/internal/controller/config/config.go
new file mode 100644
index 00000000..7f81413f
--- /dev/null
+++ b/controllers/internal/controller/config/config.go
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package config
+
+import (
+	"encoding/json"
+	"fmt"
+	"time"
+)
+
+var (
+	ControllerConfig = NewDefaultConfig()
+)
+
+const (
+	DefaultLeaderElectionID = "pixiu-ingress-gateway-leader"
+	DefaultControllerName   = "pixiu.apache.org/pixiu-ingress-controller"
+	DefaultLogLevel         = "info"
+	DefaultMetricsAddr      = ":8080"
+	DefaultProbeAddr        = ":8081"
+)
+
+type Config struct {
+	LogLevel         string          `json:"log_level" yaml:"log_level"`
+	ControllerName   string          `json:"controller_name" yaml:"controller_name"`
+	LeaderElectionID string          `json:"leader_election_id" yaml:"leader_election_id"`
+	MetricsAddr      string          `json:"metrics_addr" yaml:"metrics_addr"`
+	EnableHTTP2      bool            `json:"enable_http2" yaml:"enable_http2"`
+	ProbeAddr        string          `json:"probe_addr" yaml:"probe_addr"`
+	SecureMetrics    bool            `json:"secure_metrics" yaml:"secure_metrics"`
+	LeaderElection   *LeaderElection `json:"leader_election" yaml:"leader_election"`
+}
+
+type LeaderElection struct {
+	LeaseDuration TimeDuration `json:"lease_duration,omitempty" yaml:"lease_duration,omitempty"`
+	RenewDeadline TimeDuration `json:"renew_deadline,omitempty" yaml:"renew_deadline,omitempty"`
+	RetryPeriod   TimeDuration `json:"retry_period,omitempty" yaml:"retry_period,omitempty"`
+	Disable       bool         `json:"disable,omitempty" yaml:"disable,omitempty"`
+}
+
+func SetControllerConfig(cfg *Config) {
+	ControllerConfig = cfg
+}
+
+func NewDefaultConfig() *Config {
+	return &Config{
+		LogLevel:         DefaultLogLevel,
+		ControllerName:   DefaultControllerName,
+		LeaderElectionID: DefaultLeaderElectionID,
+		ProbeAddr:        DefaultProbeAddr,
+		MetricsAddr:      DefaultMetricsAddr,
+		LeaderElection:   NewLeaderElection(),
+	}
+}
+
+func NewLeaderElection() *LeaderElection {
+	return &LeaderElection{
+		LeaseDuration: TimeDuration{Duration: 30 * time.Second},
+		RenewDeadline: TimeDuration{Duration: 20 * time.Second},
+		RetryPeriod:   TimeDuration{Duration: 2 * time.Second},
+		Disable:       false,
+	}
+}
+
+// TimeDuration is yet another time.Duration but implements json.Unmarshaler
+// and json.Marshaler, yaml.Unmarshaler and yaml.Marshaler interfaces so one
+// can use "1h", "5s" and etc in their json/yaml configurations.
+//
+// Note the format to represent time is same as time.Duration.
+// See the comments about time.ParseDuration for more details.
+type TimeDuration struct {
+	time.Duration `json:",inline"`
+}
+
+func (d *TimeDuration) MarshalJSON() ([]byte, error) {
+	return json.Marshal(d.String())
+}
+
+func (d *TimeDuration) UnmarshalJSON(data []byte) error {
+	var value any
+	if err := json.Unmarshal(data, &value); err != nil {
+		return err
+	}
+	switch v := value.(type) {
+	case float64:
+		d.Duration = time.Duration(v)
+	case string:
+		dur, err := time.ParseDuration(v)
+		if err != nil {
+			return err
+		}
+		d.Duration = dur
+	default:
+		return fmt.Errorf("unknown type: %T", v)
+	}
+	return nil
+}
+
+func (d *TimeDuration) MarshalYAML() (any, error) {
+	return d.String(), nil
+}
+
+func (d *TimeDuration) UnmarshalYAML(unmarshal func(any) error) error {
+	var s string
+	if err := unmarshal(&s); err != nil {
+		return err
+	}
+	dur, err := time.ParseDuration(s)
+	if err != nil {
+		return err
+	}
+	d.Duration = dur
+	return nil
+}
diff --git a/controllers/internal/controller/config/utils.go b/controllers/internal/controller/config/utils.go
new file mode 100644
index 00000000..4d7e4dd6
--- /dev/null
+++ b/controllers/internal/controller/config/utils.go
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package config
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"os"
+	"strings"
+	"text/template"
+)
+
+import (
+	"gopkg.in/yaml.v3"
+)
+
+func GetConfigFromFile(filename string) (*Config, error) {
+	cfg := NewDefaultConfig()
+	data, err := os.ReadFile(filename)
+	if err != nil {
+		return nil, err
+	}
+
+	envVarMap := map[string]string{}
+	for _, e := range os.Environ() {
+		pair := strings.SplitN(e, "=", 2)
+		envVarMap[pair[0]] = pair[1]
+	}
+
+	tpl := template.New("text").Option("missingkey=error")
+	tpl, err = tpl.Parse(string(data))
+	if err != nil {
+		return nil, fmt.Errorf("error parsing configuration template %v", err)
+	}
+	buf := bytes.NewBufferString("")
+	err = tpl.Execute(buf, envVarMap)
+	if err != nil {
+		return nil, fmt.Errorf("error execute configuration template %v", err)
+	}
+
+	if strings.HasSuffix(filename, ".yaml") || strings.HasSuffix(filename, ".yml") {
+		err = yaml.Unmarshal(buf.Bytes(), cfg)
+	} else {
+		err = json.Unmarshal(buf.Bytes(), cfg)
+	}
+
+	if err != nil {
+		return nil, err
+	}
+	return cfg, nil
+}
+
+func GetControllerName() string {
+	return ControllerConfig.ControllerName
+}
diff --git a/controllers/internal/controller/gateway_controller.go b/controllers/internal/controller/gateway_controller.go
new file mode 100644
index 00000000..e0fbe2a9
--- /dev/null
+++ b/controllers/internal/controller/gateway_controller.go
@@ -0,0 +1,344 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package controller
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"reflect"
+)
+
+import (
+	"controllers/api/v1alpha1"
+
+	"controllers/internal/controller/indexer"
+	"controllers/internal/controller/status"
+
+	"controllers/internal/utils"
+
+	"github.com/go-logr/logr"
+
+	corev1 "k8s.io/api/core/v1"
+
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/types"
+
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/builder"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/handler"
+	"sigs.k8s.io/controller-runtime/pkg/predicate"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
+
+	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
+	"sigs.k8s.io/gateway-api/apis/v1beta1"
+)
+
+// GatewayReconciler reconciles a Gateway object.
+type GatewayReconciler struct { //nolint:revive
+	client.Client
+	Scheme  *runtime.Scheme
+	Log     logr.Logger
+	Updater status.Updater
+}
+
+// SetupWithManager sets up the controller with the Manager.
+func (r *GatewayReconciler) SetupWithManager(mgr ctrl.Manager) error {
+	bdr := ctrl.NewControllerManagedBy(mgr).
+		For(
+			&gatewayv1.Gateway{},
+			builder.WithPredicates(
+				predicate.NewPredicateFuncs(r.checkGatewayClass),
+			),
+		).
+		WithEventFilter(
+			predicate.Or(
+				predicate.GenerationChangedPredicate{},
+				predicate.NewPredicateFuncs(TypePredicate[*corev1.Secret]()),
+			),
+		).
+		Watches(
+			&gatewayv1.GatewayClass{},
+			handler.EnqueueRequestsFromMapFunc(r.listGatewayForGatewayClass),
+			builder.WithPredicates(
+				predicate.NewPredicateFuncs(r.matchesGatewayClass),
+			),
+		).
+		Watches(
+			&v1alpha1.GatewayProxy{},
+			handler.EnqueueRequestsFromMapFunc(r.listGatewaysForGatewayProxy),
+		)
+
+	if GetEnableReferenceGrant() {
+		bdr.Watches(&v1beta1.ReferenceGrant{},
+			handler.EnqueueRequestsFromMapFunc(r.listReferenceGrantsForGateway),
+			builder.WithPredicates(referenceGrantPredicates(KindGateway)),
+		)
+	}
+
+	return bdr.Complete(r)
+}
+
+func (r *GatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+	gateway := new(gatewayv1.Gateway)
+	if err := r.Get(ctx, req.NamespacedName, gateway); err != nil {
+		if client.IgnoreNotFound(err) == nil {
+			gateway.Namespace = req.Namespace
+			gateway.Name = req.Name
+
+			gateway.TypeMeta = metav1.TypeMeta{
+				Kind:       KindGateway,
+				APIVersion: gatewayv1.GroupVersion.String(),
+			}
+			return ctrl.Result{}, nil
+		}
+		return ctrl.Result{}, err
+	}
+	conditionProgrammedStatus, conditionProgrammedMsg := true, "Programmed"
+
+	r.Log.Info("gateway has been accepted", "gateway", gateway.GetName())
+	type conditionStatus struct {
+		status bool
+		msg    string
+	}
+	acceptStatus := conditionStatus{
+		status: true,
+		msg:    acceptedMessage("gateway"),
+	}
+
+	r.processListenerConfig(gateway)
+	if err := r.processInfrastructure(gateway); err != nil {
+		acceptStatus = conditionStatus{
+			status: false,
+			msg:    err.Error(),
+		}
+	}
+
+	listenerStatuses, err := getListenerStatus(ctx, r.Client, gateway)
+	if err != nil {
+		r.Log.Error(err, "failed to get listener status", "gateway", req.NamespacedName)
+		return ctrl.Result{}, err
+	}
+
+	accepted := SetGatewayConditionAccepted(gateway, acceptStatus.status, acceptStatus.msg)
+	programmed := SetGatewayConditionProgrammed(gateway, conditionProgrammedStatus, conditionProgrammedMsg)
+	if accepted || programmed || len(listenerStatuses) > 0 {
+		if len(listenerStatuses) > 0 {
+			gateway.Status.Listeners = listenerStatuses
+		}
+
+		r.Updater.Update(status.Update{
+			NamespacedName: utils.NamespacedName(gateway),
+			Resource:       &gatewayv1.Gateway{},
+			Mutator: status.MutatorFunc(func(obj client.Object) client.Object {
+				t, ok := obj.(*gatewayv1.Gateway)
+				if !ok {
+					err := fmt.Errorf("unsupported object type %T", obj)
+					panic(err)
+				}
+				tCopy := t.DeepCopy()
+				tCopy.Status = gateway.Status
+				return tCopy
+			}),
+		})
+
+		return ctrl.Result{}, nil
+	}
+
+	return ctrl.Result{}, nil
+}
+
+func (r *GatewayReconciler) matchesGatewayClass(obj client.Object) bool {
+	gateway, ok := obj.(*gatewayv1.GatewayClass)
+	if !ok {
+		r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to Gateway")
+		return false
+	}
+	return matchesController(string(gateway.Spec.ControllerName))
+}
+
+func (r *GatewayReconciler) listGatewayForGatewayClass(ctx context.Context, gatewayClass client.Object) []reconcile.Request {
+	gatewayList := &gatewayv1.GatewayList{}
+	if err := r.List(context.Background(), gatewayList); err != nil {
+		r.Log.Error(err, "failed to list gateways for gateway class",
+			"gatewayclass", gatewayClass.GetName(),
+		)
+		return nil
+	}
+
+	return reconcileGatewaysMatchGatewayClass(gatewayClass, gatewayList.Items)
+}
+
+func (r *GatewayReconciler) checkGatewayClass(obj client.Object) bool {
+	gateway := obj.(*gatewayv1.Gateway)
+	gatewayClass := &gatewayv1.GatewayClass{}
+	if err := r.Get(context.Background(), client.ObjectKey{Name: string(gateway.Spec.GatewayClassName)}, gatewayClass); err != nil {
+		r.Log.Error(err, "failed to get gateway class", "gateway", gateway.GetName(), "gatewayclass", gateway.Spec.GatewayClassName)
+		return false
+	}
+
+	return matchesController(string(gatewayClass.Spec.ControllerName))
+}
+
+func (r *GatewayReconciler) listGatewaysForGatewayProxy(ctx context.Context, obj client.Object) []reconcile.Request {
+	gatewayProxy, ok := obj.(*v1alpha1.GatewayProxy)
+	if !ok {
+		r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to GatewayProxy")
+		return nil
+	}
+	namespace := gatewayProxy.GetNamespace()
+	name := gatewayProxy.GetName()
+
+	gatewayList := &gatewayv1.GatewayList{}
+	if err := r.List(ctx, gatewayList, client.MatchingFields{
+		indexer.ParametersRef: indexer.GenIndexKey(namespace, name),
+	}); err != nil {
+		r.Log.Error(err, "failed to list gateways for gateway proxy", "gatewayproxy", gatewayProxy.GetName())
+		return nil
+	}
+
+	recs := make([]reconcile.Request, 0, len(gatewayList.Items))
+	for _, gateway := range gatewayList.Items {
+		if !r.checkGatewayClass(&gateway) {
+			continue
+		}
+		recs = append(recs, reconcile.Request{
+			NamespacedName: client.ObjectKey{
+				Namespace: gateway.GetNamespace(),
+				Name:      gateway.GetName(),
+			},
+		})
+	}
+	return recs
+}
+
+func (r *GatewayReconciler) listGatewaysForHTTPRoute(ctx context.Context, obj client.Object) []reconcile.Request {
+	httpRoute, ok := obj.(*gatewayv1.HTTPRoute)
+	if !ok {
+		r.Log.Error(
+			fmt.Errorf("unexpected object type"),
+			"HTTPRoute watch predicate received unexpected object type",
+			"expected", "*gatewayapi.HTTPRoute", "found", reflect.TypeOf(obj),
+		)
+		return nil
+	}
+	recs := []reconcile.Request{}
+	for _, routeParentStatus := range httpRoute.Status.Parents {
+		gatewayNamespace := httpRoute.GetNamespace()
+		parentRef := routeParentStatus.ParentRef
+		if parentRef.Group != nil && *parentRef.Group != gatewayv1.GroupName {
+			continue
+		}
+		if parentRef.Kind != nil && *parentRef.Kind != "Gateway" {
+			continue
+		}
+		if parentRef.Namespace != nil {
+			gatewayNamespace = string(*parentRef.Namespace)
+		}
+
+		gateway := new(gatewayv1.Gateway)
+		if err := r.Get(ctx, client.ObjectKey{
+			Namespace: gatewayNamespace,
+			Name:      string(parentRef.Name),
+		}, gateway); err != nil {
+			continue
+		}
+
+		if !r.checkGatewayClass(gateway) {
+			continue
+		}
+
+		recs = append(recs, reconcile.Request{
+			NamespacedName: client.ObjectKey{
+				Namespace: gatewayNamespace,
+				Name:      string(parentRef.Name),
+			},
+		})
+	}
+	return recs
+}
+
+func (r *GatewayReconciler) listReferenceGrantsForGateway(ctx context.Context, obj client.Object) (requests []reconcile.Request) {
+	grant, ok := obj.(*v1beta1.ReferenceGrant)
+	if !ok {
+		r.Log.Error(
+			errors.New("unexpected object type"),
+			"ReferenceGrant watch predicate received unexpected object type",
+			"expected", FullTypeName(new(v1beta1.ReferenceGrant)), "found", FullTypeName(obj),
+		)
+		return nil
+	}
+
+	var gatewayList gatewayv1.GatewayList
+	if err := r.List(ctx, &gatewayList); err != nil {
+		r.Log.Error(err, "failed to list gateways in watch predicate", "ReferenceGrant", grant.GetName())
+		return nil
+	}
+
+	for _, gateway := range gatewayList.Items {
+		gw := v1beta1.ReferenceGrantFrom{
+			Group:     gatewayv1.GroupName,
+			Kind:      KindGateway,
+			Namespace: v1beta1.Namespace(gateway.GetNamespace()),
+		}
+		for _, from := range grant.Spec.From {
+			if from == gw {
+				requests = append(requests, reconcile.Request{
+					NamespacedName: types.NamespacedName{
+						Namespace: gateway.GetNamespace(),
+						Name:      gateway.GetName(),
+					},
+				})
+			}
+		}
+	}
+	return requests
+}
+
+func (r *GatewayReconciler) processInfrastructure(gateway *gatewayv1.Gateway) error {
+	return ProcessGatewayProxy(r.Client, r.Log, gateway, utils.NamespacedNameKind(gateway))
+}
+
+func (r *GatewayReconciler) processListenerConfig(gateway *gatewayv1.Gateway) {
+	listeners := gateway.Spec.Listeners
+	for _, listener := range listeners {
+		if listener.TLS == nil || listener.TLS.CertificateRefs == nil {
+			continue
+		}
+		secret := corev1.Secret{}
+		for _, ref := range listener.TLS.CertificateRefs {
+			ns := gateway.GetNamespace()
+			if ref.Namespace != nil {
+				ns = string(*ref.Namespace)
+			}
+			if ref.Kind != nil && *ref.Kind == KindSecret {
+				if err := r.Get(context.Background(), client.ObjectKey{
+					Namespace: ns,
+					Name:      string(ref.Name),
+				}, &secret); err != nil {
+					r.Log.Error(err, "failed to get secret", "namespace", ns, "name", ref.Name)
+					SetGatewayListenerConditionProgrammed(gateway, string(listener.Name), false, err.Error())
+					SetGatewayListenerConditionResolvedRefs(gateway, string(listener.Name), false, err.Error())
+					break
+				}
+			}
+		}
+	}
+}
diff --git a/controllers/internal/controller/gatewayclass_controller.go b/controllers/internal/controller/gatewayclass_controller.go
new file mode 100644
index 00000000..4d05768d
--- /dev/null
+++ b/controllers/internal/controller/gatewayclass_controller.go
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package controller
+
+import (
+	"context"
+	"fmt"
+	"time"
+)
+
+import (
+	"controllers/internal/controller/config"
+	"controllers/internal/controller/indexer"
+	"controllers/internal/controller/status"
+
+	"controllers/internal/utils"
+
+	"github.com/go-logr/logr"
+
+	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/types"
+
+	"k8s.io/client-go/tools/record"
+
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
+	"sigs.k8s.io/controller-runtime/pkg/predicate"
+
+	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
+)
+
+const (
+	FinalizerGatewayClassProtection = "pixiu.apache.org/gc-protection"
+)
+
+// GatewayClassReconciler reconciles a GatewayClass object.
+type GatewayClassReconciler struct { //nolint:revive
+	client.Client
+	Scheme *runtime.Scheme
+	record.EventRecorder
+	Log     logr.Logger
+	Updater status.Updater
+}
+
+// SetupWithManager sets up the controller with the Manager.
+func (r *GatewayClassReconciler) SetupWithManager(mgr ctrl.Manager) error {
+	r.EventRecorder = mgr.GetEventRecorderFor("gatewayclass-controller")
+	return ctrl.NewControllerManagedBy(mgr).
+		For(&gatewayv1.GatewayClass{}).
+		WithEventFilter(predicate.NewPredicateFuncs(r.GatewayClassFilter)).
+		WithEventFilter(predicate.GenerationChangedPredicate{}).
+		Complete(r)
+}
+
+func (r *GatewayClassReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+	gc := new(gatewayv1.GatewayClass)
+	if err := r.Get(ctx, req.NamespacedName, gc); err != nil {
+		return ctrl.Result{}, client.IgnoreNotFound(err)
+	}
+
+	if gc.GetDeletionTimestamp().IsZero() {
+		if !controllerutil.ContainsFinalizer(gc, FinalizerGatewayClassProtection) {
+			controllerutil.AddFinalizer(gc, FinalizerGatewayClassProtection)
+			if err := r.Update(ctx, gc); err != nil {
+				return ctrl.Result{}, err
+			}
+		}
+	} else {
+		if controllerutil.ContainsFinalizer(gc, FinalizerGatewayClassProtection) {
+			var gatewayList gatewayv1.GatewayList
+			if err := r.List(ctx, &gatewayList, client.MatchingFields{indexer.GatewayClassIndexRef: gc.Name}); err != nil {
+				r.Log.Error(err, "failed to list gateways")
+				return ctrl.Result{}, err
+			}
+			if len(gatewayList.Items) > 0 {
+				var gateways []types.NamespacedName
+				for _, item := range gatewayList.Items {
+					gateways = append(gateways, types.NamespacedName{
+						Namespace: item.GetNamespace(),
+						Name:      item.GetName(),
+					})
+				}
+				r.Eventf(gc, "Warning", "DeletionBlocked", "the GatewayClass is still used by Gateways: %v", gateways)
+				return ctrl.Result{RequeueAfter: 5 * time.Second}, nil
+			} else {
+				controllerutil.RemoveFinalizer(gc, FinalizerGatewayClassProtection)
+				if err := r.Update(ctx, gc); err != nil {
+					return ctrl.Result{}, err
+				}
+			}
+		}
+
+		return ctrl.Result{}, nil
+	}
+
+	condition := meta.Condition{
+		Type:               string(gatewayv1.GatewayClassConditionStatusAccepted),
+		Status:             meta.ConditionTrue,
+		Reason:             string(gatewayv1.GatewayClassReasonAccepted),
+		ObservedGeneration: gc.Generation,
+		Message:            "the gatewayclass has been accepted by the apisix-ingress-controller",
+		LastTransitionTime: meta.Now(),
+	}
+
+	if !IsConditionPresentAndEqual(gc.Status.Conditions, condition) {
+		r.Log.Info("gatewayclass has been accepted", "gatewayclass", gc.Name)
+		setGatewayClassCondition(gc, condition)
+		r.Updater.Update(status.Update{
+			NamespacedName: utils.NamespacedName(gc),
+			Resource:       gc.DeepCopy(),
+			Mutator: status.MutatorFunc(func(obj client.Object) client.Object {
+				cp := obj.(*gatewayv1.GatewayClass).DeepCopy()
+				cp.Status = gc.Status
+				return cp
+			}),
+		})
+	}
+	return ctrl.Result{}, nil
+}
+
+func (r *GatewayClassReconciler) GatewayClassFilter(obj client.Object) bool {
+	gatewayClass, ok := obj.(*gatewayv1.GatewayClass)
+	if !ok {
+		r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to GatewayClass")
+		return false
+	}
+
+	return matchesController(string(gatewayClass.Spec.ControllerName))
+}
+
+func matchesController(controllerName string) bool {
+	return controllerName == config.ControllerConfig.ControllerName
+}
+
+func setGatewayClassCondition(gwc *gatewayv1.GatewayClass, newCondition meta.Condition) {
+	newConditions := []meta.Condition{}
+	for _, condition := range gwc.Status.Conditions {
+		if condition.Type != newCondition.Type {
+			newConditions = append(newConditions, condition)
+		}
+	}
+	newConditions = append(newConditions, newCondition)
+	gwc.Status.Conditions = newConditions
+}
diff --git a/controllers/internal/controller/gatewayproxy_controller.go b/controllers/internal/controller/gatewayproxy_controller.go
new file mode 100644
index 00000000..1917060a
--- /dev/null
+++ b/controllers/internal/controller/gatewayproxy_controller.go
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package controller
+
+import (
+	"context"
+	"errors"
+)
+
+import (
+	"controllers/api/v1alpha1"
+
+	"controllers/internal/controller/config"
+	"controllers/internal/controller/indexer"
+
+	"controllers/internal/utils"
+
+	"github.com/go-logr/logr"
+
+	corev1 "k8s.io/api/core/v1"
+
+	discoveryv1 "k8s.io/api/discovery/v1"
+
+	networkingv1 "k8s.io/api/networking/v1"
+
+	"k8s.io/apimachinery/pkg/runtime"
+
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/handler"
+	"sigs.k8s.io/controller-runtime/pkg/predicate"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
+
+	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
+)
+
+// GatewayProxyController reconciles a GatewayProxy object.
+type GatewayProxyController struct {
+	client.Client
+	Scheme *runtime.Scheme
+	Log    logr.Logger
+}
+
+func (r *GatewayProxyController) SetupWithManager(mrg ctrl.Manager) error {
+	return ctrl.NewControllerManagedBy(mrg).
+		For(&v1alpha1.GatewayProxy{}).
+		WithEventFilter(
+			predicate.Or(
+				predicate.GenerationChangedPredicate{},
+				predicate.NewPredicateFuncs(TypePredicate[*corev1.Secret]()),
+			),
+		).
+		Watches(&corev1.Service{},
+			handler.EnqueueRequestsFromMapFunc(r.listGatewayProxiesForProviderService),
+		).
+		Watches(&discoveryv1.EndpointSlice{},
+			handler.EnqueueRequestsFromMapFunc(r.listGatewayProxiesForProviderEndpointSlice),
+		).
+		Watches(&corev1.Secret{},
+			handler.EnqueueRequestsFromMapFunc(r.listGatewayProxiesForSecret),
+		).
+		Watches(&gatewayv1.Gateway{},
+			handler.EnqueueRequestsFromMapFunc(r.listGatewayProxiesByGateway),
+		).
+		Watches(&networkingv1.IngressClass{},
+			handler.EnqueueRequestsFromMapFunc(r.listGatewayProxiesForIngressClass),
+		).
+		Complete(r)
+}
+
+func (r *GatewayProxyController) Reconcile(ctx context.Context, req ctrl.Request) (reconcile.Result, error) {
+	var gp v1alpha1.GatewayProxy
+	if err := r.Get(ctx, req.NamespacedName, &gp); err != nil {
+		if client.IgnoreNotFound(err) == nil {
+			gp.Namespace = req.Namespace
+			gp.Name = req.Name
+		}
+		return ctrl.Result{}, err
+	}
+
+	// list Gateways that reference the GatewayProxy
+	var (
+		gatewayList      gatewayv1.GatewayList
+		ingressClassList networkingv1.IngressClassList
+		indexKey         = indexer.GenIndexKey(gp.GetNamespace(), gp.GetName())
+	)
+	if err := r.List(ctx, &gatewayList, client.MatchingFields{indexer.ParametersRef: indexKey}); err != nil {
+		r.Log.Error(err, "failed to list GatewayList")
+		return ctrl.Result{}, nil
+	}
+
+	var gatewayclassList gatewayv1.GatewayClassList
+	if err := r.List(ctx, &gatewayclassList, client.MatchingFields{indexer.ControllerName: config.GetControllerName()}); err != nil {
+		r.Log.Error(err, "failed to list GatewayClassList")
+		return ctrl.Result{}, nil
+	}
+	gcMatched := make(map[string]*gatewayv1.GatewayClass)
+	for _, item := range gatewayclassList.Items {
+		gcMatched[item.Name] = &item
+	}
+
+	// list IngressClasses that reference the GatewayProxy
+	if err := r.List(ctx, &ingressClassList, client.MatchingFields{indexer.IngressClassParametersRef: indexKey}); err != nil {
+		r.Log.Error(err, "failed to list IngressClassList")
+		return reconcile.Result{}, err
+	}
+
+	// append referrers to translate context
+	for _, item := range gatewayList.Items {
+		gcName := string(item.Spec.GatewayClassName)
+		if gcName == "" {
+			continue
+		}
+	}
+	for _, item := range ingressClassList.Items {
+		if item.Spec.Controller != config.GetControllerName() {
+			continue
+		}
+	}
+
+	return reconcile.Result{}, nil
+}
+
+func (r *GatewayProxyController) listGatewayProxiesForProviderService(ctx context.Context, obj client.Object) (requests []reconcile.Request) {
+	service, ok := obj.(*corev1.Service)
+	if !ok {
+		r.Log.Error(errors.New("unexpected object type"), "failed to convert object to Service")
+		return nil
+	}
+
+	return ListRequests(ctx, r.Client, r.Log, &v1alpha1.GatewayProxyList{}, client.MatchingFields{
+		indexer.ServiceIndexRef: indexer.GenIndexKey(service.GetNamespace(), service.GetName()),
+	})
+}
+
+func (r *GatewayProxyController) listGatewayProxiesForProviderEndpointSlice(ctx context.Context, obj client.Object) (requests []reconcile.Request) {
+	endpointSlice, ok := obj.(*discoveryv1.EndpointSlice)
+	if !ok {
+		r.Log.Error(errors.New("unexpected object type"), "failed to convert object to EndpointSlice")
+		return nil
+	}
+
+	return ListRequests(ctx, r.Client, r.Log, &v1alpha1.GatewayProxyList{}, client.MatchingFields{
+		indexer.ServiceIndexRef: indexer.GenIndexKey(endpointSlice.GetNamespace(), endpointSlice.Labels[discoveryv1.LabelServiceName]),
+	})
+}
+
+func (r *GatewayProxyController) listGatewayProxiesForSecret(ctx context.Context, object client.Object) []reconcile.Request {
+	secret, ok := object.(*corev1.Secret)
+	if !ok {
+		r.Log.Error(errors.New("unexpected object type"), "failed to convert object to Secret")
+		return nil
+	}
+	return ListRequests(ctx, r.Client, r.Log, &v1alpha1.GatewayProxyList{}, client.MatchingFields{
+		indexer.SecretIndexRef: indexer.GenIndexKey(secret.GetNamespace(), secret.GetName()),
+	})
+}
+
+func (r *GatewayProxyController) listGatewayProxiesForIngressClass(ctx context.Context, object client.Object) []reconcile.Request {
+	ingressClass, ok := object.(*networkingv1.IngressClass)
+	if !ok {
+		r.Log.Error(errors.New("unexpected object type"), "failed to convert object to IngressClass")
+		return nil
+	}
+	reqs := []reconcile.Request{}
+	gp, _ := GetGatewayProxyByIngressClass(ctx, r.Client, ingressClass)
+	if gp != nil {
+		reqs = append(reqs, reconcile.Request{
+			NamespacedName: utils.NamespacedName(gp),
+		})
+	}
+	return reqs
+}
+
+func (r *GatewayProxyController) listGatewayProxiesByGateway(ctx context.Context, object client.Object) []reconcile.Request {
+	gateway, ok := object.(*gatewayv1.Gateway)
+	if !ok {
+		r.Log.Error(errors.New("unexpected object type"), "failed to convert object to IngressClass")
+		return nil
+	}
+	reqs := []reconcile.Request{}
+	gp, _ := GetGatewayProxyByGateway(ctx, r.Client, gateway)
+	if gp != nil {
+		reqs = append(reqs, reconcile.Request{
+			NamespacedName: utils.NamespacedName(gp),
+		})
+	}
+	return reqs
+}
diff --git a/controllers/internal/controller/indexer/indexer.go b/controllers/internal/controller/indexer/indexer.go
new file mode 100644
index 00000000..145fb85c
--- /dev/null
+++ b/controllers/internal/controller/indexer/indexer.go
@@ -0,0 +1,268 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package indexer
+
+import (
+	"context"
+)
+
+import (
+	"controllers/api/v1alpha1"
+
+	networkingv1 "k8s.io/api/networking/v1"
+
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
+)
+
+const (
+	ServiceIndexRef           = "serviceRefs"
+	ParametersRef             = "parametersRef"
+	IngressClass              = "ingressClass"
+	IngressClassRef           = "ingressClassRef"
+	IngressClassParametersRef = "ingressClassParametersRef"
+	SecretIndexRef            = "secretRefs"
+	GatewayClassIndexRef      = "gatewayClassRef"
+	ControllerName            = "controllerName"
+)
+
+func SetupIndexer(mgr ctrl.Manager) error {
+	for _, setup := range []func(ctrl.Manager) error{
+		setupGatewayClassIndexer,
+		setupGatewayIndexer,
+		setupIngressIndexer,
+		setupIngressClassIndexer,
+		setupGatewayProxyIndexer,
+	} {
+		if err := setup(mgr); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func GenIndexKey(namespace, name string) string {
+	return client.ObjectKey{
+		Namespace: namespace,
+		Name:      name,
+	}.String()
+}
+
+func setupGatewayIndexer(mgr ctrl.Manager) error {
+	if err := mgr.GetFieldIndexer().IndexField(
+		context.Background(),
+		&gatewayv1.Gateway{},
+		ParametersRef,
+		GatewayParametersRefIndexFunc,
+	); err != nil {
+		return err
+	}
+
+	if err := mgr.GetFieldIndexer().IndexField(
+		context.Background(),
+		&gatewayv1.Gateway{},
+		GatewayClassIndexRef,
+		func(obj client.Object) (requests []string) {
+			return []string{string(obj.(*gatewayv1.Gateway).Spec.GatewayClassName)}
+		},
+	); err != nil {
+		return err
+	}
+	return nil
+}
+
+func setupIngressIndexer(mgr ctrl.Manager) error {
+	// create IngressClass index
+	if err := mgr.GetFieldIndexer().IndexField(
+		context.Background(),
+		&networkingv1.Ingress{},
+		IngressClassRef,
+		IngressClassRefIndexFunc,
+	); err != nil {
+		return err
+	}
+
+	// create Service index for quick lookup of Ingresses using specific services
+	if err := mgr.GetFieldIndexer().IndexField(
+		context.Background(),
+		&networkingv1.Ingress{},
+		ServiceIndexRef,
+		IngressServiceIndexFunc,
+	); err != nil {
+		return err
+	}
+
+	// create secret index for TLS
+	if err := mgr.GetFieldIndexer().IndexField(
+		context.Background(),
+		&networkingv1.Ingress{},
+		SecretIndexRef,
+		IngressSecretIndexFunc,
+	); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func setupIngressClassIndexer(mgr ctrl.Manager) error {
+	// create IngressClass index
+	if err := mgr.GetFieldIndexer().IndexField(
+		context.Background(),
+		&networkingv1.IngressClass{},
+		IngressClass,
+		IngressClassIndexFunc,
+	); err != nil {
+		return err
+	}
+
+	// create IngressClassParametersRef index
+	if err := mgr.GetFieldIndexer().IndexField(
+		context.Background(),
+		&networkingv1.IngressClass{},
+		IngressClassParametersRef,
+		IngressClassParametersRefIndexFunc,
+	); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func setupGatewayProxyIndexer(mgr ctrl.Manager) error {
+	if err := mgr.GetFieldIndexer().IndexField(
+		context.Background(),
+		&v1alpha1.GatewayProxy{},
+		ServiceIndexRef,
+		GatewayProxyServiceIndexFunc,
+	); err != nil {
+		return err
+	}
+
+	if err := mgr.GetFieldIndexer().IndexField(
+		context.Background(),
+		&v1alpha1.GatewayProxy{},
+		SecretIndexRef,
+		GatewayProxySecretIndexFunc,
+	); err != nil {
+		return err
+	}
+	return nil
+}
+
+func setupGatewayClassIndexer(mgr ctrl.Manager) error {
+	return mgr.GetFieldIndexer().IndexField(
+		context.Background(),
+		&gatewayv1.GatewayClass{},
+		ControllerName,
+		func(obj client.Object) []string {
+			return []string{string(obj.(*gatewayv1.GatewayClass).Spec.ControllerName)}
+		},
+	)
+}
+
+func GatewayProxyServiceIndexFunc(rawObj client.Object) []string {
+	return nil
+}
+
+func GatewayProxySecretIndexFunc(rawObj client.Object) []string {
+	secretKeys := make([]string, 0)
+	return secretKeys
+}
+
+func GatewayParametersRefIndexFunc(rawObj client.Object) []string {
+	gw := rawObj.(*gatewayv1.Gateway)
+	if gw.Spec.Infrastructure != nil && gw.Spec.Infrastructure.ParametersRef != nil {
+		// now we only care about kind: GatewayProxy
+		if gw.Spec.Infrastructure.ParametersRef.Kind == "GatewayProxy" {
+			name := gw.Spec.Infrastructure.ParametersRef.Name
+			return []string{GenIndexKey(gw.GetNamespace(), name)}
+		}
+	}
+	return nil
+}
+
+func IngressClassIndexFunc(rawObj client.Object) []string {
+	ingressClass := rawObj.(*networkingv1.IngressClass)
+	if ingressClass.Spec.Controller == "" {
+		return nil
+	}
+	controllerName := ingressClass.Spec.Controller
+	return []string{controllerName}
+}
+
+func IngressClassRefIndexFunc(rawObj client.Object) []string {
+	ingress := rawObj.(*networkingv1.Ingress)
+	if ingress.Spec.IngressClassName == nil {
+		return nil
+	}
+	return []string{*ingress.Spec.IngressClassName}
+}
+
+func IngressClassParametersRefIndexFunc(rawObj client.Object) []string {
+	ingressClass := rawObj.(*networkingv1.IngressClass)
+	// check if the IngressClass references this gateway proxy
+	if ingressClass.Spec.Parameters != nil &&
+		ingressClass.Spec.Parameters.APIGroup != nil &&
+		*ingressClass.Spec.Parameters.APIGroup == v1alpha1.GroupVersion.Group &&
+		ingressClass.Spec.Parameters.Kind == "GatewayProxy" {
+		ns := ingressClass.GetNamespace()
+		if ingressClass.Spec.Parameters.Namespace != nil {
+			ns = *ingressClass.Spec.Parameters.Namespace
+		}
+		return []string{GenIndexKey(ns, ingressClass.Spec.Parameters.Name)}
+	}
+	return nil
+}
+
+func IngressServiceIndexFunc(rawObj client.Object) []string {
+	ingress := rawObj.(*networkingv1.Ingress)
+	var services []string
+
+	for _, rule := range ingress.Spec.Rules {
+		if rule.HTTP == nil {
+			continue
+		}
+
+		for _, path := range rule.HTTP.Paths {
+			if path.Backend.Service == nil {
+				continue
+			}
+			key := GenIndexKey(ingress.Namespace, path.Backend.Service.Name)
+			services = append(services, key)
+		}
+	}
+	return services
+}
+
+func IngressSecretIndexFunc(rawObj client.Object) []string {
+	ingress := rawObj.(*networkingv1.Ingress)
+	secrets := make([]string, 0)
+
+	for _, tls := range ingress.Spec.TLS {
+		if tls.SecretName == "" {
+			continue
+		}
+
+		key := GenIndexKey(ingress.Namespace, tls.SecretName)
+		secrets = append(secrets, key)
+	}
+	return secrets
+}
diff --git a/controllers/internal/controller/ingress_controller.go b/controllers/internal/controller/ingress_controller.go
new file mode 100644
index 00000000..aea15c0a
--- /dev/null
+++ b/controllers/internal/controller/ingress_controller.go
@@ -0,0 +1,519 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package controller
+
+import (
+	"context"
+	"fmt"
+	"reflect"
+)
+
+import (
+	"controllers/api/v1alpha1"
+
+	"controllers/internal/controller/indexer"
+	"controllers/internal/controller/status"
+
+	"controllers/internal/utils"
+
+	"github.com/go-logr/logr"
+
+	corev1 "k8s.io/api/core/v1"
+
+	discoveryv1 "k8s.io/api/discovery/v1"
+
+	networkingv1 "k8s.io/api/networking/v1"
+
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/types"
+
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/builder"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/event"
+	"sigs.k8s.io/controller-runtime/pkg/handler"
+	"sigs.k8s.io/controller-runtime/pkg/predicate"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
+	"sigs.k8s.io/controller-runtime/pkg/source"
+)
+
+const defaultIngressClassAnnotation = "ingressclass.kubernetes.io/is-default-class"
+
+// IngressReconciler reconciles a Ingress object.
+type IngressReconciler struct { //nolint:revive
+	client.Client
+	Scheme       *runtime.Scheme
+	Log          logr.Logger
+	genericEvent chan event.GenericEvent
+	Updater      status.Updater
+}
+
+// SetupWithManager sets up the controller with the Manager.
+func (r *IngressReconciler) SetupWithManager(mgr ctrl.Manager) error {
+	r.genericEvent = make(chan event.GenericEvent, 100)
+
+	return ctrl.NewControllerManagedBy(mgr).
+		For(&networkingv1.Ingress{},
+			builder.WithPredicates(
+				predicate.NewPredicateFuncs(r.checkIngressClass),
+			),
+		).
+		WithEventFilter(
+			predicate.Or(
+				predicate.GenerationChangedPredicate{},
+				predicate.AnnotationChangedPredicate{},
+				predicate.NewPredicateFuncs(TypePredicate[*corev1.Secret]()),
+			),
+		).
+		Watches(
+			&networkingv1.IngressClass{},
+			handler.EnqueueRequestsFromMapFunc(r.listIngressForIngressClass),
+			builder.WithPredicates(
+				predicate.NewPredicateFuncs(r.matchesIngressController),
+			),
+		).
+		Watches(
+			&discoveryv1.EndpointSlice{},
+			handler.EnqueueRequestsFromMapFunc(r.listIngressesByService),
+		).
+		Watches(
+			&corev1.Secret{},
+			handler.EnqueueRequestsFromMapFunc(r.listIngressesBySecret),
+		).
+		Watches(&v1alpha1.GatewayProxy{},
+			handler.EnqueueRequestsFromMapFunc(r.listIngressesForGatewayProxy),
+		).
+		WatchesRawSource(
+			source.Channel(
+				r.genericEvent,
+				handler.EnqueueRequestsFromMapFunc(r.listIngressForGenericEvent),
+			),
+		).
+		Complete(r)
+}
+
+// Reconcile handles the reconciliation of Ingress resources
+func (r *IngressReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+	ingress := new(networkingv1.Ingress)
+	if err := r.Get(ctx, req.NamespacedName, ingress); err != nil {
+		if client.IgnoreNotFound(err) == nil {
+			ingress.Namespace = req.Namespace
+			ingress.Name = req.Name
+
+			ingress.TypeMeta = metav1.TypeMeta{
+				Kind:       KindIngress,
+				APIVersion: networkingv1.SchemeGroupVersion.String(),
+			}
+			r.Log.Info("deleted ingress resources", "ingress", ingress.Name)
+			return ctrl.Result{}, nil
+		}
+		return ctrl.Result{}, err
+	}
+
+	r.Log.Info("reconciling ingress", "ingress", ingress.Name)
+
+	ingressClass, err := r.getIngressClass(ctx, ingress)
+	if err != nil {
+		r.Log.Error(err, "failed to get IngressClass")
+		return ctrl.Result{}, err
+	}
+
+	// process IngressClass parameters if they reference GatewayProxy
+	if err := ProcessIngressClassParameters(r.Client, r.Log, ingress, ingressClass); err != nil {
+		r.Log.Error(err, "failed to process IngressClass parameters", "ingressClass", ingressClass.Name)
+		return ctrl.Result{}, err
+	}
+
+	// process backend services
+	if err := r.processBackends(ingress); err != nil {
+		r.Log.Error(err, "failed to process backend services", "ingress", ingress.Name)
+		return ctrl.Result{}, err
+	}
+
+	// update the ingress status
+	if err := r.updateStatus(ctx, ingress, ingressClass); err != nil {
+		r.Log.Error(err, "failed to update ingress status", "ingress", ingress.Name)
+		return ctrl.Result{}, err
+	}
+
+	return ctrl.Result{}, nil
+}
+
+// getIngressClass get the ingress class for the ingress
+func (r *IngressReconciler) getIngressClass(ctx context.Context, obj client.Object) (*networkingv1.IngressClass, error) {
+	ingress := obj.(*networkingv1.Ingress)
+	var ingressClassName string
+	if ingress.Spec.IngressClassName != nil {
+		ingressClassName = *ingress.Spec.IngressClassName
+	}
+	return GetIngressClass(ctx, r.Client, r.Log, ingressClassName)
+}
+
+// checkIngressClass check if the ingress uses the ingress class that we control
+func (r *IngressReconciler) checkIngressClass(obj client.Object) bool {
+	_, err := r.getIngressClass(context.Background(), obj)
+	return err == nil
+}
+
+// matchesIngressController check if the ingress class is controlled by us
+func (r *IngressReconciler) matchesIngressController(obj client.Object) bool {
+	ingressClass, ok := obj.(*networkingv1.IngressClass)
+	if !ok {
+		r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to IngressClass")
+		return false
+	}
+
+	return matchesController(ingressClass.Spec.Controller)
+}
+
+// listIngressForIngressClass list all ingresses that use a specific ingress class
+func (r *IngressReconciler) listIngressForIngressClass(ctx context.Context, obj client.Object) []reconcile.Request {
+	ingressClass, ok := obj.(*networkingv1.IngressClass)
+	if !ok {
+		r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to IngressClass")
+		return nil
+	}
+
+	// check if the ingress class is the default ingress class
+	if IsDefaultIngressClass(ingressClass) {
+		ingressList := &networkingv1.IngressList{}
+		if err := r.List(ctx, ingressList); err != nil {
+			r.Log.Error(err, "failed to list ingresses for ingress class", "ingressclass", ingressClass.GetName())
+			return nil
+		}
+
+		requests := make([]reconcile.Request, 0, len(ingressList.Items))
+		for _, ingress := range ingressList.Items {
+			if ingress.Spec.IngressClassName == nil || *ingress.Spec.IngressClassName == "" ||
+				*ingress.Spec.IngressClassName == ingressClass.GetName() {
+				requests = append(requests, reconcile.Request{
+					NamespacedName: client.ObjectKey{
+						Namespace: ingress.Namespace,
+						Name:      ingress.Name,
+					},
+				})
+			}
+		}
+		return requests
+	} else {
+		ingressList := &networkingv1.IngressList{}
+		if err := r.List(ctx, ingressList, client.MatchingFields{
+			indexer.IngressClassRef: ingressClass.GetName(),
+		}); err != nil {
+			r.Log.Error(err, "failed to list ingresses for ingress class", "ingressclass", ingressClass.GetName())
+			return nil
+		}
+
+		requests := make([]reconcile.Request, 0, len(ingressList.Items))
+		for _, ingress := range ingressList.Items {
+			requests = append(requests, reconcile.Request{
+				NamespacedName: client.ObjectKey{
+					Namespace: ingress.Namespace,
+					Name:      ingress.Name,
+				},
+			})
+		}
+
+		return requests
+	}
+}
+
+func IsDefaultIngressClass(obj client.Object) bool {
+	if ingressClass, ok := obj.(*networkingv1.IngressClass); ok {
+		return ingressClass.Annotations[defaultIngressClassAnnotation] == "true"
+	}
+	return false
+}
+
+// listIngressesByService list all ingresses that use a specific service
+func (r *IngressReconciler) listIngressesByService(ctx context.Context, obj client.Object) []reconcile.Request {
+	endpointSlice, ok := obj.(*discoveryv1.EndpointSlice)
+	if !ok {
+		r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to EndpointSlice")
+		return nil
+	}
+
+	namespace := endpointSlice.GetNamespace()
+	serviceName := endpointSlice.Labels[discoveryv1.LabelServiceName]
+
+	ingressList := &networkingv1.IngressList{}
+	if err := r.List(ctx, ingressList, client.MatchingFields{
+		indexer.ServiceIndexRef: indexer.GenIndexKey(namespace, serviceName),
+	}); err != nil {
+		r.Log.Error(err, "failed to list ingresses by service", "service", serviceName)
+		return nil
+	}
+
+	requests := make([]reconcile.Request, 0, len(ingressList.Items))
+	for _, ingress := range ingressList.Items {
+		if r.checkIngressClass(&ingress) {
+			requests = append(requests, reconcile.Request{
+				NamespacedName: client.ObjectKey{
+					Namespace: ingress.Namespace,
+					Name:      ingress.Name,
+				},
+			})
+		}
+	}
+	return requests
+}
+
+// listIngressesBySecret list all ingresses that use a specific secret
+func (r *IngressReconciler) listIngressesBySecret(ctx context.Context, obj client.Object) []reconcile.Request {
+	secret, ok := obj.(*corev1.Secret)
+	if !ok {
+		r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to Secret")
+		return nil
+	}
+
+	namespace := secret.GetNamespace()
+	name := secret.GetName()
+
+	ingressList := &networkingv1.IngressList{}
+	if err := r.List(ctx, ingressList, client.MatchingFields{
+		indexer.SecretIndexRef: indexer.GenIndexKey(namespace, name),
+	}); err != nil {
+		r.Log.Error(err, "failed to list ingresses by secret", "secret", name)
+		return nil
+	}
+
+	requests := make([]reconcile.Request, 0, len(ingressList.Items))
+	for _, ingress := range ingressList.Items {
+		if r.checkIngressClass(&ingress) {
+			requests = append(requests, reconcile.Request{
+				NamespacedName: client.ObjectKey{
+					Namespace: ingress.Namespace,
+					Name:      ingress.Name,
+				},
+			})
+		}
+	}
+
+	gatewayProxyList := &v1alpha1.GatewayProxyList{}
+	if err := r.List(ctx, gatewayProxyList, client.MatchingFields{
+		indexer.SecretIndexRef: indexer.GenIndexKey(secret.GetNamespace(), secret.GetName()),
+	}); err != nil {
+		r.Log.Error(err, "failed to list gateway proxies by secret", "secret", secret.GetName())
+		return nil
+	}
+
+	for _, gatewayProxy := range gatewayProxyList.Items {
+		var (
+			ingressClassList networkingv1.IngressClassList
+			indexKey         = indexer.GenIndexKey(gatewayProxy.GetNamespace(), gatewayProxy.GetName())
+			matchingFields   = client.MatchingFields{indexer.IngressClassParametersRef: indexKey}
+		)
+		if err := r.List(ctx, &ingressClassList, matchingFields); err != nil {
+			r.Log.Error(err, "failed to list ingress classes for gateway proxy", "gatewayproxy", indexKey)
+			continue
+		}
+		for _, ingressClass := range ingressClassList.Items {
+			requests = append(requests, r.listIngressForIngressClass(ctx, &ingressClass)...)
+		}
+	}
+
+	return distinctRequests(requests)
+}
+
+func (r *IngressReconciler) listIngressForGenericEvent(ctx context.Context, obj client.Object) (requests []reconcile.Request) {
+	switch obj.(type) {
+	default:
+		r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to BackendTrafficPolicy")
+		return nil
+	}
+}
+
+// processTLS process the TLS configuration of the ingress
+func (r *IngressReconciler) processTLS(ingress *networkingv1.Ingress) error {
+	for _, tls := range ingress.Spec.TLS {
+		if tls.SecretName == "" {
+			continue
+		}
+
+		secret := corev1.Secret{}
+		if err := r.Get(context.TODO(), client.ObjectKey{
+			Namespace: ingress.Namespace,
+			Name:      tls.SecretName,
+		}, &secret); err != nil {
+			r.Log.Error(err, "failed to get secret", "namespace", ingress.Namespace, "name", tls.SecretName)
+			return err
+		}
+
+		if secret.Data == nil {
+			continue
+		}
+	}
+
+	return nil
+}
+
+// processBackends process the backend services of the ingress
+func (r *IngressReconciler) processBackends(ingress *networkingv1.Ingress) error {
+	var terr error
+
+	// process all the backend services in the rules
+	for _, rule := range ingress.Spec.Rules {
+		if rule.HTTP == nil {
+			continue
+		}
+		for _, path := range rule.HTTP.Paths {
+			if path.Backend.Service == nil {
+				continue
+			}
+			service := path.Backend.Service
+			if err := r.processBackendService(ingress.Namespace, service); err != nil {
+				terr = err
+			}
+		}
+	}
+	return terr
+}
+
+// processBackendService process a single backend service
+func (r *IngressReconciler) processBackendService(namespace string, backendService *networkingv1.IngressServiceBackend) error {
+	// get the service
+	var service corev1.Service
+	serviceNS := types.NamespacedName{
+		Namespace: namespace,
+		Name:      backendService.Name,
+	}
+	if err := r.Get(context.TODO(), serviceNS, &service); err != nil {
+		if client.IgnoreNotFound(err) == nil {
+			r.Log.Info("service not found", "namespace", namespace, "name", backendService.Name)
+			return nil
+		}
+		return err
+	}
+
+	if service.Spec.Type == corev1.ServiceTypeExternalName {
+		return nil
+	}
+
+	// verify if the port exists
+	var portExists bool
+	if backendService.Port.Number != 0 {
+		for _, servicePort := range service.Spec.Ports {
+			if servicePort.Port == backendService.Port.Number {
+				portExists = true
+				break
+			}
+		}
+	} else if backendService.Port.Name != "" {
+		for _, servicePort := range service.Spec.Ports {
+			if servicePort.Name == backendService.Port.Name {
+				portExists = true
+				break
+			}
+		}
+	}
+
+	if !portExists {
+		err := fmt.Errorf("port(name: %s, number: %d) not found in service %s/%s", backendService.Port.Name, backendService.Port.Number, namespace, backendService.Name)
+		r.Log.Error(err, "service port not found")
+		return err
+	}
+
+	// get the endpoint slices
+	endpointSliceList := &discoveryv1.EndpointSliceList{}
+	if err := r.List(context.TODO(), endpointSliceList,
+		client.InNamespace(namespace),
+		client.MatchingLabels{
+			discoveryv1.LabelServiceName: backendService.Name,
+		},
+	); err != nil {
+		r.Log.Error(err, "failed to list endpoint slices", "namespace", namespace, "name", backendService.Name)
+		return err
+	}
+
+	return nil
+}
+
+// updateStatus update the status of the ingress
+func (r *IngressReconciler) updateStatus(ctx context.Context, ingress *networkingv1.Ingress, ingressClass *networkingv1.IngressClass) error {
+	var loadBalancerStatus networkingv1.IngressLoadBalancerStatus
+
+	// update the load balancer status
+	if len(loadBalancerStatus.Ingress) > 0 && !reflect.DeepEqual(ingress.Status.LoadBalancer, loadBalancerStatus) {
+		ingress.Status.LoadBalancer = loadBalancerStatus
+		r.Updater.Update(status.Update{
+			NamespacedName: utils.NamespacedName(ingress),
+			Resource:       ingress.DeepCopy(),
+			Mutator: status.MutatorFunc(func(obj client.Object) client.Object {
+				cp := obj.(*networkingv1.Ingress).DeepCopy()
+				cp.Status = ingress.Status
+				return cp
+			}),
+		})
+		return nil
+	}
+
+	return nil
+}
+
+// listIngressesForGatewayProxy list all ingresses that use a specific gateway proxy
+func (r *IngressReconciler) listIngressesForGatewayProxy(ctx context.Context, obj client.Object) []reconcile.Request {
+	return listIngressClassRequestsForGatewayProxy(ctx, r.Client, obj, r.Log, r.listIngressForIngressClass)
+}
+
+func listIngressClassRequestsForGatewayProxy(
+	ctx context.Context,
+	c client.Client,
+	obj client.Object,
+	logger logr.Logger,
+	listFunc func(context.Context, client.Object) []reconcile.Request,
+) []reconcile.Request {
+	gatewayProxy, ok := obj.(*v1alpha1.GatewayProxy)
+	if !ok {
+		return nil
+	}
+
+	ingressClassList := &networkingv1.IngressClassList{}
+	if err := c.List(ctx, ingressClassList, client.MatchingFields{
+		indexer.IngressClassParametersRef: indexer.GenIndexKey(gatewayProxy.GetNamespace(), gatewayProxy.GetName()),
+	}); err != nil {
+		logger.Error(err, "failed to list ingress classes for gateway proxy", "gatewayproxy", gatewayProxy.GetName())
+		return nil
+	}
+
+	requestSet := make(map[string]reconcile.Request)
+	for _, ingressClass := range ingressClassList.Items {
+		for _, req := range listFunc(ctx, &ingressClass) {
+			requestSet[req.String()] = req
+		}
+	}
+
+	requests := make([]reconcile.Request, 0, len(requestSet))
+	for _, req := range requestSet {
+		requests = append(requests, req)
+	}
+	return requests
+}
+
+func distinctRequests(requests []reconcile.Request) []reconcile.Request {
+	uniqueRequests := make(map[string]reconcile.Request)
+	for _, request := range requests {
+		uniqueRequests[request.String()] = request
+	}
+
+	distinctRequests := make([]reconcile.Request, 0, len(uniqueRequests))
+	for _, request := range uniqueRequests {
+		distinctRequests = append(distinctRequests, request)
+	}
+	return distinctRequests
+}
diff --git a/controllers/internal/controller/ingressclass_controller.go b/controllers/internal/controller/ingressclass_controller.go
new file mode 100644
index 00000000..0192d38f
--- /dev/null
+++ b/controllers/internal/controller/ingressclass_controller.go
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package controller
+
+import (
+	"context"
+	"fmt"
+)
+
+import (
+	"controllers/api/v1alpha1"
+
+	"controllers/internal/controller/indexer"
+
+	"github.com/go-logr/logr"
+
+	corev1 "k8s.io/api/core/v1"
+
+	networkingv1 "k8s.io/api/networking/v1"
+
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/builder"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/handler"
+	"sigs.k8s.io/controller-runtime/pkg/predicate"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
+)
+
+// IngressClassReconciler reconciles a IngressClass object.
+type IngressClassReconciler struct {
+	client.Client
+	Scheme *runtime.Scheme
+	Log    logr.Logger
+}
+
+// SetupWithManager sets up the controller with the Manager.
+func (r *IngressClassReconciler) SetupWithManager(mgr ctrl.Manager) error {
+	return ctrl.NewControllerManagedBy(mgr).
+		For(
+			&networkingv1.IngressClass{},
+			builder.WithPredicates(
+				predicate.NewPredicateFuncs(r.matchesController),
+			),
+		).
+		WithEventFilter(
+			predicate.Or(
+				predicate.GenerationChangedPredicate{},
+				predicate.AnnotationChangedPredicate{},
+				predicate.NewPredicateFuncs(TypePredicate[*corev1.Secret]()),
+			),
+		).
+		Watches(
+			&v1alpha1.GatewayProxy{},
+			handler.EnqueueRequestsFromMapFunc(r.listIngressClassesForGatewayProxy),
+		).
+		Watches(
+			&corev1.Secret{},
+			handler.EnqueueRequestsFromMapFunc(r.listIngressClassesForSecret),
+		).
+		Complete(r)
+}
+
+func (r *IngressClassReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+	ingressClass := new(networkingv1.IngressClass)
+	if err := r.Get(ctx, req.NamespacedName, ingressClass); err != nil {
+		if client.IgnoreNotFound(err) == nil {
+			ingressClass.Name = req.Name
+
+			ingressClass.TypeMeta = metav1.TypeMeta{
+				Kind:       KindIngressClass,
+				APIVersion: networkingv1.SchemeGroupVersion.String(),
+			}
+
+			return ctrl.Result{}, nil
+		}
+		return ctrl.Result{}, err
+	}
+
+	if err := r.processInfrastructure(ingressClass); err != nil {
+		r.Log.Error(err, "failed to process infrastructure for ingressclass", "ingressclass", ingressClass.GetName())
+		return ctrl.Result{}, client.IgnoreNotFound(err)
+	}
+
+	return ctrl.Result{}, nil
+}
+
+func (r *IngressClassReconciler) matchesController(obj client.Object) bool {
+	ingressClass, ok := obj.(*networkingv1.IngressClass)
+	if !ok {
+		r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to IngressClass")
+		return false
+	}
+	return matchesController(ingressClass.Spec.Controller)
+}
+
+func (r *IngressClassReconciler) listIngressClassesForGatewayProxy(ctx context.Context, obj client.Object) []reconcile.Request {
+	gatewayProxy, ok := obj.(*v1alpha1.GatewayProxy)
+	if !ok {
+		r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to GatewayProxy")
+		return nil
+	}
+	namespace := gatewayProxy.GetNamespace()
+	name := gatewayProxy.GetName()
+
+	ingressClassList := &networkingv1.IngressClassList{}
+	if err := r.List(ctx, ingressClassList, client.MatchingFields{
+		indexer.IngressClassParametersRef: indexer.GenIndexKey(namespace, name),
+	}); err != nil {
+		r.Log.Error(err, "failed to list ingress classes for gateway proxy", "gatewayproxy", gatewayProxy.GetName())
+		return nil
+	}
+
+	recs := make([]reconcile.Request, 0, len(ingressClassList.Items))
+	for _, ingressClass := range ingressClassList.Items {
+		if !r.matchesController(&ingressClass) {
+			continue
+		}
+		recs = append(recs, reconcile.Request{
+			NamespacedName: client.ObjectKey{
+				Name: ingressClass.GetName(),
+			},
+		})
+	}
+	return recs
+}
+
+func (r *IngressClassReconciler) listIngressClassesForSecret(ctx context.Context, obj client.Object) []reconcile.Request {
+	secret, ok := obj.(*corev1.Secret)
+	if !ok {
+		r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to Secret")
+		return nil
+	}
+
+	// 1. list gateway proxies by secret
+	gatewayProxyList := &v1alpha1.GatewayProxyList{}
+	if err := r.List(ctx, gatewayProxyList, client.MatchingFields{
+		indexer.SecretIndexRef: indexer.GenIndexKey(secret.GetNamespace(), secret.GetName()),
+	}); err != nil {
+		r.Log.Error(err, "failed to list gateway proxies by secret", "secret", secret.GetName())
+		return nil
+	}
+
+	// 2. list ingress classes by gateway proxies
+	requests := make([]reconcile.Request, 0)
+	for _, gatewayProxy := range gatewayProxyList.Items {
+		requests = append(requests, r.listIngressClassesForGatewayProxy(ctx, &gatewayProxy)...)
+	}
+
+	return distinctRequests(requests)
+}
+
+func (r *IngressClassReconciler) processInfrastructure(ingressClass *networkingv1.IngressClass) error {
+	if ingressClass.Spec.Parameters == nil {
+		return nil
+	}
+
+	if ingressClass.Spec.Parameters.APIGroup == nil ||
+		*ingressClass.Spec.Parameters.APIGroup != v1alpha1.GroupVersion.Group ||
+		ingressClass.Spec.Parameters.Kind != KindGatewayProxy {
+		return nil
+	}
+
+	namespace := ingressClass.Namespace
+	if ingressClass.Spec.Parameters.Namespace != nil {
+		namespace = *ingressClass.Spec.Parameters.Namespace
+	}
+
+	gatewayProxy := new(v1alpha1.GatewayProxy)
+	if err := r.Get(context.Background(), client.ObjectKey{
+		Namespace: namespace,
+		Name:      ingressClass.Spec.Parameters.Name,
+	}, gatewayProxy); err != nil {
+		return fmt.Errorf("failed to get gateway proxy: %w", err)
+	}
+
+	return nil
+}
diff --git a/controllers/internal/controller/status/status.go b/controllers/internal/controller/status/status.go
new file mode 100644
index 00000000..60bba911
--- /dev/null
+++ b/controllers/internal/controller/status/status.go
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package status
+
+import (
+	"context"
+	"sync"
+)
+
+import (
+	"controllers/internal/types"
+
+	"github.com/go-logr/logr"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
+
+	k8serrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	k8stypes "k8s.io/apimachinery/pkg/types"
+
+	"k8s.io/client-go/util/retry"
+
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
+)
+
+const UpdateChannelBufferSize = 1000
+
+var cmpIgnoreLastTransitionTime = cmp.Options{
+	cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime"),
+	cmpopts.IgnoreMapEntries(func(k string, _ any) bool {
+		return k == "lastTransitionTime"
+	}),
+}
+
+type Update struct {
+	NamespacedName k8stypes.NamespacedName
+	Resource       client.Object
+	Mutator        Mutator
+}
+
+type Updater interface {
+	Update(u Update)
+}
+
+type Mutator interface {
+	Mutate(obj client.Object) client.Object
+}
+
+type MutatorFunc func(client.Object) client.Object
+
+func (m MutatorFunc) Mutate(obj client.Object) client.Object {
+	if m == nil {
+		return nil
+	}
+
+	return m(obj)
+}
+
+type UpdateHandler struct {
+	log           logr.Logger
+	client        client.Client
+	updateChannel chan Update
+	wg            *sync.WaitGroup
+}
+
+func (u *UpdateHandler) Writer() Updater {
+	return &UpdateWriter{
+		updateChannel: u.updateChannel,
+		wg:            u.wg,
+	}
+}
+
+type UpdateWriter struct {
+	updateChannel chan<- Update
+	wg            *sync.WaitGroup
+}
+
+func (u *UpdateWriter) Update(update Update) {
+	u.wg.Wait()
+	u.updateChannel <- update
+}
+
+func NewStatusUpdateHandler(log logr.Logger, client client.Client) *UpdateHandler {
+	u := &UpdateHandler{
+		log:           log,
+		client:        client,
+		updateChannel: make(chan Update, UpdateChannelBufferSize),
+		wg:            new(sync.WaitGroup),
+	}
+
+	u.wg.Add(1)
+	return u
+}
+
+func (u *UpdateHandler) Start(ctx context.Context) error {
+	u.log.Info("started status update handler")
+	defer u.log.Info("stopped status update handler")
+
+	u.wg.Done()
+
+	for {
+		select {
+		case <-ctx.Done():
+			return nil
+		case update := <-u.updateChannel:
+			u.log.V(1).Info("received a status update", "namespace", update.NamespacedName.Namespace,
+				"name", update.NamespacedName.Name,
+				"kind", types.KindOf(update.Resource),
+			)
+
+			u.apply(ctx, update)
+		}
+	}
+}
+
+func (u *UpdateHandler) NeedsLeaderElection() bool {
+	return true
+}
+
+func (u *UpdateHandler) apply(ctx context.Context, update Update) {
+	if err := retry.OnError(retry.DefaultBackoff, func(err error) bool {
+		return k8serrors.IsConflict(err)
+	}, func() error {
+		return u.updateStatus(ctx, update)
+	}); err != nil {
+		u.log.Error(err, "unable to update status", "name", update.NamespacedName.Name,
+			"namespace", update.NamespacedName.Namespace)
+	}
+}
+
+func (u *UpdateHandler) updateStatus(ctx context.Context, update Update) error {
+	var obj = update.Resource
+	if err := u.client.Get(ctx, update.NamespacedName, obj); err != nil {
+		if k8serrors.IsNotFound(err) {
+			return nil
+		}
+		return err
+	}
+
+	newObj := update.Mutator.Mutate(obj)
+	if newObj == nil {
+		return nil
+	}
+
+	if statusEqual(obj, newObj, cmpIgnoreLastTransitionTime) {
+		u.log.V(1).Info("status is equal, skipping update", "name", update.NamespacedName.Name,
+			"namespace", update.NamespacedName.Namespace,
+			"kind", types.KindOf(obj))
+		return nil
+	}
+
+	newObj.SetUID(obj.GetUID())
+
+	u.log.Info("updating status", "name", update.NamespacedName.Name,
+		"namespace", update.NamespacedName.Namespace,
+		"kind", types.KindOf(newObj),
+	)
+
+	return u.client.Status().Update(ctx, newObj)
+}
+
+func statusEqual(a, b any, opts ...cmp.Option) bool {
+	var statusA, statusB any
+
+	switch a := a.(type) {
+	case *gatewayv1.GatewayClass:
+		b, ok := b.(*gatewayv1.GatewayClass)
+		if !ok {
+			return false
+		}
+		statusA, statusB = a.Status, b.Status
+
+	case *gatewayv1.Gateway:
+		b, ok := b.(*gatewayv1.Gateway)
+		if !ok {
+			return false
+		}
+		statusA, statusB = a.Status, b.Status
+	default:
+		return false
+	}
+
+	return cmp.Equal(statusA, statusB, opts...)
+}
diff --git a/controllers/internal/controller/utils.go b/controllers/internal/controller/utils.go
new file mode 100644
index 00000000..b71caa82
--- /dev/null
+++ b/controllers/internal/controller/utils.go
@@ -0,0 +1,731 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package controller
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"path"
+	"reflect"
+	"strings"
+)
+
+import (
+	"controllers/api/v1alpha1"
+
+	"controllers/internal/controller/config"
+	"controllers/internal/controller/indexer"
+
+	"controllers/internal/types"
+
+	"controllers/internal/utils"
+
+	"github.com/go-logr/logr"
+
+	"github.com/samber/lo"
+
+	corev1 "k8s.io/api/core/v1"
+
+	networkingv1 "k8s.io/api/networking/v1"
+
+	"k8s.io/apimachinery/pkg/api/meta"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/labels"
+
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/event"
+	"sigs.k8s.io/controller-runtime/pkg/predicate"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
+
+	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
+	"sigs.k8s.io/gateway-api/apis/v1beta1"
+)
+
+const (
+	KindGateway      = "Gateway"
+	KindGatewayClass = "GatewayClass"
+	KindIngress      = "Ingress"
+	KindIngressClass = "IngressClass"
+	KindGatewayProxy = "GatewayProxy"
+	KindSecret       = "Secret"
+)
+
+var (
+	enableReferenceGrant bool
+)
+
+func SetEnableReferenceGrant(enable bool) {
+	enableReferenceGrant = enable
+}
+
+func GetEnableReferenceGrant() bool {
+	return enableReferenceGrant
+}
+
+func TypePredicate[T client.Object]() func(obj client.Object) bool {
+	return func(obj client.Object) bool {
+		_, ok := obj.(T)
+		return ok
+	}
+}
+
+func IsConditionPresentAndEqual(conditions []metav1.Condition, condition metav1.Condition) bool {
+	for _, cond := range conditions {
+		if cond.Type == condition.Type &&
+			cond.Reason == condition.Reason &&
+			cond.Status == condition.Status &&
+			cond.ObservedGeneration == condition.ObservedGeneration {
+			return true
+		}
+	}
+	return false
+}
+
+func ListRequests(ctx context.Context, c client.Client, logger logr.Logger, listObj client.ObjectList, opts ...client.ListOption) []reconcile.Request {
+	return ListMatchingRequests(ctx, c, logger, listObj, func(obj client.Object) bool { return true }, opts...)
+}
+
+func ListMatchingRequests(ctx context.Context, c client.Client, logger logr.Logger, listObj client.ObjectList, matchFunc func(obj client.Object) bool, opts ...client.ListOption) []reconcile.Request {
+	if err := c.List(ctx, listObj, opts...); err != nil {
+		logger.Error(err, "failed to list resource")
+		return nil
+	}
+
+	items, err := meta.ExtractList(listObj)
+	if err != nil {
+		logger.Error(err, "failed to extract list items")
+		return nil
+	}
+
+	var requests []reconcile.Request
+	for _, item := range items {
+		obj, ok := item.(client.Object)
+		if !ok {
+			continue
+		}
+
+		if matchFunc(obj) {
+			requests = append(requests, reconcile.Request{
+				NamespacedName: utils.NamespacedName(obj),
+			})
+		}
+	}
+	return requests
+}
+
+func ProcessIngressClassParameters(c client.Client, log logr.Logger, object client.Object, ingressClass *networkingv1.IngressClass) error {
+	if ingressClass == nil || ingressClass.Spec.Parameters == nil {
+		return nil
+	}
+
+	parameters := ingressClass.Spec.Parameters
+	// check if the parameters reference GatewayProxy
+	if parameters.APIGroup != nil && *parameters.APIGroup == v1alpha1.GroupVersion.Group && parameters.Kind == KindGatewayProxy {
+		ns := object.GetNamespace()
+		if parameters.Namespace != nil {
+			ns = *parameters.Namespace
+		}
+
+		gatewayProxy := &v1alpha1.GatewayProxy{}
+		if err := c.Get(context.TODO(), client.ObjectKey{
+			Namespace: ns,
+			Name:      parameters.Name,
+		}, gatewayProxy); err != nil {
+			log.Error(err, "failed to get GatewayProxy", "namespace", ns, "name", parameters.Name)
+			return err
+		}
+	}
+
+	return nil
+}
+
+func GetIngressClass(ctx context.Context, c client.Client, log logr.Logger, ingressClassName string) (*networkingv1.IngressClass, error) {
+	if ingressClassName == "" {
+		// Check for default ingress class
+		ingressClassList := &networkingv1.IngressClassList{}
+		if err := c.List(ctx, ingressClassList, client.MatchingFields{
+			indexer.IngressClass: config.GetControllerName(),
+		}); err != nil {
+			log.Error(err, "failed to list ingress classes")
+			return nil, err
+		}
+
+		// Find the ingress class that is marked as default
+		for _, ic := range ingressClassList.Items {
+			if IsDefaultIngressClass(&ic) && matchesController(ic.Spec.Controller) {
+				return &ic, nil
+			}
+		}
+		return nil, errors.New("no default ingress class found")
+	}
+
+	// Check if the specified ingress class is controlled by us
+	var ingressClass networkingv1.IngressClass
+	if err := c.Get(ctx, client.ObjectKey{Name: ingressClassName}, &ingressClass); err != nil {
+		return nil, err
+	}
+
+	if matchesController(ingressClass.Spec.Controller) {
+		return &ingressClass, nil
+	}
+
+	return nil, errors.New("ingress class is not controlled by use")
+}
+
+func GetGatewayProxyByIngressClass(ctx context.Context, r client.Client, ingressClass *networkingv1.IngressClass) (*v1alpha1.GatewayProxy, error) {
+	if ingressClass.Spec.Parameters == nil {
+		return nil, nil
+	}
+
+	if ingressClass.Spec.Parameters.APIGroup == nil ||
+		*ingressClass.Spec.Parameters.APIGroup != v1alpha1.GroupVersion.Group ||
+		ingressClass.Spec.Parameters.Kind != KindGatewayProxy {
+		return nil, nil
+	}
+
+	namespace := ingressClass.Namespace
+	if ingressClass.Spec.Parameters.Namespace != nil {
+		namespace = *ingressClass.Spec.Parameters.Namespace
+	}
+
+	gatewayProxy := new(v1alpha1.GatewayProxy)
+	if err := r.Get(ctx, client.ObjectKey{
+		Namespace: namespace,
+		Name:      ingressClass.Spec.Parameters.Name,
+	}, gatewayProxy); err != nil {
+		return nil, fmt.Errorf("failed to get gateway proxy: %w", err)
+	}
+	return gatewayProxy, nil
+}
+
+func GetGatewayProxyByGateway(ctx context.Context, r client.Client, gateway *gatewayv1.Gateway) (*v1alpha1.GatewayProxy, error) {
+	if gateway == nil {
+		return nil, nil
+	}
+	infra := gateway.Spec.Infrastructure
+	if infra == nil || infra.ParametersRef == nil {
+		return nil, nil
+	}
+
+	ns := gateway.GetNamespace()
+	paramRef := infra.ParametersRef
+	if string(paramRef.Group) != v1alpha1.GroupVersion.Group || string(paramRef.Kind) != KindGatewayProxy {
+		return nil, nil
+	}
+	gatewayProxy := &v1alpha1.GatewayProxy{}
+	if err := r.Get(context.Background(), client.ObjectKey{
+		Namespace: ns,
+		Name:      paramRef.Name,
+	}, gatewayProxy); err != nil {
+		return nil, fmt.Errorf("failed to get GatewayProxy: %w", err)
+	}
+	return gatewayProxy, nil
+}
+
+func getListenerStatus(ctx context.Context, mrgc client.Client, gateway *gatewayv1.Gateway) ([]gatewayv1.ListenerStatus, error) {
+	statuses := make(map[gatewayv1.SectionName]gatewayv1.ListenerStatus, len(gateway.Spec.Listeners))
+
+	for i, listener := range gateway.Spec.Listeners {
+		attachedRoutes, err := getAttachedRoutesForListener(ctx, mrgc, *gateway, listener)
+		if err != nil {
+			return nil, err
+		}
+		var (
+			now                 = metav1.Now()
+			conditionProgrammed = metav1.Condition{
+				Type:               string(gatewayv1.ListenerConditionProgrammed),
+				Status:             metav1.ConditionTrue,
+				ObservedGeneration: gateway.GetGeneration(),
+				LastTransitionTime: now,
+				Reason:             string(gatewayv1.ListenerReasonProgrammed),
+			}
+			conditionAccepted = metav1.Condition{
+				Type:               string(gatewayv1.ListenerConditionAccepted),
+				Status:             metav1.ConditionTrue,
+				ObservedGeneration: gateway.GetGeneration(),
+				LastTransitionTime: now,
+				Reason:             string(gatewayv1.ListenerReasonAccepted),
+			}
+			conditionConflicted = metav1.Condition{
+				Type:               string(gatewayv1.ListenerConditionConflicted),
+				Status:             metav1.ConditionTrue,
+				ObservedGeneration: gateway.GetGeneration(),
+				LastTransitionTime: now,
+				Reason:             string(gatewayv1.ListenerReasonNoConflicts),
+			}
+			conditionResolvedRefs = metav1.Condition{
+				Type:               string(gatewayv1.ListenerConditionResolvedRefs),
+				Status:             metav1.ConditionTrue,
+				ObservedGeneration: gateway.GetGeneration(),
+				LastTransitionTime: now,
+				Reason:             string(gatewayv1.ListenerReasonResolvedRefs),
+			}
+
+			supportedKinds = []gatewayv1.RouteGroupKind{}
+		)
+
+		status := gatewayv1.ListenerStatus{
+			Name: listener.Name,
+			Conditions: []metav1.Condition{
+				conditionProgrammed,
+				conditionAccepted,
+				conditionConflicted,
+				conditionResolvedRefs,
+			},
+			SupportedKinds: supportedKinds,
+			AttachedRoutes: attachedRoutes,
+		}
+
+		changed := false
+		if len(gateway.Status.Listeners) > i {
+			if gateway.Status.Listeners[i].AttachedRoutes != attachedRoutes {
+				changed = true
+			}
+			for _, condition := range status.Conditions {
+				if !IsConditionPresentAndEqual(gateway.Status.Listeners[i].Conditions, condition) {
+					changed = true
+					break
+				}
+			}
+		} else {
+			changed = true
+		}
+
+		if changed {
+			statuses[listener.Name] = status
+		} else {
+			statuses[listener.Name] = gateway.Status.Listeners[i]
+		}
+	}
+
+	// check for conflicts
+
+	statusArray := []gatewayv1.ListenerStatus{}
+	for _, status := range statuses {
+		statusArray = append(statusArray, status)
+	}
+
+	return statusArray, nil
+}
+
+func getAttachedRoutesForListener(ctx context.Context, mgrc client.Client, gateway gatewayv1.Gateway, listener gatewayv1.Listener) (int32, error) {
+	httpRouteList := gatewayv1.HTTPRouteList{}
+	if err := mgrc.List(ctx, &httpRouteList); err != nil {
+		return 0, err
+	}
+	var attachedRoutes int32
+	for _, route := range httpRouteList.Items {
+		route := route
+		acceptedByGateway := lo.ContainsBy(route.Status.Parents, func(parentStatus gatewayv1.RouteParentStatus) bool {
+			parentRef := parentStatus.ParentRef
+			if parentRef.Group != nil && *parentRef.Group != gatewayv1.GroupName {
+				return false
+			}
+			if parentRef.Kind != nil && *parentRef.Kind != KindGateway {
+				return false
+			}
+			gatewayNamespace := route.Namespace
+			if parentRef.Namespace != nil {
+				gatewayNamespace = string(*parentRef.Namespace)
+			}
+			return gateway.Namespace == gatewayNamespace && gateway.Name == string(parentRef.Name)
+		})
+		if !acceptedByGateway {
+			continue
+		}
+
+		for _, parentRef := range route.Spec.ParentRefs {
+			ok, _, err := checkRouteAcceptedByListener(
+				ctx,
+				mgrc,
+				&route,
+				gateway,
+				listener,
+				parentRef,
+			)
+			if err != nil {
+				return 0, err
+			}
+			if ok {
+				attachedRoutes++
+			}
+		}
+	}
+	return attachedRoutes, nil
+}
+
+func SetGatewayConditionAccepted(gw *gatewayv1.Gateway, status bool, message string) (ok bool) {
+	condition := metav1.Condition{
+		Type:               string(gatewayv1.GatewayConditionAccepted),
+		Status:             ConditionStatus(status),
+		Reason:             string(gatewayv1.GatewayReasonAccepted),
+		ObservedGeneration: gw.GetGeneration(),
+		Message:            message,
+		LastTransitionTime: metav1.Now(),
+	}
+
+	if !IsConditionPresentAndEqual(gw.Status.Conditions, condition) {
+		setGatewayCondition(gw, condition)
+		ok = true
+	}
+	return
+}
+
+func ConditionStatus(status bool) metav1.ConditionStatus {
+	if status {
+		return metav1.ConditionTrue
+	}
+	return metav1.ConditionFalse
+}
+
+func setGatewayCondition(gw *gatewayv1.Gateway, newCondition metav1.Condition) {
+	gw.Status.Conditions = MergeCondition(gw.Status.Conditions, newCondition)
+}
+
+func acceptedMessage(kind string) string {
+	return fmt.Sprintf("the %s has been accepted by the apisix-ingress-controller", kind)
+}
+
+func referenceGrantPredicates(kind gatewayv1.Kind) predicate.Funcs {
+	var filter = func(obj client.Object) bool {
+		grant, ok := obj.(*v1beta1.ReferenceGrant)
+		if !ok {
+			return false
+		}
+		for _, from := range grant.Spec.From {
+			if from.Kind == kind && string(from.Group) == gatewayv1.GroupName {
+				return true
+			}
+		}
+		return false
+	}
+	predicates := predicate.NewPredicateFuncs(filter)
+	predicates.UpdateFunc = func(e event.UpdateEvent) bool {
+		return filter(e.ObjectOld) || filter(e.ObjectNew)
+	}
+	return predicates
+}
+
+func checkRouteAcceptedByListener(ctx context.Context, mgrc client.Client, route client.Object, gateway gatewayv1.Gateway, listener gatewayv1.Listener, parentRef gatewayv1.ParentReference) (bool, gatewayv1.RouteConditionReason, error) {
+	if parentRef.SectionName != nil {
+		if *parentRef.SectionName != "" && *parentRef.SectionName != listener.Name {
+			return false, gatewayv1.RouteReasonNoMatchingParent, nil
+		}
+	}
+	if parentRef.Port != nil {
+		if *parentRef.Port != listener.Port {
+			return false, gatewayv1.RouteReasonNoMatchingParent, nil
+		}
+	}
+	if !routeMatchesListenerType(route, listener) {
+		return false, gatewayv1.RouteReasonNoMatchingParent, nil
+	}
+	if !routeHostnamesIntersectsWithListenerHostname(route, listener) {
+		return false, gatewayv1.RouteReasonNoMatchingListenerHostname, nil
+	}
+	if ok, err := routeMatchesListenerAllowedRoutes(ctx, mgrc, route, listener.AllowedRoutes, gateway.Namespace, parentRef.Namespace); err != nil {
+		return false, gatewayv1.RouteReasonNotAllowedByListeners, fmt.Errorf("failed matching listener %s to a route %s for gateway %s: %w",
+			listener.Name, route.GetName(), gateway.Name, err,
+		)
+	} else if !ok {
+		return false, gatewayv1.RouteReasonNotAllowedByListeners, nil
+	}
+	return true, gatewayv1.RouteReasonAccepted, nil
+}
+
+func routeMatchesListenerAllowedRoutes(ctx context.Context, mgrc client.Client, route client.Object, allowedRoutes *gatewayv1.AllowedRoutes, gatewayNamespace string, parentRefNamespace *gatewayv1.Namespace) (bool, error) {
+	if allowedRoutes == nil {
+		return true, nil
+	}
+
+	if !isRouteKindAllowed(route, allowedRoutes.Kinds) {
+		return false, fmt.Errorf("route %s/%s is not allowed in the kind", route.GetNamespace(), route.GetName())
+	}
+
+	if !isRouteNamespaceAllowed(ctx, route, mgrc, gatewayNamespace, parentRefNamespace, allowedRoutes.Namespaces) {
+		return false, fmt.Errorf("route %s/%s is not allowed in the namespace", route.GetNamespace(), route.GetName())
+	}
+
+	return true, nil
+}
+
+func isRouteKindAllowed(route client.Object, kinds []gatewayv1.RouteGroupKind) (ok bool) {
+	ok = true
+	if len(kinds) > 0 {
+		_, ok = lo.Find(kinds, func(rgk gatewayv1.RouteGroupKind) bool {
+			gvk := route.GetObjectKind().GroupVersionKind()
+			return (rgk.Group != nil && string(*rgk.Group) == gvk.Group) && string(rgk.Kind) == gvk.Kind
+		})
+	}
+	return
+}
+
+func isRouteNamespaceAllowed(ctx context.Context, route client.Object, mgrc client.Client, gatewayNamespace string, parentRefNamespace *gatewayv1.Namespace, routeNamespaces *gatewayv1.RouteNamespaces) bool {
+	if routeNamespaces == nil || routeNamespaces.From == nil {
+		return true
+	}
+
+	switch *routeNamespaces.From {
+	case gatewayv1.NamespacesFromAll:
+		return true
+
+	case gatewayv1.NamespacesFromSame:
+		if parentRefNamespace == nil {
+			return gatewayNamespace == route.GetNamespace()
+		}
+		return route.GetNamespace() == string(*parentRefNamespace)
+
+	case gatewayv1.NamespacesFromSelector:
+		namespace := corev1.Namespace{}
+		if err := mgrc.Get(ctx, client.ObjectKey{Name: route.GetNamespace()}, &namespace); err != nil {
+			return false
+		}
+
+		s, err := metav1.LabelSelectorAsSelector(routeNamespaces.Selector)
+		if err != nil {
+			return false
+		}
+		return s.Matches(labels.Set(namespace.Labels))
+	default:
+		return true
+	}
+}
+
+func routeMatchesListenerType(route client.Object, listener gatewayv1.Listener) bool {
+	switch route.(type) {
+	case *gatewayv1.HTTPRoute:
+		if listener.Protocol != gatewayv1.HTTPProtocolType && listener.Protocol != gatewayv1.HTTPSProtocolType {
+			return false
+		}
+
+		if listener.Protocol == gatewayv1.HTTPSProtocolType {
+			if listener.TLS == nil {
+				return false
+			}
+
+			if listener.TLS.Mode != nil && *listener.TLS.Mode != gatewayv1.TLSModeTerminate {
+				return false
+			}
+		}
+	default:
+		return false
+	}
+	return true
+}
+
+func routeHostnamesIntersectsWithListenerHostname(route client.Object, listener gatewayv1.Listener) bool {
+	switch r := route.(type) {
+	case *gatewayv1.HTTPRoute:
+		return listenerHostnameIntersectWithRouteHostnames(listener, r.Spec.Hostnames)
+	default:
+		return false
+	}
+}
+
+func reconcileGatewaysMatchGatewayClass(gatewayClass client.Object, gateways []gatewayv1.Gateway) (recs []reconcile.Request) {
+	for _, gateway := range gateways {
+		if string(gateway.Spec.GatewayClassName) == gatewayClass.GetName() {
+			recs = append(recs, reconcile.Request{
+				NamespacedName: client.ObjectKey{
+					Name:      gateway.GetName(),
+					Namespace: gateway.GetNamespace(),
+				},
+			})
+		}
+	}
+	return
+}
+
+func listenerHostnameIntersectWithRouteHostnames(listener gatewayv1.Listener, hostnames []gatewayv1.Hostname) bool {
+	if len(hostnames) == 0 {
+		return true
+	}
+
+	if listener.Hostname == nil || *listener.Hostname == "" {
+		return true
+	}
+
+	for _, hostname := range hostnames {
+		if HostnamesIntersect(string(*listener.Hostname), string(hostname)) {
+			return true
+		}
+	}
+
+	return false
+}
+
+func ProcessGatewayProxy(r client.Client, log logr.Logger, gateway *gatewayv1.Gateway, rk types.NamespacedNameKind) error {
+	if gateway == nil {
+		return nil
+	}
+	infra := gateway.Spec.Infrastructure
+	if infra == nil || infra.ParametersRef == nil {
+		return nil
+	}
+
+	ns := gateway.GetNamespace()
+	paramRef := infra.ParametersRef
+	if string(paramRef.Group) == v1alpha1.GroupVersion.Group && string(paramRef.Kind) == KindGatewayProxy {
+		gatewayProxy := &v1alpha1.GatewayProxy{}
+		if err := r.Get(context.Background(), client.ObjectKey{
+			Namespace: ns,
+			Name:      paramRef.Name,
+		}, gatewayProxy); err != nil {
+			log.Error(err, "failed to get GatewayProxy", "namespace", ns, "name", paramRef.Name)
+			return err
+		}
+	}
+
+	return nil
+}
+
+func FullTypeName(a any) string {
+	typeOf := reflect.TypeOf(a)
+	pkgPath := typeOf.PkgPath()
+	name := typeOf.String()
+	if typeOf.Kind() == reflect.Ptr {
+		pkgPath = typeOf.Elem().PkgPath()
+	}
+	return path.Join(path.Dir(pkgPath), name)
+}
+
+func SetGatewayConditionProgrammed(gw *gatewayv1.Gateway, status bool, message string) (ok bool) {
+	condition := metav1.Condition{
+		Type:               string(gatewayv1.GatewayConditionProgrammed),
+		Status:             ConditionStatus(status),
+		Reason:             string(gatewayv1.GatewayReasonProgrammed),
+		ObservedGeneration: gw.GetGeneration(),
+		Message:            message,
+		LastTransitionTime: metav1.Now(),
+	}
+
+	if !IsConditionPresentAndEqual(gw.Status.Conditions, condition) {
+		setGatewayCondition(gw, condition)
+		ok = true
+	}
+	return
+}
+
+func SetGatewayListenerConditionProgrammed(gw *gatewayv1.Gateway, listenerName string, status bool, message string) (ok bool) {
+	condition := metav1.Condition{
+		Type:               string(gatewayv1.ListenerConditionProgrammed),
+		Status:             ConditionStatus(status),
+		Reason:             string(gatewayv1.ListenerReasonProgrammed),
+		ObservedGeneration: gw.GetGeneration(),
+		Message:            message,
+		LastTransitionTime: metav1.Now(),
+	}
+
+	if !IsConditionPresentAndEqual(gw.Status.Conditions, condition) {
+		setListenerCondition(gw, listenerName, condition)
+		ok = true
+	}
+	return
+}
+
+func SetGatewayListenerConditionResolvedRefs(gw *gatewayv1.Gateway, listenerName string, status bool, message string) (ok bool) {
+	condition := metav1.Condition{
+		Type:               string(gatewayv1.ListenerConditionResolvedRefs),
+		Status:             ConditionStatus(status),
+		Reason:             string(gatewayv1.ListenerReasonResolvedRefs),
+		ObservedGeneration: gw.GetGeneration(),
+		Message:            message,
+		LastTransitionTime: metav1.Now(),
+	}
+
+	if !IsConditionPresentAndEqual(gw.Status.Conditions, condition) {
+		setListenerCondition(gw, listenerName, condition)
+		ok = true
+	}
+	return
+}
+
+func setListenerCondition(gw *gatewayv1.Gateway, listenerName string, newCondition metav1.Condition) {
+	for i, listener := range gw.Status.Listeners {
+		if listener.Name == gatewayv1.SectionName(listenerName) {
+			gw.Status.Listeners[i].Conditions = MergeCondition(listener.Conditions, newCondition)
+			return
+		}
+	}
+}
+
+func MergeCondition(conditions []metav1.Condition, newCondition metav1.Condition) []metav1.Condition {
+	if newCondition.LastTransitionTime.IsZero() {
+		newCondition.LastTransitionTime = metav1.Now()
+	}
+	newConditions := []metav1.Condition{}
+	for _, condition := range conditions {
+		if condition.Type != newCondition.Type {
+			newConditions = append(newConditions, condition)
+		}
+	}
+	newConditions = append(newConditions, newCondition)
+	return newConditions
+}
+
+func HostnamesIntersect(a, b string) bool {
+	return HostnamesMatch(a, b) || HostnamesMatch(b, a)
+}
+
+// HostnamesMatch checks that the hostnameB matches the hostnameA. HostnameA is treated as mask
+// to be checked against the hostnameB.
+func HostnamesMatch(hostnameA, hostnameB string) bool {
+	// the hostnames are in the form of "foo.bar.com"; split them
+	// in a slice of substrings
+	hostnameALabels := strings.Split(hostnameA, ".")
+	hostnameBLabels := strings.Split(hostnameB, ".")
+
+	var a, b int
+	var wildcard bool
+
+	// iterate over the parts of both the hostnames
+	for a, b = 0, 0; a < len(hostnameALabels) && b < len(hostnameBLabels); a, b = a+1, b+1 {
+		var matchFound bool
+
+		// if the current part of B is a wildcard, we need to find the first
+		// A part that matches with the following B part
+		if wildcard {
+			for ; b < len(hostnameBLabels); b++ {
+				if hostnameALabels[a] == hostnameBLabels[b] {
+					matchFound = true
+					break
+				}
+			}
+		}
+
+		// if no match was found, the hostnames don't match
+		if wildcard && !matchFound {
+			return false
+		}
+
+		// check if at least on of the current parts are a wildcard; if so, continue
+		if hostnameALabels[a] == "*" {
+			wildcard = true
+			continue
+		}
+		// reset the wildcard  variables
+		wildcard = false
+
+		// if the current a part is different from the b part, the hostnames are incompatible
+		if hostnameALabels[a] != hostnameBLabels[b] {
+			return false
+		}
+	}
+	return len(hostnameBLabels)-b == len(hostnameALabels)-a
+}
diff --git a/controllers/internal/manager/controllers.go b/controllers/internal/manager/controllers.go
new file mode 100644
index 00000000..fee87445
--- /dev/null
+++ b/controllers/internal/manager/controllers.go
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package manager
+
+import (
+	"context"
+)
+
+import (
+	"controllers/internal/controller"
+	"controllers/internal/controller/indexer"
+	"controllers/internal/controller/status"
+
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/manager"
+)
+
+// K8s
+// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch
+// +kubebuilder:rbac:groups=coordination.k8s.io,resources=leases,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups="discovery.k8s.io",resources=endpointslices,verbs=get;list;watch
+// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch
+// +kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch
+// +kubebuilder:rbac:groups="",resources=namespaces,verbs=get;list;watch
+// GatewayAPI
+// +kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=gatewayclasses,verbs=get;list;watch;update
+// +kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=gatewayclasses/status,verbs=get;update
+// +kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=gateways,verbs=get;list;watch;update
+// +kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=gateways/status,verbs=get;update
+// +kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=httproutes,verbs=get;list;watch
+// +kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=httproutes/status,verbs=get;update
+// +kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=referencegrants,verbs=list;watch;update
+// +kubebuilder:rbac:groups=gateway.networking.k8s.io,resources=referencegrants/status,verbs=get;update
+// Networking
+// +kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses,verbs=get;list;watch;update
+// +kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses/status,verbs=get;update
+// +kubebuilder:rbac:groups=networking.k8s.io,resources=ingressclasses,verbs=get;list;watch
+type Controller interface {
+	SetupWithManager(mgr manager.Manager) error
+}
+
+func setupControllers(ctx context.Context, mgr manager.Manager, updater status.Updater) ([]Controller, error) {
+	if err := indexer.SetupIndexer(mgr); err != nil {
+		return nil, err
+	}
+	return []Controller{
+		&controller.GatewayClassReconciler{
+			Client:  mgr.GetClient(),
+			Scheme:  mgr.GetScheme(),
+			Log:     ctrl.LoggerFrom(ctx).WithName("controllers").WithName("GatewayClass"),
+			Updater: updater,
+		},
+		&controller.GatewayReconciler{
+			Client:  mgr.GetClient(),
+			Scheme:  mgr.GetScheme(),
+			Log:     ctrl.LoggerFrom(ctx).WithName("controllers").WithName("Gateway"),
+			Updater: updater,
+		},
+		&controller.IngressReconciler{
+			Client:  mgr.GetClient(),
+			Scheme:  mgr.GetScheme(),
+			Log:     ctrl.LoggerFrom(ctx).WithName("controllers").WithName("Ingress"),
+			Updater: updater,
+		},
+		&controller.IngressClassReconciler{
+			Client: mgr.GetClient(),
+			Scheme: mgr.GetScheme(),
+			Log:    ctrl.LoggerFrom(ctx).WithName("controllers").WithName("IngressClass"),
+		},
+		&controller.GatewayProxyController{
+			Client: mgr.GetClient(),
+			Scheme: mgr.GetScheme(),
+			Log:    ctrl.LoggerFrom(ctx).WithName("controllers").WithName("GatewayProxy"),
+		},
+	}, nil
+}
diff --git a/controllers/internal/manager/run.go b/controllers/internal/manager/run.go
new file mode 100644
index 00000000..848f9837
--- /dev/null
+++ b/controllers/internal/manager/run.go
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package manager
+
+import (
+	"context"
+	"crypto/tls"
+	"os"
+)
+
+import (
+	"controllers/api/v1alpha1"
+
+	"controllers/internal/controller"
+	"controllers/internal/controller/config"
+	"controllers/internal/controller/status"
+
+	"github.com/go-logr/logr"
+
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+
+	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
+
+	"k8s.io/utils/ptr"
+
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/healthz"
+	"sigs.k8s.io/controller-runtime/pkg/metrics/filters"
+	metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
+	"sigs.k8s.io/controller-runtime/pkg/webhook"
+
+	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
+	"sigs.k8s.io/gateway-api/apis/v1beta1"
+)
+
+var (
+	scheme = runtime.NewScheme()
+)
+
+func init() {
+	utilruntime.Must(clientgoscheme.AddToScheme(scheme))
+
+	if err := gatewayv1.Install(scheme); err != nil {
+		panic(err)
+	}
+	if err := v1alpha1.AddToScheme(scheme); err != nil {
+		panic(err)
+	}
+	if err := v1beta1.Install(scheme); err != nil {
+		panic(err)
+	}
+	// +kubebuilder:scaffold:scheme
+}
+
+func Run(ctx context.Context, logger logr.Logger) error {
+	cfg := config.ControllerConfig
+	setupLog := ctrl.LoggerFrom(ctx).WithName("setup")
+	disableHTTP2 := func(c *tls.Config) {
+		setupLog.Info("disabling http/2")
+		c.NextProtos = []string{"http/1.1"}
+	}
+	var tlsOpts []func(*tls.Config)
+	if !cfg.EnableHTTP2 {
+		tlsOpts = append(tlsOpts, disableHTTP2)
+	}
+	webhookServer := webhook.NewServer(webhook.Options{
+		TLSOpts: tlsOpts,
+	})
+	metricsServerOptions := metricsserver.Options{
+		BindAddress:   cfg.MetricsAddr,
+		SecureServing: cfg.SecureMetrics,
+		TLSOpts:       tlsOpts,
+	}
+
+	if cfg.SecureMetrics {
+		metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization
+	}
+
+	namespace := os.Getenv("POD_NAMESPACE")
+	if namespace == "" {
+		namespace = "default"
+	}
+
+	mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
+		Scheme:                  scheme,
+		Metrics:                 metricsServerOptions,
+		WebhookServer:           webhookServer,
+		HealthProbeBindAddress:  cfg.ProbeAddr,
+		LeaderElection:          !config.ControllerConfig.LeaderElection.Disable,
+		LeaderElectionID:        cfg.LeaderElectionID,
+		LeaderElectionNamespace: namespace,
+		LeaseDuration:           ptr.To(config.ControllerConfig.LeaderElection.LeaseDuration.Duration),
+		RenewDeadline:           ptr.To(config.ControllerConfig.LeaderElection.RenewDeadline.Duration),
+		RetryPeriod:             ptr.To(config.ControllerConfig.LeaderElection.RetryPeriod.Duration),
+		// LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily
+		// when the Manager ends. This requires the binary to immediately end when the
+		// Manager is stopped, otherwise, this setting is unsafe. Setting this significantly
+		// speeds up voluntary leader transitions as the new leader don't have to wait
+		// LeaseDuration time first.
+		//
+		// In the default scaffold provided, the program ends immediately after
+		// the manager stops, so would be fine to enable this option. However,
+		// if you are doing or is intended to do any operation such as perform cleanups
+		// after the manager stops then its usage might be unsafe.
+		// LeaderElectionReleaseOnCancel: true,
+	})
+	if err != nil {
+		setupLog.Error(err, "unable to start manager")
+		return err
+	}
+
+	updater := status.NewStatusUpdateHandler(ctrl.LoggerFrom(ctx).WithName("status").WithName("updater"), mgr.GetClient())
+	if err := mgr.Add(updater); err != nil {
+		setupLog.Error(err, "unable to add status updater")
+		return err
+	}
+
+	setupLog.Info("check ReferenceGrants is enabled")
+	_, err = mgr.GetRESTMapper().KindsFor(schema.GroupVersionResource{
+		Group:    v1beta1.GroupVersion.Group,
+		Version:  v1beta1.GroupVersion.Version,
+		Resource: "referencegrants",
+	})
+	if err != nil {
+		setupLog.Info("CRD ReferenceGrants is not installed", "err", err)
+	}
+	controller.SetEnableReferenceGrant(err == nil)
+	setupLog.Info("setting up controllers")
+	controllers, err := setupControllers(ctx, mgr, updater.Writer())
+	if err != nil {
+		setupLog.Error(err, "unable to set up controllers")
+		return err
+	}
+
+	for _, c := range controllers {
+		if err := c.SetupWithManager(mgr); err != nil {
+			return err
+		}
+	}
+
+	// +kubebuilder:scaffold:builder
+
+	setupLog.Info("setting up health checks")
+	if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
+		setupLog.Error(err, "unable to set up health check")
+		return err
+	}
+
+	setupLog.Info("setting up ready checks")
+	if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
+		setupLog.Error(err, "unable to set up ready check")
+		return err
+	}
+
+	setupLog.Info("starting controller manager")
+	return mgr.Start(ctrl.SetupSignalHandler())
+}
diff --git a/controllers/internal/types/k8s.go b/controllers/internal/types/k8s.go
new file mode 100644
index 00000000..2ec9c608
--- /dev/null
+++ b/controllers/internal/types/k8s.go
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package types
+
+import (
+	"controllers/api/v1alpha1"
+
+	corev1 "k8s.io/api/core/v1"
+
+	netv1 "k8s.io/api/networking/v1"
+
+	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
+)
+
+const (
+	KindGateway      = "Gateway"
+	KindGatewayClass = "GatewayClass"
+	KindIngress      = "Ingress"
+	KindSecret       = "Secret"
+	KindIngressClass = "IngressClass"
+	KindGatewayProxy = "GatewayProxy"
+)
+
+func KindOf(obj any) string {
+	switch obj.(type) {
+	case *gatewayv1.Gateway:
+		return KindGateway
+	case *gatewayv1.GatewayClass:
+		return KindGatewayClass
+	case *netv1.Ingress:
+		return KindIngress
+	case *netv1.IngressClass:
+		return KindIngressClass
+	case *corev1.Secret:
+		return KindSecret
+	case *v1alpha1.GatewayProxy:
+		return KindGatewayProxy
+	default:
+		return "Unknown"
+	}
+}
diff --git a/controllers/internal/types/namespacednamekind.go b/controllers/internal/types/namespacednamekind.go
new file mode 100644
index 00000000..478e3546
--- /dev/null
+++ b/controllers/internal/types/namespacednamekind.go
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package types
+
+import (
+	"fmt"
+	"strings"
+)
+
+import (
+	k8stypes "k8s.io/apimachinery/pkg/types"
+)
+
+type NamespacedNameKind struct {
+	Namespace string
+	Name      string
+	Kind      string
+}
+
+func (n NamespacedNameKind) NamespacedName() k8stypes.NamespacedName {
+	return k8stypes.NamespacedName{
+		Namespace: n.Namespace,
+		Name:      n.Name,
+	}
+}
+
+func (n NamespacedNameKind) String() string {
+	return n.Kind + "/" + n.Namespace + "/" + n.Name
+}
+
+func (n NamespacedNameKind) MarshalText() ([]byte, error) {
+	return []byte(n.String()), nil
+}
+
+func (n *NamespacedNameKind) UnmarshalText(text []byte) error {
+	return n.FromString(string(text))
+}
+
+func (n *NamespacedNameKind) FromString(s string) error {
+	parts := strings.Split(s, "/")
+	if len(parts) != 3 {
+		return fmt.Errorf("invalid format for NamespacedNameKind: %q, expected Kind/Namespace/Name", s)
+	}
+
+	n.Kind = parts[0]
+	n.Namespace = parts[1]
+	n.Name = parts[2]
+	return nil
+}
diff --git a/controllers/internal/utils/error.go b/controllers/internal/utils/error.go
new file mode 100644
index 00000000..d64fad24
--- /dev/null
+++ b/controllers/internal/utils/error.go
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package utils
+
+type ReasonError struct {
+	Reason  string
+	Message string
+}
+
+func (e ReasonError) Error() string {
+	return e.Message
+}
diff --git a/controllers/internal/utils/utils.go b/controllers/internal/utils/utils.go
new file mode 100644
index 00000000..9086dffe
--- /dev/null
+++ b/controllers/internal/utils/utils.go
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package utils
+
+import (
+	"controllers/internal/types"
+
+	k8stypes "k8s.io/apimachinery/pkg/types"
+
+	"sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+func NamespacedName(obj client.Object) k8stypes.NamespacedName {
+	return k8stypes.NamespacedName{
+		Namespace: obj.GetNamespace(),
+		Name:      obj.GetName(),
+	}
+}
+
+func NamespacedNameKind(obj client.Object) types.NamespacedNameKind {
+	return types.NamespacedNameKind{
+		Namespace: obj.GetNamespace(),
+		Name:      obj.GetName(),
+		Kind:      obj.GetObjectKind().GroupVersionKind().Kind,
+	}
+}
diff --git a/docker-compose.yml b/docker-compose.yml
index 0de21f62..1d2283f5 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -19,13 +19,14 @@ services:
   # etcd service
   etcd:
     image: quay.io/coreos/etcd:v3.6.1
-    container_name: etcd_pixiu
+    container_name: pixiu_admin_etcd
     environment:
       - ETCD_LISTEN_PEER_URLS=http://0.0.0.0:2380
       - ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
-      - ETCD_ADVERTISE_CLIENT_URLS=http://etcd_pixiu:2379
+      - ETCD_ADVERTISE_CLIENT_URLS=http://pixiu_admin_etcd:2379
       - ETCD_DATA_DIR=/etcd-data
       - ETCD_NAME=etcd-node-1
+      - ALLOW_NONE_AUTHENTICATION=yes
     volumes:
       - ./etcd-data:/etcd-data
     ports:
@@ -36,7 +37,7 @@ services:
 
   mysql_pixiu:
     image: mysql:8
-    container_name: mysql_pixiu
+    container_name: pixiu_admin_mysql
     environment:
       MYSQL_ROOT_PASSWORD: root_password
       MYSQL_DATABASE: pixiu
@@ -49,10 +50,33 @@ services:
     networks:
       - app_network
 
+  # pixiu service
+  pixiu:
+    build:
+      context: .
+      dockerfile: Dockerfile
+    container_name: pixiu_admin_pixiu_gateway
+    ports:
+      - "8888:8888"
+    volumes:
+      - ./configs/pixiu_with_admin_config.yaml:/etc/pixiu/conf.yaml  # Mount the custom config file
+      - ./configs/log.yml:/etc/pixiu/log.yml  # Mount log configuration if needed
+    networks:
+      - app_network
+    depends_on:
+      - etcd  # Ensure etcd is ready before starting the backend
+      - backend # backend is the xds server for pixiu
+    entrypoint: ["/bin/sh", "-c", "echo 'Waiting for backend to be ready on port 8081...' && \
+      until nc -z pixiu_admin_go_backend 8081; do \
+        sleep 3; \
+      done && \
+      echo 'Backend is ready!' && \
+      /app/docker-entrypoint.sh"]
+
   # backend service(GO)
   backend:
-    image: golang:1.23
-    container_name: go_backend
+    image: golang:1.25
+    container_name: pixiu_admin_go_backend
     working_dir: /app
     volumes:
       - .:/app
@@ -60,7 +84,7 @@ services:
       - "8081:8081"
     command: >
       sh -c "
-        curl -sS http://etcd_pixiu:2379/health || exit 1 &&
+        curl -sS http://pixiu_admin_etcd:2379/health || exit 1 &&
         go mod tidy &&
         go run ./cmd/admin/admin.go -c ./configs/admin_docker_config.yaml
       "
@@ -70,19 +94,19 @@ services:
     networks:
       - app_network
     depends_on:
-      - etcd
+      - etcd  # Ensure etcd is ready before starting the backend
 
   # frontend（Web）
   frontend:
     image: node:18.20.8
-    container_name: frontend
+    container_name: pixiu_admin_frontend
     working_dir: /app/web
     volumes:
       - ./admin/web:/app/web
     ports:
       - "8080:8080"
     environment:
-      - VUE_APP_BACKEND_URL=http://go_backend:8081
+      - VUE_APP_BACKEND_URL=http://pixiu_admin_go_backend:8081
     command: >
       sh -c "
         yarn install &&
@@ -90,8 +114,6 @@ services:
       "
     networks:
       - app_network
-    depends_on:
-      - backend
 
 networks:
   app_network:
diff --git a/docs/docs.go b/docs/docs.go
deleted file mode 100644
index fba071ce..00000000
--- a/docs/docs.go
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
-
-// Package docs GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
-// This file was generated by swaggo/swag
-package docs
-
-import (
-	"bytes"
-	"encoding/json"
-	"strings"
-	"text/template"
-)
-
-import (
-	"github.com/swaggo/swag"
-)
-
-var doc = `{
-    "schemes": {{ marshal .Schemes }},
-    "swagger": "2.0",
-    "info": {
-        "description": "{{escape .Description}}",
-        "title": "{{.Title}}",
-        "contact": {},
-        "version": "{{.Version}}"
-    },
-    "host": "{{.Host}}",
-    "basePath": "{{.BasePath}}",
-    "paths": {
-        "/register": {
-            "post": {
-                "produces": [
-                    "application/json"
-                ],
-                "tags": [
-                    "Register"
-                ],
-                "summary": "用户注册",
-                "parameters": [
-                    {
-                        "type": "string",
-                        "description": "用户名",
-                        "name": "username",
-                        "in": "formData",
-                        "required": true
-                    },
-                    {
-                        "type": "string",
-                        "description": "密码",
-                        "name": "password",
-                        "in": "formData",
-                        "required": true
-                    }
-                ],
-                "responses": {
-                    "200": {
-                        "description": "{\"success\":true,\"data\":{},\"msg\":\"\"}",
-                        "schema": {
-                            "type": "string"
-                        }
-                    }
-                }
-            }
-        }
-    }
-}`
-
-type swaggerInfo struct {
-	Version     string
-	Host        string
-	BasePath    string
-	Schemes     []string
-	Title       string
-	Description string
-}
-
-// SwaggerInfo holds exported Swagger Info so clients can modify it
-var SwaggerInfo = swaggerInfo{
-	Version:     "",
-	Host:        "",
-	BasePath:    "",
-	Schemes:     []string{},
-	Title:       "",
-	Description: "",
-}
-
-type s struct{}
-
-func (s *s) ReadDoc() string {
-	sInfo := SwaggerInfo
-	sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1)
-
-	t, err := template.New("swagger_info").Funcs(template.FuncMap{
-		"marshal": func(v any) string {
-			a, _ := json.Marshal(v)
-			return string(a)
-		},
-		"escape": func(v any) string {
-			// escape tabs
-			str := strings.Replace(v.(string), "\t", "\\t", -1)
-			// replace " with \", and if that results in \\", replace that with \\\"
-			str = strings.Replace(str, "\"", "\\\"", -1)
-			return strings.Replace(str, "\\\\\"", "\\\\\\\"", -1)
-		},
-	}).Parse(doc)
-	if err != nil {
-		return doc
-	}
-
-	var tpl bytes.Buffer
-	if err := t.Execute(&tpl, sInfo); err != nil {
-		return doc
-	}
-
-	return tpl.String()
-}
-
-func init() {
-	swag.Register("swagger", &s{})
-}
diff --git a/docs/images/admin/1.png b/docs/images/admin/1.png
new file mode 100644
index 00000000..99d1548d
Binary files /dev/null and b/docs/images/admin/1.png differ
diff --git a/docs/images/admin/10.png b/docs/images/admin/10.png
new file mode 100644
index 00000000..dfc66c99
Binary files /dev/null and b/docs/images/admin/10.png differ
diff --git a/docs/images/admin/11.png b/docs/images/admin/11.png
new file mode 100644
index 00000000..13bf750e
Binary files /dev/null and b/docs/images/admin/11.png differ
diff --git a/docs/images/admin/12.png b/docs/images/admin/12.png
new file mode 100644
index 00000000..bfb2d19a
Binary files /dev/null and b/docs/images/admin/12.png differ
diff --git a/docs/images/admin/13.png b/docs/images/admin/13.png
new file mode 100644
index 00000000..f6bff7c5
Binary files /dev/null and b/docs/images/admin/13.png differ
diff --git a/docs/images/admin/2.png b/docs/images/admin/2.png
new file mode 100644
index 00000000..203e2fab
Binary files /dev/null and b/docs/images/admin/2.png differ
diff --git a/docs/images/admin/3.png b/docs/images/admin/3.png
new file mode 100644
index 00000000..86b08782
Binary files /dev/null and b/docs/images/admin/3.png differ
diff --git a/docs/images/admin/4.png b/docs/images/admin/4.png
new file mode 100644
index 00000000..e77324ea
Binary files /dev/null and b/docs/images/admin/4.png differ
diff --git a/docs/images/admin/5.png b/docs/images/admin/5.png
new file mode 100644
index 00000000..562bc989
Binary files /dev/null and b/docs/images/admin/5.png differ
diff --git a/docs/images/admin/6.png b/docs/images/admin/6.png
new file mode 100644
index 00000000..4d4bb3a7
Binary files /dev/null and b/docs/images/admin/6.png differ
diff --git a/docs/images/admin/7.png b/docs/images/admin/7.png
new file mode 100644
index 00000000..1bac76aa
Binary files /dev/null and b/docs/images/admin/7.png differ
diff --git a/docs/images/admin/8.png b/docs/images/admin/8.png
new file mode 100644
index 00000000..5a25e920
Binary files /dev/null and b/docs/images/admin/8.png differ
diff --git a/docs/images/admin/9.png b/docs/images/admin/9.png
new file mode 100644
index 00000000..f46b8f32
Binary files /dev/null and b/docs/images/admin/9.png differ
diff --git a/docs/index copy.md b/docs/index copy.md
deleted file mode 100644
index 8c232eab..00000000
--- a/docs/index copy.md	
+++ /dev/null
@@ -1,27 +0,0 @@
-# dubbo-go-pixiu Documentation
-
-## [User Guide](user/README.md)
-
-* [Quick Start](user/start.md)
-* [Config](user/config.md)
-* [Metric](user/metric.md)
-
-## [Samples](sample/README.md)
-
-* [dubbo](sample/dubbo/dubbo.md)
-* [http](sample/http/http.md)
-* [mock](sample/mock.md)
-* [plugins](../samples/plugins/index.md)
-* [Tracing](sample/others/jaeger.md)
-* [springcloud](sample/springcloud/README.md)
-
-## [Developer Guide](developer/README.md)
-
-* [Concept](developer/concept.md)
-* [Auto Registry](developer/auto-registy.md)
-* [Filter](developer/filter.md)
-* [Log](developer/log.md)
-
-## [Common](common/README.md)
-
-* [FAQ](common/faq.md)
diff --git a/docs/swagger.json b/docs/swagger.json
deleted file mode 100644
index 8759022e..00000000
--- a/docs/swagger.json
+++ /dev/null
@@ -1,43 +0,0 @@
-{
-    "swagger": "2.0",
-    "info": {
-        "contact": {}
-    },
-    "paths": {
-        "/register": {
-            "post": {
-                "produces": [
-                    "application/json"
-                ],
-                "tags": [
-                    "Register"
-                ],
-                "summary": "用户注册",
-                "parameters": [
-                    {
-                        "type": "string",
-                        "description": "用户名",
-                        "name": "username",
-                        "in": "formData",
-                        "required": true
-                    },
-                    {
-                        "type": "string",
-                        "description": "密码",
-                        "name": "password",
-                        "in": "formData",
-                        "required": true
-                    }
-                ],
-                "responses": {
-                    "200": {
-                        "description": "{\"success\":true,\"data\":{},\"msg\":\"\"}",
-                        "schema": {
-                            "type": "string"
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/docs/swagger.yaml b/docs/swagger.yaml
deleted file mode 100644
index f6453ff5..00000000
--- a/docs/swagger.yaml
+++ /dev/null
@@ -1,27 +0,0 @@
-info:
-  contact: {}
-paths:
-  /register:
-    post:
-      parameters:
-      - description: 用户名
-        in: formData
-        name: username
-        required: true
-        type: string
-      - description: 密码
-        in: formData
-        name: password
-        required: true
-        type: string
-      produces:
-      - application/json
-      responses:
-        "200":
-          description: '{"success":true,"data":{},"msg":""}'
-          schema:
-            type: string
-      summary: 用户注册
-      tags:
-      - Register
-swagger: "2.0"
diff --git a/go.mod b/go.mod
index e14a1bba..6fa1bf70 100644
--- a/go.mod
+++ b/go.mod
@@ -48,7 +48,7 @@ require (
 	github.com/stretchr/testify v1.11.1
 	github.com/swaggo/files v1.0.1
 	github.com/swaggo/gin-swagger v1.6.0
-	github.com/swaggo/swag v1.8.12
+	github.com/swaggo/swag v1.16.6
 	go.etcd.io/etcd/api/v3 v3.5.7
 	go.etcd.io/etcd/client/v3 v3.5.7
 	go.opentelemetry.io/otel v1.10.0
@@ -61,8 +61,8 @@ require (
 	go.opentelemetry.io/otel/sdk/metric v0.32.1
 	go.opentelemetry.io/otel/trace v1.10.0
 	go.uber.org/zap v1.21.0
-	golang.org/x/crypto v0.43.0
-	golang.org/x/net v0.45.0
+	golang.org/x/crypto v0.44.0
+	golang.org/x/net v0.47.0
 	google.golang.org/grpc v1.66.2
 	google.golang.org/protobuf v1.36.6
 	gopkg.in/yaml.v3 v3.0.1
@@ -77,8 +77,6 @@ require (
 	filippo.io/edwards25519 v1.1.0 // indirect
 	github.com/KyleBanks/depth v1.2.1 // indirect
 	github.com/OneOfOne/xxhash v1.2.8 // indirect
-	github.com/PuerkitoBio/purell v1.1.1 // indirect
-	github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
 	github.com/RoaringBitmap/roaring v1.2.3 // indirect
 	github.com/Workiva/go-datastructures v1.0.52 // indirect
 	github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 // indirect
@@ -137,10 +135,16 @@ require (
 	github.com/go-logr/logr v1.2.4 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/go-ole/go-ole v1.2.6 // indirect
-	github.com/go-openapi/jsonpointer v0.19.5 // indirect
-	github.com/go-openapi/jsonreference v0.19.6 // indirect
-	github.com/go-openapi/spec v0.20.4 // indirect
-	github.com/go-openapi/swag v0.19.15 // indirect
+	github.com/go-openapi/jsonpointer v0.22.3 // indirect
+	github.com/go-openapi/jsonreference v0.21.3 // indirect
+	github.com/go-openapi/spec v0.22.1 // indirect
+	github.com/go-openapi/swag/conv v0.25.4 // indirect
+	github.com/go-openapi/swag/jsonname v0.25.4 // indirect
+	github.com/go-openapi/swag/jsonutils v0.25.4 // indirect
+	github.com/go-openapi/swag/loading v0.25.4 // indirect
+	github.com/go-openapi/swag/stringutils v0.25.4 // indirect
+	github.com/go-openapi/swag/typeutils v0.25.4 // indirect
+	github.com/go-openapi/swag/yamlutils v0.25.4 // indirect
 	github.com/go-playground/locales v0.14.1 // indirect
 	github.com/go-playground/universal-translator v0.18.1 // indirect
 	github.com/go-playground/validator/v10 v10.20.0 // indirect
@@ -170,7 +174,6 @@ require (
 	github.com/jinzhu/inflection v1.0.0 // indirect
 	github.com/jinzhu/now v1.1.5 // indirect
 	github.com/jmespath/go-jmespath v0.4.0 // indirect
-	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/jtolds/gls v4.20.0+incompatible // indirect
 	github.com/k0kubun/pp v3.0.1+incompatible // indirect
@@ -187,7 +190,6 @@ require (
 	github.com/lestrrat-go/strftime v1.1.1 // indirect
 	github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
 	github.com/magiconair/properties v1.8.5 // indirect
-	github.com/mailru/easyjson v0.7.6 // indirect
 	github.com/mattn/go-colorable v0.1.13 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/mitchellh/copystructure v1.2.0 // indirect
@@ -246,13 +248,15 @@ require (
 	go.opentelemetry.io/proto/otlp v1.0.0 // indirect
 	go.uber.org/atomic v1.10.0 // indirect
 	go.uber.org/multierr v1.8.0 // indirect
+	go.yaml.in/yaml/v3 v3.0.4 // indirect
 	golang.org/x/arch v0.20.0 // indirect
+	golang.org/x/mod v0.30.0 // indirect
 	golang.org/x/oauth2 v0.30.0 // indirect
-	golang.org/x/sync v0.17.0 // indirect
-	golang.org/x/sys v0.37.0 // indirect
-	golang.org/x/text v0.30.0 // indirect
+	golang.org/x/sync v0.18.0 // indirect
+	golang.org/x/sys v0.38.0 // indirect
+	golang.org/x/text v0.31.0 // indirect
 	golang.org/x/time v0.12.0 // indirect
-	golang.org/x/tools v0.37.0 // indirect
+	golang.org/x/tools v0.39.0 // indirect
 	google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
diff --git a/go.sum b/go.sum
index 8da9ce63..b40f11b9 100644
--- a/go.sum
+++ b/go.sum
@@ -410,10 +410,6 @@ github.com/MicahParks/keyfunc v1.0.0/go.mod h1:R8RZa27qn+5cHTfYLJ9/+7aSb5JIdz7cl
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
 github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
-github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
-github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
-github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 github.com/RoaringBitmap/roaring v1.2.3 h1:yqreLINqIrX22ErkKI0vY47/ivtJr6n+kMhVOVmhWBY=
 github.com/RoaringBitmap/roaring v1.2.3/go.mod h1:plvDsJQpxOC5bw8LRteu/MLWHsHez/3y6cubLI4/1yE=
 github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
@@ -761,16 +757,33 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
 github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
 github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
 github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
-github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
-github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
-github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
-github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
-github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
-github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
-github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
-github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/jsonpointer v0.22.3 h1:dKMwfV4fmt6Ah90zloTbUKWMD+0he+12XYAsPotrkn8=
+github.com/go-openapi/jsonpointer v0.22.3/go.mod h1:0lBbqeRsQ5lIanv3LHZBrmRGHLHcQoOXQnf88fHlGWo=
+github.com/go-openapi/jsonreference v0.21.3 h1:96Dn+MRPa0nYAR8DR1E03SblB5FJvh7W6krPI0Z7qMc=
+github.com/go-openapi/jsonreference v0.21.3/go.mod h1:RqkUP0MrLf37HqxZxrIAtTWW4ZJIK1VzduhXYBEeGc4=
+github.com/go-openapi/spec v0.22.1 h1:beZMa5AVQzRspNjvhe5aG1/XyBSMeX1eEOs7dMoXh/k=
+github.com/go-openapi/spec v0.22.1/go.mod h1:c7aeIQT175dVowfp7FeCvXXnjN/MrpaONStibD2WtDA=
 github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
-github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
+github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4=
+github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU=
+github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI=
+github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
+github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA=
+github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY=
+github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo=
+github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM=
+github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s=
+github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE=
+github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8=
+github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0=
+github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw=
+github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
+github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw=
+github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc=
+github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4=
+github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg=
+github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls=
+github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
 github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
 github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@@ -1073,8 +1086,6 @@ github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqx
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
 github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
 github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
-github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
-github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
 github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@@ -1164,10 +1175,6 @@ github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0Q
 github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
 github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
-github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
-github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 github.com/mark3labs/mcp-go v0.32.0 h1:fgwmbfL2gbd67obg57OfV2Dnrhs1HtSdlY/i5fn7MU8=
 github.com/mark3labs/mcp-go v0.32.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4=
 github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
@@ -1381,8 +1388,8 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
-github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
-github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
+github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
+github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ=
@@ -1475,8 +1482,8 @@ github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
 github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
 github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
 github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
-github.com/swaggo/swag v1.8.12 h1:pctzkNPu0AlQP2royqX3apjKCQonAnf7KGoxeO4y64w=
-github.com/swaggo/swag v1.8.12/go.mod h1:lNfm6Gg+oAq3zRJQNEMBE66LIJKM44mxFqhEEgy2its=
+github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
+github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
 github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes=
 github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
 github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ=
@@ -1633,6 +1640,8 @@ go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
 go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
 go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
 go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
+go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
+go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
 golang.org/x/arch v0.0.0-20200826200359-b19915210f00/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
 golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
 golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
@@ -1665,8 +1674,8 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf
 golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
 golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
 golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
-golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
-golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
+golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
+golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1712,8 +1721,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
 golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
-golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
+golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
+golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
 golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1765,7 +1774,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
 golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
-golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
 golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
 golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -1796,8 +1804,8 @@ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
 golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
 golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
 golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
-golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM=
-golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
+golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
+golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1842,8 +1850,8 @@ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
-golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
+golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
+golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1918,7 +1926,6 @@ golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1964,8 +1971,8 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
-golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
+golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@@ -1997,8 +2004,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
-golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
+golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
+golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -2082,8 +2089,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
 golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
 golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
-golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
-golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
+golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
+golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -2392,7 +2399,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -2415,8 +2421,9 @@ rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
-sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
 sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
+sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
+sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
 sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
 v.marlon.life/toolkit v0.0.0-20211025131614-e4a91730b4ab h1:a6cEnTfYq9TiNuFyvtyGsQ/05FfG6W1FkR61o4sWfMk=
 v.marlon.life/toolkit v0.0.0-20211025131614-e4a91730b4ab/go.mod h1:v4RzgEKUNEmIL9GEsV1AbChohLekk/qSK+Ed2Ho0R84=
diff --git a/pkg/client/dubbo/dubbo.go b/pkg/client/dubbo/dubbo.go
index f56ea652..492806fb 100644
--- a/pkg/client/dubbo/dubbo.go
+++ b/pkg/client/dubbo/dubbo.go
@@ -48,7 +48,6 @@ import (
 import (
 	"github.com/apache/dubbo-go-pixiu/pkg/client"
 	cst "github.com/apache/dubbo-go-pixiu/pkg/common/constant"
-	"github.com/apache/dubbo-go-pixiu/pkg/config"
 	"github.com/apache/dubbo-go-pixiu/pkg/logger"
 )
 
@@ -76,7 +75,6 @@ var (
 		Organization: "dubbo-go-pixiu",
 		Name:         "Dubbogo Pixiu",
 		Module:       "dubbogo Pixiu",
-		Version:      config.Version,
 		Owner:        "Dubbogo Pixiu",
 		Environment:  "dev",
 	}
diff --git a/pkg/common/constant/http.go b/pkg/common/constant/http.go
index 2156ab90..1ec456b8 100644
--- a/pkg/common/constant/http.go
+++ b/pkg/common/constant/http.go
@@ -57,7 +57,8 @@ const (
 
 	HeaderValueAuthorization = "Authorization"
 
-	HeaderValueAll = "*"
+	HeaderValueAll       = "*"
+	HeaderValueAllLevels = "**"
 
 	PathSlash           = "/"
 	ProtocolSlash       = "://"
diff --git a/pkg/common/router/mock/router.go b/pkg/common/router/mock/router.go
new file mode 100644
index 00000000..317f37e4
--- /dev/null
+++ b/pkg/common/router/mock/router.go
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package mock
+
+import (
+	stdHttp "net/http"
+	"strings"
+	"sync"
+)
+
+import (
+	"github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/router/trie"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/util/stringutil"
+	"github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+	"github.com/apache/dubbo-go-pixiu/pkg/model"
+	"github.com/apache/dubbo-go-pixiu/pkg/server"
+)
+
+// * =================================
+// This is a old implementation before updating to the new router framework (#777)
+// It is a reserved for function verification only
+// DO NOT USE IT IN ANYWHERE BUT TEST IN pkg/common/router/router_test.go
+// * =================================
+type (
+	// RouterCoordinator the router coordinator for http connection manager
+	RouterCoordinator struct {
+		activeConfig *model.RouteConfiguration
+		rw           sync.RWMutex
+	}
+)
+
+// CreateRouterCoordinator create coordinator for http connection manager
+func CreateRouterCoordinator(routeConfig *model.RouteConfiguration) *RouterCoordinator {
+	rc := &RouterCoordinator{activeConfig: routeConfig}
+	if routeConfig.Dynamic {
+		server.GetRouterManager().AddRouterListener(rc)
+	}
+	rc.initTrie()
+	rc.initRegex()
+	return rc
+}
+
+// Route find routeAction for request
+func (rm *RouterCoordinator) Route(hc *http.HttpContext) (*model.RouteAction, error) {
+	rm.rw.RLock()
+	defer rm.rw.RUnlock()
+
+	return rm.route(hc.Request)
+}
+
+func (rm *RouterCoordinator) RouteByPathAndName(path, method string) (*model.RouteAction, error) {
+	rm.rw.RLock()
+	defer rm.rw.RUnlock()
+
+	return rm.activeConfig.RouteByPathAndMethod(path, method)
+}
+
+func (rm *RouterCoordinator) route(req *stdHttp.Request) (*model.RouteAction, error) {
+	// match those route that only contains headers first
+	var matched []*model.Router
+	for _, route := range rm.activeConfig.Routes {
+		if len(route.Match.Prefix) > 0 {
+			continue
+		}
+		if route.Match.MatchHeader(req) {
+			matched = append(matched, route)
+		}
+	}
+
+	// always return the first match of header if got any
+	if len(matched) > 0 {
+		if len(matched[0].Route.Cluster) == 0 {
+			return nil, errors.New("action is nil. please check your configuration.")
+		}
+		return &matched[0].Route, nil
+	}
+
+	// match those route that only contains prefix
+	// TODO: may consider implementing both prefix and header in the future
+	return rm.activeConfig.Route(req)
+}
+
+func getTrieKey(method string, path string, isPrefix bool) string {
+	if isPrefix {
+		if !strings.HasSuffix(path, constant.PathSlash) {
+			path = path + constant.PathSlash
+		}
+		path = path + "**"
+	}
+	return stringutil.GetTrieKey(method, path)
+}
+
+func (rm *RouterCoordinator) initTrie() {
+	if rm.activeConfig.RouteTrie.IsEmpty() {
+		rm.activeConfig.RouteTrie = trie.NewTrie()
+	}
+	for _, router := range rm.activeConfig.Routes {
+		rm.OnAddRouter(router)
+	}
+}
+
+func (rm *RouterCoordinator) initRegex() {
+	for _, router := range rm.activeConfig.Routes {
+		headers := router.Match.Headers
+		for i := range headers {
+			if headers[i].Regex && len(headers[i].Values) > 0 {
+				// regexp always use first value of header
+				err := headers[i].SetValueRegex(headers[i].Values[0])
+				if err != nil {
+					logger.Errorf("invalid regexp in headers[%d]: %v", i, err)
+					panic(err)
+				}
+			}
+		}
+	}
+}
+
+// OnAddRouter add router
+func (rm *RouterCoordinator) OnAddRouter(r *model.Router) {
+	//TODO: lock move to trie node
+	rm.rw.Lock()
+	defer rm.rw.Unlock()
+	if r.Match.Methods == nil {
+		r.Match.Methods = []string{constant.Get, constant.Put, constant.Delete, constant.Post, constant.Options}
+	}
+	isPrefix := r.Match.Prefix != ""
+	for _, method := range r.Match.Methods {
+		var key string
+		if isPrefix {
+			key = getTrieKey(method, r.Match.Prefix, isPrefix)
+		} else {
+			key = getTrieKey(method, r.Match.Path, isPrefix)
+		}
+		_, _ = rm.activeConfig.RouteTrie.Put(key, r.Route)
+	}
+}
+
+// OnDeleteRouter delete router
+func (rm *RouterCoordinator) OnDeleteRouter(r *model.Router) {
+	rm.rw.Lock()
+	defer rm.rw.Unlock()
+
+	if r.Match.Methods == nil {
+		r.Match.Methods = []string{constant.Get, constant.Put, constant.Delete, constant.Post}
+	}
+	isPrefix := r.Match.Prefix != ""
+	for _, method := range r.Match.Methods {
+		var key string
+		if isPrefix {
+			key = getTrieKey(method, r.Match.Prefix, isPrefix)
+		} else {
+			key = getTrieKey(method, r.Match.Path, isPrefix)
+		}
+		_, _ = rm.activeConfig.RouteTrie.Remove(key)
+	}
+}
diff --git a/pkg/common/router/router.go b/pkg/common/router/router.go
index cd175eb2..6a8393c5 100644
--- a/pkg/common/router/router.go
+++ b/pkg/common/router/router.go
@@ -19,8 +19,10 @@ package router
 
 import (
 	stdHttp "net/http"
-	"strings"
+	"slices"
 	"sync"
+	"sync/atomic"
+	"time"
 )
 
 import (
@@ -28,7 +30,6 @@ import (
 )
 
 import (
-	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
 	"github.com/apache/dubbo-go-pixiu/pkg/common/router/trie"
 	"github.com/apache/dubbo-go-pixiu/pkg/common/util/stringutil"
 	"github.com/apache/dubbo-go-pixiu/pkg/context/http"
@@ -37,136 +38,216 @@ import (
 	"github.com/apache/dubbo-go-pixiu/pkg/server"
 )
 
-type (
-	// RouterCoordinator the router coordinator for http connection manager
-	RouterCoordinator struct {
-		activeConfig *model.RouteConfiguration
-		rw           sync.RWMutex
-	}
-)
+// RouterCoordinator the router coordinator for http connection manager
+type RouterCoordinator struct {
+	mainSnapshot atomic.Pointer[model.RouteSnapshot] // atomic snapshot
+	mu           sync.Mutex
+
+	nextSnapshot []*model.Router // temp store for dynamic update, DO NOT read directly
+
+	timer    *time.Timer   // debounce timer
+	debounce time.Duration // merge window, default 50ms
+}
 
 // CreateRouterCoordinator create coordinator for http connection manager
 func CreateRouterCoordinator(routeConfig *model.RouteConfiguration) *RouterCoordinator {
-	rc := &RouterCoordinator{activeConfig: routeConfig}
+	rc := &RouterCoordinator{
+		nextSnapshot: make([]*model.Router, 0, len(routeConfig.Routes)),
+		debounce:     50 * time.Millisecond, // merge window
+	}
 	if routeConfig.Dynamic {
 		server.GetRouterManager().AddRouterListener(rc)
 	}
-	rc.initTrie()
-	rc.initRegex()
+	// build initial config and store snapshot
+	rc.mainSnapshot.Store(model.ToSnapshot(buildRouteConfiguration(routeConfig.Routes).Routes))
+	// copy initial routes to store, keep origin order
+	rc.nextSnapshot = append(rc.nextSnapshot, routeConfig.Routes...)
 	return rc
 }
 
-// Route find routeAction for request
 func (rm *RouterCoordinator) Route(hc *http.HttpContext) (*model.RouteAction, error) {
-	rm.rw.RLock()
-	defer rm.rw.RUnlock()
-
 	return rm.route(hc.Request)
 }
 
 func (rm *RouterCoordinator) RouteByPathAndName(path, method string) (*model.RouteAction, error) {
-	rm.rw.RLock()
-	defer rm.rw.RUnlock()
-
-	return rm.activeConfig.RouteByPathAndMethod(path, method)
+	s := rm.mainSnapshot.Load()
+	if s == nil {
+		return nil, errors.New("router configuration is empty")
+	}
+	t := s.MethodTries[method]
+	if t == nil {
+		return nil, errors.Errorf("route failed for %s, no rules matched", stringutil.GetTrieKey(method, path))
+	}
+	node, _, ok := t.Match(stringutil.GetTrieKey(method, path))
+	if !ok || node == nil || node.GetBizInfo() == nil {
+		return nil, errors.Errorf("route failed for %s, no rules matched", stringutil.GetTrieKey(method, path))
+	}
+	act, ok := node.GetBizInfo().(model.RouteAction)
+	if !ok {
+		return nil, errors.Errorf("route failed for %s, invalid route action type", stringutil.GetTrieKey(method, path))
+	}
+	return &act, nil
 }
 
 func (rm *RouterCoordinator) route(req *stdHttp.Request) (*model.RouteAction, error) {
-	// match those route that only contains headers first
-	var matched []*model.Router
-	for _, route := range rm.activeConfig.Routes {
-		if len(route.Match.Prefix) > 0 {
+	s := rm.mainSnapshot.Load()
+	if s == nil {
+		return nil, errors.New("router configuration is empty")
+	}
+
+	// header-only first
+	for _, hr := range s.HeaderOnly {
+		if !model.MethodAllowed(hr.Methods, req.Method) {
 			continue
 		}
-		if route.Match.MatchHeader(req) {
-			matched = append(matched, route)
+		if matchHeaders(hr.Headers, req) {
+			if len(hr.Action.Cluster) == 0 {
+				return nil, errors.New("action is nil. please check your configuration.")
+			}
+			return &hr.Action, nil
 		}
 	}
+	// Trie
+	t := s.MethodTries[req.Method]
+	if t == nil {
+		return nil, errors.Errorf("route failed for %s, no rules matched", stringutil.GetTrieKey(req.Method, req.URL.Path))
 
-	// always return the first match of header if got any
-	if len(matched) > 0 {
-		if len(matched[0].Route.Cluster) == 0 {
-			return nil, errors.New("action is nil. please check your configuration.")
-		}
-		return &matched[0].Route, nil
 	}
 
-	// match those route that only contains prefix
-	// TODO: may consider implementing both prefix and header in the future
-	return rm.activeConfig.Route(req)
+	node, _, ok := t.Match(stringutil.GetTrieKey(req.Method, req.URL.Path))
+	if !ok || node == nil || node.GetBizInfo() == nil {
+		return nil, errors.Errorf("route failed for %s, no rules matched", stringutil.GetTrieKey(req.Method, req.URL.Path))
+	}
+	act, ok := node.GetBizInfo().(model.RouteAction)
+	if !ok {
+		return nil, errors.Errorf("route failed for %s, invalid route action type", stringutil.GetTrieKey(req.Method, req.URL.Path))
+	}
+	return &act, nil
 }
 
-func getTrieKey(method string, path string, isPrefix bool) string {
-	if isPrefix {
-		if !strings.HasSuffix(path, constant.PathSlash) {
-			path = path + constant.PathSlash
+// reset timer or publish directly
+func (rm *RouterCoordinator) schedulePublishLocked() {
+	if rm.debounce <= 0 {
+		// fallback: immediate
+		rm.publishLocked()
+		return
+	}
+	if rm.timer == nil {
+		rm.timer = time.NewTimer(rm.debounce)
+		go rm.awaitAndPublish()
+		return
+	}
+	// clear timer channel
+	if !rm.timer.Stop() {
+		select {
+		case <-rm.timer.C:
+		default:
 		}
-		path = path + "**"
 	}
-	return stringutil.GetTrieKey(method, path)
+	rm.timer.Reset(rm.debounce)
 }
 
-func (rm *RouterCoordinator) initTrie() {
-	if rm.activeConfig.RouteTrie.IsEmpty() {
-		rm.activeConfig.RouteTrie = trie.NewTrie()
-	}
-	for _, router := range rm.activeConfig.Routes {
-		rm.OnAddRouter(router)
+// wait for timer and publish
+func (rm *RouterCoordinator) awaitAndPublish() {
+	<-rm.timer.C
+	rm.mu.Lock()
+	defer rm.mu.Unlock()
+	rm.publishLocked()
+	rm.timer = nil
+}
+
+// publish: clone from store -> build new config -> atomic switch
+func (rm *RouterCoordinator) publishLocked() {
+	// 1) clone routes
+	next := make([]*model.Router, len(rm.nextSnapshot))
+	copy(next, rm.nextSnapshot)
+	// 2) build new config
+	cfg := buildRouteConfiguration(next)
+	// 3) atomic switch
+	rm.mainSnapshot.Store(model.ToSnapshot(cfg.Routes))
+}
+
+func buildRouteConfiguration(routes []*model.Router) *model.RouteConfiguration {
+	cfg := &model.RouteConfiguration{
+		RouteTrie: trie.NewTrie(),
+		Routes:    make([]*model.Router, 0, len(routes)),
+		Dynamic:   false,
 	}
+	cfg.Routes = append(cfg.Routes, routes...)
+	initRegex(cfg)
+	fillTrieFromRoutes(cfg)
+	return cfg
 }
 
-func (rm *RouterCoordinator) initRegex() {
-	for _, router := range rm.activeConfig.Routes {
+func initRegex(cfg *model.RouteConfiguration) {
+	for _, router := range cfg.Routes {
 		headers := router.Match.Headers
 		for i := range headers {
 			if headers[i].Regex && len(headers[i].Values) > 0 {
-				// regexp always use first value of header
-				err := headers[i].SetValueRegex(headers[i].Values[0])
-				if err != nil {
-					logger.Errorf("invalid regexp in headers[%d]: %v", i, err)
-					panic(err)
+				if err := headers[i].SetValueRegex(headers[i].Values[0]); err != nil {
+					logger.Warnf("invalid regexp in headers[%d]: %v", i, err)
 				}
 			}
 		}
 	}
 }
 
-// OnAddRouter add router
+// OnAddRouter add router, every call of OnAdd will ADD a new rule, instead of OVERWRITE the same rule
 func (rm *RouterCoordinator) OnAddRouter(r *model.Router) {
-	//TODO: lock move to trie node
-	rm.rw.Lock()
-	defer rm.rw.Unlock()
-	if r.Match.Methods == nil {
-		r.Match.Methods = []string{constant.Get, constant.Put, constant.Delete, constant.Post, constant.Options}
-	}
-	isPrefix := r.Match.Prefix != ""
-	for _, method := range r.Match.Methods {
-		var key string
-		if isPrefix {
-			key = getTrieKey(method, r.Match.Prefix, isPrefix)
-		} else {
-			key = getTrieKey(method, r.Match.Path, isPrefix)
+	rm.mu.Lock()
+	defer rm.mu.Unlock()
+	rm.nextSnapshot = append(rm.nextSnapshot, r)
+	rm.schedulePublishLocked()
+}
+
+func fillTrieFromRoutes(cfg *model.RouteConfiguration) {
+	for _, r := range cfg.Routes {
+		methods := r.Match.Methods
+		if len(methods) == 0 {
+			methods = []string{"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"}
+		}
+		for _, m := range methods {
+			key := stringutil.GetTrieKeyWithPrefix(m, r.Match.Path, r.Match.Prefix, r.Match.Prefix != "")
+			_, _ = cfg.RouteTrie.Put(key, r.Route)
 		}
-		_, _ = rm.activeConfig.RouteTrie.Put(key, r.Route)
 	}
 }
 
 // OnDeleteRouter delete router
 func (rm *RouterCoordinator) OnDeleteRouter(r *model.Router) {
-	rm.rw.Lock()
-	defer rm.rw.Unlock()
+	rm.mu.Lock()
+	defer rm.mu.Unlock()
 
-	if r.Match.Methods == nil {
-		r.Match.Methods = []string{constant.Get, constant.Put, constant.Delete, constant.Post}
+	if len(rm.nextSnapshot) == 0 {
+		return
 	}
-	isPrefix := r.Match.Prefix != ""
-	for _, method := range r.Match.Methods {
-		var key string
-		if isPrefix {
-			key = getTrieKey(method, r.Match.Prefix, isPrefix)
+	out := rm.nextSnapshot[:0]
+	for _, rr := range rm.nextSnapshot {
+		if rr.ID == r.ID {
+			continue
+		}
+		out = append(out, rr)
+	}
+	rm.nextSnapshot = out
+	rm.schedulePublishLocked()
+}
+
+func matchHeaders(chs []model.CompiledHeader, r *stdHttp.Request) bool {
+	for _, ch := range chs {
+		if val := r.Header.Get(ch.Name); len(val) > 0 {
+			if ch.Regex != nil {
+				if ok := ch.Regex.MatchString(val); !ok {
+					return false
+				}
+				continue
+			}
+
+			if !slices.Contains(ch.Values, val) {
+				return false
+			}
 		} else {
-			key = getTrieKey(method, r.Match.Path, isPrefix)
+			return false
 		}
-		_, _ = rm.activeConfig.RouteTrie.Remove(key)
 	}
+	return true
 }
diff --git a/pkg/common/util/stringutil/stringutil.go b/pkg/common/util/stringutil/stringutil.go
index 23ba5eaf..405d0dec 100644
--- a/pkg/common/util/stringutil/stringutil.go
+++ b/pkg/common/util/stringutil/stringutil.go
@@ -59,32 +59,53 @@ func IsPathVariableOrWildcard(key string) bool {
 
 // IsWildcard return if is *
 func IsWildcard(key string) bool {
-	return key == "*"
+	return key == constant.HeaderValueAll
 }
 
+// IsMatchAll return if is **
 func IsMatchAll(key string) bool {
-	return key == "**"
+	return key == constant.HeaderValueAllLevels
 }
 
 func GetTrieKey(method string, path string) string {
+	// "http://localhost:8882/api/v1/test-dubbo/user?name=tc/"
 	ret := ""
-	//"http://localhost:8882/api/v1/test-dubbo/user?name=tc"
+
 	if strings.Contains(path, constant.ProtocolSlash) {
 		path = path[strings.Index(path, constant.ProtocolSlash)+len(constant.ProtocolSlash):]
 		path = path[strings.Index(path, constant.PathSlash)+1:]
 	}
+	// "api/v1/test-dubbo/user?name=tc/"
+
 	if strings.HasPrefix(path, constant.PathSlash) {
 		ret = method + path
 	} else {
 		ret = method + constant.PathSlash + path
 	}
+	// "METHOD/api/v1/test-dubbo/user?name=tc/"
+
 	if strings.HasSuffix(ret, constant.PathSlash) {
 		ret = ret[0 : len(ret)-1]
 	}
+	// "METHOD/api/v1/test-dubbo/user?name=tc"
+
 	ret = strings.Split(ret, "?")[0]
+	// "METHOD/api/v1/test-dubbo/user"
+
 	return ret
 }
 
+func GetTrieKeyWithPrefix(method, path, prefix string, isPrefix bool) string {
+	if isPrefix {
+		if prefix != "" && prefix[len(prefix)-1] != '/' {
+			prefix += constant.PathSlash
+		}
+		prefix += constant.HeaderValueAllLevels
+		return GetTrieKey(method, prefix)
+	}
+	return GetTrieKey(method, path)
+}
+
 func GetIPAndPort(address string) ([]*net.TCPAddr, error) {
 	if len(address) <= 0 {
 		return nil, errors.Errorf("invalid address, %s", address)
diff --git a/pkg/config/version.go b/pkg/config/version.go
index c45167df..cf1437a7 100644
--- a/pkg/config/version.go
+++ b/pkg/config/version.go
@@ -18,5 +18,6 @@
 package config
 
 const (
+	// Version TODO: delete this if not used in the future
 	Version = "0.4.0"
 )
diff --git a/pkg/filter/accesslog/access_log.go b/pkg/filter/accesslog/access_log.go
index 0b9e1cca..2aa26e0c 100644
--- a/pkg/filter/accesslog/access_log.go
+++ b/pkg/filter/accesslog/access_log.go
@@ -76,8 +76,10 @@ func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
 
 // PrepareFilterChain prepare chain when http context init
 func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error {
+	// Make a shallow copy of the factory config to avoid sharing the factory's pointer.
+	cpConf := *factory.conf
 	f := &Filter{
-		conf: factory.conf,
+		conf: &cpConf,
 		alw:  factory.alw,
 	}
 	chain.AppendDecodeFilters(f)
diff --git a/pkg/filter/auth/jwt/jwt.go b/pkg/filter/auth/jwt/jwt.go
index f4ab3bec..28561aad 100644
--- a/pkg/filter/auth/jwt/jwt.go
+++ b/pkg/filter/auth/jwt/jwt.go
@@ -82,7 +82,8 @@ func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
 }
 
 func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error {
-	f := &Filter{cfg: factory.cfg, errMsg: factory.errMsg, providerJwks: factory.providerJwks}
+	// Deep copy config to avoid pointer sharing (factory.cfg may change at runtime)
+	f := &Filter{cfg: factory.cfg.DeepCopy(), errMsg: factory.errMsg, providerJwks: factory.providerJwks}
 	chain.AppendDecodeFilters(f)
 	return nil
 }
@@ -225,3 +226,66 @@ func checkToken(value, prefix, providerName string, provider Provider) bool {
 func (factory *FilterFactory) Config() any {
 	return factory.cfg
 }
+
+// DeepCopy returns a new independent copy of Config
+// Deep copy slices/maps to avoid sharing pointers with the factory
+func (config *Config) DeepCopy() *Config {
+	if config == nil {
+		return nil
+	}
+	cpConfig := *config
+
+	if config.Rules != nil {
+		cpConfig.Rules = make([]Rules, 0, len(config.Rules))
+		for _, rules := range config.Rules {
+			cpRules := *rules.DeepCopy()
+			cpConfig.Rules = append(cpConfig.Rules, cpRules)
+		}
+	}
+
+	if config.Providers != nil {
+		cpConfig.Providers = make([]Providers, 0, len(config.Providers))
+		for _, providers := range config.Providers {
+			cpProviders := *providers.DeepCopy()
+			cpConfig.Providers = append(cpConfig.Providers, cpProviders)
+		}
+	}
+
+	return &cpConfig
+}
+
+func (rules *Rules) DeepCopy() *Rules {
+	if rules == nil {
+		return nil
+	}
+	cpRules := *rules
+	cpRules.Requires = *rules.Requires.DeepCopy()
+	return &cpRules
+}
+
+func (providers *Providers) DeepCopy() *Providers {
+	if providers == nil {
+		return nil
+	}
+	cpProviders := *providers
+
+	if providers.Local != nil {
+		cpLocal := *providers.Local
+		cpProviders.Local = &cpLocal
+	}
+	if providers.Remote != nil {
+		cpRemote := *providers.Remote
+		cpProviders.Remote = &cpRemote
+	}
+	return &cpProviders
+}
+
+func (requires *Requires) DeepCopy() *Requires {
+	if requires == nil {
+		return nil
+	}
+	cpRequires := *requires
+	cpRequires.RequiresAll = make([]Requirement, len(requires.RequiresAll))
+	copy(cpRequires.RequiresAll, requires.RequiresAll)
+	return &cpRequires
+}
diff --git a/pkg/filter/authority/authority.go b/pkg/filter/authority/authority.go
index cf79e05d..84223ffa 100644
--- a/pkg/filter/authority/authority.go
+++ b/pkg/filter/authority/authority.go
@@ -61,7 +61,8 @@ func (factory *FilterFactory) Apply() error {
 }
 
 func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error {
-	f := &Filter{cfg: factory.cfg}
+	// Deep copy config to avoid pointer sharing (factory.cfg may change at runtime)
+	f := &Filter{cfg: factory.cfg.DeepCopy()}
 	chain.AppendDecodeFilters(f)
 	return nil
 }
diff --git a/pkg/filter/authority/config.go b/pkg/filter/authority/config.go
index d77810c0..a5625eb8 100644
--- a/pkg/filter/authority/config.go
+++ b/pkg/filter/authority/config.go
@@ -73,3 +73,43 @@ type (
 	// LimitType the authority rule limit enum
 	LimitType int32
 )
+
+// DeepCopy returns a new independent copy of Config
+// Deep copy slices/maps to avoid sharing pointers with the factory
+func (rule *AuthorityRule) DeepCopy() *AuthorityRule {
+	if rule == nil {
+		return nil
+	}
+
+	cp := *rule
+
+	if rule.Items != nil {
+		cp.Items = make([]string, len(rule.Items))
+		copy(cp.Items, rule.Items)
+	} else {
+		cp.Items = nil
+	}
+
+	return &cp
+}
+
+// DeepCopy returns a new independent copy of Config
+// Deep copy slices/maps to avoid sharing pointers with the factory
+func (ac *AuthorityConfiguration) DeepCopy() *AuthorityConfiguration {
+	if ac == nil {
+		return nil
+	}
+
+	cp := *ac
+
+	if ac.Rules != nil {
+		cp.Rules = make([]AuthorityRule, len(ac.Rules))
+		for i := range ac.Rules {
+			cp.Rules[i] = *ac.Rules[i].DeepCopy()
+		}
+	} else {
+		cp.Rules = nil
+	}
+
+	return &cp
+}
diff --git a/pkg/filter/cors/cors.go b/pkg/filter/cors/cors.go
index 780960f4..f56c2dc2 100644
--- a/pkg/filter/cors/cors.go
+++ b/pkg/filter/cors/cors.go
@@ -73,7 +73,8 @@ func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
 }
 
 func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error {
-	f := &Filter{cfg: factory.cfg}
+	// Deep copy config to avoid pointer sharing (factory.cfg may change at runtime)
+	f := &Filter{cfg: factory.cfg.DeepCopy()}
 	chain.AppendDecodeFilters(f)
 	return nil
 }
@@ -132,3 +133,22 @@ func (factory *FilterFactory) Apply() error {
 func (factory *FilterFactory) Config() any {
 	return factory.cfg
 }
+
+// DeepCopy returns a new independent copy of Config
+// Deep copy slices/maps to avoid sharing pointers with the factory
+func (config *Config) DeepCopy() *Config {
+	if config == nil {
+		return nil
+	}
+
+	cp := *config
+
+	if config.AllowOrigin != nil {
+		cp.AllowOrigin = make([]string, len(config.AllowOrigin))
+		copy(cp.AllowOrigin, config.AllowOrigin)
+	} else {
+		cp.AllowOrigin = nil
+	}
+
+	return &cp
+}
diff --git a/pkg/filter/csrf/csrf.go b/pkg/filter/csrf/csrf.go
index efedcd20..9ad2246c 100644
--- a/pkg/filter/csrf/csrf.go
+++ b/pkg/filter/csrf/csrf.go
@@ -74,7 +74,8 @@ func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
 }
 
 func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error {
-	f := &Filter{cfg: factory.cfg}
+	// Deep copy config to avoid pointer sharing (factory.cfg may change at runtime)
+	f := &Filter{cfg: factory.cfg.DeepCopy()}
 	chain.AppendDecodeFilters(f)
 	return nil
 }
@@ -137,3 +138,22 @@ func (factory *FilterFactory) Apply() error {
 func (factory *FilterFactory) Config() any {
 	return factory.cfg
 }
+
+// DeepCopy returns a new independent copy of Config
+// Deep copy slices/maps to avoid sharing pointers with the factory
+func (config *Config) DeepCopy() *Config {
+	if config == nil {
+		return nil
+	}
+
+	cp := *config
+
+	if config.IgnoreMethods != nil {
+		cp.IgnoreMethods = make([]string, len(config.IgnoreMethods))
+		copy(cp.IgnoreMethods, config.IgnoreMethods)
+	} else {
+		cp.IgnoreMethods = nil
+	}
+
+	return &cp
+}
diff --git a/pkg/filter/event/event.go b/pkg/filter/event/event.go
index 0d5cbc96..6dbd9fc4 100644
--- a/pkg/filter/event/event.go
+++ b/pkg/filter/event/event.go
@@ -63,7 +63,9 @@ func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
 }
 
 func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error {
-	f := &Filter{cfg: factory.cfg}
+	// Make a shallow copy of the factory config to avoid sharing the factory's pointer
+	cfgCopy := *factory.cfg
+	f := &Filter{cfg: &cfgCopy}
 	chain.AppendDecodeFilters(f)
 	return nil
 }
diff --git a/pkg/filter/failinject/filter.go b/pkg/filter/failinject/filter.go
index f7a664b3..c2f9ec92 100644
--- a/pkg/filter/failinject/filter.go
+++ b/pkg/filter/failinject/filter.go
@@ -57,8 +57,9 @@ func (factory *FilterFactory) Config() any {
 }
 
 func (factory *FilterFactory) PrepareFilterChain(ctx *contextHttp.HttpContext, chain filter.FilterChain) error {
+	// Deep copy config to avoid pointer sharing (factory.cfg may change at runtime)
 	f := Filter{
-		cfg: factory.cfg,
+		cfg: factory.cfg.DeepCopy(),
 	}
 	chain.AppendDecodeFilters(f)
 	return nil
@@ -151,3 +152,29 @@ func percentage(odds int) bool {
 	num := rand.Intn(100) + 1 // NOSONAR
 	return num <= odds
 }
+
+// DeepCopy returns a new independent copy of Config
+// Deep copy slices/maps to avoid sharing pointers with the factory
+func (config *Config) DeepCopy() *Config {
+	if config == nil {
+		return nil
+	}
+
+	cp := &Config{}
+
+	if config.Rules != nil {
+		cp.Rules = make(map[URI]*Rule, len(config.Rules))
+		for k, r := range config.Rules {
+			if r == nil {
+				cp.Rules[k] = nil
+			} else {
+				rr := *r
+				cp.Rules[k] = &rr
+			}
+		}
+	} else {
+		cp.Rules = nil
+	}
+
+	return cp
+}
diff --git a/pkg/filter/header/header.go b/pkg/filter/header/header.go
index 4b078eda..16062a72 100644
--- a/pkg/filter/header/header.go
+++ b/pkg/filter/header/header.go
@@ -56,7 +56,7 @@ func (p *Plugin) Kind() string {
 }
 
 func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
-	return &FilterFactory{}, nil
+	return &FilterFactory{cfg: &Config{}}, nil
 }
 
 func (factory *FilterFactory) Config() any {
@@ -68,7 +68,9 @@ func (factory *FilterFactory) Apply() error {
 }
 
 func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error {
-	f := &Filter{cfg: factory.cfg}
+	// Make a shallow copy of the factory config to avoid sharing the factory's pointer
+	cfgCopy := *factory.cfg
+	f := &Filter{cfg: &cfgCopy}
 	chain.AppendDecodeFilters(f)
 	return nil
 }
diff --git a/pkg/filter/host/host.go b/pkg/filter/host/host.go
index 233cf43c..2d7c77b4 100644
--- a/pkg/filter/host/host.go
+++ b/pkg/filter/host/host.go
@@ -55,11 +55,13 @@ func (p *Plugin) Kind() string {
 }
 
 func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
-	return &FilterFactory{}, nil
+	return &FilterFactory{cfg: &Config{}}, nil
 }
 
 func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
-	f := &Filter{cfg: factory.cfg}
+	// Make a shallow copy of the factory config to avoid sharing the factory's pointer
+	cfgCopy := *factory.cfg
+	f := &Filter{cfg: &cfgCopy}
 	chain.AppendDecodeFilters(f)
 	return nil
 }
diff --git a/pkg/filter/http/grpcproxy/grpc.go b/pkg/filter/http/grpcproxy/grpc.go
index cfedb01f..ca43e317 100644
--- a/pkg/filter/http/grpcproxy/grpc.go
+++ b/pkg/filter/http/grpcproxy/grpc.go
@@ -152,7 +152,8 @@ func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
 }
 
 func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error {
-	f := &Filter{cfg: factory.cfg, descriptor: factory.descriptor, pools: factory.pools, extReg: factory.extReg, registered: factory.registered}
+	// Deep copy config to avoid pointer sharing (factory.cfg may change at runtime)
+	f := &Filter{cfg: factory.cfg.DeepCopy(), descriptor: factory.descriptor, pools: factory.pools, extReg: factory.extReg, registered: factory.registered}
 	chain.AppendDecodeFilters(f)
 	return nil
 }
@@ -431,3 +432,34 @@ func configCheck(cfg *Config) error {
 	}
 	return nil
 }
+
+// DeepCopy returns a new independent copy of Config
+// Deep copy slices/maps to avoid sharing pointers with the factory
+func (config *Config) DeepCopy() *Config {
+	if config == nil {
+		return nil
+	}
+
+	cp := *config
+
+	if config.Rules != nil {
+		cp.Rules = make([]*Rule, len(config.Rules))
+		for i, r := range config.Rules {
+			if r == nil {
+				cp.Rules[i] = nil
+				continue
+			}
+			nr := &Rule{
+				Selector: r.Selector,
+				Match: Match{
+					Method: r.Match.Method,
+				},
+			}
+			cp.Rules[i] = nr
+		}
+	} else {
+		cp.Rules = nil
+	}
+
+	return &cp
+}
diff --git a/pkg/filter/http/loadbalancer/loadbalancer.go b/pkg/filter/http/loadbalancer/loadbalancer.go
index ffc6166f..9c4a5933 100644
--- a/pkg/filter/http/loadbalancer/loadbalancer.go
+++ b/pkg/filter/http/loadbalancer/loadbalancer.go
@@ -69,7 +69,9 @@ func (factory *FilterFactory) Apply() error {
 }
 
 func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
-	f := &Filter{cfg: factory.cfg}
+	// Make a shallow copy of the factory config to avoid sharing the factory's pointer
+	cfgCopy := *factory.cfg
+	f := &Filter{cfg: &cfgCopy}
 	chain.AppendDecodeFilters(f)
 	return nil
 }
diff --git a/pkg/filter/llm/tokenizer/tokenizer.go b/pkg/filter/llm/tokenizer/tokenizer.go
index bc9d6825..71d0c664 100644
--- a/pkg/filter/llm/tokenizer/tokenizer.go
+++ b/pkg/filter/llm/tokenizer/tokenizer.go
@@ -134,7 +134,9 @@ func (factory *FilterFactory) Config() any { return factory.cfg }
 func (factory *FilterFactory) Apply() error { return registerLLMMetrics() }
 
 func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
-	f := &Filter{cfg: factory.cfg}
+	// Make a shallow copy of the factory config to avoid sharing the factory's pointer
+	cfgCopy := *factory.cfg
+	f := &Filter{cfg: &cfgCopy}
 	chain.AppendDecodeFilters(f)
 	chain.AppendEncodeFilters(f)
 	return nil
diff --git a/pkg/filter/mcp/mcpserver/filter.go b/pkg/filter/mcp/mcpserver/filter.go
index 1a56cb75..9707c519 100644
--- a/pkg/filter/mcp/mcpserver/filter.go
+++ b/pkg/filter/mcp/mcpserver/filter.go
@@ -108,8 +108,9 @@ func (f *FilterFactory) PrepareFilterChain(_ *contexthttp.HttpContext, chain fil
 	sseHandler := transport.NewSSEHandler(sessionManager)
 	contentNegotiator := transport.NewContentNegotiator()
 
+	// Deep copy config to avoid pointer sharing (factory.cfg may change at runtime)
 	mcpFilter := &MCPServerFilter{
-		cfg:               f.cfg,
+		cfg:               f.cfg.DeepCopy(),
 		registry:          f.registry,
 		errorHandler:      NewErrorHandler(),
 		responseBuilder:   NewResponseBuilder(),
diff --git a/pkg/filter/opa/opa.go b/pkg/filter/opa/opa.go
index 56bae54d..1d6fca3f 100644
--- a/pkg/filter/opa/opa.go
+++ b/pkg/filter/opa/opa.go
@@ -99,7 +99,9 @@ func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain fi
 		return fmt.Errorf("failed to prepare OPA query: %w", err)
 	}
 
-	f := &Filter{cfg: factory.cfg, preparedQuery: &preparedQuery}
+	// Make a shallow copy of the factory config to avoid sharing the factory's pointer.
+	cfgCopy := *factory.cfg
+	f := &Filter{cfg: &cfgCopy, preparedQuery: &preparedQuery}
 	chain.AppendDecodeFilters(f)
 	return nil
 }
diff --git a/pkg/filter/prometheus/metric.go b/pkg/filter/prometheus/metric.go
index 08e9e081..ac830530 100644
--- a/pkg/filter/prometheus/metric.go
+++ b/pkg/filter/prometheus/metric.go
@@ -76,8 +76,10 @@ func (factory *FilterFactory) Apply() error {
 
 func (factory *FilterFactory) PrepareFilterChain(ctx *contextHttp.HttpContext, chain filter.FilterChain) error {
 
+	// Shallow copy config to avoid pointer sharing (factory.cfg may change at runtime)
+	cpConfig := *factory.Cfg
 	f := &Filter{
-		Cfg:  factory.Cfg,
+		Cfg:  &cpConfig,
 		Prom: factory.Prom,
 	}
 	f.Prom.SetPushGatewayUrl(f.Cfg.Rules.PushGatewayURL, f.Cfg.Rules.MetricPath)
diff --git a/pkg/filter/sentinel/circuitbreaker/circuit_breaker.go b/pkg/filter/sentinel/circuitbreaker/circuit_breaker.go
index fd22ad88..c8ad9e55 100644
--- a/pkg/filter/sentinel/circuitbreaker/circuit_breaker.go
+++ b/pkg/filter/sentinel/circuitbreaker/circuit_breaker.go
@@ -77,8 +77,9 @@ func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
 	return &FilterFactory{cfg: &Config{}}, nil
 }
 
+// Deep copy config to avoid pointer sharing (factory.cfg may change at runtime)
 func (factory *FilterFactory) PrepareFilterChain(ctx *http.HttpContext, chain filter.FilterChain) error {
-	chain.AppendDecodeFilters(&Filter{cfg: factory.cfg, matcher: factory.matcher})
+	chain.AppendDecodeFilters(&Filter{cfg: factory.cfg.DeepCopy(), matcher: factory.matcher})
 	return nil
 }
 
@@ -165,3 +166,33 @@ func loadRules(rules []*circuitbreaker.Rule) error {
 	}
 	return nil
 }
+
+// DeepCopy returns a new independent copy of Config
+// Deep copy slices/maps to avoid sharing pointers with the factory
+func (config *Config) DeepCopy() *Config {
+	if config == nil {
+		return nil
+	}
+	cpConfig := *config
+
+	if config.Resources != nil {
+		cpConfig.Resources = make([]*pkgs.Resource, len(config.Resources))
+		for index, resource := range config.Resources {
+			if resource != nil {
+				cpConfig.Resources[index] = resource.DeepCopy()
+			}
+		}
+	}
+
+	if config.Rules != nil {
+		cpConfig.Rules = make([]*circuitbreaker.Rule, len(config.Rules))
+		for index, rule := range config.Rules {
+			if rule != nil {
+				cpRule := *rule
+				cpConfig.Rules[index] = &cpRule
+			}
+		}
+	}
+
+	return &cpConfig
+}
diff --git a/pkg/filter/sentinel/config.go b/pkg/filter/sentinel/config.go
index 8fc84edc..cc672f1a 100644
--- a/pkg/filter/sentinel/config.go
+++ b/pkg/filter/sentinel/config.go
@@ -40,3 +40,22 @@ type (
 	// MatchStrategy API match strategy
 	MatchStrategy int32
 )
+
+// DeepCopy returns a new independent copy of Config
+// Deep copy slices/maps to avoid sharing pointers with the factory
+func (resource *Resource) DeepCopy() *Resource {
+	if resource == nil {
+		return nil
+	}
+	cpResource := *resource
+	if resource.Items != nil {
+		cpResource.Items = make([]*Item, len(resource.Items))
+		for index, orig := range resource.Items {
+			if orig != nil {
+				cp := *orig
+				cpResource.Items[index] = &cp
+			}
+		}
+	}
+	return &cpResource
+}
diff --git a/pkg/filter/sentinel/ratelimit/rate_limit.go b/pkg/filter/sentinel/ratelimit/rate_limit.go
index 5ea4ad91..ee7516ab 100644
--- a/pkg/filter/sentinel/ratelimit/rate_limit.go
+++ b/pkg/filter/sentinel/ratelimit/rate_limit.go
@@ -68,7 +68,8 @@ func (p *Plugin) CreateFilterFactory() (filter.HttpFilterFactory, error) {
 }
 
 func (factory *FilterFactory) PrepareFilterChain(ctx *contexthttp.HttpContext, chain filter.FilterChain) error {
-	f := &Filter{conf: factory.conf, matcher: factory.matcher}
+	// Deep copy config to avoid pointer sharing (factory.cfg may change at runtime)
+	f := &Filter{conf: factory.conf.DeepCopy(), matcher: factory.matcher}
 	chain.AppendDecodeFilters(f)
 	return nil
 }
@@ -131,3 +132,32 @@ func OnRulesUpdate(rules []*Rule) {
 		logger.Warnf("rate limit load rules err: %v", err)
 	}
 }
+
+// DeepCopy returns a new independent copy of Config
+// // Deep copy slices/maps to avoid sharing pointers with the factory
+func (config *Config) DeepCopy() *Config {
+	if config == nil {
+		return nil
+	}
+	cpConfig := *config
+	if config.Resources != nil {
+		cpConfig.Resources = make([]*pkgs.Resource, len(config.Resources))
+		for index, resource := range config.Resources {
+			if resource != nil {
+				cpConfig.Resources[index] = resource.DeepCopy()
+			}
+		}
+	}
+
+	if config.Rules != nil {
+		cpConfig.Rules = make([]*Rule, len(config.Rules))
+		for index, rule := range config.Rules {
+			if rule != nil {
+				cpRule := *rule
+				cpConfig.Rules[index] = &cpRule
+			}
+		}
+	}
+
+	return &cpConfig
+}
diff --git a/pkg/model/mcpserver.go b/pkg/model/mcpserver.go
index 45f270a4..27d89732 100644
--- a/pkg/model/mcpserver.go
+++ b/pkg/model/mcpserver.go
@@ -222,3 +222,187 @@ func GetPathParameterNames(pathTemplate string) []string {
 
 	return names
 }
+
+// DeepCopy returns a new independent copy of Config
+// Deep copy slices/maps to avoid sharing pointers with the factory
+func (config *McpServerConfig) DeepCopy() *McpServerConfig {
+	if config == nil {
+		return nil
+	}
+
+	cpConfig := *config
+
+	// Deep copy Tools
+	if config.Tools != nil {
+		cpConfig.Tools = make([]ToolConfig, len(config.Tools))
+		for i := range config.Tools {
+			cpConfig.Tools[i] = *config.Tools[i].DeepCopy()
+		}
+	}
+
+	// Deep copy Resources
+	if config.Resources != nil {
+		cpConfig.Resources = make([]ResourceConfig, len(config.Resources))
+		copy(cpConfig.Resources, config.Resources)
+	}
+
+	// Deep copy ResourceTemplates
+	if config.ResourceTemplates != nil {
+		cpConfig.ResourceTemplates = make([]ResourceTemplateConfig, len(config.ResourceTemplates))
+		for i := range config.ResourceTemplates {
+			cpConfig.ResourceTemplates[i] = *config.ResourceTemplates[i].DeepCopy()
+		}
+	}
+
+	// Deep copy Prompts
+	if config.Prompts != nil {
+		cpConfig.Prompts = make([]PromptConfig, len(config.Prompts))
+		for i := range config.Prompts {
+			cpConfig.Prompts[i] = *config.Prompts[i].DeepCopy()
+		}
+	}
+
+	return &cpConfig
+}
+
+// DeepCopy returns a new independent copy of Config
+// Deep copy slices/maps to avoid sharing pointers with the factory
+func (toolConfig *ToolConfig) DeepCopy() *ToolConfig {
+	if toolConfig == nil {
+		return nil
+	}
+	cpConfig := *toolConfig
+	cpConfig.Request = *toolConfig.Request.DeepCopy()
+
+	if toolConfig.Args != nil {
+		cpConfig.Args = make([]ArgConfig, len(toolConfig.Args))
+		for index := range toolConfig.Args {
+			argPtr := &toolConfig.Args[index]
+			cpConfig.Args[index] = *argPtr.DeepCopy()
+		}
+	}
+
+	return &cpConfig
+}
+
+// DeepCopy returns a new independent copy of Config
+// Deep copy slices/maps to avoid sharing pointers with the factory
+func (config *RequestConfig) DeepCopy() *RequestConfig {
+	if config == nil {
+		return nil
+	}
+	cpConfig := *config
+	if config.Headers != nil {
+		cpConfig.Headers = make(map[string]string, len(config.Headers))
+		for k, v := range config.Headers {
+			cpConfig.Headers[k] = v
+		}
+	}
+
+	return &cpConfig
+}
+
+// DeepCopy returns a new independent copy of Config
+// Deep copy slices/maps to avoid sharing pointers with the factory
+func (config *ArgConfig) DeepCopy() *ArgConfig {
+	if config == nil {
+		return nil
+	}
+	cpConfig := *config
+	if config.Enum != nil {
+		cpConfig.Enum = make([]string, len(config.Enum))
+		copy(cpConfig.Enum, config.Enum)
+	}
+	return &cpConfig
+}
+
+// DeepCopy returns a new independent copy of Config
+// Deep copy slices/maps to avoid sharing pointers with the factory
+func (param *ResourceTemplateParameter) DeepCopy() *ResourceTemplateParameter {
+	if param == nil {
+		return nil
+	}
+
+	cpParam := *param
+
+	if param.Enum != nil {
+		cpParam.Enum = make([]string, len(param.Enum))
+		copy(cpParam.Enum, param.Enum)
+	}
+
+	return &cpParam
+}
+
+// DeepCopy returns a new independent copy of Config
+// Deep copy slices/maps to avoid sharing pointers with the factory
+func (ann *ResourceTemplateAnnotations) DeepCopy() *ResourceTemplateAnnotations {
+	if ann == nil {
+		return nil
+	}
+
+	cpAnn := *ann
+
+	// Deep copy Audience slice
+	if ann.Audience != nil {
+		cpAnn.Audience = make([]string, len(ann.Audience))
+		copy(cpAnn.Audience, ann.Audience)
+	}
+
+	// Deep copy Priority pointer
+	if ann.Priority != nil {
+		p := *ann.Priority
+		cpAnn.Priority = &p
+	}
+
+	return &cpAnn
+}
+
+// DeepCopy returns a new independent copy of Config
+// Deep copy slices/maps to avoid sharing pointers with the factory
+func (config *ResourceTemplateConfig) DeepCopy() *ResourceTemplateConfig {
+	if config == nil {
+		return nil
+	}
+
+	cpConfig := *config
+
+	// Deep copy Parameters slice
+	if config.Parameters != nil {
+		cpConfig.Parameters = make([]ResourceTemplateParameter, len(config.Parameters))
+		for i := range config.Parameters {
+			paramPtr := &config.Parameters[i]
+			cpConfig.Parameters[i] = *paramPtr.DeepCopy()
+		}
+	}
+
+	// Deep copy Annotations pointer
+	if config.Annotations != nil {
+		cpConfig.Annotations = config.Annotations.DeepCopy()
+	}
+
+	return &cpConfig
+}
+
+// DeepCopy returns a new independent copy of Config
+// Deep copy slices/maps to avoid sharing pointers with the factory
+func (config *PromptConfig) DeepCopy() *PromptConfig {
+	if config == nil {
+		return nil
+	}
+
+	cp := *config
+
+	// Deep copy Arguments slice
+	if config.Arguments != nil {
+		cp.Arguments = make([]PromptArgumentConfig, len(config.Arguments))
+		copy(cp.Arguments, config.Arguments)
+	}
+
+	// Deep copy Messages slice
+	if config.Messages != nil {
+		cp.Messages = make([]PromptMessageConfig, len(config.Messages))
+		copy(cp.Messages, config.Messages)
+	}
+
+	return &cp
+}
diff --git a/pkg/model/router.go b/pkg/model/router.go
index d3e7fadb..ff4f6a9e 100644
--- a/pkg/model/router.go
+++ b/pkg/model/router.go
@@ -97,7 +97,7 @@ func (rc *RouteConfiguration) Route(req *stdHttp.Request) (*RouteAction, error)
 	return rc.RouteByPathAndMethod(req.URL.Path, req.Method)
 }
 
-// MatchHeader used when there's only headers to match
+// MatchHeader used when there are only headers to match
 func (rm *RouterMatch) MatchHeader(req *stdHttp.Request) bool {
 	if len(rm.Methods) > 0 {
 		for _, method := range rm.Methods {
@@ -146,7 +146,12 @@ func (hm *HeaderMatcher) SetValueRegex(regex string) error {
 func (r *Router) String() string {
 	var builder strings.Builder
 	builder.WriteString("[" + strings.Join(r.Match.Methods, ",") + "] ")
-	if r.Match.Prefix != "" {
+	if r.Match.Path == "" && r.Match.Prefix == "" && len(r.Match.Headers) > 0 {
+		builder.WriteString("headers ")
+		for _, h := range r.Match.Headers {
+			builder.WriteString(h.Name + "=" + strings.Join(h.Values, "|"))
+		}
+	} else if r.Match.Prefix != "" {
 		builder.WriteString("prefix " + r.Match.Prefix)
 	} else {
 		builder.WriteString("path " + r.Match.Path)
diff --git a/pkg/model/router_snapshot.go b/pkg/model/router_snapshot.go
new file mode 100644
index 00000000..bae890cd
--- /dev/null
+++ b/pkg/model/router_snapshot.go
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package model
+
+import (
+	"regexp"
+	"sync"
+)
+
+import (
+	"github.com/apache/dubbo-go-pixiu/pkg/common/router/trie"
+	"github.com/apache/dubbo-go-pixiu/pkg/common/util/stringutil"
+	"github.com/apache/dubbo-go-pixiu/pkg/logger"
+)
+
+var (
+	constMethods = []string{"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"}
+)
+
+// RouteSnapshot Read-only snapshot for routing
+type RouteSnapshot struct {
+	// multi-trie for each method, built once and read-only
+	MethodTries map[string]*trie.Trie
+
+	// precompiled regex for header-only routes
+	HeaderOnly []HeaderRoute
+}
+
+type HeaderRoute struct {
+	Methods []string
+	Headers []CompiledHeader
+	Action  RouteAction
+}
+
+type CompiledHeader struct {
+	Name   string
+	Regex  *regexp.Regexp
+	Values []string
+}
+
+func MethodAllowed(methods []string, m string) bool {
+	if len(methods) == 0 {
+		return true
+	}
+	for _, x := range methods {
+		if x == m {
+			return true
+		}
+	}
+	return false
+}
+
+var regexCache sync.Map // map[string]*regexp.Regexp
+
+func getRegexpWithCache(pat string) *regexp.Regexp {
+	if v, ok := regexCache.Load(pat); ok {
+		return v.(*regexp.Regexp)
+	}
+	// Compile fail return nil (caller will ignore this regex)
+	re, err := regexp.Compile(pat)
+	if err != nil {
+		return nil
+	}
+	if v, ok := regexCache.LoadOrStore(pat, re); ok {
+		return v.(*regexp.Regexp)
+	}
+	return re
+}
+
+// compiledHeaderSlicePool is a pool for temporary []CompiledHeader slices during snapshot building
+var compiledHeaderSlicePool = sync.Pool{
+	New: func() any {
+		s := make([]CompiledHeader, 0, 4) // start with small capacity, grow as needed
+		return &s
+	},
+}
+
+func ToSnapshot(routes []*Router) *RouteSnapshot {
+	s := &RouteSnapshot{
+		MethodTries: make(map[string]*trie.Trie, 8),
+	}
+
+	// pre-scan header-only routes count
+	headerOnlyCount := 0
+	for _, r := range routes {
+		if r.Match.Path == "" && r.Match.Prefix == "" && len(r.Match.Headers) > 0 {
+			headerOnlyCount++
+		}
+	}
+
+	if headerOnlyCount > 0 {
+		s.HeaderOnly = make([]HeaderRoute, 0, headerOnlyCount)
+	}
+
+	// part to get or create trie for a method
+	getTrie := func(m string) *trie.Trie {
+		if t := s.MethodTries[m]; t != nil {
+			return t
+		}
+		nt := trie.NewTrie()
+		s.MethodTries[m] = &nt
+		return &nt
+	}
+
+	for _, r := range routes {
+		// A) header-only：with Headers, without Path / Prefix
+		if r.Match.Path == "" && r.Match.Prefix == "" && len(r.Match.Headers) > 0 {
+			hr := HeaderRoute{
+				Methods: r.Match.Methods,
+				Action:  r.Route,
+			}
+
+			// use temporary slice from pool to build compiled headers
+			chPtr := compiledHeaderSlicePool.Get().(*[]CompiledHeader)
+			ch := (*chPtr)[:0] // reset
+
+			for _, h := range r.Match.Headers {
+				c := CompiledHeader{Name: h.Name}
+				if h.Regex {
+					// 1) the model already has compiled regex (if any) → use it directly
+					if h.valueRE != nil {
+						c.Regex = h.valueRE
+					} else if len(h.Values) > 0 && h.Values[0] != "" {
+						// 2) else use global cache/compile (cross-snapshot reuse)
+						if re := getRegexpWithCache(h.Values[0]); re != nil {
+							c.Regex = re
+						} else {
+							// invalid regex → skip this header matcher
+							logger.Errorf("Header regex compiled fail for %v", h.Values[0])
+							continue
+						}
+					}
+				} else {
+					// not regex → copy values directly (if any)
+					if len(h.Values) > 0 {
+						// direct assignment is ok here (string slice)
+						c.Values = append(c.Values, h.Values...)
+					}
+				}
+				ch = append(ch, c)
+			}
+
+			// move the temporary slice content to snapshot (ownership transferred)
+			hr.Headers = make([]CompiledHeader, len(ch))
+			copy(hr.Headers, ch)
+
+			// reset and put back the temporary slice to pool
+			*chPtr = (*chPtr)[:0]
+			compiledHeaderSlicePool.Put(chPtr)
+
+			s.HeaderOnly = append(s.HeaderOnly, hr)
+			continue
+		}
+
+		// B) Trie
+		methods := r.Match.Methods
+		if len(methods) == 0 {
+			methods = constMethods
+		}
+		for _, m := range methods {
+			t := getTrie(m)
+			t.Put(stringutil.GetTrieKeyWithPrefix(m, r.Match.Path, r.Match.Prefix, r.Match.Prefix != ""), r.Route)
+		}
+	}
+	return s
+}
diff --git a/tools/benchmark/README.md b/tools/benchmark/README.md
index 987aca56..64e77cb0 100644
--- a/tools/benchmark/README.md
+++ b/tools/benchmark/README.md
@@ -1,5 +1,35 @@
 # Benchmark Results
 
+* dubbo:
+
+```
+dubbo protocol performance test
+      Name                 | N   | Min   | Median | Mean  | StdDev | Max   
+      =====================================================================
+      GetUser [duration]   | 500 | 100µs | 200µs  | 600µs | 2.8ms  | 20.7ms
+      ---------------------------------------------------------------------
+      GetGender [duration] | 500 | 0s    | 100µs  | 200µs | 100µs  | 600µs 
+      ---------------------------------------------------------------------
+      GetUser0 [duration]  | 500 | 100µs | 100µs  | 200µs | 0s     | 400µs 
+      ---------------------------------------------------------------------
+      GetUsers [duration]  | 500 | 100µs | 200µs  | 200µs | 100µs  | 600µs 
+      ---------------------------------------------------------------------
+      GetUser2 [duration]  | 500 | 0s    | 200µs  | 200µs | 100µs  | 500µs 
+      ---------------------------------------------------------------------
+      GetErr [duration]    | 500 | 300µs | 500µs  | 500µs | 200µs  | 1.4ms 
+
+ pixiu to dubbo protocol performance test
+      Name                 | N   | Min   | Median | Mean  | StdDev | Max  
+      ====================================================================
+      GetUser [duration]   | 500 | 200µs | 600µs  | 1.8ms | 8.4ms  | 61ms 
+      --------------------------------------------------------------------
+      GetGender [duration] | 499 | 200µs | 500µs  | 600µs | 300µs  | 4.1ms
+      --------------------------------------------------------------------
+      GetUser0 [duration]  | 497 | 200µs | 500µs  | 600µs | 300µs  | 2.5ms
+      --------------------------------------------------------------------
+      GetUsers [duration]  | 495 | 200µs | 600µs  | 700µs | 300µs  | 2.9ms
+```
+
 * grpc:
 
 ```
@@ -22,6 +52,20 @@ pixiu to grpc protocol performance test
       GetUserByName [duration] | 500 | 800µs | 1.9ms  | 2.7ms | 3.8ms  | 33.4ms
 ```
 
+* triple:
+
+```
+triple protocol performance test
+      Name                | N   | Min   | Median | Mean  | StdDev | Max  
+      ===================================================================
+      SayHello [duration] | 500 | 100µs | 200µs  | 300µs | 300µs  | 2.6ms
+
+pixiu to triple protocol performance test
+      Name                | N   | Min    | Median | Mean | StdDev | Max   
+      ====================================================================
+      SayHello [duration] | 490 | 10.1ms | 12.7ms | 14ms | 4.1ms  | 43.3ms
+```
+
 # How to Run
 
 1. Build the Pixiu executable
diff --git a/tools/benchmark/README_CN.md b/tools/benchmark/README_CN.md
index 94b24c8b..53cfc147 100644
--- a/tools/benchmark/README_CN.md
+++ b/tools/benchmark/README_CN.md
@@ -1,5 +1,35 @@
 # Benchmark 结果
 
+* dubbo:
+
+```
+dubbo protocol performance test
+      Name                 | N   | Min   | Median | Mean  | StdDev | Max   
+      =====================================================================
+      GetUser [duration]   | 500 | 100µs | 200µs  | 600µs | 2.8ms  | 20.7ms
+      ---------------------------------------------------------------------
+      GetGender [duration] | 500 | 0s    | 100µs  | 200µs | 100µs  | 600µs 
+      ---------------------------------------------------------------------
+      GetUser0 [duration]  | 500 | 100µs | 100µs  | 200µs | 0s     | 400µs 
+      ---------------------------------------------------------------------
+      GetUsers [duration]  | 500 | 100µs | 200µs  | 200µs | 100µs  | 600µs 
+      ---------------------------------------------------------------------
+      GetUser2 [duration]  | 500 | 0s    | 200µs  | 200µs | 100µs  | 500µs 
+      ---------------------------------------------------------------------
+      GetErr [duration]    | 500 | 300µs | 500µs  | 500µs | 200µs  | 1.4ms 
+
+ pixiu to dubbo protocol performance test
+      Name                 | N   | Min   | Median | Mean  | StdDev | Max  
+      ====================================================================
+      GetUser [duration]   | 500 | 200µs | 600µs  | 1.8ms | 8.4ms  | 61ms 
+      --------------------------------------------------------------------
+      GetGender [duration] | 499 | 200µs | 500µs  | 600µs | 300µs  | 4.1ms
+      --------------------------------------------------------------------
+      GetUser0 [duration]  | 497 | 200µs | 500µs  | 600µs | 300µs  | 2.5ms
+      --------------------------------------------------------------------
+      GetUsers [duration]  | 495 | 200µs | 600µs  | 700µs | 300µs  | 2.9ms
+```
+
 * grpc:
 
 ```
@@ -22,6 +52,20 @@ pixiu to grpc protocol performance test
       GetUserByName [duration] | 500 | 800µs | 1.9ms  | 2.7ms | 3.8ms  | 33.4ms
 ```
 
+* triple:
+
+```
+triple protocol performance test
+      Name                | N   | Min   | Median | Mean  | StdDev | Max  
+      ===================================================================
+      SayHello [duration] | 500 | 100µs | 200µs  | 300µs | 300µs  | 2.6ms
+
+pixiu to triple protocol performance test
+      Name                | N   | Min    | Median | Mean | StdDev | Max   
+      ====================================================================
+      SayHello [duration] | 490 | 10.1ms | 12.7ms | 14ms | 4.1ms  | 43.3ms
+```
+
 # 运行方法
 
 1. 构建 pixiu 可执行文件
diff --git a/tools/benchmark/protocol/dubbo/go-server/conf/dubbogo.yml b/tools/benchmark/protocol/dubbo/go-server/conf/dubbogo.yml
index 5f770fa0..eed0d783 100644
--- a/tools/benchmark/protocol/dubbo/go-server/conf/dubbogo.yml
+++ b/tools/benchmark/protocol/dubbo/go-server/conf/dubbogo.yml
@@ -25,11 +25,13 @@ dubbo:
       protocol: zookeeper
       timeout: 3s
       address: 127.0.0.1:2181
+      registry-type: all
   protocols:
     dubbo:
       name: dubbo
       port: 20000
   provider:
+    registry-ids: demoZK
     services:
       UserProvider:
         interface: org.apache.dubbo.sample.UserProvider
diff --git a/tools/benchmark/protocol/triple/pb/go-server/conf/dubbogo.yml b/tools/benchmark/protocol/triple/pb/go-server/conf/dubbogo.yml
index e26721f0..988845f1 100644
--- a/tools/benchmark/protocol/triple/pb/go-server/conf/dubbogo.yml
+++ b/tools/benchmark/protocol/triple/pb/go-server/conf/dubbogo.yml
@@ -21,11 +21,13 @@ dubbo:
   registries:
     zk:
       address: zookeeper://127.0.0.1:2181
+      registry-type: all
   protocols:
     triple:
       name: tri
       port: 20001
   provider:
+    registry-ids: zk
     services:
       GreeterProvider:
         protocol-ids: triple # 使用上面定义的协议id



Test patch:
diff --git a/pkg/common/http/manager_test.go b/pkg/common/http/manager_test.go
index 33080f38..69cb4458 100644
--- a/pkg/common/http/manager_test.go
+++ b/pkg/common/http/manager_test.go
@@ -39,7 +39,6 @@ import (
 	"github.com/apache/dubbo-go-pixiu/pkg/common/constant"
 	"github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter"
 	commonmock "github.com/apache/dubbo-go-pixiu/pkg/common/mock"
-	"github.com/apache/dubbo-go-pixiu/pkg/common/router/trie"
 	contexthttp "github.com/apache/dubbo-go-pixiu/pkg/context/http"
 	"github.com/apache/dubbo-go-pixiu/pkg/context/mock"
 	"github.com/apache/dubbo-go-pixiu/pkg/logger"
@@ -121,10 +120,19 @@ func TestCreateHttpConnectionManager(t *testing.T) {
 
 	hcmc := model.HttpConnectionManagerConfig{
 		RouteConfig: model.RouteConfiguration{
-			RouteTrie: trie.NewTrieWithDefault("POST/api/v1/**", model.RouteAction{
-				Cluster:                     "test_dubbo",
-				ClusterNotFoundResponseCode: 505,
-			}),
+			Routes: []*model.Router{
+				{
+					ID: "1",
+					Match: model.RouterMatch{
+						Path:    "/api/v1/**",
+						Methods: []string{"POST"},
+					},
+					Route: model.RouteAction{
+						Cluster:                     "test_dubbo",
+						ClusterNotFoundResponseCode: 505,
+					},
+				},
+			},
 			Dynamic: false,
 		},
 		HTTPFilters: []*model.HTTPFilter{
@@ -157,9 +165,20 @@ func TestCreateHttpConnectionManager(t *testing.T) {
 func TestStreamingResponse(t *testing.T) {
 	hcmc := model.HttpConnectionManagerConfig{
 		RouteConfig: model.RouteConfiguration{
-			RouteTrie: trie.NewTrieWithDefault("GET/api/sse", model.RouteAction{
-				Cluster: "mock_stream_cluster",
-			}),
+			Routes: []*model.Router{
+				{
+					ID: "1",
+					Match: model.RouterMatch{
+						Path:    "/api/sse",
+						Methods: []string{"GET"},
+					},
+					Route: model.RouteAction{
+						Cluster:                     "mock_stream_cluster",
+						ClusterNotFoundResponseCode: 505,
+					},
+				},
+			},
+			Dynamic: false,
 		},
 		HTTPFilters: []*model.HTTPFilter{
 			{
@@ -360,9 +379,20 @@ func TestStreamableHTTPResponse(t *testing.T) {
 func testStreamableResponse(t *testing.T, contentType string) {
 	hcmc := model.HttpConnectionManagerConfig{
 		RouteConfig: model.RouteConfiguration{
-			RouteTrie: trie.NewTrieWithDefault("GET/api/stream", model.RouteAction{
-				Cluster: "mock_stream_cluster",
-			}),
+			Routes: []*model.Router{
+				{
+					ID: "1",
+					Match: model.RouterMatch{
+						Path:    "/api/stream",
+						Methods: []string{"GET"},
+					},
+					Route: model.RouteAction{
+						Cluster:                     "mock_stream_cluster",
+						ClusterNotFoundResponseCode: 505,
+					},
+				},
+			},
+			Dynamic: false,
 		},
 		HTTPFilters: []*model.HTTPFilter{
 			{
diff --git a/pkg/common/router/router_bench_test.go b/pkg/common/router/router_bench_test.go
new file mode 100644
index 00000000..e44599d8
--- /dev/null
+++ b/pkg/common/router/router_bench_test.go
@@ -0,0 +1,504 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package router
+
+import (
+	"math/rand"
+	stdHttp "net/http"
+	"reflect"
+	"strconv"
+	"testing"
+	"time"
+)
+
+import (
+	oldrouter "github.com/apache/dubbo-go-pixiu/pkg/common/router/mock"
+	"github.com/apache/dubbo-go-pixiu/pkg/context/http"
+	"github.com/apache/dubbo-go-pixiu/pkg/model"
+)
+
+/*
+==============================
+this is the benchmark for router
+contrast oldrouter and newrouter
+oldrouter: "github.com/apache/dubbo-go-pixiu/pkg/common/router/mock"
+newrouter: "github.com/apache/dubbo-go-pixiu/pkg/common/router"
+==============================
+*/
+type benchShape struct {
+	NRoutes         int     // router number
+	PrefixRatio     float64 // prefix router ratio（others are accurate path）
+	HeaderOnlyRatio float64 // header only router ratio（without path/prefix）
+	Methods         []string
+}
+
+func buildOldCoordinator(routes []*model.Router) *oldrouter.RouterCoordinator {
+	cfg := &model.RouteConfiguration{
+		Routes:  routes,
+		Dynamic: false,
+	}
+	return oldrouter.CreateRouterCoordinator(cfg)
+}
+
+func genRoutes(sh benchShape) []*model.Router {
+	routes := make([]*model.Router, 0, sh.NRoutes)
+	if len(sh.Methods) == 0 {
+		sh.Methods = []string{"GET", "POST"}
+	}
+	nHeader := int(float64(sh.NRoutes) * sh.HeaderOnlyRatio)
+	nPrefix := int(float64(sh.NRoutes-nHeader) * sh.PrefixRatio)
+	nPath := sh.NRoutes - nHeader - nPrefix
+
+	// 1) Header-only
+	for i := 0; i < nHeader; i++ {
+		id := "hdr-" + strconv.Itoa(i)
+		r := &model.Router{
+			ID: id,
+			Match: model.RouterMatch{
+				Methods: sh.Methods,
+				Headers: []model.HeaderMatcher{
+					{Name: "X-Env", Values: []string{"prod"}, Regex: false},
+				},
+			},
+			Route: model.RouteAction{Cluster: "c-h-" + id},
+		}
+		routes = append(routes, r)
+	}
+	// 2) Prefix routes
+	for i := 0; i < nPrefix; i++ {
+		id := "pre-" + strconv.Itoa(i)
+		p := "/api/v1/service" + strconv.Itoa(i%50) + "/"
+		r := &model.Router{
+			ID: id,
+			Match: model.RouterMatch{
+				Methods: sh.Methods,
+				Prefix:  p,
+			},
+			Route: model.RouteAction{Cluster: "c-p-" + id},
+		}
+		routes = append(routes, r)
+	}
+	// 3) Exact path
+	for i := 0; i < nPath; i++ {
+		id := "pth-" + strconv.Itoa(i)
+		pp := "/api/v1/item/" + strconv.Itoa(i)
+		r := &model.Router{
+			ID: id,
+			Match: model.RouterMatch{
+				Methods: sh.Methods,
+				Path:    pp,
+			},
+			Route: model.RouteAction{Cluster: "c-x-" + id},
+		}
+		routes = append(routes, r)
+	}
+	return routes
+}
+
+func buildNewCoordinator(routes []*model.Router) *RouterCoordinator {
+	cfg := &model.RouteConfiguration{
+		Routes:  routes,
+		Dynamic: false,
+	}
+	return CreateRouterCoordinator(cfg)
+}
+
+func buildDelta(base []*model.Router, seed int64) []*model.Router {
+	cp := make([]*model.Router, len(base))
+	copy(cp, base)
+	rnd := rand.New(rand.NewSource(seed))
+	k := len(cp) / 100 // 1%
+	out := make([]*model.Router, 0, k)
+	for i := 0; i < k; i++ {
+		idx := rnd.Intn(len(cp)) // NOSONAR
+		old := cp[idx]
+		newPath := "/api/v1/item/" + strconv.Itoa(rnd.Intn(100000)) // NOSONAR
+		nr := &model.Router{
+			ID: old.ID,
+			Match: model.RouterMatch{
+				Methods: old.Match.Methods,
+				Path:    newPath,
+				Headers: old.Match.Headers,
+			},
+			Route: old.Route,
+		}
+		out = append(out, nr)
+	}
+	return out
+}
+
+func genRequests(n int) []*stdHttp.Request {
+	reqs := make([]*stdHttp.Request, 0, n)
+	methods := []string{"GET", "POST"}
+	for i := 0; i < n; i++ {
+		var path string
+		switch i % 3 {
+		case 0:
+			path = "/api/v1/item/" + strconv.Itoa(i%10000)
+		case 1:
+			path = "/api/v1/service" + strconv.Itoa(i%50) + "/foo/bar"
+		default:
+			path = "/unknown/" + strconv.Itoa(i)
+		}
+		req, _ := stdHttp.NewRequest(methods[i%len(methods)], path, nil)
+		if i%5 == 0 { // trigger header-only route
+			req.Header.Set("X-Env", "prod")
+		}
+		reqs = append(reqs, req)
+	}
+	return reqs
+}
+
+// helper: assert Route behavior of old/new is the same on a set of requests
+func assertRouteSame(b testing.TB, oldc *oldrouter.RouterCoordinator, newc *RouterCoordinator, reqs []*stdHttp.Request) {
+	b.Helper()
+	for i, r := range reqs {
+		ctxOld := http.HttpContext{Request: r}
+		ctxNew := http.HttpContext{Request: r}
+
+		oldRes, oldErr := oldc.Route(&ctxOld)
+		newRes, newErr := newc.Route(&ctxNew)
+
+		if (oldErr != nil && newErr == nil) || (oldErr == nil && newErr != nil) {
+			b.Fatalf("route error text mismatch on #%d path=%s method=%s: oldErr=%v newErr=%v",
+				i, r.URL.Path, r.Method, oldErr, newErr)
+		}
+		if !reflect.DeepEqual(oldRes, newRes) {
+			b.Fatalf("route result mismatch on #%d path=%s method=%s: old=%#v new=%#v",
+				i, r.URL.Path, r.Method, oldRes, newRes)
+		}
+	}
+}
+
+// helper: assert RouteByPathAndName behavior is the same
+func assertRouteByPathAndNameSame(b testing.TB, oldc *oldrouter.RouterCoordinator, newc *RouterCoordinator, paths []string, method string) {
+	b.Helper()
+	for i, p := range paths {
+		oldRes, oldErr := oldc.RouteByPathAndName(p, method)
+		newRes, newErr := newc.RouteByPathAndName(p, method)
+
+		if (oldErr != nil && newErr == nil) || (oldErr == nil && newErr != nil) {
+			b.Fatalf("route error text mismatch on #%d path=%s method=%s: oldRes=%v oldErr=%v  newRes=%v newErr=%v",
+				i, p, method, oldRes, oldErr, newRes, newErr)
+		}
+		if !reflect.DeepEqual(oldRes, newRes) {
+			b.Fatalf("RouteByPathAndName result mismatch on #%d path=%s method=%s: old=%#v new=%#v",
+				i, p, method, oldRes, newRes)
+		}
+	}
+}
+
+// ============= Bench 1：read throughput (one goroutine) =============
+
+func BenchmarkRouteReadThroughput(b *testing.B) {
+	shape := benchShape{NRoutes: 30000, PrefixRatio: 0.4, HeaderOnlyRatio: 0.1, Methods: []string{"GET", "POST"}}
+
+	oldRoutes := genRoutes(shape)
+	newRoutes := genRoutes(shape)
+	reqs := genRequests(4096)
+
+	oldc := buildOldCoordinator(oldRoutes)
+	newc := buildNewCoordinator(newRoutes)
+
+	assertRouteSame(b, oldc, newc, reqs)
+
+	b.Run("old/locked-read-30k", func(b *testing.B) {
+		b.ReportAllocs()
+		b.ResetTimer()
+		for i := 0; i < b.N; i++ {
+			r := reqs[i%len(reqs)]
+			httpContext := http.HttpContext{
+				Request: r,
+			}
+			_, _ = oldc.Route(&httpContext)
+		}
+	})
+
+	b.Run("new/rcu-read-30k", func(b *testing.B) {
+		b.ReportAllocs()
+		b.ResetTimer()
+		for i := 0; i < b.N; i++ {
+			r := reqs[i%len(reqs)]
+			httpContext := http.HttpContext{
+				Request: r,
+			}
+			_, _ = newc.Route(&httpContext)
+		}
+	})
+}
+
+// ============= Bench 2：read throughput (parallel) =============
+
+func BenchmarkRouteReadParallel(b *testing.B) {
+	shape := benchShape{NRoutes: 30000, PrefixRatio: 0.4, HeaderOnlyRatio: 0.1, Methods: []string{"GET", "POST"}}
+
+	oldRoutes := genRoutes(shape)
+	newRoutes := genRoutes(shape)
+	reqs := genRequests(8192)
+
+	oldc := buildOldCoordinator(oldRoutes)
+	newc := buildNewCoordinator(newRoutes)
+
+	assertRouteSame(b, oldc, newc, reqs)
+
+	b.Run("old/parallel-30k", func(b *testing.B) {
+		b.ReportAllocs()
+		b.SetParallelism(40)
+		b.ResetTimer()
+		b.RunParallel(func(pb *testing.PB) {
+			i := rand.Int() // NOSONAR
+			for pb.Next() {
+				r := reqs[i%len(reqs)]
+				httpContext := http.HttpContext{
+					Request: r,
+				}
+				_, _ = oldc.Route(&httpContext)
+				i++
+			}
+		})
+	})
+
+	b.Run("new/parallel-30k", func(b *testing.B) {
+		b.ReportAllocs()
+		b.SetParallelism(40)
+		b.ResetTimer()
+		b.RunParallel(func(pb *testing.PB) {
+			i := rand.Int() // NOSONAR
+			for pb.Next() {
+				r := reqs[i%len(reqs)]
+				httpContext := http.HttpContext{
+					Request: r,
+				}
+				_, _ = newc.Route(&httpContext)
+				i++
+			}
+		})
+	})
+}
+
+// ============= Bench 3：read and write（1% write） =============
+
+func BenchmarkReloadLatency(b *testing.B) {
+	shape := benchShape{NRoutes: 30000, PrefixRatio: 0.4, HeaderOnlyRatio: 0.1, Methods: []string{"GET", "POST"}}
+	base := genRoutes(shape)
+
+	oldc := buildOldCoordinator(base)
+	newc := buildNewCoordinator(base)
+
+	{
+		checkOld := buildOldCoordinator(base)
+		checkNew := buildNewCoordinator(base)
+		deltaOld := buildDelta(base, 1)
+		for i := range deltaOld {
+			checkOld.OnAddRouter(deltaOld[i])
+			checkNew.OnAddRouter(deltaOld[i])
+		}
+		reqs := genRequests(1024)
+		time.Sleep(100 * time.Millisecond)
+		assertRouteSame(b, checkOld, checkNew, reqs)
+	}
+
+	b.Run("old/reload-1percent-30k", func(b *testing.B) {
+		b.ReportAllocs()
+		b.ResetTimer()
+		for i := 0; i < b.N; i++ {
+			for _, r := range buildDelta(base, int64(i)) {
+				oldc.OnAddRouter(r)
+			}
+		}
+	})
+
+	b.Run("new/reload-1percent-30k", func(b *testing.B) {
+		b.ReportAllocs()
+		b.ResetTimer()
+		for i := 0; i < b.N; i++ {
+			for _, r := range buildDelta(base, int64(i)) {
+				newc.OnAddRouter(r)
+			}
+		}
+	})
+}
+
+func BenchmarkRoute100kReadThroughput(b *testing.B) {
+	shape := benchShape{
+		NRoutes:         100_000,
+		PrefixRatio:     0.4,
+		HeaderOnlyRatio: 0.1,
+		Methods:         []string{"GET", "POST"},
+	}
+	reqs := genRequests(16_384)
+
+	oldc := buildOldCoordinator(genRoutes(shape))
+	newc := buildNewCoordinator(genRoutes(shape))
+
+	assertRouteSame(b, oldc, newc, reqs)
+
+	b.Run("old/locked-read-100k", func(b *testing.B) {
+		b.ReportAllocs()
+		for i := 0; i < b.N; i++ {
+			r := reqs[i%len(reqs)]
+			httpContext := http.HttpContext{
+				Request: r,
+			}
+			_, _ = oldc.Route(&httpContext)
+		}
+	})
+	b.Run("new/rcu-read-100k", func(b *testing.B) {
+		b.ReportAllocs()
+		for i := 0; i < b.N; i++ {
+			r := reqs[i%len(reqs)]
+			httpContext := http.HttpContext{
+				Request: r,
+			}
+			_, _ = newc.Route(&httpContext)
+		}
+	})
+}
+
+func BenchmarkRoute100kReadParallel(b *testing.B) {
+	shape := benchShape{
+		NRoutes:         100_000,
+		PrefixRatio:     0.4,
+		HeaderOnlyRatio: 0.1,
+		Methods:         []string{"GET", "POST"},
+	}
+	reqs := genRequests(32_768)
+
+	oldc := buildOldCoordinator(genRoutes(shape))
+	newc := buildNewCoordinator(genRoutes(shape))
+
+	assertRouteSame(b, oldc, newc, reqs)
+
+	b.Run("old/parallel-100k", func(b *testing.B) {
+		b.ReportAllocs()
+		b.SetParallelism(4)
+		b.RunParallel(func(pb *testing.PB) {
+			i := 0
+			for pb.Next() {
+				r := reqs[i&(len(reqs)-1)]
+				httpContext := http.HttpContext{
+					Request: r,
+				}
+				_, _ = oldc.Route(&httpContext)
+				i++
+			}
+		})
+	})
+	b.Run("new/parallel-100k", func(b *testing.B) {
+		b.ReportAllocs()
+		b.SetParallelism(4)
+		b.RunParallel(func(pb *testing.PB) {
+			i := 0
+			for pb.Next() {
+				r := reqs[i&(len(reqs)-1)]
+				httpContext := http.HttpContext{
+					Request: r,
+				}
+				_, _ = newc.Route(&httpContext)
+				i++
+			}
+		})
+	})
+}
+
+func BenchmarkReload100kLatency1Percent(b *testing.B) {
+	shape := benchShape{
+		NRoutes:         100_000,
+		PrefixRatio:     0.4,
+		HeaderOnlyRatio: 0.1,
+		Methods:         []string{"GET", "POST"},
+	}
+	base := genRoutes(shape)
+
+	oldc := buildOldCoordinator(genRoutes(shape))
+	newc := buildNewCoordinator(genRoutes(shape))
+
+	{
+		checkOld := buildOldCoordinator(base)
+		checkNew := buildNewCoordinator(base)
+		deltaOld := buildDelta(base, 1)
+		for i := range deltaOld {
+			checkOld.OnAddRouter(deltaOld[i])
+			checkNew.OnAddRouter(deltaOld[i])
+		}
+		reqs := genRequests(2048)
+		time.Sleep(100 * time.Millisecond)
+		assertRouteSame(b, checkOld, checkNew, reqs)
+	}
+
+	b.Run("old/reload-1percent-100k", func(b *testing.B) {
+		b.ReportAllocs()
+		b.ResetTimer()
+		for i := 0; i < b.N; i++ {
+			for _, r := range buildDelta(base, int64(i)) {
+				oldc.OnAddRouter(r)
+			}
+		}
+	})
+
+	b.Run("new/reload-1percent-100k", func(b *testing.B) {
+		b.ReportAllocs()
+		b.ResetTimer()
+		for i := 0; i < b.N; i++ {
+			for _, r := range buildDelta(base, int64(i)) {
+				newc.OnAddRouter(r)
+			}
+		}
+	})
+}
+
+// ============= Bench 4：RouteByPathAndName（API behavior must same） =============
+
+func BenchmarkRouteByPathAndName(b *testing.B) {
+	shape := benchShape{
+		NRoutes:         20000,
+		PrefixRatio:     0.5,
+		HeaderOnlyRatio: 0.0,
+		Methods:         []string{"GET"},
+	}
+
+	oldc := buildOldCoordinator(genRoutes(shape))
+	newc := buildNewCoordinator(genRoutes(shape))
+
+	paths := []string{
+		"/api/v1/item/12345",
+		"/api/v1/service7/xxx/yyy",
+		"/no/match/path",
+	}
+	method := "GET"
+
+	assertRouteByPathAndNameSame(b, oldc, newc, paths, method)
+
+	b.Run("old/RouteByPathAndName", func(b *testing.B) {
+		b.ReportAllocs()
+		b.ResetTimer()
+		for i := 0; i < b.N; i++ {
+			path := paths[i%len(paths)]
+			_, _ = oldc.RouteByPathAndName(path, method)
+		}
+	})
+
+	b.Run("new/RouteByPathAndName", func(b *testing.B) {
+		b.ReportAllocs()
+		b.ResetTimer()
+		for i := 0; i < b.N; i++ {
+			path := paths[i%len(paths)]
+			_, _ = newc.RouteByPathAndName(path, method)
+		}
+	})
+}
diff --git a/pkg/common/router/router_test.go b/pkg/common/router/router_test.go
index 46441514..f3063a00 100644
--- a/pkg/common/router/router_test.go
+++ b/pkg/common/router/router_test.go
@@ -19,7 +19,9 @@ package router
 
 import (
 	"bytes"
-	"net/http"
+	"math/rand"
+	stdHttp "net/http"
+	"strconv"
 	"testing"
 )
 
@@ -28,36 +30,24 @@ import (
 )
 
 import (
-	"github.com/apache/dubbo-go-pixiu/pkg/common/router/trie"
+	oldrouter "github.com/apache/dubbo-go-pixiu/pkg/common/router/mock"
+	"github.com/apache/dubbo-go-pixiu/pkg/context/http"
 	"github.com/apache/dubbo-go-pixiu/pkg/context/mock"
 	"github.com/apache/dubbo-go-pixiu/pkg/model"
 )
 
 func TestCreateRouterCoordinator(t *testing.T) {
-	hcmc := model.HttpConnectionManagerConfig{
-		RouteConfig: model.RouteConfiguration{
-			RouteTrie: trie.NewTrieWithDefault("POST/api/v1/**", model.RouteAction{
-				Cluster:                     "test_dubbo",
-				ClusterNotFoundResponseCode: 505,
-			}),
-			Dynamic: false,
-		},
-		HTTPFilters: []*model.HTTPFilter{
-			{
-				Name:   "test",
-				Config: nil,
-			},
-		},
-		ServerName:        "test_http_dubbo",
-		GenerateRequestID: false,
-		IdleTimeoutStr:    "100",
+	specs := []RouteSpec{
+		// exact
+		{ID: "test", Methods: []string{"POST"}, Path: "/api/v1/**", Cluster: "test_dubbo"},
 	}
 
-	r := CreateRouterCoordinator(&hcmc.RouteConfig)
-	request, err := http.NewRequest("POST", "http://www.dubbogopixiu.com/api/v1?name=tc", bytes.NewReader([]byte("{\"id\":\"12345\"}")))
+	coordinator := BuildNew(specs)
+
+	request, err := stdHttp.NewRequest("POST", "http://www.dubbogopixiu.com/api/v1?name=tc", bytes.NewReader([]byte("{\"id\":\"12345\"}")))
 	assert.NoError(t, err)
 	c := mock.GetMockHTTPContext(request)
-	a, err := r.Route(c)
+	a, err := coordinator.Route(c)
 	assert.NoError(t, err)
 	assert.Equal(t, a.Cluster, "test_dubbo")
 
@@ -71,13 +61,19 @@ func TestCreateRouterCoordinator(t *testing.T) {
 		},
 	}
 
-	r.OnAddRouter(router)
-	r.OnDeleteRouter(router)
+	coordinator.OnAddRouter(router)
+	coordinator.OnDeleteRouter(router)
 }
 
 func TestRoute(t *testing.T) {
 	const (
 		Cluster1 = "test-cluster-1"
+		Cluster2 = "test-cluster-2"
+		Cluster3 = "test-cluster-3"
+		Cluster4 = "test-cluster-4"
+		Cluster5 = "test-cluster-5"
+		Cluster6 = "test-cluster-6"
+		Cluster7 = "test-cluster-7"
 	)
 
 	hcmc := model.HttpConnectionManagerConfig{
@@ -89,16 +85,52 @@ func TestRoute(t *testing.T) {
 						Headers: []model.HeaderMatcher{
 							{
 								Name:   "A",
-								Values: []string{"1", "2", "3"},
+								Values: []string{"1", "2", "0"},
 							},
+						},
+						Methods: []string{"GET", "POST"},
+					},
+					Route: model.RouteAction{
+						Cluster:                     Cluster1,
+						ClusterNotFoundResponseCode: 505,
+					},
+				},
+				{
+					ID: "2",
+					Match: model.RouterMatch{
+						Headers: []model.HeaderMatcher{
 							{
 								Name:   "A",
 								Values: []string{"3", "4", "5"},
 							},
+						},
+						Methods: []string{"GET", "POST"},
+					},
+					Route: model.RouteAction{
+						Cluster:                     Cluster2,
+						ClusterNotFoundResponseCode: 505,
+					},
+				},
+				{
+					ID: "3",
+					Match: model.RouterMatch{
+						Headers: []model.HeaderMatcher{
 							{
 								Name:   "B",
 								Values: []string{"1"},
 							},
+						},
+						Methods: []string{"GET", "POST"},
+					},
+					Route: model.RouteAction{
+						Cluster:                     Cluster3,
+						ClusterNotFoundResponseCode: 505,
+					},
+				},
+				{
+					ID: "4",
+					Match: model.RouterMatch{
+						Headers: []model.HeaderMatcher{
 							{
 								Name:   "normal-regex",
 								Values: []string{"(k){2}"},
@@ -113,7 +145,65 @@ func TestRoute(t *testing.T) {
 						Methods: []string{"GET", "POST"},
 					},
 					Route: model.RouteAction{
-						Cluster:                     Cluster1,
+						Cluster:                     Cluster4,
+						ClusterNotFoundResponseCode: 505,
+					},
+				},
+				{
+					ID: "5",
+					Match: model.RouterMatch{
+						Headers: []model.HeaderMatcher{
+							{
+								Name:   "broken-regex",
+								Values: []string{"(t){2]]"},
+								Regex:  true,
+							},
+						},
+						Methods: []string{"GET", "POST"},
+					},
+					Route: model.RouteAction{
+						Cluster:                     Cluster5,
+						ClusterNotFoundResponseCode: 505,
+					},
+				},
+				{
+					ID: "6",
+					Match: model.RouterMatch{
+						Headers: []model.HeaderMatcher{
+							{
+								Name:   "C",
+								Values: []string{"1", "2", "0"},
+							},
+							{
+								Name:   "D",
+								Values: []string{"3", "4", "5"},
+							},
+						},
+						Methods: []string{"GET", "POST"},
+					},
+					Route: model.RouteAction{
+						Cluster:                     Cluster6,
+						ClusterNotFoundResponseCode: 505,
+					},
+				},
+				{
+					ID: "7",
+					Match: model.RouterMatch{
+						Headers: []model.HeaderMatcher{
+							{
+								Name:   "E",
+								Values: []string{"1", "2", "0"},
+							},
+							{
+								Name:   "normal-regex",
+								Values: []string{"(k){2}"},
+								Regex:  true,
+							},
+						},
+						Methods: []string{"GET", "POST"},
+					},
+					Route: model.RouteAction{
+						Cluster:                     Cluster7,
 						ClusterNotFoundResponseCode: 505,
 					},
 				},
@@ -144,7 +234,7 @@ func TestRoute(t *testing.T) {
 			Header: map[string]string{
 				"A": "1",
 			},
-			Expect: "test-cluster-1",
+			Expect: Cluster1,
 		},
 		{
 			Name: "one header matched",
@@ -152,7 +242,7 @@ func TestRoute(t *testing.T) {
 			Header: map[string]string{
 				"A": "3",
 			},
-			Expect: Cluster1,
+			Expect: Cluster2,
 		},
 		{
 			Name: "more header with one regex matched",
@@ -161,16 +251,16 @@ func TestRoute(t *testing.T) {
 				"A":            "5",
 				"normal-regex": "kkkk",
 			},
-			Expect: Cluster1,
+			Expect: Cluster2,
 		},
 		{
 			Name:   "one header but wrong method",
 			URL:    "/user",
 			Method: "PUT",
 			Header: map[string]string{
-				"A": "3",
+				"A": "0",
 			},
-			Expect: "route failed for PUT/user, no rules matched.",
+			Expect: "route failed for PUT/user, no rules matched",
 		},
 		{
 			Name: "one broken regex header",
@@ -178,14 +268,22 @@ func TestRoute(t *testing.T) {
 			Header: map[string]string{
 				"broken-regex": "tt",
 			},
-			Expect: "route failed for GET/user, no rules matched.",
+			Expect: "route failed for GET/user, no rules matched",
 		},
 		{
 			Name: "one matched header 2",
 			Header: map[string]string{
 				"B": "1",
 			},
-			Expect: Cluster1,
+			Expect: Cluster3,
+		},
+		{
+			Name:   "only header but wrong method",
+			Method: "DELETE",
+			Header: map[string]string{
+				"B": "1",
+			},
+			Expect: "route failed for DELETE, no rules matched",
 		},
 		{
 			Name:   "only header but wrong method",
@@ -193,7 +291,25 @@ func TestRoute(t *testing.T) {
 			Header: map[string]string{
 				"B": "1",
 			},
-			Expect: "route failed for DELETE, no rules matched.",
+			Expect: "route failed for DELETE, no rules matched",
+		},
+		{
+			Name: "regex AND normal",
+			URL:  "/user",
+			Header: map[string]string{
+				"E":            "0",
+				"normal-regex": "kk",
+			},
+			Expect: Cluster7,
+		},
+		{
+			Name: "normal AND normal",
+			URL:  "/user",
+			Header: map[string]string{
+				"C": "1",
+				"D": "3",
+			},
+			Expect: Cluster6,
 		},
 	}
 
@@ -205,7 +321,7 @@ func TestRoute(t *testing.T) {
 			if len(tc.Method) > 0 {
 				method = tc.Method
 			}
-			request, err := http.NewRequest(method, tc.URL, nil)
+			request, err := stdHttp.NewRequest(method, tc.URL, nil)
 			assert.NoError(t, err)
 
 			if tc.Header != nil {
@@ -224,3 +340,619 @@ func TestRoute(t *testing.T) {
 		})
 	}
 }
+
+/* ==============================
+   below are parity test between old and new router
+   ============================== */
+
+type HeaderSpec struct {
+	Name   string
+	Values []string
+	Regex  bool
+}
+
+type RouteSpec struct {
+	ID      string
+	Methods []string
+	Path    string
+	Prefix  string
+	Headers []HeaderSpec
+	Cluster string
+}
+
+func (s RouteSpec) toRouter() *model.Router {
+	h := make([]model.HeaderMatcher, 0, len(s.Headers))
+	for _, x := range s.Headers {
+		h = append(h, model.HeaderMatcher{Name: x.Name, Values: append([]string(nil), x.Values...), Regex: x.Regex})
+	}
+	return &model.Router{
+		ID: s.ID,
+		Match: model.RouterMatch{
+			Methods: append([]string(nil), s.Methods...),
+			Path:    s.Path,
+			Prefix:  s.Prefix,
+			Headers: h,
+		},
+		Route: model.RouteAction{Cluster: s.Cluster},
+	}
+}
+
+func buildOld(specs []RouteSpec) *oldrouter.RouterCoordinator {
+	rs := make([]*model.Router, 0, len(specs))
+	for _, s := range specs {
+		rs = append(rs, s.toRouter())
+	}
+	cfg := &model.RouteConfiguration{Routes: rs, Dynamic: false}
+	return oldrouter.CreateRouterCoordinator(cfg)
+}
+
+func BuildNew(specs []RouteSpec) *RouterCoordinator {
+	rs := make([]*model.Router, 0, len(specs))
+	for _, s := range specs {
+		rs = append(rs, s.toRouter())
+	}
+	cfg := &model.RouteConfiguration{Routes: rs, Dynamic: false}
+	return CreateRouterCoordinator(cfg)
+}
+
+type res struct {
+	ok      bool
+	cluster string
+	err     string
+}
+
+func call(cOld *oldrouter.RouterCoordinator, cNew *RouterCoordinator, method, path string, hdr map[string]string) (res, res) {
+	req, _ := stdHttp.NewRequest(method, path, nil)
+	httpContext := http.HttpContext{
+		Request: req,
+	}
+	for k, v := range hdr {
+		req.Header.Set(k, v)
+	}
+	oa, oe := cOld.Route(&httpContext)
+	na, ne := cNew.Route(&httpContext)
+
+	or := res{}
+	if oe != nil || oa == nil {
+		if oe != nil {
+			or.err = oe.Error()
+		}
+	} else {
+		or.ok = true
+		or.cluster = oa.Cluster
+	}
+
+	nr := res{}
+	if ne != nil || na == nil {
+		if ne != nil {
+			nr.err = ne.Error()
+		}
+	} else {
+		nr.ok = true
+		nr.cluster = na.Cluster
+	}
+	return or, nr
+}
+
+func assertSame(t *testing.T, oldc *oldrouter.RouterCoordinator, newc *RouterCoordinator,
+	method, path string, hdr map[string]string, wantOK bool, wantCluster string) {
+
+	ro, rn := call(oldc, newc, method, path, hdr)
+	if ro.ok != rn.ok || ro.cluster != rn.cluster {
+		t.Fatalf("mismatch: %s %s hdr=%v\n old={ok:%v cluster:%q err:%q}\n new={ok:%v cluster:%q err:%q}",
+			method, path, hdr, ro.ok, ro.cluster, ro.err, rn.ok, rn.cluster, rn.err)
+	}
+	if ro.ok != wantOK || rn.ok != wantOK {
+		t.Fatalf("ok mismatch: %s %s hdr=%v wantOK=%v oldOK=%v newOK=%v", method, path, hdr, wantOK, ro.ok, rn.ok)
+	}
+	if wantOK && wantCluster != "" && (ro.cluster != wantCluster || rn.cluster != wantCluster) {
+		t.Fatalf("cluster mismatch: %s %s hdr=%v want=%q old=%q new=%q", method, path, hdr, wantCluster, ro.cluster, rn.cluster)
+	}
+}
+
+type varSyntax struct {
+	simplePattern func(seg string) string         // /users/:id
+	digitsPattern func(seg string) (string, bool) // /users/:id(\d+)
+	multiPattern  func(a, b string) string        // /shops/:a/orders/:b
+}
+
+func colonSyntax() varSyntax {
+	return varSyntax{
+		simplePattern: func(seg string) string {
+			return "/users/:" + seg
+		},
+		digitsPattern: func(seg string) (string, bool) {
+			return "/users/:" + seg + "(\\d+)", true
+		},
+		multiPattern: func(a, b string) string {
+			return "/shops/:" + a + "/orders/:" + b
+		},
+	}
+}
+
+/* ==============================
+   test cases (var/regex/priority/header/)
+   ============================== */
+
+func TestParitySimpleCases(t *testing.T) {
+	syntax = colonSyntax()
+
+	specs := []RouteSpec{
+		// exact
+		{ID: "exact", Methods: []string{"GET"}, Path: "/api/v1/item/100", Cluster: "c-exact"},
+		// prefix（/**）
+		{ID: "pre", Methods: []string{"GET"}, Prefix: "/api/v1/svc/", Cluster: "c-pre"},
+		// var
+		{ID: "var", Methods: []string{"GET"}, Path: syntax.simplePattern("id"), Cluster: "c-var"},
+		// multi
+		{ID: "multi", Methods: []string{"GET", "POST"}, Path: "/multi", Cluster: "c-multi"},
+		// Header regex
+		{ID: "hdr", Methods: []string{"GET"}, Headers: []HeaderSpec{{Name: "X-Env", Values: []string{"^prod|staging$"}, Regex: true}}, Cluster: "c-hdr"},
+	}
+
+	oldc := buildOld(specs)
+	newc := BuildNew(specs)
+
+	cases := []struct {
+		name    string
+		method  string
+		path    string
+		hdr     map[string]string
+		ok      bool
+		cluster string
+	}{
+		{"exact", "GET", "/api/v1/item/100", nil, true, "c-exact"},
+		{"prefix.deep", "GET", "/api/v1/svc/a/b", nil, true, "c-pre"},
+		{"var.hit", "GET", "/users/42", nil, true, "c-var"},
+		{"var.not_deeper", "GET", "/users/42/extra", nil, false, ""},
+		{"multi.get", "GET", "/multi", nil, true, "c-multi"},
+		{"multi.post", "POST", "/multi", nil, true, "c-multi"},
+		{"hdr.regex", "GET", "/whatever", map[string]string{"X-Env": "prod"}, true, "c-hdr"},
+		{"miss", "GET", "/no/match", nil, false, ""},
+	}
+
+	for _, tc := range cases {
+		t.Run(tc.name, func(t *testing.T) {
+			assertSame(t, oldc, newc, tc.method, tc.path, tc.hdr, tc.ok, tc.cluster)
+		})
+	}
+}
+
+func TestPrioritySpecificOverWildcard(t *testing.T) {
+	syntax = colonSyntax()
+
+	specs := []RouteSpec{
+		{ID: "wild", Methods: []string{"GET"}, Prefix: "/api/v1/**", Cluster: "c-wild"},
+		{ID: "spec", Methods: []string{"GET"}, Path: "/api/v1/test-dubbo/user/name/" + syntax.simplePattern("name")[len("/users/"):], Cluster: "c-spec"},
+		// equals to /api/v1/test-dubbo/user/name/:name
+	}
+	oldc := buildOld(specs)
+	newc := BuildNew(specs)
+
+	assertSame(t, oldc, newc, "GET",
+		"/api/v1/test-dubbo/user/name/yqxu", nil, true, "c-spec")
+}
+
+func TestPriorityDeeperWins(t *testing.T) {
+	specs := []RouteSpec{
+		{ID: "shallow", Methods: []string{"GET"}, Prefix: "/api/v1/", Cluster: "c-shallow"},
+		{ID: "deeper", Methods: []string{"GET"}, Prefix: "/api/v1/test-dubbo/", Cluster: "c-deeper"},
+	}
+	oldc := buildOld(specs)
+	newc := BuildNew(specs)
+
+	assertSame(t, oldc, newc, "GET",
+		"/api/v1/test-dubbo/user/name/abc", nil, true, "c-deeper")
+}
+
+func TestPrioritySingleStarOverDoubleStar(t *testing.T) {
+	// use var to express "/*"
+	syntax = colonSyntax()
+	specs := []RouteSpec{
+		{ID: "multi", Methods: []string{"GET"}, Prefix: "/api/", Cluster: "c-**"},
+		{ID: "single", Methods: []string{"GET"}, Path: "/api/" + syntax.simplePattern("seg")[len("/users/"):] + "/users", Cluster: "c-*"},
+		// equals to /api/:seg/users
+	}
+	oldc := buildOld(specs)
+	newc := BuildNew(specs)
+
+	assertSame(t, oldc, newc, "GET", "/api/v1/users", nil, true, "c-*")
+	assertSame(t, oldc, newc, "GET", "/api/v1/x/users", nil, true, "c-**")
+}
+
+func TestVariablesSingleAndMulti(t *testing.T) {
+	syntax = colonSyntax()
+	specs := []RouteSpec{
+		{ID: "one", Methods: []string{"GET"}, Path: syntax.simplePattern("id"), Cluster: "c-one"},
+		{ID: "two", Methods: []string{"GET"}, Path: syntax.multiPattern("shopId", "orderId"), Cluster: "c-two"},
+		{ID: "pre", Methods: []string{"GET"}, Prefix: "/shops/", Cluster: "c-pre"},
+	}
+	oldc := buildOld(specs)
+	newc := BuildNew(specs)
+
+	assertSame(t, oldc, newc, "GET", "/users/777", nil, true, "c-one")
+	assertSame(t, oldc, newc, "GET", syntax.multiPattern("12", "34"), nil, true, "c-two")
+	assertSame(t, oldc, newc, "GET", syntax.multiPattern("12", "34")+"/extra", nil, true, "c-pre")
+}
+
+func TestHeaderRegexWithRoutes(t *testing.T) {
+	specs := []RouteSpec{
+		{ID: "hdr", Methods: []string{"GET"}, Headers: []HeaderSpec{{Name: "X-Env", Values: []string{"^prod|staging$"}, Regex: true}}, Cluster: "c-hdr"},
+		{ID: "pre", Methods: []string{"GET"}, Prefix: "/api/", Cluster: "c-pre"},
+	}
+	oldc := buildOld(specs)
+	newc := BuildNew(specs)
+
+	assertSame(t, oldc, newc, "GET", "/whatever", map[string]string{"X-Env": "prod"}, true, "c-hdr")
+	assertSame(t, oldc, newc, "GET", "/api/foo", map[string]string{"X-Env": "dev"}, true, "c-pre")
+}
+
+/* ==============================
+   random data fuzz test
+   ============================== */
+
+func TestParityRandomized(t *testing.T) {
+	syntax = colonSyntax()
+	const (
+		nRoutes           = 20000
+		nRequests         = 10000
+		prefixRatio       = 0.40
+		headerRatio       = 0.10
+		seed        int64 = 20250929
+	)
+
+	specs := genRandomSpecsWithVars(syntax, nRoutes, prefixRatio, headerRatio, seed)
+	oldc := buildOld(specs)
+	newc := BuildNew(specs)
+
+	reqs := genRandomRequests(nRequests, seed+1)
+	for i, req := range reqs {
+		ro, rn := call(oldc, newc, req.Method, req.URL.Path, headerFromReq(req))
+		if ro.ok != rn.ok || ro.cluster != rn.cluster {
+			t.Fatalf("Randomized mismatch at #%d: %s %s old={ok:%v cluster:%q err:%q} new={ok:%v cluster:%q err:%q}",
+				i, req.Method, req.URL.Path, ro.ok, ro.cluster, ro.err, rn.ok, rn.cluster, rn.err)
+		}
+	}
+}
+
+func headerFromReq(r *stdHttp.Request) map[string]string {
+	if len(r.Header) == 0 {
+		return nil
+	}
+	out := make(map[string]string)
+	for k, vs := range r.Header {
+		if len(vs) > 0 {
+			out[k] = vs[0]
+		}
+	}
+	return out
+}
+
+/* ==============================
+   random data generation tools
+   ============================== */
+
+var syntax varSyntax
+
+func genRandomSpecsWithVars(s varSyntax, n int, prefixRatio, headerOnlyRatio float64, seed int64) []RouteSpec {
+	rnd := rand.New(rand.NewSource(seed))
+	out := make([]RouteSpec, 0, n)
+
+	nHeader := int(float64(n) * headerOnlyRatio)
+	nPrefix := int(float64(n-nHeader) * prefixRatio)
+	// preserve 20% for "variable path", the rest for exact path
+	nVars := int(float64(n) * 0.20)
+	nPath := n - nHeader - nPrefix - nVars
+	if nPath < 0 {
+		nPath = 0
+	}
+
+	// Header-only (regex + normal)
+	for i := 0; i < nHeader; i++ {
+		if i%5 == 0 {
+			out = append(out, RouteSpec{
+				ID:      "hdrx-" + strconv.Itoa(i),
+				Methods: []string{"GET", "POST"},
+				Headers: []HeaderSpec{{Name: "X-Trace", Values: []string{"^pixiu-[0-9a-f]{8}$"}, Regex: true}},
+				Cluster: "c-hx-" + strconv.Itoa(i),
+			})
+		} else {
+			out = append(out, RouteSpec{
+				ID:      "hdr-" + strconv.Itoa(i),
+				Methods: []string{"GET", "POST"},
+				Headers: []HeaderSpec{{Name: "X-Env", Values: []string{"prod"}, Regex: false}},
+				Cluster: "c-h-" + strconv.Itoa(i),
+			})
+		}
+	}
+
+	// Prefix
+	for i := 0; i < nPrefix; i++ {
+		base := "/api/v" + strconv.Itoa(1+rnd.Intn(3)) + "/svc" + strconv.Itoa(rnd.Intn(50)) + "/" // NOSONAR
+		out = append(out, RouteSpec{
+			ID:      "pre-" + strconv.Itoa(i),
+			Methods: []string{"GET", "POST"},
+			Prefix:  base,
+			Cluster: "c-p-" + strconv.Itoa(i),
+		})
+	}
+
+	// Variables
+	for i := 0; i < nVars; i++ {
+		if i%3 == 0 {
+			out = append(out, RouteSpec{
+				ID:      "var-" + strconv.Itoa(i),
+				Methods: []string{"GET"},
+				Path:    s.simplePattern("id"),
+				Cluster: "c-v-" + strconv.Itoa(i),
+			})
+		} else {
+			out = append(out, RouteSpec{
+				ID:      "var2-" + strconv.Itoa(i),
+				Methods: []string{"GET"},
+				Path:    s.multiPattern("a", "b"),
+				Cluster: "c-v2-" + strconv.Itoa(i),
+			})
+		}
+	}
+
+	// Exact Path
+	for i := 0; i < nPath; i++ {
+		out = append(out, RouteSpec{
+			ID:      "pth-" + strconv.Itoa(i),
+			Methods: []string{"GET"},
+			Path:    "/api/v1/item/" + strconv.Itoa(i),
+			Cluster: "c-x-" + strconv.Itoa(i),
+		})
+	}
+	return out
+}
+
+func genRandomRequests(n int, seed int64) []*stdHttp.Request {
+	rnd := rand.New(rand.NewSource(seed))
+	reqs := make([]*stdHttp.Request, 0, n)
+	methods := []string{"GET", "POST"}
+
+	for i := 0; i < n; i++ {
+		var path string
+		switch rnd.Intn(5) { // NOSONAR
+		case 0: // exact style
+			path = "/api/v1/item/" + strconv.Itoa(rnd.Intn(50000)) // NOSONAR
+		case 1: // prefix style
+			path = "/api/v" + strconv.Itoa(1+rnd.Intn(3)) + "/svc" + strconv.Itoa(rnd.Intn(50)) + "/foo/bar" // NOSONAR
+		case 2: // var
+			path = "/users/" + strconv.Itoa(1000+rnd.Intn(9000)) // NOSONAR
+		case 3: // var
+			path = "/shops/" + strconv.Itoa(rnd.Intn(100)) + "/orders/" + strconv.Itoa(rnd.Intn(1000)) // NOSONAR
+		default:
+			path = "/unknown/" + strconv.Itoa(rnd.Intn(100000)) // NOSONAR
+		}
+		req, _ := stdHttp.NewRequest(methods[rnd.Intn(len(methods))], path, nil) // NOSONAR
+		// header-only
+		switch rnd.Intn(7) { // NOSONAR
+		case 0:
+			req.Header.Set("X-Env", "prod")
+		case 1:
+			req.Header.Set("X-Trace", "pixiu-"+strconv.FormatInt(rnd.Int63()&0xffffffff, 16)) // NOSONAR
+		}
+		reqs = append(reqs, req)
+	}
+	return reqs
+}
+
+func newTestRouter(id, method, path, cluster string) *model.Router {
+	return &model.Router{
+		ID: id,
+		Match: model.RouterMatch{
+			Methods: []string{method},
+			Path:    path,
+		},
+		Route: model.RouteAction{
+			Cluster: cluster,
+		},
+	}
+}
+
+func mustRouteCluster(t *testing.T, rm *RouterCoordinator, method, path string, want string) {
+	t.Helper()
+	req, err := stdHttp.NewRequest(method, path, nil)
+	if err != nil {
+		t.Fatalf("failed to build request: %v", err)
+	}
+	ctx := http.HttpContext{Request: req}
+	act, err := rm.Route(&ctx)
+	if err != nil {
+		t.Fatalf("unexpected route error for %s %s: %v", method, path, err)
+	}
+	if act == nil {
+		t.Fatalf("route action is nil for %s %s", method, path)
+	}
+	if act.Cluster != want {
+		t.Fatalf("unexpected cluster for %s %s, want %q, got %q", method, path, want, act.Cluster)
+	}
+}
+
+func mustRouteNotFound(t *testing.T, rm *RouterCoordinator, method, path string) {
+	t.Helper()
+	req, err := stdHttp.NewRequest(method, path, nil)
+	if err != nil {
+		t.Fatalf("failed to build request: %v", err)
+	}
+	ctx := http.HttpContext{Request: req}
+	act, err := rm.Route(&ctx)
+	if err == nil {
+		t.Fatalf("expected error for %s %s, got nil (action=%#v)", method, path, act)
+	}
+}
+
+func mustRouteByPathAndNameCluster(t *testing.T, rm *RouterCoordinator, method, path string, want string) {
+	t.Helper()
+	act, err := rm.RouteByPathAndName(path, method)
+	if err != nil {
+		t.Fatalf("unexpected RouteByPathAndName error for %s %s: %v", method, path, err)
+	}
+	if act == nil {
+		t.Fatalf("route action is nil for %s %s", method, path)
+	}
+	if act.Cluster != want {
+		t.Fatalf("unexpected cluster for %s %s, want %q, got %q", method, path, want, act.Cluster)
+	}
+}
+
+func mustRouteByPathAndNameNotFound(t *testing.T, rm *RouterCoordinator, method, path string) {
+	t.Helper()
+	act, err := rm.RouteByPathAndName(path, method)
+	if err == nil {
+		t.Fatalf("expected RouteByPathAndName error for %s %s, got nil (action=%#v)", method, path, act)
+	}
+}
+
+func TestCreateRouterCoordinatorInitialSnapshot(t *testing.T) {
+	r1 := newTestRouter("r1", "GET", "/foo", "cluster-foo")
+	r2 := newTestRouter("r2", "GET", "/bar", "cluster-bar")
+
+	cfg := &model.RouteConfiguration{
+		Routes:  []*model.Router{r1, r2},
+		Dynamic: false,
+	}
+
+	rm := CreateRouterCoordinator(cfg)
+	rm.debounce = 0
+
+	mustRouteCluster(t, rm, "GET", "/foo", "cluster-foo")
+	mustRouteCluster(t, rm, "GET", "/bar", "cluster-bar")
+
+	mustRouteByPathAndNameCluster(t, rm, "GET", "/foo", "cluster-foo")
+	mustRouteByPathAndNameCluster(t, rm, "GET", "/bar", "cluster-bar")
+}
+
+// TestRouterCoordinatorOnAddRouterUpdatesSnapshot 验证 Add 后新路由生效，旧路由保持
+func TestRouterCoordinatorOnAddRouterUpdatesSnapshot(t *testing.T) {
+	r1 := newTestRouter("r1", "GET", "/foo", "cluster-foo")
+
+	cfg := &model.RouteConfiguration{
+		Routes:  []*model.Router{r1},
+		Dynamic: false,
+	}
+
+	rm := CreateRouterCoordinator(cfg)
+	rm.debounce = 0
+
+	// only /foo
+	mustRouteCluster(t, rm, "GET", "/foo", "cluster-foo")
+	mustRouteNotFound(t, rm, "GET", "/bar")
+	mustRouteByPathAndNameCluster(t, rm, "GET", "/foo", "cluster-foo")
+	mustRouteByPathAndNameNotFound(t, rm, "GET", "/bar")
+
+	// Add /bar
+	r2 := newTestRouter("r2", "GET", "/bar", "cluster-bar")
+	rm.OnAddRouter(r2)
+
+	// /foo available
+	mustRouteCluster(t, rm, "GET", "/foo", "cluster-foo")
+	mustRouteByPathAndNameCluster(t, rm, "GET", "/foo", "cluster-foo")
+
+	// /bar available
+	mustRouteCluster(t, rm, "GET", "/bar", "cluster-bar")
+	mustRouteByPathAndNameCluster(t, rm, "GET", "/bar", "cluster-bar")
+}
+
+// TestRouterCoordinatorOnDeleteRouterUpdatesSnapshot 验证 Delete 后路由失效
+func TestRouterCoordinatorOnDeleteRouterUpdatesSnapshot(t *testing.T) {
+	r1 := newTestRouter("r1", "GET", "/foo", "cluster-foo")
+	r2 := newTestRouter("r2", "GET", "/bar", "cluster-bar")
+
+	cfg := &model.RouteConfiguration{
+		Routes:  []*model.Router{r1, r2},
+		Dynamic: false,
+	}
+
+	rm := CreateRouterCoordinator(cfg)
+	rm.debounce = 0
+
+	// /foo、/bar available
+	mustRouteCluster(t, rm, "GET", "/foo", "cluster-foo")
+	mustRouteCluster(t, rm, "GET", "/bar", "cluster-bar")
+	mustRouteByPathAndNameCluster(t, rm, "GET", "/foo", "cluster-foo")
+	mustRouteByPathAndNameCluster(t, rm, "GET", "/bar", "cluster-bar")
+
+	// delete /bar
+	rm.OnDeleteRouter(r2)
+
+	// /foo available
+	mustRouteCluster(t, rm, "GET", "/foo", "cluster-foo")
+	mustRouteByPathAndNameCluster(t, rm, "GET", "/foo", "cluster-foo")
+
+	// /bar fail
+	mustRouteNotFound(t, rm, "GET", "/bar")
+	mustRouteByPathAndNameNotFound(t, rm, "GET", "/bar")
+}
+
+func TestOldVsNew_OnAddRouterWithSameID(t *testing.T) {
+	base := []*model.Router{
+		{
+			ID: "r1",
+			Match: model.RouterMatch{
+				Methods: []string{"POST"},
+				Path:    "/api/v1/item/711",
+			},
+			Route: model.RouteAction{Cluster: "c-x-r1"},
+		},
+	}
+
+	oldc := buildOldCoordinator(base)
+	newc := buildNewCoordinator(base)
+	newc.debounce = 0
+
+	{
+		req, _ := stdHttp.NewRequest("POST", "/api/v1/item/711", nil)
+		ctxOld := http.HttpContext{Request: req}
+		ctxNew := http.HttpContext{Request: req}
+
+		oldRes, oldErr := oldc.Route(&ctxOld)
+		newRes, newErr := newc.Route(&ctxNew)
+
+		if oldErr != nil || newErr != nil {
+			t.Fatalf("initial route error: oldErr=%v newErr=%v", oldErr, newErr)
+		}
+		if oldRes.Cluster != "c-x-r1" || newRes.Cluster != "c-x-r1" {
+			t.Fatalf("initial cluster mismatch: old=%v new=%v", oldRes, newRes)
+		}
+	}
+
+	delta := &model.Router{
+		ID: "r1",
+		Match: model.RouterMatch{
+			Methods: []string{"POST"},
+			Path:    "/api/v1/item/999999",
+		},
+		Route: model.RouteAction{Cluster: "c-x-r1"},
+	}
+
+	oldc.OnAddRouter(delta)
+	newc.OnAddRouter(delta)
+
+	{
+		req, _ := stdHttp.NewRequest("POST", "/api/v1/item/711", nil)
+		ctxOld := http.HttpContext{Request: req}
+		ctxNew := http.HttpContext{Request: req}
+
+		oldRes, oldErr := oldc.Route(&ctxOld)
+		newRes, newErr := newc.Route(&ctxNew)
+
+		assert.Equal(t, oldRes, newRes)
+		assert.Equal(t, oldErr, newErr)
+	}
+
+	{
+		req, _ := stdHttp.NewRequest("POST", "/api/v1/item/999999", nil)
+		ctxOld := http.HttpContext{Request: req}
+		ctxNew := http.HttpContext{Request: req}
+
+		oldRes, oldErr := oldc.Route(&ctxOld)
+		newRes, newErr := newc.Route(&ctxNew)
+
+		assert.Equal(t, oldRes, newRes)
+		assert.Equal(t, oldErr, newErr)
+	}
+}
diff --git a/tools/benchmark/test/dubbo_suite/dubbo_test.go b/tools/benchmark/test/dubbo_suite/dubbo_test.go
index 54d1c193..6bf32541 100644
--- a/tools/benchmark/test/dubbo_suite/dubbo_test.go
+++ b/tools/benchmark/test/dubbo_suite/dubbo_test.go
@@ -21,6 +21,7 @@ import (
 	"context"
 	"fmt"
 	"io"
+	"net"
 	"net/http"
 	"os"
 	"os/exec"
@@ -62,76 +63,21 @@ var _ = Describe("test", Ordered, func() {
 		test.CurPath, err = os.Getwd()
 		gomega.Expect(err).NotTo(gomega.HaveOccurred())
 
-		dubboServerSession = prepareDubboServer()
-		pixiuSession = test.PreparePixiu("../../dist/pixiu", test.CurPath+"/../../protocol/dubbo/pixiu/conf/config.yaml")
+		// Wait for ports to be available before starting servers
+		waitForPortAvailable("20000", 10*time.Second)
+		waitForPortAvailable("8881", 10*time.Second)
 
-		time.Sleep(3 * time.Second)
+		dubboServerSession = prepareDubboServer()
+		// Wait for dubbo server to register to Zookeeper
+		time.Sleep(5 * time.Second)
 
-		prepareDubboClient()
+		pixiuSession = test.PreparePixiu("../../dist/pixiu", test.CurPath+"/../../protocol/dubbo/pixiu/conf/config.yaml")
+		// Wait for pixiu to discover services from Zookeeper
+		time.Sleep(5 * time.Second)
 	})
 
 	//TODO(kenwaycai): output to external file
 
-	It("dubbo protocol performance test", func() {
-		defer GinkgoRecover()
-
-		experiment := gmeasure.NewExperiment("dubbo protocol performance test")
-		AddReportEntry(experiment.Name, experiment)
-
-		experiment.Sample(func(idx int) {
-			reqUser := &pkg.User{}
-			reqUser.ID = "003"
-			experiment.MeasureDuration("GetUser", func() {
-				_, err := userProvider.GetUser(context.TODO(), reqUser)
-				gomega.Expect(err).To(gomega.Succeed())
-				//fmt.Printf("consumer:%+v", user)
-			})
-		}, test.SampleConfig)
-
-		experiment.Sample(func(idx int) {
-			experiment.MeasureDuration("GetGender", func() {
-				_, err := userProvider.GetGender(context.TODO(), 1)
-				gomega.Expect(err).To(gomega.Succeed())
-				//fmt.Printf("consumer:%+v", gender)
-			})
-		}, test.SampleConfig)
-
-		experiment.Sample(func(idx int) {
-			experiment.MeasureDuration("GetUser0", func() {
-				_, err := userProvider.GetUser0("003", "Moorse")
-				gomega.Expect(err).To(gomega.Succeed())
-				//fmt.Printf("consumer:%+v", ret)
-			})
-		}, test.SampleConfig)
-
-		experiment.Sample(func(idx int) {
-			experiment.MeasureDuration("GetUsers", func() {
-				_, err := userProvider.GetUsers([]string{"002", "003"})
-				gomega.Expect(err).To(gomega.Succeed())
-				//fmt.Printf("consumer:%+v", ret1)
-			})
-		}, test.SampleConfig)
-
-		experiment.Sample(func(idx int) {
-			experiment.MeasureDuration("GetUser2", func() {
-				var i int32 = 1
-				_, err := userProvider.GetUser2(context.TODO(), i)
-				gomega.Expect(err).To(gomega.Succeed())
-				//fmt.Printf("consumer:%+v", user)
-			})
-		}, test.SampleConfig)
-
-		experiment.Sample(func(idx int) {
-			experiment.MeasureDuration("GetErr", func() {
-				reqUser := &pkg.User{}
-				reqUser.ID = "003"
-				_, err := userProvider.GetErr(context.TODO(), reqUser)
-				gomega.Expect(err).To(gomega.HaveOccurred())
-				//fmt.Printf("consumer:%+v", err.Error())
-			})
-		}, test.SampleConfig)
-	})
-
 	It("pixiu to dubbo protocol performance test", func() {
 
 		urlPrefix := "http://localhost:8881/dubbo.io/org.apache.dubbo.sample.UserProvider/%s"
@@ -155,6 +101,7 @@ var _ = Describe("test", Ordered, func() {
 
 				resp, err := http.Post(url, "application/json", strings.NewReader(data))
 				gomega.Expect(err).NotTo(gomega.HaveOccurred())
+				defer resp.Body.Close()
 				gomega.Expect(resp.Status).To(gomega.Equal("200 OK"))
 				_, err = io.ReadAll(resp.Body)
 				gomega.Expect(err).NotTo(gomega.HaveOccurred())
@@ -172,6 +119,7 @@ var _ = Describe("test", Ordered, func() {
 `
 				resp, err := http.Post(url, "application/json", strings.NewReader(data))
 				gomega.Expect(err).NotTo(gomega.HaveOccurred())
+				defer resp.Body.Close()
 				gomega.Expect(resp.Status).To(gomega.Equal("200 OK"))
 
 				_, err = io.ReadAll(resp.Body)
@@ -193,6 +141,7 @@ var _ = Describe("test", Ordered, func() {
 `
 				resp, err := http.Post(url, "application/json", strings.NewReader(data))
 				gomega.Expect(err).NotTo(gomega.HaveOccurred())
+				defer resp.Body.Close()
 				gomega.Expect(resp.Status).To(gomega.Equal("200 OK"))
 
 				_, err = io.ReadAll(resp.Body)
@@ -216,19 +165,84 @@ var _ = Describe("test", Ordered, func() {
 `
 
 				resp, err := http.Post(url, "application/json", strings.NewReader(data))
-				_, err = io.ReadAll(resp.Body)
 				gomega.Expect(err).NotTo(gomega.HaveOccurred())
+				defer resp.Body.Close()
 				gomega.Expect(resp.Status).To(gomega.Equal("200 OK"))
+				_, err = io.ReadAll(resp.Body)
 				gomega.Expect(err).NotTo(gomega.HaveOccurred())
 			})
 
 		}, test.SampleConfig)
 	})
 
+	It("dubbo protocol performance test", func() {
+		defer GinkgoRecover()
+
+		// Initialize dubbo client only when needed for this test
+		prepareDubboClient()
+
+		experiment := gmeasure.NewExperiment("dubbo protocol performance test")
+		AddReportEntry(experiment.Name, experiment)
+
+		experiment.Sample(func(idx int) {
+			reqUser := &pkg.User{}
+			reqUser.ID = "003"
+			experiment.MeasureDuration("GetUser", func() {
+				_, err := userProvider.GetUser(context.TODO(), reqUser)
+				gomega.Expect(err).To(gomega.Succeed())
+			})
+		}, test.SampleConfig)
+
+		experiment.Sample(func(idx int) {
+			experiment.MeasureDuration("GetGender", func() {
+				_, err := userProvider.GetGender(context.TODO(), 1)
+				gomega.Expect(err).To(gomega.Succeed())
+			})
+		}, test.SampleConfig)
+
+		experiment.Sample(func(idx int) {
+			experiment.MeasureDuration("GetUser0", func() {
+				_, err := userProvider.GetUser0("003", "Moorse")
+				gomega.Expect(err).To(gomega.Succeed())
+			})
+		}, test.SampleConfig)
+
+		experiment.Sample(func(idx int) {
+			experiment.MeasureDuration("GetUsers", func() {
+				_, err := userProvider.GetUsers([]string{"002", "003"})
+				gomega.Expect(err).To(gomega.Succeed())
+			})
+		}, test.SampleConfig)
+
+		experiment.Sample(func(idx int) {
+			experiment.MeasureDuration("GetUser2", func() {
+				var i int32 = 1
+				_, err := userProvider.GetUser2(context.TODO(), i)
+				gomega.Expect(err).To(gomega.Succeed())
+			})
+		}, test.SampleConfig)
+
+		experiment.Sample(func(idx int) {
+			experiment.MeasureDuration("GetErr", func() {
+				reqUser := &pkg.User{}
+				reqUser.ID = "003"
+				_, err := userProvider.GetErr(context.TODO(), reqUser)
+				gomega.Expect(err).To(gomega.HaveOccurred())
+			})
+		}, test.SampleConfig)
+	})
+
 	AfterAll(func() {
-		time.Sleep(5 * time.Second)
-		dubboServerSession.Terminate().Wait()
-		pixiuSession.Terminate().Wait(5 * time.Second)
+		// Use Kill instead of Terminate to ensure processes are fully stopped
+		if pixiuSession != nil {
+			pixiuSession.Kill().Wait(10 * time.Second)
+		}
+		if dubboServerSession != nil {
+			dubboServerSession.Kill().Wait(10 * time.Second)
+		}
+		gexec.CleanupBuildArtifacts()
+		// Wait for ports to be released
+		time.Sleep(2 * time.Second)
 	})
 
 })
@@ -237,7 +251,9 @@ func prepareDubboServer() *gexec.Session {
 	serverProcess, err := gexec.Build("dubbo-go-pixiu-benchmark/protocol/dubbo/go-server/cmd")
 	gomega.Expect(err).NotTo(gomega.HaveOccurred())
 	command := exec.Command(serverProcess)
-	session, err := gexec.Start(command, io.Discard, io.Discard)
+	// Set working directory to test/dubbo_suite so that relative paths in server.go work correctly
+	command.Dir = test.CurPath
+	session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
 	//session, err := gexec.Start(command, os.Stdout, os.Stderr)
 	gomega.Expect(err).NotTo(gomega.HaveOccurred())
 
@@ -254,3 +270,21 @@ func prepareDubboClient() {
 	err := config.Load(config.WithPath(test.CurPath + "/../../protocol/dubbo/go-client/conf/dubbogo.yml"))
 	gomega.Expect(err).NotTo(gomega.HaveOccurred())
 }
+
+// waitForPortAvailable waits until the port is available (not in use)
+func waitForPortAvailable(port string, timeout time.Duration) {
+	deadline := time.Now().Add(timeout)
+	for time.Now().Before(deadline) {
+		conn, err := net.DialTimeout("tcp", "127.0.0.1:"+port, 100*time.Millisecond)
+		if err != nil {
+			// Port is available (connection refused means no one is listening)
+			if conn != nil {
+				conn.Close()
+			}
+			return
+		}
+		conn.Close()
+		time.Sleep(500 * time.Millisecond)
+	}
+	gomega.Expect(fmt.Errorf("port %s is still in use after %v", port, timeout)).NotTo(gomega.HaveOccurred())
+}
diff --git a/tools/benchmark/test/triple_suite/proto_suite/proto_test.go b/tools/benchmark/test/triple_suite/proto_suite/proto_test.go
index 5b65c231..24503d6a 100644
--- a/tools/benchmark/test/triple_suite/proto_suite/proto_test.go
+++ b/tools/benchmark/test/triple_suite/proto_suite/proto_test.go
@@ -6,7 +6,7 @@
  * (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
+ * 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,
@@ -21,6 +21,7 @@ import (
 	"context"
 	"fmt"
 	"io"
+	"net"
 	"net/http"
 	"os"
 	"os/exec"
@@ -62,33 +63,17 @@ var _ = Describe("triple protocol performance test", Ordered, func() {
 		test.CurPath, err = os.Getwd()
 		gomega.Expect(err).NotTo(gomega.HaveOccurred())
 
-		tripleServerSession = prepareTripleServer()
-		pixiuSession = test.PreparePixiu("../../../dist/pixiu", test.CurPath+"/../../../protocol/triple/pb/pixiu/conf/config.yaml")
-
-		time.Sleep(3 * time.Second)
-
-		prepareTripleClient()
-
-	})
+		// Wait for ports to be available before starting servers
+		waitForPortAvailable("20000", 10*time.Second)
+		waitForPortAvailable("8881", 10*time.Second)
 
-	It("triple protocol performance test", func() {
-
-		experiment := gmeasure.NewExperiment("triple protocol performance test")
-		AddReportEntry(experiment.Name, experiment)
-
-		experiment.Sample(func(idx int) {
-			defer GinkgoRecover()
+		tripleServerSession = prepareTripleServer()
+		// Wait for dubbo server to register to Zookeeper
+		time.Sleep(5 * time.Second)
 
-			experiment.MeasureDuration("SayHello", func() {
-				req := &triplepb.HelloRequest{
-					Name: "laurence",
-				}
-				ctx := context.WithValue(context.Background(), tripleConstant.TripleCtxKey("tri-req-id"), "test_value_XXXXXXXX")
-				_, err := grpcGreeterImpl.SayHello(ctx, req)
-				gomega.Expect(err).NotTo(gomega.HaveOccurred())
-				//fmt.Printf("consumer:%+v\n", reply)
-			})
-		}, test.SampleConfig)
+		pixiuSession = test.PreparePixiu("../../../dist/pixiu", test.CurPath+"/../../../protocol/triple/pb/pixiu/conf/config.yaml")
+		// Wait for pixiu to discover services from Zookeeper
+		time.Sleep(5 * time.Second)
 	})
 
 	It("pixiu to triple protocol performance test", func() {
@@ -106,11 +91,12 @@ var _ = Describe("triple protocol performance test", Ordered, func() {
 				url := fmt.Sprintf(urlPrefix, "SayHello")
 				data := `
 {
-	"name":"test"
+    "name":"test"
 }
 `
 				resp, err := http.Post(url, "application/json", strings.NewReader(data))
 				gomega.Expect(err).NotTo(gomega.HaveOccurred())
+				defer resp.Body.Close()
 				gomega.Expect(resp.Status).To(gomega.Equal("200 OK"))
 				reply, err := io.ReadAll(resp.Body)
 				gomega.Expect(err).NotTo(gomega.HaveOccurred())
@@ -120,9 +106,39 @@ var _ = Describe("triple protocol performance test", Ordered, func() {
 		}, test.SampleConfig)
 	})
 
+	It("triple protocol performance test", func() {
+		// Initialize dubbo client only when needed for this test
+		prepareTripleClient()
+
+		experiment := gmeasure.NewExperiment("triple protocol performance test")
+		AddReportEntry(experiment.Name, experiment)
+
+		experiment.Sample(func(idx int) {
+			defer GinkgoRecover()
+
+			experiment.MeasureDuration("SayHello", func() {
+				req := &triplepb.HelloRequest{
+					Name: "laurence",
+				}
+				ctx := context.WithValue(context.Background(), tripleConstant.TripleCtxKey("tri-req-id"), "test_value_XXXXXXXX")
+				_, err := grpcGreeterImpl.SayHello(ctx, req)
+				gomega.Expect(err).NotTo(gomega.HaveOccurred())
+				//fmt.Printf("consumer:%+v\n", reply)
+			})
+		}, test.SampleConfig)
+	})
+
 	AfterAll(func() {
-		tripleServerSession.Terminate().Wait(5 * time.Second)
-		pixiuSession.Terminate().Wait(5 * time.Second)
+		// Use Kill instead of Terminate to ensure processes are fully stopped
+		if pixiuSession != nil {
+			pixiuSession.Kill().Wait(10 * time.Second)
+		}
+		if tripleServerSession != nil {
+			tripleServerSession.Kill().Wait(10 * time.Second)
+		}
+		gexec.CleanupBuildArtifacts()
+		// Wait for ports to be released
+		time.Sleep(2 * time.Second)
 	})
 })
 
@@ -131,6 +147,8 @@ func prepareTripleServer() *gexec.Session {
 	gomega.Expect(err).NotTo(gomega.HaveOccurred())
 
 	command := exec.Command(serverProcess)
+	// Set working directory
+	command.Dir = test.CurPath
 	session, err := gexec.Start(command, io.Discard, io.Discard)
 	//session, err := gexec.Start(command, os.Stdout, os.Stderr)
 
@@ -144,3 +162,17 @@ func prepareTripleClient() {
 	err := config.Load(config.WithPath(test.CurPath + "/../../../protocol/triple/pb/go-client/conf/dubbogo.yml"))
 	gomega.Expect(err).NotTo(gomega.HaveOccurred())
 }
+
+// waitForPortAvailable waits until the port is available (not in use)
+func waitForPortAvailable(port string, timeout time.Duration) {
+	deadline := time.Now().Add(timeout)
+	for time.Now().Before(deadline) {
+		conn, err := net.DialTimeout("tcp", "127.0.0.1:"+port, 100*time.Millisecond)
+		if err != nil {
+			// Port is available (connection refused means no one is listening)
+			return
+		}
+		conn.Close()
+		time.Sleep(500 * time.Millisecond)
+	}
+}