Skip to content

插件开发

ForwardX 插件用于给面板增加可选能力。插件可以声明设置项、说明页、数据资产、扩展点、受控动作、主机使用页和 Agent 动态资源,面板负责安装、更新、权限校验、任务调度、状态保存和通用界面渲染。

插件入口默认隐藏,管理员可在「系统设置 -> 左侧导航栏菜单展示设置 -> 管理菜单开关」中开启"插件"。开启后左侧会显示「插件」菜单。

安装方式

管理员可以通过三种方式安装插件:

  • 官方插件:面板从 ForwardX GitHub 仓库读取 plugins/official-store.json,页面中可一键安装。
  • 第三方商店:添加一个或多个 GitHub 商店仓库,面板读取其中的 forwardx-store.json 并把插件合并到商店。
  • 上传插件包:上传 .zip.tar.gz.tgz 插件包。

官方插件列表本身也在 GitHub 上维护,所以新增官方插件不需要用户手动输入仓库地址。

面板安装本身不会自动安装任何插件。插件入口默认隐藏,管理员开启后再到插件商店手动安装。

Manifest

插件仓库推荐在根目录提供 forwardx-plugin.json。面板也会尝试读取 plugin.json.forwardx/plugin.json

json
{
  "schemaVersion": 1,
  "id": "demo-tools",
  "name": "演示插件",
  "version": "0.1.0",
  "description": "一个声明式插件示例",
  "detailsMarkdown": "这里可以写更完整的插件介绍,支持 **Markdown**。\n\n- 说明插件解决什么问题\n- 说明安装后在哪里使用\n- 说明是否会同步文件、调用动作或展示页面",
  "author": "ForwardX",
  "logo": "https://example.com/plugin-logo.png",
  "releaseDate": "2026-07-09",
  "updatedAt": "2026-07-09",
  "changelog": "初始版本,提供说明页和设置项。",
  "tags": ["demo", "page"],
  "license": "MIT",
  "repository": "https://github.com/example/demo-tools",
  "features": [
    {
      "title": "说明页",
      "description": "在插件详情内展示 Markdown 页面。"
    },
    {
      "title": "设置项",
      "description": "提供可保存的插件配置。"
    }
  ],
  "permissions": ["ui:page"],
  "extensionPoints": ["sidebar.page"],
  "settingsSchema": [
    {
      "key": "title",
      "label": "展示标题",
      "type": "text",
      "defaultValue": "Hello ForwardX"
    }
  ],
  "pages": [
    {
      "id": "home",
      "title": "说明页",
      "contentType": "markdown",
      "assetPath": "README.md"
    }
  ],
  "actions": [
    {
      "id": "ping",
      "label": "运行测试动作",
      "type": "noop"
    }
  ],
  "assets": [
    {
      "path": "README.md",
      "label": "说明文档",
      "contentType": "text/markdown"
    }
  ]
}

设置项

settingsSchema 会在插件详情页渲染成表单。支持字段:

类型用途
text单行文本
textarea多行文本
password密钥类输入
number数字
boolean开关
select下拉选择
multi-select多选
urlHTTP/HTTPS 地址或面板内路径

设置值会保存在插件 manifest 的 settingsValues 中,由面板校验类型后写入数据库。

页面和资产

pages 可以声明插件内页面。页面内容可以直接写在 content,也可以通过 assetPath 引用上传包里的资产。

上传包示例:

json
{
  "manifest": {
    "id": "hello-panel",
    "name": "Hello Panel",
    "version": "0.1.0",
    "author": "ForwardX",
    "updatedAt": "2026-07-09",
    "features": [
      {
        "title": "插件页面",
        "description": "展示一个 Markdown 页面。"
      }
    ],
    "permissions": ["ui:page"],
    "extensionPoints": ["sidebar.page"],
    "sidebar": {
      "label": "Hello Panel",
      "target": "page",
      "pageId": "home"
    },
    "pages": [
      {
        "id": "home",
        "title": "首页",
        "contentType": "markdown",
        "assetPath": "README.md"
      }
    ]
  },
  "assets": {
    "README.md": "# Hello Panel\n这是一个插件页面。"
  }
}

资产会存入数据库,不会写入服务器任意目录。

