规则编写指南
本指南涵盖规则引擎的常用模式、优先级策略和调试技巧。
基础格式
规则通过 HTTP API 或 MCP 工具以 JSON 格式创建。每条规则至少需要指定 id、filter 和至少一个 action:
// 最简规则:为所有 /api/ 请求添加日志头
{
"id": "log-api",
"name": "Log API requests",
"active": true,
"stage": "RequestHeaders",
"priority": 0,
"termination": "Continue",
"filter": {
"type": "Url",
"config": { "mode": "Contains", "value": "/api/" }
},
"actions": [
{
"type": "AddRequestHeader",
"config": { "name": "X-Logged", "value": "true" }
}
]
} 常用模式
1. 阻止追踪脚本
{
"id": "block-tracking",
"name": "Block analytics trackers",
"active": true,
"stage": "RequestHeaders",
"priority": 100,
"termination": "Stop",
"filter": {
"type": "Or",
"config": [
{ "type": "Url", "config": { "mode": "Contains", "value": "google-analytics.com" }},
{ "type": "Url", "config": { "mode": "Contains", "value": "doubleclick.net" }},
{ "type": "Url", "config": { "mode": "Contains", "value": "facebook.com/tr" }}
]
},
"actions": [{ "type": "Drop" }]
} 2. API Mock 响应
{
"id": "mock-api",
"name": "Mock /api/users",
"active": true,
"stage": "RequestHeaders",
"priority": 50,
"termination": "Stop",
"filter": {
"type": "And",
"config": [
{ "type": "Path", "config": { "mode": "Exact", "value": "/api/users" }},
{ "type": "Method", "config": { "mode": "Exact", "value": "GET" }}
]
},
"actions": [{
"type": "MockResponse",
"config": {
"status": 200,
"headers": { "Content-Type": "application/json" },
"body": { "type": "Text", "value": "[{\"id\":1,\"name\":\"Alice\"}]" }
}
}]
} 3. 环境切换(Map Remote)
{
"id": "map-staging",
"name": "Route to staging backend",
"active": true,
"stage": "RequestHeaders",
"priority": 50,
"termination": "Stop",
"filter": {
"type": "Host",
"config": { "mode": "Exact", "value": "api.example.com" }
},
"actions": [{
"type": "MapRemote",
"config": {
"url": "https://staging-api.example.com{{request.path}}",
"preserve_host": false
}
}]
} 4. 注入认证头
{
"id": "inject-auth",
"name": "Inject authorization header",
"active": true,
"stage": "RequestHeaders",
"priority": 10,
"termination": "Continue",
"filter": {
"type": "Host",
"config": { "mode": "Contains", "value": "internal" }
},
"actions": [{
"type": "UpdateRequestHeader",
"config": { "name": "Authorization", "value": "Bearer internal-token", "add_if_missing": true }
}]
} 5. CORS 头注入
{
"id": "cors-headers",
"name": "Add CORS headers",
"active": true,
"stage": "ResponseHeaders",
"priority": 100,
"termination": "Continue",
"filter": { "type": "All" },
"actions": [
{ "type": "AddResponseHeader", "config": { "name": "Access-Control-Allow-Origin", "value": "*" }},
{ "type": "AddResponseHeader", "config": { "name": "Access-Control-Allow-Methods", "value": "GET,POST,PUT,DELETE" }},
{ "type": "AddResponseHeader", "config": { "name": "Access-Control-Allow-Headers", "value": "Content-Type,Authorization" }}
]
} 0.5.1 注意:ResponseHeaders 阶段的修改会写入 flow 记录,但当前 CLI 代理尚未将变更后的响应头写回实际 HTTP 响应(已知限制)。
6. 响应体内容替换
{
"id": "redact-secrets",
"name": "Redact sensitive data in responses",
"active": true,
"stage": "ResponseBody",
"priority": 100,
"termination": "Continue",
"filter": {
"type": "ResponseHeader",
"config": { "name": "Content-Type", "value": { "mode": "Contains", "value": "json" }}
},
"actions": [{
"type": "TransformResponseBody",
"config": {
"type": "RegexReplace",
"config": { "pattern": "\"token\":\\s*\"[^\"]+\"", "replacement": "\"token\":\"***\"" }
}
}]
} 7. IP 黑名单(Connect 阶段)
{
"id": "block-ips",
"name": "Block internal IPs",
"active": true,
"stage": "Connect",
"priority": 100,
"termination": "Stop",
"filter": { "type": "SrcIp", "config": "10.0.0.0/8" },
"actions": [{ "type": "Drop" }]
} 8. 限速保护
{
"id": "rate-limit",
"name": "Per-client rate limiting",
"active": true,
"stage": "RequestHeaders",
"priority": 90,
"termination": "Continue",
"filter": { "type": "All" },
"actions": [{
"type": "RateLimit",
"config": { "key": "{{ip}}", "limit": 60, "window_ms": 60000 }
}]
} 9. 跨规则变量传递
// 规则 1:提取 API 版本并存入变量
{
"id": "extract-version",
"priority": 100,
"stage": "RequestHeaders",
"filter": { "type": "Path", "config": { "mode": "Regex", "value": "/api/v(\\d+)/" }},
"actions": [{
"type": "SetVariable",
"config": { "name": "api_version", "value": "v1" }
}]
}
// 规则 2:读取变量写入响应头
{
"id": "add-version-header",
"priority": 50,
"stage": "ResponseHeaders",
"filter": { "type": "All" },
"actions": [{
"type": "AddResponseHeader",
"config": { "name": "X-Api-Version", "value": "{{variable.api_version}}" }
}]
} 10. WebSocket 消息过滤
{
"id": "ws-mock",
"name": "Mock incoming heartbeat messages",
"active": true,
"stage": "WebSocketMessage",
"priority": 50,
"termination": "Stop",
"filter": {
"type": "WebSocketMessage",
"config": { "mode": "Contains", "value": "heartbeat" }
},
"actions": [{
"type": "MockWebSocketMessage",
"config": { "direction": "Incoming", "message": "{\"type\":\"heartbeat\",\"ts\":\"mocked\"}" }
}]
} 优先级策略
优先级(priority)是规则排序的核心机制。推荐策略:
| 优先级范围 | 用途 | 典型 termination |
|---|---|---|
| 100-90 | 阻断/安全规则(Block/Drop) | Stop |
| 89-50 | 路由规则(Mock/MapRemote/Redirect) | Stop |
| 49-10 | 修改规则(Headers/Body) | Continue |
| 9-0 | 观测规则(Tag/Log/Inspect) | Continue |
Termination 选择指南
- Stop:当规则执行后后续规则没有意义时使用。例如 Block、MockResponse、MapRemote 后不需要其他规则再处理
- Continue:修改型规则(修改 header/body)、观测型规则(打 tag/设变量)通常应 Continue,允许多条规则叠加效果
规则↔脚本联动
RelayCore 独家——规则与脚本互感知,实现单一引擎无法完成的复杂逻辑。
0.5.1 注意:脚本在 onRequestHeaders 阶段先于规则执行;读取 flow.rule_variables 请在 onRequest。脚本直接返回 { action: "respond" } 尚未支持,Mock/阻断请用规则 action。
联动方向
| 方向 | 机制 | 1.0 |
|---|---|---|
| 规则→规则 | SetVariable + Mustache | ✅ |
| 规则→脚本 | flow.matched_rules / rule_variables | ✅ |
| 脚本→脚本 | sharedState 同引擎实例 | ✅ |
| 脚本→规则 | setTag / setVariable | 1.x |
场景 1:规则提取 → 脚本决策
规则执行轻量级匹配并提取变量,脚本做复杂判断和处理:
// ===== 规则 JSON =====
[{
"id": "extract-api-version",
"stage": "RequestHeaders",
"priority": 100, "termination": "Continue",
"filter": { "type": "Path", "config": { "mode": "Regex", "value": "/api/v(\d+)/" }},
"actions": [{ "type": "SetVariable", "config": { "name": "api_version", "value": "v1" }}]
}, {
"id": "extract-auth-info",
"stage": "RequestHeaders",
"priority": 90, "termination": "Continue",
"filter": { "type": "RequestHeader",
"config": { "name": "Authorization", "value": { "mode": "Contains", "value": "Bearer" }}},
"actions": [{ "type": "SetVariable", "config": { "name": "auth_type", "value": "bearer" }}]
}]
// ===== 脚本 =====
globalThis.onRequest = (ctx, flow) => {
const v = flow.rule_variables["api_version"];
const a = flow.rule_variables["auth_type"];
if (v === "v1" && a !== "bearer") {
flow.tags.push("deprecated-api");
// 返回 410 请用 MockResponse 规则;{ action: "respond" } 为 1.x 路线图
}
if (v === "v2") {
const key = flow.network.client_ip + ":v2";
const counts = sharedState.get("_v2counts") || {};
counts[key] = (counts[key] || 0) + 1;
sharedState.set("_v2counts", counts);
flow.layer.data.request.headers.push(["X-Rate-Count", String(counts[key])]);
}
return flow;
}; 场景 2:规则打标记 → 脚本继承
规则在 RequestHeaders 阶段打 Tag 和 SetVariable,脚本在 Response 阶段读取并追加性能头:
// ===== 规则 =====
{
"id": "tag-slow", "stage": "RequestHeaders", "priority": 50,
"filter": { "type": "Or", "config": [
{ "type": "Path", "config": { "mode": "Contains", "value": "/report" }},
{ "type": "Path", "config": { "mode": "Contains", "value": "/export" }}
]},
"actions": [
{ "type": "Tag", "config": { "key": "type", "value": "slow" }},
{ "type": "SetVariable", "config": { "name": "trace", "value": "1" }}
]
}
// ===== 脚本 =====
globalThis.onResponse = (ctx, flow) => {
if (!flow.matched_rules.includes("tag-slow")) return;
if (flow.layer.type !== "Http" || !flow.layer.data.response) return;
const dur = Date.now() - new Date(flow.start_time).getTime();
flow.layer.data.response.headers.push(["X-Server-Timing", "total;dur=" + dur]);
sharedState.delete(flow.id);
}; 场景 3:规则限速 + 脚本智能降级
规则触发限速后,脚本可缓存成功响应;直接返回缓存/429 需 { action: "respond" }(1.x 路线图)。0.5.1 可先做缓存与打标:
// ===== 规则 =====
{
"id": "rate-limit", "stage": "RequestHeaders", "priority": 100,
"filter": { "type": "All" },
"actions": [
{ "type": "RateLimit",
"config": { "key": "{{ip}}", "limit": 100, "window_ms": 60000 }},
{ "type": "SetVariable",
"config": { "name": "limited", "value": "1" }}
]
}
// ===== 脚本 =====
function pathname(url) {
const start = url.indexOf("://");
const slash = start >= 0 ? url.indexOf("/", start + 3) : url.indexOf("/");
return (slash >= 0 ? url.slice(slash) : "/").split("?")[0];
}
globalThis.onResponse = (ctx, flow) => {
if (flow.layer.type !== "Http" || !flow.layer.data.response) return;
const res = flow.layer.data.response;
if (res.status >= 300) return;
const path = pathname(flow.layer.data.request.url);
const cache = sharedState.get("_cache") || {};
cache[path] = {
status: res.status, headers: res.headers,
body: res.body, cachedAt: Date.now() };
sharedState.set("_cache", cache);
};
globalThis.onRequest = (ctx, flow) => {
if (flow.rule_variables["limited"] !== "1") return flow;
const path = pathname(flow.layer.data.request.url);
const cache = sharedState.get("_cache") || {};
const c = cache[path];
if (c && Date.now() - c.cachedAt < 300000) {
flow.tags.push("cache-hit");
// 1.x:{ action: "respond" } 可直接返回 c
}
return flow;
}; 场景 4:脚本→规则(1.x 路线图)
当前 Script → Rule 方向(ctx.setTag / ctx.setVariable)已降级 1.x,未来可实现:
// 1.x 愿景
globalThis.onRequest = (ctx, flow) => {
const body = flow.layer.data.request.body;
const raw = body ? atob(body.content || "") : "";
if (raw.includes("DROP TABLE")) {
ctx.setTag("attack", "sqli"); // 1.x
ctx.setVariable("why", "SQLi"); // 1.x
}
};
// 后续规则匹配脚本标记并自动 Drop 调试技巧
查看命中规则
# 在脚本中查看
globalThis.onResponse = (ctx, flow) => {
console.log("Matched rules:", flow.matched_rules);
console.log("Rule variables:", flow.rule_variables);
}; 规则验证
# CLI 验证规则文件
relay-core-cli rules validate rules.json API 查询规则列表
curl http://127.0.0.1:8082/api/v1/rules | python3 -m json.tool 常见问题
- 规则不生效:检查
"active": true和 stage 是否匹配流量阶段 - 多个动作执行顺序:actions 数组按顺序执行;若前一个动作是终端动作(MockResponse 等),后续动作不执行
- Inspect 断点在 CLI 模式不工作:CLI 没有桌面 UI 来展示拦截界面。Inspect 需要 Tauri(RelayCraft 桌面端)才能暂停流并等待用户操作。CLI 下 Inspect 是 no-op,不会暂停流量
- MapLocal 文件访问:需配置 sandbox_root;未配置时发出 deprecation warning,1.1 起将拒绝执行
- body 较大时性能:ResponseBody filter 需要缓存完整响应体;大文件建议用 Path filter 替代
- ResponseHeaders 未写回客户端:0.5.1 CLI 下
AddResponseHeader等修改会出现在 flow 记录中,但尚未同步到 curl/浏览器实际响应 - MapRemote 循环:注意不要将流量转发回 RelayCore 自身代理端口