跳到正文

实验中的数据 ​

值在一次运行中流动:一个参数选择目标,一个响应的字段成为下一个请求的标头,一个生成的 id 随命令发出又在检查中返回。本页介绍这些值来自哪里,以及字段如何使用它们。

来源写法在哪里设置
参数{{api}} 或 {{params.api}}参数 面板、配置、自定义运行…
变量{{token}} 或 {{vars.token}}运行中的某个节点:提取值、等待节点、发送并等待回复的节点
机密{{secret.API_TOKEN}}计算机的凭据存储,或服务器的环境变量和文件
内置值{{run.seed}}、{{now.iso}}、{{counter}}运行本身
生成器{{uuid}}、{{random_int(1, 100)}}从本次运行的种子中抽取

参数 ​

参数是一个具名文本值,任何模板字段都可以使用它。把目标放进参数里,这样更改地址只需改一处,而不是每个节点各改一次。

添加参数 ​

  1. 在编辑器工具栏中按 参数({ })。
  2. 在 默认值 标签页上,按 添加参数。
  3. 输入 名称 和 值,例如 api 和 http://127.0.0.1:8080。
  4. 在某个节点的字段中,写出 {{api}}/login。

面板中的每次更改都是对实验的一次编辑:它会随实验一起保存,也能像其他编辑一样用 Ctrl+Z 撤销。

规则 ​

规则限制
名称以字母或 _ 开头,之后是字母、数字和 _
保留名称vars、params、secret、run、node、now、uuid、counter、random_int、random_float、pick
每个实验的参数64
单个值的大小64 KiB
名称唯一;变量不能与参数同名

值就是纯文本,按原样插入:值内部的 {{…}} 不会被解析。当某个字段要求参数的一部分 ({{config.ports[0]}})时,该值会按 JSON 读取;不是 JSON 的值没有部分。

名称无效、为保留名或重复的参数不会阻止实验保存,因此您可以继续输入;在名称修正之前,实验不会运行。

配置 ​

配置是一组具名的参数值——笔记本、舞台、场馆——因此切换目标是一种选择,而不是逐个编辑节点。配置更改部分参数;其余参数保持默认值。

创建配置 ​

  1. 打开 参数 并按 配置。会打开一个新标签页。
  2. 在 配置名称 中重命名它。
  3. 对配置更改的每个参数,输入其值。留空的字段保持 默认值,字段中会以灰色显示;使用默认值(↺)会清除 一个值。
  4. 按 用于运行 用它来运行。使用中的配置标签页带有 ● 使用中。在 默认值 标签页上按 用于运行 会回到默认值。

实验有了配置后,工具栏中的 配置 列表可在它们之间切换。运行、预览和 立即发送 都使用使用中的配置,它保存在实验中,因此导出的文件打开后具有相同的目标。删除配置 会删除屏幕上显示的配置。

规则限制
每个实验的配置32
名称1–64 个字符,唯一(首尾空格不计)
值只能是已存在的参数;最多 64 个

重命名或删除参数会同时更改每个配置中的它。

一次运行使用哪个值 ​

后者生效:

  1. 参数的默认值,在 默认值 标签页上;
  2. 使用中的配置的值(如果它设置了该值);
  3. 在 自定义运行… 中仅为本次运行输入的值—— 参见使用其他值运行。

自定义运行… 只能设置实验已有的参数。运行报告 会记录所用的配置、为本次运行输入的值以及它使用的每一个值。

无法运行的配置 ​

每次检查实验时,其他配置和默认值也会被检查。某个会失败的配置——比如一个不是 http:// 或 https:// 的 URL——会在标签页和工具栏列表中带有 ⚠,其工具提示会说明原因。它不会阻止使用其他配置的运行。

模板 ​

{{ }} 内的文本是一个表达式;字段中的其他一切都会按原样保留。