插件需要左侧菜单入口时,必须同时声明 ui:page 权限、sidebar.page 扩展点和 sidebar 配置,并提供对应的使用界面、设置表单或声明式页面。只有目标界面有效时,插件详情才会出现"菜单入口"开关;插件不能通过清单自行开启入口。sidebar 支持以下字段:

字段说明
label侧边栏显示名称,未填写时使用插件名称
icon可选的图片 URL 或 Base64 图片,未填写时使用插件 Logo
targetusage 打开插件使用界面,settings 打开设置表单,page 打开声明式页面
pageIdtargetpage 时指定 pages 中的页面 ID

入口在管理员打开"菜单入口"开关且目标界面存在时显示。插件停用不会隐藏入口,进入页面后会展示当前停用状态;卸载或声明不完整后入口会自动移除,插件升级会保留管理员的开关选择。

压缩包插件需要在包内包含 forwardx-plugin.jsonplugin.json.forwardx/plugin.json。压缩包内的文本资产会被读取到数据库。插件不会执行任意面板后端代码;声明 agent:readagent:writeagent:execute 后,可以让 Agent 在独立任务队列中执行插件包内固定脚本入口。

动作

actions 用来声明插件按钮。当前支持:

类型说明
noop测试动作
http.request由面板后端按 manifest 声明发起受控 HTTP/HTTPS API 请求
agent.request在使用页已选主机上执行插件包内固定脚本,并回传文本或 JSON 结果
panel.request仅限管理员手动信任的插件调用固定面板 API 操作
data.asset.refresh刷新 GitHub 来源插件资产
data.whitelist.refresh刷新白名单类插件数据

http.request 需要插件同时声明 net:http 权限。它不会执行插件后端代码,只会按 manifest 中声明的 request 发起 HTTP/HTTPS 请求,并限制超时和响应体大小。请求中的字符串支持模板变量:

  • {{settings.xxx}}:引用插件设置项,例如 baseUrlapiToken
  • {{input.xxx}}:引用动作执行前让管理员填写的输入项。
  • {{plugin.id}}{{plugin.name}}{{plugin.version}}:引用当前插件信息。

request 支持 methodurlbaseUrlSetting + pathheadersquerybodytimeoutMsresponseTypeauthauth 可选 bearerheadercookie,也可以直接通过 headers 声明认证头。

对接 3x-ui 这类提供 Swagger/OpenAPI 的面板时,可以把面板地址和 API Token 放到 settingsSchema,再把具体接口做成动作。示例:

json
{
  "permissions": ["net:http"],
  "settingsSchema": [
    {
      "key": "baseUrl",
      "label": "3x-ui 面板地址",
      "type": "url",
      "placeholder": "https://xui.example.com"
    },
    {
      "key": "apiToken",
      "label": "API Token",
      "type": "password"
    }
  ],
  "actions": [
    {
      "id": "list-inbounds",
      "label": "读取入站列表",
      "type": "http.request",
      "request": {
        "method": "GET",
        "baseUrlSetting": "baseUrl",
        "path": "/panel/api/inbounds/list",
        "headers": {
          "Authorization": "Bearer {{settings.apiToken}}"
        },
        "responseType": "json",
        "timeoutMs": 10000
      }
    },
    {
      "id": "get-inbound",
      "label": "读取指定入站",
      "type": "http.request",
      "inputSchema": [
        {
          "key": "inboundId",
          "label": "入站 ID",
          "type": "number",
          "required": true
        }
      ],
      "request": {
        "method": "GET",
        "baseUrlSetting": "baseUrl",
        "path": "/panel/api/inbounds/get/{{input.inboundId}}",
        "headers": {
          "Authorization": "Bearer {{settings.apiToken}}"
        },
        "responseType": "json"
      }
    }
  ]
}

具体 3x-ui 路径、请求体和认证方式以 3x-ui 自身 OpenAPI 为准。ForwardX 插件层只负责保存配置、渲染输入表单、发起声明式请求和展示响应结果。

信任插件与面板 API

panel.request 用于需要直接管理 ForwardX 数据的插件。信任状态保存在当前面板的插件实例中,默认关闭,不能由 manifest 声明,也不会在插件安装或更新时自动开启。

