规则编写指南

本指南涵盖规则引擎的常用模式、优先级策略和调试技巧。

基础格式

规则通过 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 / setVariable1.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 自身代理端口