text
{{api}}/users/{{user.id}}?trace={{uuid}}
Bearer {{secret.API_TOKEN}}
  • 花括号内的空格无关紧要:{{ token }} 就是 {{token}}。
  • \{{ 写出一个字面量 {{。
  • 单独的 }} 是纯文本。
  • 值按原样插入,不带引号。在 JSON 正文中,引号请自行书写:"id": "{{uuid}}"。

名称 ​

表达式值
{{name}}如果这条路径上设置了变量 name,则为该变量,否则为参数 name
{{vars.name}}仅变量
{{params.name}}仅参数
{{secret.NAME}}已存储的机密 NAME——参见机密
{{name.field}}JSON 值的一个字段
{{name[0]}}JSON 数组的一个元素
{{name["a b"]}}、{{name['a b']}}名称含其他字符的字段

. 之后的字段名可以包含字母、数字、_ 和 -。可以连续使用: {{reply.args[0]}}、{{order.items[2].sku}}。

值的写法 ​

值写法
文本文本本身
数字其最短形式:42、0.5
true、falsetrue、false
nullnull
对象、数组紧凑 JSON:["x","y"]

内置值 ​

表达式值
{{run.id}}本次运行的任务编号;在预览和 立即发送 中为 0
{{run.seed}}本次运行的种子
{{node.id}}正在执行的节点的 id
{{now}}当前时间,Unix 毫秒
{{now.iso}}当前 UTC 时间,ISO 8601,带毫秒:2026-09-30T12:34:56.789Z
{{counter}}此节点在本次运行中运行了多少次,包括本次,从 1 开始

{{counter}} 按节点计数:在循环的循环体中它是 迭代的编号,在重复的节点中它是发送的编号。 run、node 和 now 只有列出的字段;其他任何内容都是错误。

生成器 ​

表达式值
{{uuid}} 或 {{uuid()}}一个版本 4 UUID
{{random_int(min, max)}}从 min 到 max 的整数,两端都包含;参数为整数,min ≤ max
{{random_float(min, max)}}从 min 到(但不包括)max 的数,带 3 位小数;min < max
{{random_float(min, max, digits)}}同上,带 digits 位小数,0–9
{{pick(a, b, c)}}参数之一,至少一个

参数用逗号分隔。带引号的参数("dark blue" 或 'a, b') 可以包含除自身引号外的任何内容;不带引号的参数可以包含字母、 数字和 _ - . : / +。空参数是错误。

每个生成器都从本次运行的种子中抽取。一个节点的一次执行所得到的值 只取决于种子、节点的 id 以及该节点已运行了多少次,因此并行分支绝不会 改变彼此的值,相同的种子会再次生成相同的值。一个节点内的抽取 遵循其字段的顺序。{{now}} 和 {{run.id}} 不可重现。参见 种子。

建议 ​

在模板字段中输入 {{,或按 Ctrl+Space, 会打开一个分四组的列表:参数(带其值)、 变量(在此节点上游设置的,带设置它们的节点 (回复的字段也是,例如 reply.args[0]))、机密 和 生成器。↑ 和 ↓ 选择, Enter 或 Tab 插入,Esc 关闭列表并 保留字段内容。

未知名称是错误 ​

没有值的名称绝不会变成空字符串。运行之前,字段使用的每个 名称都必须是参数、有效的机密名称,或者在通往该节点的每一条路径上都设置了变量。编辑器会指出节点和 字段:

问题运行之前运行期间
没有人设置的名称name.unknown—
只在一部分路径上设置的变量name.not_on_every_path—
{{params.x}} 却没有参数 xparam.unknown—
值不具有的字段—template.no_field
未闭合的 {{、空的 {{}}、格式错误的参数template.*,带位置—

这些代码的文本见错误。

哪些字段接受模板 ​

节点模板字段
HTTP 请求URL、标头名称和值、正文、Basic 和 Digest 的用户名和密码、Bearer 令牌
OSC 消息目标、地址、文本参数;带回复时:其地址模式和规则值
UDP 数据报目标、载荷;带回复时:其模式
TCP 消息主机、载荷
MQTT 发布代理主机、主题、载荷
日志标记消息
响应文本预期文本
响应标头标头名称、预期文本
检查值、按值分支、循环 的退出条件值、预期值
等待 OSC地址模式、规则值
等待 UDP、等待 WebSocket模式
等待 MQTT代理和主题(仅参数)、模式
等待 HTTP 请求路径模式、条件
网络损伤监听和目标(仅参数)
WebSocket 连接URL、标头名称和值
WebSocket 发送载荷
WebSocket 关闭原因

数字——端口、超时、延时、状态码、带类型的 OSC 数字——以及等待节点的 监听地址都是字面量。模拟器 用收到的内容 ({{request.…}})和参数来渲染自己的回复;参见 故障。

仅参数。 有些字段在第一个步骤之前就已打开,此时还没有 变量存在:等待 MQTT 的代理和主题、一个 网络损伤 的监听和目标。它们只接受文本和参数, 没有别的(node.params_only)。

像字面量一样检查。 只使用参数的字段会在运行前解析,并按 本次运行将发送的文本进行检查:URL 必须是 http:// 或 https://,OSC 目标为 IP:port 或 host:port,标头名称有效。带 变量或生成器的字段在运行时检查。

预览 ​

当选中的节点带有模板时,其属性会显示它在已知当前值的情况下将会做什么:发送为 将发送, 等待为 将等待,比较为 将比较。 引擎会解析它,使用的代码与运行完全相同,因此预览绝不会与运行不一致。

  • 参数来自使用中的配置。
  • 变量来自编辑器在此会话中见过的内容:上次运行的步骤, 以及 立即发送。
  • 已存储的机密显示为 ••••。
  • 尚无值的名称按原样保留,预览会列出它。未存储的 机密会单独列出。
  • 生成器使用实验固定的种子,未固定时使用 0,作为 节点的首次执行。固定了种子时,预览会显示节点在运行中首次执行 将发送的生成值。

提取值 ​

提取值 读取其路径上最近一次 HTTP 响应中的一个值, 并把它写入变量。

字段内容
变量要写入的变量;适用参数的命名规则
提取自值来自哪里(见下文)
JSON 路径、标头名称 或 模式(有分组时取第 1 组)取决于来源,要读取的内容
提取自读取值
JSON 字段按路径读取作为 JSON 的正文JSON 值:文本、数字、对象、数组
标头该名称的第一个标头,不区分大小写文本
状态码状态码数字
整个正文整个正文文本
正则表达式正文中的第一个匹配如果模式有分组,则为第 1 个捕获组,否则为整个匹配

JSON 路径。 $.token、$.items[0].id、$["a b"]、$['a b']['c-d']; 开头的 $. 可以省略(token、items[0].id),单独的 $ 表示 整个正文。

正则表达式使用 Rust regex 引擎的语法,它没有 环视,也没有反向引用。匹配会在正文中任意位置搜索;需要时 用 ^ 和 $ 锚定。

在下列情况下,步骤会失败并指出缺少什么:

  • 在这条路径上它之前没有 HTTP 请求运行过(check.no_response;编辑器 在无法成立的图中已经拒绝,graph.needs_http);
  • 正文不是 JSON,或者路径不在其中;
  • 标头不存在,或者模式不匹配;
  • 对于 JSON 路径或整个正文,正文超过响应保留的 256 KiB; 或者对于模式,在保留的部分中没有任何匹配 (extract.truncated)。

时间线会显示写入的值:token = abc123。

点击即可提取

在 HTTP 请求 上使用 立即发送 会显示其 JSON 响应。点击其中的一个 值:请求之后会添加一个 提取值 节点,路径已填好,名称取自该键,该值会立即 为预览所知。

变量 ​

变量保存一个 JSON 值。以下节点会写入变量:

节点写入在哪个输出上
提取值提取的值其输出
等待 OSC、等待 UDP、等待 MQTT、等待 HTTP 请求、等待 WebSocket到达的内容,默认名 reply(HTTP 为 request)仅 已匹配
OSC 消息、UDP 数据报 带 等待回复回复,默认名 reply其输出

等待节点写入的是一个对象;之后的字段读取它的部分:

等待字段
OSCaddress、args、from、ms
UDPtext、hex、bytes、from、ms,以及带模式时的 match
MQTTtopic,以及 UDP 的字段
WebSocketUDP 的字段,消息为 JSON 时还有 json
HTTP 请求method、path、query、headers、body、json、params、from、ms

ms 是从分支上最近一次动作到到达的时间。确切内容见节点参考。

变量在哪里可见 ​

变量从写入它的那个输出起存在,在经过该输出的路径上存在:

  • 在可选路径合并之后——分支的 是 和 否 再次汇合——只有每一条路径都设置的值才是已知的。
  • 在 汇合分支 之后,任何汇入它的分支任意一个 设置的值都是已知的:它们全都运行过。
  • 在 循环 的 完成 或 上限 之后,以及在其退出条件中,循环体的每次迭代都设置的值是已知的。
  • 等待节点的变量在其 超时 输出之后不再已知。

每个并行分支都在自己的变量副本上工作。Join 按传入接线的顺序合并这些 副本,两条都设置了同名值时后面的接线胜出,因此结果绝不取决于 哪个分支先完成。参见 运行如何流动。

比较值 ​

检查值 在比较不成立时使运行失败; 按值分支 从 是 或 否 离开; 一个 循环 使用同样的比较作为其退出条件。每个都有 值、条件 和 预期值 值,两段 文本都是模板:

值条件预期值
{{status}}小于300
{{reply.args[0]}}等于{{nonce}}
条件成立条件
等于、不等于两者相等(不相等)——都为数字时按数字比较(200 等于 200.0),否则按精确文本比较,区分大小写
小于、不大于、大于、不小于按数字比较;某一侧不是数字时步骤失败(compare.not_numbers),而不是安静地给出否
包含值包含预期文本,区分大小写
匹配正则预期值中的正则表达式在值中任意位置匹配
为空、不为空去除空格后值为空,或不为空;不使用预期值

数字是去除空格后能读作数字的文本:42、-1.5、 1e3。时间线会显示实际进行的比较,401 = 200,每一侧截断 到 120 个字符。

机密 ​

令牌或密码以 {{secret.NAME}} 的形式放入字段。实验 文件只保留名称;值留在它被存储的地方,绝不 进入界面。

机密存放在哪里 ​

Signal Lab 运行在哪里存储从界面
Windows 桌面应用Windows 凭据管理器,服务名 SignalLab 下,每个名称一条设置、替换、移除
Linux 桌面应用无:需要机密的运行会以 secret.unsupported 失败—
服务器环境变量 SIGNALLAB_SECRET_<NAME>,否则是其机密文件夹中的文件 <NAME>,默认 /run/secrets/signallab,除非另有设置只读
命令行 signallab像服务器一样,或者用 --secrets system 使用 Windows 凭据管理器—

机密 部分的标题带有一个工具提示,说明您所在的位置适用于其中哪一种: Windows 存储、服务器的环境变量和 文件,或者——在 Linux 桌面应用中——没有存储。在那里, secret.unsupported 说明机密保存在 Windows 凭据 管理器中,而该系统没有它。

机密属于计算机或服务器,而不属于某一个实验:使用 {{secret.API_TOKEN}} 的两个实验使用同一个值。

在服务器上,环境变量优先于文件。文件末尾的 换行不算值的一部分,空文件视为无机密。服务器的 文件夹用 --secrets-dir 或 SIGNALLAB_SECRETS_DIR 设置; 参见服务器。命令行见 signallab run。

规则限制
名称以字母或 _ 开头,之后是字母、数字和 _;最多 128 个字符
值非空,最多 16 KiB

设置机密 ​

在 Windows 上:

  1. 打开 参数。机密 部分列出实验的字段 使用的每一个机密,每个都标有 已存储 或 此计算机上未设置。
  2. 按名称旁边的 设置…,或对尚无字段使用的名称按 机密。
  3. 输入值——字段显示为圆点——然后按 保存 或 Enter。字段会被清空;任何东西都无法再读回该值。

替换… 存储一个新值,移除 从凭据存储中删除它。在此会话中存储过的名称也会出现在建议中。

在连接到服务器的浏览器中,该部分只会显示 已在服务器上设置 或 服务器上未设置:在服务器运行的地方设置值,有两种方式:

bash
# in the server's environment
SIGNALLAB_SECRET_API_TOKEN='…'
# or as a file in its secrets folder
printf '%s' '…' > /run/secrets/signallab/API_TOKEN

文件每次运行开始时都会读取,因此更改过的文件从下一次运行起生效;更改过的环境变量需要重启服务器。

运行之前 ​

本次运行的字段使用的每一个机密都必须已存储。缺少的机密会在任何流量之前、 在第一个使用它的节点和字段处停止运行 (secret.missing)。立即发送 对其节点做同样的检查。

遮蔽 ​

当一次运行或一次 立即发送 使用机密时,它们值的每次出现 都会在离开引擎的一切内容中被替换为 ••••:

  • 步骤文本、错误,以及步骤写入的变量;
  • 运行报告;
  • 立即发送 的结果,包括它显示的 HTTP 响应;
  • 检查器 的帧,在运行持续期间捕获的——在十六进制 转储中,值的每个字节变成 *,因此偏移量保持正确。

Basic 认证以 base64 发送 name:password;当其中任一部分包含 机密时,那段 base64 文本也会被遮蔽。流量本身携带真实 值。预览将已存储的机密显示为 ••••。一个 模拟器 的回复不能使用机密。

命令 ​

预览是 experiment_resolve; 机密用 secret_status、 secret_set 和 secret_delete 列出、设置和移除。没有任何命令会返回 机密的值。