执行一个面板动作必须同时满足:

  1. 当前插件已启用。
  2. 插件包含有效的 panel.request 高权限动作,详情页才会显示"插件信任"开关,并由管理员手动确认开启。
  3. manifest 声明了该操作要求的细分权限。
  4. panel.operation 位于 ForwardX 固定操作白名单中。
json
{
  "permissions": ["read:users", "write:rules", "telegram:send"],
  "actions": [
    {
      "id": "list-users",
      "label": "读取用户",
      "type": "panel.request",
      "intent": "read",
      "panel": { "operation": "users.list" }
    },
    {
      "id": "toggle-rule",
      "label": "切换规则状态",
      "type": "panel.request",
      "intent": "write",
      "inputSchema": [
        { "key": "id", "label": "规则 ID", "type": "number", "required": true },
        { "key": "isEnabled", "label": "启用", "type": "boolean", "defaultValue": true }
      ],
      "panel": { "operation": "rules.toggle" }
    }
  ]
}

不包含 panel.request 的普通插件不会显示信任开关。插件升级新增或改变高权限操作时,面板会撤销旧授权并要求管理员重新确认。

高权限操作按资源拆分权限:read:userswrite:userswrite:hostswrite:ruleswrite:tunnelsread:forward-groupswrite:forward-groupstelegram:send。已有的 read:systemread:hostsread:rulesread:tunnelsread:traffic 继续用于只读操作。

当前操作覆盖系统摘要、用户及用户授权、主机、规则、隧道、转发组、流量摘要和 Telegram 消息发送。可用操作及其权限映射可以从插件开发能力接口的 panelOperations 查看。插件不能提交任意 tRPC 路径、SQL 或后端代码。写操作复用面板已有业务校验;返回结果会递归移除密码、2FA Secret、会话 Token、Agent Token、隧道 Secret 和证书私钥。所有允许、拒绝和失败的高权限动作都会写入面板审计日志,但不会记录动作参数。

Agent 操作

agent.request 需要关联一个 host-asset-sync 使用页。目标主机来自该使用页已经保存的生效主机,不接受插件自行指定任意主机。

动作通过 intent 声明用途并使用对应权限:

intent权限用途
readagent:read检测程序、读取配置、列表和运行状态
writeagent:write新增、修改、删除或应用 Agent 配置
execute 或省略agent:execute执行不能归类为资源读写的受控操作;省略时兼容旧插件

动态资源动作通常把 agent.target 设置为 selected-hosts,面板只向当前界面选中的单台 Agent 下发。普通批量动作仍可使用 usage-hosts

json
{
  "id": "demo-tools",
  "permissions": ["agent:read"],
  "usageViews": [
    {
      "id": "sync-to-hosts",
      "type": "host-asset-sync",
      "title": "主机工具",
      "targetDirectory": "/var/lib/forwardx-agent/plugins/demo-tools",
      "assetMode": "all-plugin-assets"
    }
  ],
  "actions": [
    {
      "id": "read-status",
      "label": "读取主机状态",
      "type": "agent.request",
      "intent": "read",
      "inputSchema": [
        {
          "key": "scope",
          "label": "状态范围",
          "type": "text",
          "defaultValue": "summary"
        }
      ],
      "agent": {
        "executor": "script",
        "interpreter": "bash",
        "target": "selected-hosts",
        "usageViewId": "sync-to-hosts",
        "entry": "status.sh",
        "arguments": ["--json", "{{input.scope}}"],
        "timeoutMs": 15000,
        "outputType": "json"
      }
    }
  ]
}

targetDirectory 必须位于 /var/lib/forwardx-agent/plugins/<插件 ID> 下。entry 必须是插件同步目录内的相对路径,不能包含 ..arguments 以参数数组传递,支持与 HTTP 动作相同的模板变量,不会拼接为任意 shell 命令。当前支持 bashshpython3 解释器,目标主机需要自行具备对应解释器。

插件任务需要 Agent 2.2.151 或更高版本,并使用独立 worker,不占用转发规则执行队列。Agent 心跳必须同时回报 pluginVersionspluginSyncSignatures,同步目录的 manifest.json 只使用 version 字段;早期实验协议不再兼容。旧清单可按升级和备份手动迁移。首次执行前需要先保存插件使用配置,未同步、离线、版本或签名不匹配时,动态资源界面会禁用写入按钮并显示原因。

