上篇《ads.txt / app-ads.txt / sellers.json》 讲透了三份静态声明——媒体用 ads.txt/app-ads.txt 声明「谁被授权卖我」,中介用 sellers.json 声明「系统内每个账号背后是谁」。但静态声明只回答「谁被授权」,回答不了「一条具体的请求实际经过了哪些中介、顺序如何」。补上后半问的,是 OpenRTB 的 SupplyChain 对象(schain)——本篇的主角。
三份静态声明是事先公布的授权表,schain 则是随每条 bid request 逐跳生长的运单:每个中介转发请求前,都须将自身作为一个 node 追加进去,记录「我是谁、从谁手中接手」。买方收到请求后,以运单上的每一跳比对授权表,即可判定这条链是否完整、每一手是否被授权——这正是把供应链从「可声明」推进到「可核验」的最后一环。本篇同时收入买方侧的完整工程:先离线将三份声明抓取、清洗、编译成索引,再在竞价热路径上以 schain 逐跳核验。
schain 是一条「谁经手了谁」的运行时账。
nodes[0] 最靠近媒体,每多一跳中介就 append 一个 node;complete=1 是完整性承诺。每个 node 的 asi/sid 正是 sellers.json 和 ads.txt 的对账键——运行时记录在这里接回上篇的静态声明。
TL;DR
- schain 是什么:OpenRTB 的
source.schain(早期在source.ext.schain),一个有序 node 数组,随请求逐跳生长,记录库存实际经过的每一手中介。 - node 字段:
asi(该中介域名)、sid(卖方在该系统里的账号)、hp(是否参与本次结算);顶层complete(1=链从媒体到发出请求的交易所完整无缺)、ver。 - 两条铁规:① 每个中介转发前必须把自己追加为一个 node,且
nodes[0]最靠近媒体、顺序不能乱;② 只要有一跳没加进来,complete就该标0——它是一句可被证伪的承诺。 - 核验闭环:对 schain 逐跳走一遍——
complete==1?→ 每个 node 的sid在asi的 sellers.json 里存在?→ 从 sellers.json 拿到卖方domain、回查其 ads.txt 是否授权(asi, sid)?全过且 complete → 可信路径。 - 信任锚点:伪造者能改 bid request 的字段,但改不了媒体根目录那份自己控制的 ads.txt——要让整条链自洽,得同时篡改 ads.txt + sellers.json + schain 且互相对得上,成本陡增到不划算。
- 离线工程:买方先把全网 ads.txt/app-ads.txt/sellers.json 抓全、清洗、编译成
authMap/sellersMap/bundleMap三张内存索引——分布式爬虫 + 定时刷新,两大坑是「200 ≠ 有文件」软 404 与「文件普遍很脏」。 - 热路径工程:核验挂在竞价漏斗很靠前一段,纯读离线建好的三张内存索引、亚毫秒完成、失败向「降权/放行」降级;核验是尽力而为,灰色地带降权而非硬拒。
- 它还喂给 SPO:schain 暴露真实跳数,买方据此收敛冗余路径、砍 ad tech tax——透明度是供应链优化的前提。
Table of contents
Open Table of contents
一、schain 是什么:运行时逐跳记录
上篇的 ads.txt/sellers.json 都是静态声明:媒体和中介各自「事先」贴出的授权表。但一条具体的 bid request 到底实际经过了哪些中介、顺序如何?这要靠 OpenRTB 里的 SupplyChain 对象(schain)——它随请求逐跳生长,记录真实路径。
schain 挂在 source.schain(OpenRTB 2.6;早期实现放在 source.ext.schain),核心是一个有序的 node 数组。node 与顶层字段:
| 字段 | 含义 |
|---|---|
asi | 广告系统标识(该中介的域名)——对应 sellers.json 发布域名、ads.txt 的广告系统域名 |
sid | 卖方在该系统里的账号 ID——对应 sellers.json 的 seller_id、ads.txt 的账号 ID |
hp | 该 node 是否参与本次结算(handles payment),通常为 1 |
rid / name / domain | 请求 ID、可选的名称和域名 |
complete(顶层) | 1=链完整(从媒体到发出请求的交易所每一跳都在);0=不完整 |
ver(顶层) | schain 版本 |
asi与sid正是上篇反复强调的对账键:asi是广告系统域名(据此即可拼出https://{asi}/sellers.json),sid是卖方在该系统内的账号编号。schain 的每个 node 即「在某个 asi 内、由某个 sid 经手的这一跳」——运行时记录采用的正是静态声明的同一套键,两边才能对齐。
几条工程规则:
- 每个中介必须将自己追加为一个 node,且顺序正确(
nodes[0]是最上游、直连媒体的那一跳)。漏加或乱序都会导致下游核验失败。 complete=1是一句可被证伪的承诺:只要有任一跳未把自己加入,理论上就应标0。买方看到complete=0,即知这条链存在未申报的中间跳。- schain 不替代 ads.txt/sellers.json,而是将它们串联:它给出「实际路径」,前两者给出「每一跳是否被授权」——买方以 schain 的每个 node 回查另外两份声明,方才完成核验。
这恰好回应上篇留下的库存洗钱难题:洗钱的本质是「买方看不到经了几手、每一手是否被授权」。schain 将「经了几手」变成一个公开的有序数组——若想隐去中间跳,就须不把自己加入,complete 便只能标 0(暴露异常);若如实全部加入,那几手转售则完全暴露在买方眼前(进入下文的逐跳核验)。两条路都被堵死。
二、把声明和运行时接上:买方交叉核验闭环
单看每个标准都只是「一份表」;真正产生价值的,是买方将它们交叉核验的那一刻。核验逻辑是一条决策链:
核验是一条「层层收紧」的漏斗:先看链完不完整(schain.complete),再看每一跳的卖方在不在中介的 sellers.json 里,再回查卖方域名的 ads.txt 授没授权这一跳;全过才是可信路径,还能顺带按跳数做 SPO。任一步不过,对应到「降权」或「丢弃」。
一条 bid request 的完整核验,就是对它的 schain 走一遍:
- 完整性:
schain.complete == 1?不完整说明有隐藏的中间跳——保守买方直接拒,多数买方降权。 - 卖方存在性:对每个 node,去
asi的 sellers.json(离线建好的sellersMap,见第三节)里查sid是否存在。查不到 = 未知卖方(unauthorized),拒。 - 授权一致性:从 sellers.json 拿到该卖方的
domain,回查该domain的 ads.txt/app-ads.txt(离线建好的authMap)——里面是否有(asi, sid)这条授权记录?没有 = 域名伪造或非法转售,丢弃。 - 路径质量(SPO):全部通过后,链是否有冗余跳数?这不是「真伪」问题而是「效率」问题——买方据此优先走更短、更少抽成的路径(见第五节)。
以域名伪造这一典型欺诈验证闭环:一条请求声称库存来自 nytimes.com,schain 中带有某个 (asi, sid)。买方回查 nytimes.com/ads.txt——发现其中并无这条 (asi, sid) 的授权 → 判定伪造、丢弃。伪造者可篡改 bid request 中的字段,却改不了 nytimes.com 根目录下那份由纽约时报自身控制的 ads.txt。信任锚点从「上游自报」转移到「媒体自身掌控的公开文件」——这正是整套设计的支点。
再以上篇那条库存洗钱链(X → SSP-A → ADX-B → SSP-C)验证一遍:若 X 试图隐去自己这一跳,schain 中便少一个 node、complete 应为 0,第①步即被拦下;若如实记录,则走到第③步,回查 nytimes.com/ads.txt 会发现并无授权 X 对应的 (asi, sid)——未授权,丢弃。无论隐去与否,洗钱链都通不过这个闭环。
一句话本质:四件套单独均可被绕过或造假,但交叉核验将它们相互锁定——伪造者须同时篡改「媒体的 ads.txt + 中介的 sellers.json + 请求的 schain」并使三者自洽,成本陡增至不划算。
三、买方侧工程(离线):全网抓取、解析、建索引
核验逻辑清楚了,但真正的工程量在于把这三份文件抓取完整、清洗干净、编译成可在竞价热路径上 O(1) 查询的索引。这是一道典型的「离线重、在线轻」读写分离题:重活儿全部放在离线,热路径只做纯读——与频次控制工程篇属同一套思路。
① 离线:全网抓取 + 定时刷新。 买方要维护三类数据:
- 全网媒体的
ads.txt(按 Web 域名)——数百万量级; - App 开发者的
app-ads.txt(外加bundle id → 开发者域名的商店映射); - 各中介的
sellers.json(按广告系统域名)——数量少但更新频繁。
这是一个分布式爬虫 + 存储 + 调度问题:按域名分片抓取、遵守缓存头、错峰刷新(热门域名勤刷、长尾域名稀刷)、处理超时与 404、监控覆盖率。文件并非一成不变——媒体会增删授权、中介会调整 sellers.json,刷新周期直接决定核验的时效与误杀率。sellers.json 还常见大厂文件达数十万条、上百 MB(用 gzip + ETag 增量刷新)、302 跳转至 CDN 等情况。
抓取阶段有两个极易踩中的坑,值得单列:
200≠ 有文件:大量站点对不存在的/app-ads.txt不返回 404,而返回一个 HTTP 200 的网页(首页、SPA 路由页、软 404)。仅凭状态码即认定「抓到了」,会将大量 HTML 垃圾灌入索引。判定文件是否存在,必须校验content-type为纯文本(sellers.json 则须为 JSON 且能解析出sellers[])、且内容能解析出合法记录行,否则一律视同「无」。- 文件普遍很脏,解析须容错 + 归一化:即便是大厂的正规 app-ads.txt,也常见域名/关系字段大小写混用(
Pubmatic.comvspubmatic.com、RESELLERvsreseller)、逗号前后带空格或漏逗号、第④字段 TAG-ID 抄错 / 截断,以及海量重复行。解析器须:统一转小写、trim 空格、按逗号切分后校验字段数、忽略#注释与空行、按(asi,sid,关系)去重,并对畸形行「跳过而非整文件报错」。
② 预构索引。 把抓来的原始文件编译成热路径能 O(1) 查的结构,典型三张:
authMap: (publisher_domain) → set of authorized (asi, sid) # 来自 ads.txt/app-ads.txt
sellersMap: (asi) → { sid → {domain, seller_type, confidential} } # 来自 sellers.json
bundleMap: (app.bundle) → developer_domain # 来自商店爬取
这三张表就是核验闭环的「弹药库」:authMap 回答「该媒体授权了哪些 (asi,sid)」、sellersMap 回答「某 asi 下某 sid 背后是谁」、bundleMap 将 App 请求翻译为可回查的开发者域名。它们与频次控制的本地缓存一致:离线全量灌入进程内存、定时刷新,在线纯读——下一节的逐跳核验,读的正是这三张表。
四、买方侧工程(在线):热路径逐跳核验
索引备好后,核验就要跑在十万级 QPS 的竞价热路径上。重活儿(全网抓取、解析、建索引)已在上一节离线做完,热路径只需纯读那三张内存索引、亚毫秒完成。
每条 bid request 进来,核验就是对 schain 的一次线性扫描:
extract publisher_domain / bundle + source.schain
for each node in schain:
seller = sellersMap[node.asi]?.[node.sid]
if seller == null → 未知卖方,标记不可信
if authMap[seller.domain] not contains (node.asi, node.sid)
→ 未授权 / 伪造,标记不可信
if !schain.complete or 任一 node 不可信:
→ no-bid / 降权 / 不参与(按买方策略)
几个工程要点:
- 索引纯内存、亚毫秒命中:
sellersMap/authMap/bundleMap离线全量灌进进程内存、定时刷新,在线只读——绝不能因为一次索引 miss 或抖动把竞价链路拖垮,或把真实库存误杀。 - 尽量前置:核验通常挂在竞价漏斗很靠前的请求解析 / 定向过滤一段,让不可信流量尽早出局,省下后面排序和出价的算力。
- App 请求先翻译:对 App 流量,先用
bundleMap把app.bundle翻成开发者域名,再回查authMap——这层映射的缺口会直接变成 App 侧的核验盲区。 - 失败降级方向要对:索引 miss、文件缺失、confidential 卖方——这些「查不到」的情况向放行/降权降级;只有「明确对不上」(sellers.json 有但 domain 对不上、ads.txt 明确没授权)才走拒/丢弃。
尽力而为的边界(几个必须承认的现实):
- 覆盖不全:仍有媒体没部署 ads.txt、有中介 sellers.json 不完整、app-ads.txt 商店映射有缺口——「查不到」降权、「明确对不上」才拒。
- confidential / 弱域名:sellers.json 允许隐藏身份,或
domain指向*.blogspot.com、*.firebaseapp.com、*.app-ads-txt.com这类弱绑定域——这类路径无法完全核验,只能按风险偏好降权。 - 时效窗口:文件更新与索引刷新之间存在滞后,可能造成短暂误判——应给予宽限期、软失败,勿将偶发的对不上直接判为欺诈而硬拒。
- schain 本身可能被伪造:node 可被人为编造,故 schain 并非「自证清白」,必须逐跳回查另外两份声明方才作数——单凭
complete=1不等于可信。
五、透明度之上:供应链优化(SPO)
核验解决「真不真」,schain 还一并解决「值不值」。由于 schain 把真实跳数与每一跳的中介都摊开,买方首次得以回答:同一份库存,究竟是经 3 跳中介买到的,还是经 1 跳 DIRECT 买到的?中间每一跳都在抽成——这正是生态图所讲的 ad tech tax。
供应链优化(SPO,Supply Path Optimization) 即买方据此收敛路径:对同一份库存的多条可达路径,优先选择更短、更少抽成、更多 DIRECT、可核验性更高的那条,砍掉冗余的转售跳。这既节省预算(更多预算落到真实库存),也降低风险(路径越短越难藏匿未授权环节)。
因此透明度与 SPO 互为因果:先有可核验的供应链(透明度),才谈得上有依据地优化供应链(SPO)。没有 schain,买方无从得知预算经了几手,优化亦无从着手——这恰好回应了生态图第 7 节那 15% 的 unknown delta:能被测量的浪费,才能被消除。
六、常见误区 ↔ 正解
| 常见误区 | 正解 |
|---|---|
| schain 能替代 ads.txt/sellers.json | 不能。schain 是运行时路径,那两者是授权声明;核验要三者交叉,缺一不可 |
complete=1 就一定可信 | 它只是一句可被证伪的承诺;仍要逐 node 回查 sellers.json 和 ads.txt 才算数 |
| schain 里有 node 就代表被授权 | node 只是「自报经了这一手」;被没被授权,要拿 (asi,sid) 回查 sellers.json + ads.txt 才知道 |
| 核验就该「对不上就拒」 | 「查不到」(覆盖/时效/confidential)降权、「明确对不上」才拒——尽力而为,避免误杀真实库存 |
| 抓到 HTTP 200 就是有文件 | 很多站点对不存在的路径返回 200 网页 / 软 404;必须校验是纯文本 + 可解析记录(sellers.json 须是 JSON)才算数 |
| schain 只是防欺诈的 | 它同时是 SPO 的数据基础:暴露真实跳数,才能收敛路径、砍 ad tech tax |
| S2S / Header Bidding 不用管 schain | 恰恰相反——服务器到服务器的多跳场景更依赖 schain 逐跳生长来还原真实路径(见 Prebid Server) |
程序化供应链的核验,静态声明(上篇)回答「谁被授权」,schain(本篇)回答「谁实际参与了」。买方以运行时的每一跳比对事先公布的授权表,域名伪造与库存洗钱便同时失去藏身之处——伪造者要让整条链自洽,须同时骗过媒体的 ads.txt、中介的 sellers.json 与请求的 schain。理解了对账键 (asi, sid) 如何在这三处咬合,也就理解了整个 AdTech 透明度运动的缩影——将每一个「信任来的」环节,逐一换成「可核验的」环节。
延伸阅读
- 供应链透明度(上):ads.txt / sellers.json:本篇的上篇——三份静态声明的字段与对账关系。
- 程序化广告生态全景:母篇——第 7 节的 ad tech tax 与 15% unknown delta,正是 schain 与 SPO 要解决的问题。
- Last Look 深挖:透明度运动的另一半——拍卖侧从「偷看」到「一价 + 可审计日志」,与供应链侧同一个母题。
- 程序化交易模式:RTB / PMP / PD / PG:不同交易模式下供应链的长短与可控性差别,是 SPO 的背景。
- Header Bidding:Prebid.js vs Prebid Server:schain 在多路并行竞价里怎么逐跳生长、S2S 场景为何更要靠它。
- Bidder 请求解析:schain / domain / bundle 在 bid request 里怎么被解析出来,供应链核验挂在漏斗的哪一段。
- Bidder 频次控制工程篇:本篇「热路径本地缓存核验」的同款工程打法。
一手资料与规范:
- IAB Tech Lab. Supply Chain Object:schain 的 node 结构、complete 语义与在 OpenRTB 中的挂载点。
- IAB Tech Lab. OpenRTB 2.6 Specification:
source.schain等字段的权威定义。 - IAB Tech Lab. sellers.json Specification:schain 回查所依赖的卖方身份声明。
- ISBA & PwC. Programmatic Supply Chain Transparency Study:unknown delta 与「日志对不上账」的实证来源。