单个任务最长 60 秒,标准输出和错误输出分别限制为 256KB。outputType: "json" 时,脚本标准输出必须是完整 JSON;面板会按主机展示等待中、执行中、成功、已生效、离线、失败和超时状态。动态资源最近一次任务状态会写入数据库,刷新页面或面板重启后仍可看到最后状态和错误原因。

结构化结果 resultSchema

agent.request 返回 JSON 时,可以声明 resultSchema,让面板渲染信息卡或表格,不再固定展示原始 JSON。

json
{
  "id": "read-services",
  "label": "读取服务",
  "type": "agent.request",
  "intent": "read",
  "agent": {
    "executor": "script",
    "target": "selected-hosts",
    "usageViewId": "sync-to-hosts",
    "entry": "manage.sh",
    "arguments": ["list"],
    "outputType": "json"
  },
  "resultSchema": {
    "type": "table",
    "itemsPath": "items",
    "fields": [
      { "key": "name", "label": "名称", "copyable": true },
      { "key": "status", "label": "状态", "type": "statusBadge" },
      { "key": "token", "label": "Token", "secret": true, "revealable": true, "copyable": true },
      { "key": "url", "label": "管理地址", "openable": true }
    ]
  }
}

resultSchema.type 支持:

  • keyValue:键值信息卡。
  • table:数组表格,通过 itemsPath 指定数组路径。

字段类型支持 textnumberbooleanstatusBadgecodedatetimecopyable 显示复制按钮,openable 只允许打开 HTTP/HTTPS 地址。secret 默认遮挡;同时声明 revealable 且插件拥有 secret:reveal 权限时,用户才能点击显示和复制。

没有声明 resultSchema 的旧插件继续展示原始 JSON,不受影响。

Agent 动态资源 resourceSchema

resourceSchema 用于声明通用节点管理界面。插件只负责通过固定 Agent 脚本读取和保存数据,ForwardX 会在当前插件的"插件使用"页下方提供 Agent 列表、资源表格、详情、编辑表单、删除确认、任务状态和刷新流程。主机较多时可直接搜索,移动端会改用紧凑选择器。

json
{
  "permissions": ["read:hosts", "agent:read", "agent:write", "ui:interactive"],
  "resourceSchema": {
    "id": "services",
    "type": "agent-resource",
    "title": "服务管理",
    "usageViewId": "sync-to-hosts",
    "rowKey": "serviceId",
    "idInputKey": "serviceId",
    "onOpen": "list-services",
    "itemsPath": "items",
    "detailAction": {
      "actionId": "service-detail",
      "inputKey": "serviceId"
    },
    "columns": [
      { "key": "name", "label": "名称", "copyable": true },
      { "key": "status", "label": "状态", "type": "status" },
      { "key": "port", "label": "端口", "type": "number" }
    ],
    "fields": [
      {
        "key": "protocol",
        "label": "协议",
        "type": "select",
        "optionsSource": {
          "sourceId": "capabilities",
          "path": "protocols",
          "valueKey": "value",
          "labelKey": "label",
          "disabledKey": "disabled"
        }
      },
      {
        "key": "port",
        "label": "端口",
        "type": "number",
        "required": true,
        "visibleWhen": [{ "field": "protocol", "operator": "in", "value": ["tcp", "udp"] }]
      },
      {
        "key": "tls",
        "label": "TLS",
        "type": "boolean",
        "disabledWhen": [{ "field": "source.capabilities.tlsAvailable", "operator": "falsy" }]
      }
    ],
    "sources": [
      {
        "id": "capabilities",
        "actionId": "read-capabilities",
        "triggers": ["onOpen", "onHostSelected"]
      }
    ],
    "operations": {
      "create": { "actionId": "create-service", "refreshAfter": ["list"] },
      "update": { "actionId": "update-service", "refreshAfter": ["list"] },
      "delete": {
        "actionId": "delete-service",
        "confirmRequired": true,
        "refreshAfter": ["list"]
      }
    }
  }
}

onOpen 会在打开资源管理或切换 Agent 时调用列表动作。点击一行后,面板把 rowKey 对应的稳定 ID 传给 detailAction,读取详情并自动回填 fields。新增、修改和删除时会自动传递:

  • resourceId:当前资源稳定 ID。
  • idInputKey 指定的字段,例如 serviceId
  • payload:当前编辑表单的完整对象。
  • 所有表单字段的顶层值。

因此插件不需要让用户手工填写或复制节点编号、名称和端口。动作参数可以使用 {{input.serviceId}}{{input.payload}}

refreshAfter 指定动作成功后自动重新加载的数据源。sources 还可以声明 onOpenonHostSelectedmanual 触发方式;字段的 optionsSource 可从任意已加载数据源生成实时选择项。

multi-select 的静态选项可以声明 "exclusive": true。选中该选项时会清除其他值;选择普通选项时也会自动取消已有的互斥值,适合"全国"与"按省份"这类选择。

visibleWhendisabledWhen 支持 eqneqinnot-intruthyfalsy。条件既可以读取其他表单字段,也可以用 source.<数据源 ID>.<路径> 读取 Agent 返回能力。

每个数据源缓存都按 插件 + resourceSchema + Agent 隔离。切换主机时会取消接受上一台主机的迟到结果,不会混用列表、详情或选项缓存。

使用页

插件可以通过 usageViews 声明自己的使用界面。面板不会执行插件前端代码,而是按声明渲染通用 UI。

当前支持的使用页类型:

类型说明
host-asset-sync选择主机、插件字段和可选资产,保存后由 Agent 心跳同步到主机本地目录

示例:

json
{
  "usageViews": [
    {
      "id": "sync-to-hosts",
      "type": "host-asset-sync",
      "storageKey": "demoUsage",
      "title": "主机文件同步",
      "description": "选择主机和数据文件后,Agent 会把文件同步到指定目录。",
      "enableLabel": "启用同步",
      "targetDirectory": "/var/lib/forwardx-agent/plugins/demo-tools",
      "assetMode": "selected-assets",
      "hostSelector": {
        "title": "生效主机",
        "selectedLabel": "已选",
        "selectAllLabel": "全选",
        "clearLabel": "清空"
      },
      "assetSelector": {
        "title": "同步内容",
        "selectedLabel": "已选",
        "clearLabel": "清空"
      },
      "operationSelector": {
        "label": "执行方式",
        "defaultValue": "sync",
        "options": [
          { "value": "sync", "label": "仅同步" }
        ]
      },
      "fields": [
        {
          "key": "mode",
          "label": "模式",
          "type": "select",
          "options": [
            { "value": "basic", "label": "基础" }
          ]
        }
      ],
      "noteField": {
        "label": "备注"
      },
      "footer": {
        "submitLabel": "保存使用配置"
      }
    }
  ]
}

storageKey 用于保存这个使用页的配置。省略时面板会按插件 ID 和使用页 ID 自动生成。

assetMode 默认为 selected-assets,用户需要选择具体文件。设置为 all-plugin-assets 时,面板会把插件包内允许同步的文本资产整体下发,适合需要脚本、数据目录和配置一起工作的主机类插件。

fields 支持 texttextareabooleanselectmulti-select。这些字段不会执行插件前端代码,而是由面板按声明渲染并保存。

官方插件商店

官方插件清单在仓库的 plugins/official-store.json,面板在线读取后展示在插件商店页。读取失败时会回退到面板内置的最小官方插件列表。

官方商店一键安装优先下载仓库内的 packagePath 插件压缩包,如 plugins/packages/china-region-whitelist.tar.gz。这些压缩包提交在仓库中,不作为 GitHub Release 资产发布,面板升级包也不会携带插件包。更新插件包时,在本地执行:

bash
pnpm plugins:package

执行后把生成的 plugins/packages/*.tar.gz 和对应清单一起提交即可。

ForwardX 中国区域白名单

插件 ID:china-region-whitelist,当前版本:0.7.0,许可证:AGPL-3.0-only。

按主机实时管理中国大陆全国、省级 CIDR 和 ASN 白名单规则。插件程序和数据会自动同步到所有已选 Agent 主机,每台主机的配置与状态独立保存。

主要功能:

  • 支持全国 CN 或按省份选择入站白名单,以及额外 ASN 和端口优先白名单。
  • 支持 nftables 或 iptables/ipset 防火墙后端,ForwardX 自有适配,不依赖上游 shell 脚本运行时。
  • 在 Agent 节点管理中实时读取、新增、编辑、应用和清理各主机独立配置。
  • 实时展示防火墙后端、规则数量、持久化状态和执行错误。

安装后进入插件详情的"使用"页,开启使用配置并选择生效主机。Agent 心跳会把完整插件目录写入目标主机的 /var/lib/forwardx-agent/plugins/china-region-whitelist/。在插件使用页下方的"Agent 节点管理"中选择一台在线主机,面板会自动读取当前配置和规则状态;点击编辑回填表单后保存并应用,只修改当前 Agent;"清理规则"会清理该 Agent 上由插件创建的规则和配置。

插件定义、运行时和白名单数据随本项目维护,数据位于 plugins/china-region-whitelist/data/,入口位于 plugins/china-region-whitelist/forwardx-agent-run.sh;第三方数据来源和许可证边界见插件目录的 THIRD_PARTY_NOTICES.md

ForwardX Live2D 看板娘

插件 ID:live2d-widget,当前版本:1.0.0,许可证:AGPL-3.0-only + GPL-3.0-or-later。

官方对 stevenjoezhang/live2d-widget 的固定适配。面板只为此官方插件 ID 提供 Live2D 宿主,不会执行插件包中的任意前端代码。

插件默认停用。启用后可在插件设置中配置:

  • 显示范围、移动端显示开关、左右停靠和模型尺寸。
  • 模型 CDN、默认模型和默认服装。
  • 工具按钮:一言、切换模型、换装、拍照、项目说明和关闭。
  • 拖动行为、关闭后的重新唤起方式和日志等级。

上游 Cubism 运行时版本由 ForwardX 固定,避免普通插件通过设置项加载任意脚本;模型 JSON、纹理和动作仍由所选模型仓库提供。ForwardX 不打包模型资源,默认模型端点和可选工具可能有独立的版权、隐私和商业使用限制,管理员应在启用前确认对应仓库和服务的条款。插件目录中的 README.mdTHIRD_PARTY_NOTICES.mdLICENSE.live2d-widget.txt 保留了上游运行时、Cubism Core 与模型资源的声明。

第三方插件商店

第三方商店仓库在 main 分支根目录提供 forwardx-store.json。管理员可在"插件来源"中每行填写一个 GitHub 仓库地址并批量添加。商店页会标注每个插件来自官方商店还是具体第三方来源。

json
{
  "schemaVersion": 1,
  "name": "Example Plugin Store",
  "items": [
    {
      "id": "example-monitor",
      "name": "Example Monitor",
      "description": "主机状态集成插件",
      "detailsMarkdown": "插件的完整介绍。",
      "version": "1.2.0",
      "updatedAt": "2026-07-12",
      "author": "Example",
      "logo": "https://raw.githubusercontent.com/example/store/main/assets/example-monitor.png",
      "repository": "https://github.com/example/example-monitor",
      "homepage": "https://github.com/example/example-monitor",
      "packageUrl": "https://github.com/example/example-monitor/releases/download/v1.2.0/example-monitor.zip",
      "category": "integration",
      "permissions": ["read:hosts", "agent:read"],
      "extensionPoints": []
    }
  ]
}

items 也可写为 plugins。插件包下载地址使用 packageUrl;若压缩包直接保存在商店仓库中,可改用 packagePath,面板会从该商店仓库下载。压缩包仍必须是 .zip.tar.gz.tgz,并包含合法插件 manifest。配置清单中的名称、Logo、版本、说明等只用于商店展示,安装后以压缩包内 manifest 为准。

商店右上角的刷新按钮会同时强制同步官方商店和全部第三方来源。某个来源暂时不可用时,面板保留其上次成功同步的插件清单,并在来源管理中显示错误;第三方插件 ID 与官方插件冲突时,始终保留官方插件。

当前边界

插件不会直接运行第三方 JavaScript 或任意面板后端代码。插件能力通过 manifest 声明,由面板解释执行。声明 Agent 权限的插件可以执行随插件包下发的固定脚本入口;panel.request 还必须由管理员单独开启信任并经过操作白名单和权限校验。管理员仍应只安装可信插件,因为 Agent 通常拥有修改系统网络配置所需的权限。