これは BrowserHive WACZ Profile 1.9.0 の日本語訳です。正典は 英語原典であり、判断の根拠にする場面ではそちらを確認してください。 訳と原典が食い違った場合は原典が優先します。
本仕様は [[WACZ]] のプロファイルです。WACZ パッケージの書き方を制約し、BrowserHive が その中に記録する追加の member を定めます。アーカイブだけを手にした読み手が、その取り込みが 何を取得し何を取得できなかったか、どのような設定の下で走ったかを判断できるようにするためです。
これは作業中の草案です。BrowserHive が現在書き出しているものを記述しています。同プロジェクト 以外での位置づけを持たず、[[WACZ]] の発行者から承認されたものでもありません。
この版は、取り込みにウィンドウを開かせません。新しいウィンドウやタブを求めたページ ——
window.open によるもの、それを target にした link や form によるもの、
タブを開くことになる click によるもの —— には何も与えられず、求めたことをパッケージが
記録します( を参照)。以前の版はウィンドウを開かせ、そのことを
何も述べませんでした。ウィンドウのリクエストは相手のオリジンに届き、パッケージのどこにも
現れませんでした。ウィンドウが開いた取り込みが 1.11.0 に対して書いたパッケージは、
1.12.0 に適合しません。これは意図されたもので、conformsTo が版を名指ししているのは
そのためです —— 古いパッケージを持つ読み手も、それが何に対して書かれたかを辿れます。
以前の版 —— 1.0.0・1.1.0・1.2.0・1.3.0・1.4.0・1.5.0・1.6.0・1.7.0・1.8.0・1.9.0・1.10.0・1.11.0 —— がそれぞれの URL で公開され続けるのはこの理由によりますが、
BrowserHive はもうそれらを出力しません。
取り込みは、ページをいつ撮ってよいかを決めなければならず、どの決め方も賭けです —— 固定の間は、もう揃っていたページから時間を奪い、揃っていないページには足りません。 合図で終える待ちは、合図より先に期限に当たることがあります。この版まで、パッケージは そのどれが起きたかを言えませんでした。これはパッケージを証拠として読む人にとって 重要です。「ページはこう見えていた」と「ページはまだ読み込み中に、こう見えていた」の 違いは、パッケージの他のどこからも取り戻せません —— 記録が言うのは届いたものであって、 取り込みが先へ進んだときにまだ向かっていたものではないからです。
[[WACZ]] のパッケージは、何が取り込まれたかを述べます。しかし何が抜けたかは述べず、 どのような条件下で取り込みが走ったかも述べません。それらは取り込み時にしか知り得ず、 取り込みを行ったプロセスが終了した時点で失われます。
この喪失が問題になるのは、旅をするのがアーカイブの方だからです。3 年後にパッケージを開いた 読み手には、ログも API の応答も、それを作った機械も残っていません。「ここに欠けているものは あるか」にパッケージ自身が答えられないなら、他の誰も答えられません。
そこで本プロファイルは、パッケージが取り込みについての自身の申告を運ぶことを要求します —— どの本文が欠けていてそれはなぜか、どの上限が効いていたか、どのリクエストを送らず どのリクエストを送って保持しなかったか、記録の前にページから何を取り除いたか、 各オリジンがどの証明書を提示したか。これらは [[WACZ]] の意味を何ひとつ変えません。変えずに、そのままでは取り戻せないものを 記録します。
本仕様の適合対象はパッケージ、すなわちファイル 1 つです。適合はファイルの性質で あって、それを書くソフトウェアの性質ではありません。ある実装が、あるときは適合する パッケージを、別のときは適合しないパッケージを書くことはあり得ます。本仕様はそのどちらで あるかについて何も述べません。
適合するパッケージは、適合する [[WACZ]] パッケージです。本仕様は要求を追加します。 取り除くことも、緩めることもしません。
本仕様における MUST(〜しなければならない)、 MUST NOT(〜してはならない)、 SHOULD(〜すべきである)、 MAY(〜してもよい)は [[RFC2119]] の記述に従って解釈されます。
JSON の member 名は大文字と小文字を区別します。timestamp を保持すると記した member の値は、 UTC の [[RFC3339]] date-time でなければならない。
browserhive:capture member
適合するパッケージの datapackage.json は、値が JSON オブジェクトである
browserhive:capture という名前の member を持た
なければならない。
この member を config または metadata の中に置いては
ならない。これらの名前は [[WACZ]] の読み手にとって意味を持ちます
( を参照)。
ここで定義されていない member が browserhive:capture に存在しても
よい。読み手は認識しない member を無視し
なければならない。
browserhive:capture は、そのパッケージが満たすと主張する本仕様の版の URL を
値とする conformsTo という member を持たなければならない。
"browserhive:capture": {
"conformsTo": "https://uraitakahito.github.io/browserhive-specs/wacz-profile/1.12.0/"
}
宣言をここに置き、datapackage.json の profile member に置かないのは、
そちらがこの用途に使えないためです。
読み手は browserhive:capture が存在するというだけで適合を推定しては ならない。conformsTo が無ければ、そのパッケージが どの版に向けて書かれたかは不明です。
build
browserhive:capture は、パッケージを作ったソフトウェアを機械可読な形で
識別する build member を持たなければならない。
| Member | 型 | 必須 | 意味 |
|---|---|---|---|
version | string | はい | リリースの識別子。 |
revision | string | はい | ビルド元のソース revision。 |
buildTime | string | はい | ソフトウェアがビルドされた時刻。 |
"build": {
"version": "3.4.0",
"revision": "0776cae",
"buildTime": "2026-08-21T04:04:09.000Z"
}
version の書式は定めません。リリースそのものから作られた build は
"3.4.0" のような値を持ちますが、リリースとリリースの間で作られた build は
"3.4.0-12-gabc1234" のように、直近のリリースからの距離を含んだ値になり
得ます。どちらも「どの build か」を言うという点では同じで、読み手はこれを版数として
解釈するのではなく、同じ値なら同じ build であるという識別子として
扱うべきです。
[[WACZ]] は既に software を定めています。あちらは散文で、書き方が producer
ごとに違うため誰も解析しません。build があるのは、同じ事実を形式を推測せずに
機械が読めるようにするためです。
browser
browserhive:capture は、取り込みを行った browser を記述する browser
member を持たなければならない。値が string である
product member を持たなければならない。
"browser": {
"product": "Chrome/151.0.7922.137"
}
綴りは定めません。値は browser 自身が名乗るものなので、別のエンジンが作った パッケージは別の形を運びます。読み手は 2 つの値が等しいかどうかを比べられますが、 書式を仮定することはできません。
値は代用のものであってはならない。これを取得できない producer は、 適合するパッケージを作ってはならない。記録した browser が不明な アーカイブはこのプロファイルの外にあり、member を省くことはそれを表明する手段では ありません。
settings
browserhive:capture は、この取り込みに効いていた設定を記録する
settings member を持たなければならない。
値は解決後の値でなければならない。要求が指定した値ではありません。 何も指定しない要求も何らかの設定の下で走っており、アーカイブが報告すべきなのはその設定です。
| Member | 型 | 必須 | 意味 |
|---|---|---|---|
signature | string | はい | "required" または "none"。この取り込みが署名なしで
出されてよかったか。 |
viewport | object | はい | member width と height。ともに整数、単位は CSS ピクセル。 |
devicePixelRatios | array of number | はい | ページを読み込んだときの device pixel ratio を、読み込んだ順に。要素数が そのまま読み込みの回数。 |
session | string | はい | その取り込みのために作られ、終われば捨てられる browser context で 走ったのか、取り込みをまたいで使い回される context で走ったのか。 前の取り込みが残した状態にパッケージが依存しうるかどうかを、 読み手はこれで判定します。 |
behaviors | array of object | はい | 読み込み後に走った behavior。走った順のまま。
各項目は id(その behavior が走った名前)と
origin("builtin"・"site"・
"custom" のいずれか)を持ちます。自前の behavior を
持たない producer は、すべての項目に "custom" と書きます。
behaviors ディレクトリを参照。 |
preload | array of object | はい | 遷移の前に走らせるために持ち込まれたスクリプト。登録した順のまま ——
報告したものではありません(それら自身は何も報告できないため)。
各項目は id(持ち込まれた名前)を持ちます。空の配列は
「1 本も持ち込まれなかった」と述べます。
preload ディレクトリを参照。 |
limits | object | はい | この取り込みに効いていた本文の上限。member maxResponseBytes と
maxTaskBytes。ともに整数で、単位はバイト。 |
urlPolicies | array of object | はい | 効いていた URL パターンのそれぞれに対して取り込みが何をしたかを、 評価した順に。urlPolicies を参照。 |
contentTypePolicies | array of object | はい | 効いていたメディア型のそれぞれに対して取り込みが何をしたか。 contentTypePolicies を参照。 |
acceptLanguage | string | いいえ | 取り込みが設定したときにのみ存在する。オリジンが返すものが変わるため。 |
"settings": {
"signature": "none",
"viewport": { "width": 1280, "height": 800 },
"devicePixelRatios": [1],
"session": "isolated",
"behaviors": [
{ "id": "autoscroll", "origin": "builtin" },
{ "id": "autofetch", "origin": "builtin" }
],
"limits": {
"maxResponseBytes": 20971520,
"maxTaskBytes": 209715200
},
"urlPolicies": [
{ "pattern": "*://*.example-analytics.com/*", "action": "deny" }
],
"contentTypePolicies": []
}
この例の session は、取り込みがそれ自身の context で走ったことを
表しています。devicePixelRatios の要素が 1 つなのは、ページを
1 回だけ読み込んだという意味です。behaviors に並ぶ 2 つは実際に走った
behavior です —— 有効化されていた
ものの一覧ではありません。acceptLanguage は、この取り込みが設定しなかった
ので在りません。値そのものは producer が決めるもので、ここに挙げた綴りを
このプロファイルが定めているわけではありません。
例外は origin です。behavior の id はそれを提供した側が
決めた名前で、本仕様はその綴りについて何も述べません。しかし
origin が答えるのは behavior についての問いではなく
パッケージについての問い —— そのコードは producer から来たのか、
リクエストから来たのか —— なので、この 3 値はここで定めます。
devicePixelRatios が数値ではなくリストなのは、取り込みがページを 2 回以上
読み込むことがあるためです。単一の値で記録すると、在る描画が無いと読み手に
伝えてしまいます。
値は読み込んだ順序で並んでいなければならない。読み手が 「どの版がアーカイブに在るか」だけでなく「どの版が最後に描かれたか」を言えるように するためです —— ページの見た目を伴う成果物は、最後の読み込みが残した状態に従います。
2 回読み込む取り込みでは、読み込んだ数だけ値が並びます。要求が
acceptLanguage を指定した場合は、それもここに現れます:
"settings": {
"signature": "required",
"viewport": { "width": 1280, "height": 800 },
"devicePixelRatios": [1, 2],
"session": "isolated",
"behaviors": [
{ "id": "autoscroll", "origin": "builtin" },
{ "id": "example.com:gallery", "origin": "custom" }
],
"acceptLanguage": "ja,en;q=0.9",
"limits": {
"maxResponseBytes": 20971520,
"maxTaskBytes": 209715200
},
"urlPolicies": [],
"contentTypePolicies": [{ "prefix": "video/", "action": "no-body" }]
}
2 つは測る対象が違います。maxResponseBytes は
レスポンス 1 件の本文に掛かり、maxTaskBytes はこのパッケージへ書いた
本文の累計に掛かります。
この違いは読み手にとって実際的です。maxResponseBytes は 1 件ずつを量るので、
それを下回るレスポンスは何件でも入り、パッケージ全体の大きさはこの値からは
分かりません。maxTaskBytes は積み上げた総量を見るので、線を越えた
後は大きさに関わらず本文が落ちます —— 落ちた中に小さなレスポンスが
混じっていても、それは矛盾ではありません。
本文が 1 つも落とされなかった場合でも limits は存在し
なければならない。切り詰めがどの線を超えたのかを
アーカイブが言える場所は、ここだけです —— 切り詰めの記録は落とした事実と実際の
大きさを運びますが、その大きさが多すぎたかどうかは線と並べて初めて分かります。
completeness の truncatedUrls は、この 2 つのどちらを超えて落ちたのかを
区別しません。
対処が違う (前者は 1 件が大きすぎ、後者はページ全体が重い) にもかかわらずです。
区別が要る読み手は、パッケージの外 —— 取り込みが出した報告 —— を見ることになります。
completeness
browserhive:capture は、そのパッケージが保持していない本文についての
自身の申告である completeness member を持たなければならない。
| Member | 型 | 必須 | 意味 |
|---|---|---|---|
bodylessUrls | array of string | はい | 304 としてしか現れなかった URL。本文が wire を渡っていないので、
アーカイブは保持し得ません。 |
truncatedUrls | array of string | はい | サイズ上限に達して取り込みが本文を落とした URL。 |
complete | boolean | はい | 両方の配列が空のとき、かつそのときに限り true。 |
"completeness": {
"bodylessUrls": [],
"truncatedUrls": [],
"complete": true
}
何も欠けなかった取り込みはこの形になります。complete は両方の配列から
導かれるので、空の配列 2 つと false という組み合わせは現れません。
次は、両方の理由で本文を欠いた取り込みです:
"completeness": {
"bodylessUrls": [
"https://example.com/assets/app.css"
],
"truncatedUrls": [
"https://example.com/media/hero.mp4",
"https://example.com/media/poster.png"
],
"complete": false
}
この取り込みでは、CSS が 304 としてしか現れませんでした —— 本文が wire を
渡っていないので、アーカイブは持ちようがありません。動画とポスター画像は上限に当たって
落ちたものです。同じ URL が 2 回落ちても、配列には 1 度だけ現れます。
同じページの 2 つの取り込みを比較できるよう、両方の配列は整列されて いなければならない。
2 つが別の member なのは、求められる対応が違うからです。304 はオリジン側の
事情でここからは回復できませんが、上限で落とした本文はこちら側の事情で、上限を上げれば
戻ります。
正当に本文を持たないレスポンスを列挙してはならない ——
redirect、204、および要求者が設定した方針で省かれた本文
(urlPolicies の no-body、または contentTypePolicies の項目)。
方針で省いたものを報告しても、読み手が既に決めたこと以上を何も伝えません。
これは省いた本文の大きさに関係なく成り立ちます。方針で省いた本文は
取り込みがそもそも取りに行かなかったもので、上限で落とされたはずがありません。
maxResponseBytes より大きくても、累計が maxTaskBytes に達した後に
届いても、truncatedUrls に載せてはならない(MUST NOT)。
そこに載せれば、要求者が保存しないよう頼んだ本文を、上限を上げれば取り戻せると
読み手に伝えることになります。
coverage
browserhive:capture は、取り込みがページのどこまで下れたかを記述する
coverage member を持ってもよい。
| Member | 型 | 必須 | 意味 |
|---|---|---|---|
scrollExhausted | boolean | はい | スクロールがページの終端ではなく段数の上限で止まったとき true ——
つまりその下にあるものが一度も要求されていないとき。 |
scrollSteps | number | はい | 実際に進んだ段数。 |
scrolledPx | number | はい | 到達した深さの概算。段数 × viewport の高さ。 |
"coverage": {
"scrollExhausted": true,
"scrollSteps": 40,
"scrolledPx": 32000
}
3 つの値は互いを読み合うためのものです。scrolledPx は
scrollSteps に settings が記録した viewport の高さを
掛けたもの —— ここでは 40 × 800 —— なので、読み手は一方を他方で検算できます。
そして scrollExhausted が true である以上、
この 32,000 ピクセルは取り込みが止まった地点であって、ページが
終わった地点ではありません。その下にあったものは一度も要求されておらず、
したがってパッケージの他のどこにも痕跡を残していません。
スクロールの behavior が何も報告しなかった場合、coverage は存在しては
ならない。既定値を入れて書いてはならない ——
scrollExhausted: false はページを覆えたという主張であり、一度もスクロール
しなかった取り込みはその主張を得ていません。
settle
browserhive:capture は、読み込み後の待ちがどう終わったか ——
ページが静まって終わったのか、まだ動いているうちに期限で終わったのか —— を述べる
settle member を持たなければならない。
| Member | 型 | 必須 | 意味 |
|---|---|---|---|
strategy | string | はい | どの合図を、どの順で見たか。この版が定めるのは "fully-loaded" ——
load、次に limits.minMs の間を置き、それから
network・cpu・dom を並べて見る。 |
endedBy | string | はい | 見ていた合図がすべて期限の前に満ちたとき "quiet"、そうでなければ
"deadline"。 |
waitedMs | number | はい | navigation が返ってから待ちが終わるまでのミリ秒。 |
devicePixelRatio | number | はい | この観測がどの読み込みのものか。devicePixelRatios の要素の 1 つで、
複数あるときは最後のもの。 |
limits | object | はい | 効いていた上限。どれも数値で、minMs、maxMs(期限)、
quietMs(静止の合図がそれぞれ満たすべき窓)、
networkConcurrency(静かとみなしてよい要求の数)。 |
load | object | はい | atMs: document が読み込み終わったのを見た時刻。 |
network | object | はい | quietAtMs: in-flight の要求が limits.networkConcurrency
以下のまま limits.quietMs 続いた時刻。 |
cpu | object | はい | quietAtMs: long task —— メインスレッドを 50 ms 以上握ったタスク ——
が limits.quietMs の間走らなかった時刻。 |
dom | object | はい | quietAtMs: 要素の出入りも文字の書き換えも limits.quietMs
の間無かった時刻。属性の変化は数えない。 |
"settle": {
"strategy": "fully-loaded",
"endedBy": "quiet",
"waitedMs": 2310,
"devicePixelRatio": 1,
"limits": { "minMs": 1000, "maxMs": 3000, "quietMs": 1000, "networkConcurrency": 0 },
"load": { "atMs": 210 },
"network": { "quietAtMs": 2205 },
"cpu": { "quietAtMs": 2104 },
"dom": { "quietAtMs": 2004 }
}
時刻はすべて navigation が返ってからのミリ秒で、waitedMs と同じ時計で測る。
producer が自分の操作の間に差し込んだ間は、この時計に含まない。
合図の時刻は、合図が満ちたときは数値、先に期限が来たときは null である。
時刻は null であっても書かなければならない。
書かずに省くと「この合図は見ていなかった」と読まれ、それは別の事実です。
endedBy は 4 つの時刻から導かれる。producer は、どれかが null で
あるのに "quiet" を書いてはならず、どれも
null でないのに "deadline" を書いてもならない。
期限が先に来たページ:
"settle": {
"strategy": "fully-loaded",
"endedBy": "deadline",
"waitedMs": 3116,
"devicePixelRatio": 1,
"limits": { "minMs": 1000, "maxMs": 3000, "quietMs": 1000, "networkConcurrency": 0 },
"load": { "atMs": 188 },
"network": { "quietAtMs": 2011 },
"cpu": { "quietAtMs": 2108 },
"dom": { "quietAtMs": null }
}
ここではページが文字を書き換え続けていた —— たとえば時計 —— ので、DOM は 1 秒と 静かにならず、取り込みは 3 秒の期限で先へ進みました。網と CPU は静まっており、 静まらなかったのは DOM だけです。
endedBy: "deadline" は取り込みの失敗を意味しません。パッケージは、その時点までに
ページがなったものを保持しています。記録しているのはページが静まっていなかったことで、
それはパッケージを証拠として読む人が知る必要があり、しかもパッケージの他のどこからも
取り戻せないものです。
静止の合図はそれぞれ「limits.quietMs の間ページが動かなかったか」を問うもので、
「この先も動かないか」を問うものではありません。窓より長く黙ってから変わるページは、
変わる前に静まったと読まれます。
dismissal
browserhive:capture は、取り込みが記録の前にページから何を取り除いたか、
そして何を探したかを記録する dismissal member を持ってもよい。
同意バナーを取り除いた取り込みは、そのあと保存する文書そのものを変えています。 それを述べるものは他にありません —— 取り除かれた要素は WARC にも索引にも 描画後のスナップショットにも痕跡を残さず、読み手はもともとバナーの無いページと 区別できません。
| Member | 型 | 必須 | 意味 |
|---|---|---|---|
selectors | object の配列 | はい | 効いていた選択子。評価した順のまま。
member は framework と selector で、どちらも文字列。 |
heuristic | object | はい | どの選択子も名指ししていない要素の除去を支配していた閾値。
member は enabled (boolean)・
minViewportCoverageRatio (number)・
minZIndex (number)。 |
framework | 文字列または null | いいえ | 最初に一致した項目の framework。閾値だけが発火したときは
"heuristic"。何も取り除かなかったときは null。 |
removedSelectors | 文字列の配列 | いいえ | 要素が取り除かれた選択子。selectors の部分集合で、評価した順。 |
unusableSelectors | 文字列の配列 | いいえ | ページを読む側が評価できなかった selectors の項目。
これらが何にも当たらなかったのは選択子として成立していなかったからであって、
「当たらなかった」とは別の主張。 |
removedOverlays | number | いいえ | 選択子ではなく閾値によって取り除かれた要素の数。 |
devicePixelRatio | number | いいえ | どの読み込みの観測か。devicePixelRatios の項目のいずれか。 |
unreadable | true | いいえ | 除去を試みたが遂行できなかったときに在る。 |
"dismissal": {
"selectors": [
{ "framework": "OneTrust", "selector": "#onetrust-banner-sdk" },
{ "framework": "Cookiebot", "selector": "#CybotCookiebotDialog" }
],
"heuristic": { "enabled": true, "minViewportCoverageRatio": 0.3, "minZIndex": 1000 },
"framework": "OneTrust",
"removedSelectors": ["#onetrust-banner-sdk"],
"unusableSelectors": [],
"removedOverlays": 0,
"devicePixelRatio": 1
}
除去を試みなかった場合、dismissal は存在しては
ならない。したがって不在は「配信されたままのページを
保存した」を意味します。「何も見つからなかった」ではありません。
dismissal が在るとき、何も取り除かなかった場合でも
selectors と heuristic は在らなければならない。
結果を検証可能にしているのはこの 2 つです —— 20 件の項目の隣に在る空の
removedSelectors は「そのどれもが名指しするバナーをページは持たなかった」
と述べますが、項目が 1 件も無い隣の同じ空リストは「何も探さなかった」としか
述べません。1 件も濾さなかった取り込みにも urlPolicies を必須にしているのと
同じ理屈です。
パッケージは unreadable を framework・
removedSelectors・unusableSelectors・
removedOverlays と並べて持ってはならない。
遂行できなかった除去は何も観測しておらず、その隣に空の結果を書くことは
「ページはバナーを持たなかった」という主張になります。
スクロールもまた取り込みが見るものを変えますが、それが何をしたかは既に coverage —— ページのどこまで下れたかを述べるもの —— が記録しています。 同じ行為の 2 つ目の申告をここに置けばそれは写しであり、いずれ片方が 古いほうになります。
ページを 2 回以上読み込む取り込みは、読み込みごとに要素を取り除きます ——
除去は同意ではないので、同意フレームワークは次の読み込みで再び描画されます。
removedSelectors は読み込みをまたいだ和ではなく、ある瞬間の
1 つの文書に対する観測であり、それをどの読み込みのものかに結び付けるのが
devicePixelRatio です。
document
browserhive:capture は、ページ由来の記録をどの文書から読んだかを名乗り、
それらを伏せたかどうかを述べる document member を持たなければなりません
(MUST)。
ページ由来の記録とは、pages/pages.jsonl にある取り込みの
エントリの text と title、datapackage.json の
title、そして accessibility/axtree.jsonl です。これらは
ネットワークからではなく描画されたページから読むので、WARC の本文はそれらを縛りません ——
方針が文書の本文を archive に入れなくても、それだけではその文書の文字は
締め出されません。
| Member | 型 | 必須 | 意味 |
|---|---|---|---|
url | string | はい(withheld が "unattributed" のときを除く) | browser が確定させた、その文書の URL。"deny" のときは、リクエストを断った URL。 |
withheld | string | いいえ | ページ由来の記録がパッケージに無い理由。在るときは置かない。 |
"document": {
"url": "https://example.com/next",
"withheld": "url-policy"
}
文書とは、ページ由来の記録を読んだ時点で主フレームにあった文書です。
要求した URL の文書とは限りません。サーバのリダイレクトや、ページが自分で行う遷移は
文書を置き換え、記録は置き換わった後のものを記述します。url は、そうした
リダイレクトや遷移の後に browser が確定させた、その文書の URL です。
deny の項目が主フレームの遷移を止めたとき(urlPolicies を参照)、
前の文書を置き換える文書はありません。browser はその場所に誤りのページを表示しますが、
それは取り込みの文書ではありません。このとき文書は遷移が向かっていた先のもので、
url はリクエストを断った URL です。
withheld は、置くなら次のどれかでなければなりません
(MUST)。はじめの 4 つは、文書を、あるいはその本文を archive に
入れなかった方針を名指します( を参照)。
"url-policy" —— 文書の URL に最初に当たる urlPolicies の項目の
action が no-body。"content-type" —— 文書の URL に当たる urlPolicies の項目が無く、
contentTypePolicies の項目がその media type に当たる。"no-archive" —— 文書の URL に最初に当たる urlPolicies の項目の
action が no-archive。パッケージはその文書の記録を一切持たない。"deny" —— 文書の URL に最初に当たる urlPolicies の項目の
action が deny。その文書は一度も要求されていない。その時点で読んだ
ページ由来の記録は、文書ではなく browser の誤りのページを記述することになる。"unattributed" —— どの文書から記録を読んだかを producer が言えない。
たとえば、読んでいる間に主フレームが遷移した。
producer は、その方針が文書に当たったとき、そしてそのときに限り、
withheld を "url-policy"・"content-type"・
"no-archive"・"deny" にしなければなりません
(MUST)。
この決まりは両向きに働きます。リダイレクトの中継だけ、あるいは
ページが遷移して離れた文書だけに当たった方針は、何も伏せません。文書に当たった方針は、
要求した URL に当たっていなくても、その記録を伏せます。
withheld を置くとき、パッケージはページ由来の記録を持ってはなりません
(MUST NOT)。pages/pages.jsonl にある取り込みの
エントリは text と title を持ってはならず
(MUST NOT)、datapackage.json の
title はページから読んではならず(MUST NOT)、
パッケージは accessibility/axtree.jsonl を含んではなりません
(MUST NOT)。エントリは理由を textWithheld として
持ってもよく(MAY)、持つなら値は withheld と
等しくなければなりません(MUST)。
大きさの上限で落とした本文は、ページ由来の記録を伏せません。上限は取り込みの側の ものであって要求者のものではなく、本文が archive に収まらなかったページこそ、 文字が最も役に立ちます。
tls
browserhive:capture は、各 HTTPS 接続について browser が見たものを記録する
tls member を持ってもよい。存在する場合、
hosts と chains を持たなければならない。
tls.hostshost を鍵とするオブジェクト。各値は以下の member を持つオブジェクトか、null。
| Member | 型 | 必須 | 意味 |
|---|---|---|---|
protocol | string | はい | ネゴシエートされた TLS の版。 |
cipher | string | はい | ネゴシエートされた cipher。 |
subject | string | はい | subject の common name。browser が報告したそのまま。 |
san | array of string | はい | その証明書が有効な host 名の一覧 (subjectAltName)。SAN を持たない証明書では空。 |
issuer | string | はい | issuer の common name。 |
validFrom | string | はい | 証明書の有効期間の開始。 |
validTo | string | はい | その終了。 |
chainRef | string | いいえ | tls.chains への鍵。チェーンを取得できなかったときは存在しない。 |
HTTPS で到達したが接続が何も明かさなかった host は、値 null として記録され
なければならない。鍵ごと省くと「この host は素の HTTP だった」と
読めてしまいますが、それは別の事実です。
host 名の照合に使われるのは subject ではなく san です。
subject が記録するのは common name であり、現在の読み手はこれを照合に
用いません —— subject の値をもって「その host 名で通用した」と読んでは
ならない。
chainRef が無いことを「この host は証明書を提示しなかった」と読んでは
ならない。チェーンの取得は host と無関係な理由でも失敗します。
tls.chains
鍵が chainRef の値であり、値が base64 符号化された DER 証明書の配列である
オブジェクト。各配列はリーフを先頭として並んでいなければならない。
チェーンを共有する host は 1 つの項目を共有すべきである。 1 ページの host 間で証明書が再利用されることは多く、host ごとに複製を持つとパッケージが 何の利得もなく膨らみます。
チェーンは参照ではなく保存します。Certificate Transparency が同じバイト列を持っては いますが、第三者のサービスが到達可能であり続けることに依存する証拠は、その分だけ弱く なります —— そして検討する価値のある事例である私設 CA のチェーンは、そもそもそこに 記録されたことがありません。
"tls": {
"hosts": {
"example.com": {
"protocol": "TLS 1.3",
"cipher": "AES_128_GCM",
"subject": "example.com",
"san": ["example.com", "www.example.com"],
"issuer": "Example Issuing CA",
"validFrom": "2026-06-30T20:37:58.000Z",
"validTo": "2026-09-28T21:37:55.000Z",
"chainRef": "9a57318f8c89976c"
},
"cdn.example.net": {
"protocol": "QUIC",
"cipher": "AES_128_GCM",
"subject": "*.example.net",
"san": ["*.example.net", "example.net"],
"issuer": "Example Issuing CA",
"validFrom": "2026-08-05T20:42:14.000Z",
"validTo": "2026-10-28T20:42:13.000Z",
"chainRef": "c52653c92800f6fd"
},
"img.example.net": {
"protocol": "TLS 1.3",
"cipher": "AES_128_GCM",
"subject": "*.example.net",
"san": ["*.example.net", "example.net"],
"issuer": "Example Issuing CA",
"validFrom": "2026-08-05T20:42:14.000Z",
"validTo": "2026-10-28T20:42:13.000Z",
"chainRef": "c52653c92800f6fd"
},
"api.example.org": {
"protocol": "TLS 1.3",
"cipher": "AES_256_GCM",
"subject": "api.example.org",
"san": ["api.example.org"],
"issuer": "Example Issuing CA",
"validFrom": "2026-05-14T09:03:11.000Z",
"validTo": "2026-08-12T09:03:10.000Z"
},
"beacon.example.org": null
},
"chains": {
"9a57318f8c89976c": [
"MIIDbzCCAxSgAwIBAgIRALaMq73qMP3mE30B…",
"MIICjDCCAhKgAwIBAgIQf5wJDMcM1Y2ZBpFB…"
],
"c52653c92800f6fd": [
"MIIIUDCCBzigAwIBAgIQHnJSlrCYIdkSw8c/…",
"MIIFCzCCAvOgAwIBAgIQVJ5xIVMKmZ0sNHxs…"
]
}
}
この例は 5 つの host を記録しています。上の規定がそれぞれどう現れるかを見てください。
cdn.example.net と img.example.net は同じ
chainRef を指しています —— 同じ証明書を提示したので、チェーンは
chains に 1 度だけ保存されています。api.example.org には chainRef がありません。
証明書は提示されており (subject 以下がその証拠です)、取れなかったのは
チェーンだけです。beacon.example.org は null です。HTTPS で到達しましたが、
接続は何も明かしませんでした。鍵ごと省いていないのは、省くと「この host は素の HTTP
だった」と読めてしまうためです。cdn.example.net の protocol は "QUIC" です。
この member は browser が報告した文字列であり、TLS の版とは限りません。
chains の値は base64 符号化された DER です。例では読みやすさのため途中で
切ってありますが、実際には 1 通あたり 1–2 KB 程度の文字列になります。
取り込みは、一部のリクエストを他と違う扱いにすることがあります ——
送らない、記録しない、本文なしで記録する。
browserhive:capture.settings は何をしたかを
MUST 記録します。取り込みの設定に手が届かない読み手が、
そのどれであったかを判定できるようにするためです。
方針が働くのはネットワークの本文です。描画されたページから読む記録 —— ページの文字・ 題・アクセシビリティツリー —— に方針が何をするかは、document で定めます。
urlPoliciesurlPolicies は順序付きの配列です。各項目は 2 つの member を持つ オブジェクト:
| Member | Type | 必須 | 説明 |
|---|---|---|---|
pattern | string | はい | 効いていたパターン。取り込みが設定されたとおりの綴りで。 |
action | string | はい | 下の値のいずれか。 |
pattern は、リクエストの URL 全体に当てます。* は 0 字を含む
任意の文字の並びに当たり、ほかの文字は ? も含めてその字だけに当たり、
大文字と小文字は区別します。そのため、パターンは query を見込んで書く必要があります ——
*://example.com/a.js は https://example.com/a.js?v=2 に
当たりません。
action | リクエストの送信 | パッケージが持つもの |
|---|---|---|
deny | しない | リクエストの記録のみ。その URL について取り込みを行った機械から 出たものは無く、それを通じて取り込みを知ったオリジンも在りません。 |
no-archive | する | リクエストの記録のみ。オリジンは応答しましたが、パッケージは その応答を保持しません。 |
no-body | する | request / response のレコード。ただし response の本文は含みません。 |
deny の項目は、ページのどの部分が起こしたかに関わらず、取り込みが起こす
すべてのリクエストに働きます。主フレームの遷移(サーバのリダイレクトの先と、script が
始めた遷移を含む)、フレーム(別サイトのフレームを含む)、そして dedicated worker と
service worker です。これらのリクエストの 1 つでも取り込みを行う機械から出した
producer は、その項目を守っていません。取り込みの間、ページはウィンドウを開きません
( を参照)。項目が届くべきウィンドウはありません。
最初の遷移に deny の項目が当たった取り込みは、パッケージを作っては
なりません(MUST NOT)。何も読み込まれていないので、
settle の報告は走らなかった待ちを記述することになります。ページを読み込んだ後に
遷移を断られた取り込みは、パッケージを作ってもよく(MAY)、
その document は withheld: "deny" を持ちます。
WebSocket の handshake は、ほかのリクエストのようには止められません。URL に
deny の項目が当たる WebSocket を開いた取り込みは、パッケージを作っては
なりません(MUST NOT)。リクエストは送られており、
パッケージはそうでないと主張することになるからです。
この配列は空のときでも存在して MUST、 空の配列は「効いていた policy が無い」という意味で MUST。この規則が無いと読み手は、 特別なものを何も要求しなかったページと、要求されたものを記録しないことにした 取り込みとを区別できません。同じパッケージについての、別の主張です。
順序は取り込みがパターンを評価した順であり、取り込みは 一致した最初の項目を適用して MUST。 順序を記録することが、複数のパターンが当たりうる URL について 「どの policy が適用されたのか」に読み手が答えられる根拠になります。
"urlPolicies": [
{ "pattern": "*://cdn.example.com/tracker.js", "action": "deny" },
{ "pattern": "*://cdn.example.com/*", "action": "no-body" }
]
この例では tracker.js へのリクエストは一度も送られず、
同じホストの他のものは取得されて本文なしで記録されます。2 つを入れ替えると
取り込みがしたことが変わります —— 広いほうのパターンが先に当たり、
tracker は取得されます。
contentTypePolicies
contentTypePolicies は、prefix
(応答のメディア型に当てる)と action を持つ項目の配列です。
この版が定める action は no-body のみ ——
メディア型は応答が届いてからしか分からないので、deny は
適用できません。
この配列も、空のときでも存在して MUST。 理由は urlPolicies と同じです。
"contentTypePolicies": [
{ "prefix": "video/", "action": "no-body" }
]
deny または no-archive の policy が適用された
リクエストのそれぞれについて、パッケージは WARC の metadata
レコード —— 以下 withheld request のレコード —— を
MUST 含みます。その
WARC-Target-URI はリクエストの URL で
MUST、application/warc-fields の
本文は次を MUST 含みます:
| Field | 必須 | 説明 |
|---|---|---|
action | はい | 適用された action: deny または
no-archive。リクエストが送られたかどうかを
述べるのはこの欄です。 |
pattern | はい | 一致したパターン。urlPolicies に現れるとおりの綴りで、 読み手がそちらで引き当てられるように。 |
method | はい | リクエストのメソッド。 |
resourceType | いいえ | ページが何を取りに行っていたか。取り込みが知っている場合に。 |
target | いいえ | 何がリクエストを出したか: page、iframe
(browser がページと別に走らせるフレーム。多くは別サイトのもの)、
worker、service_worker。取り込みが述べないときは
置きません。 |
WARC/1.1 WARC-Type: metadata WARC-Target-URI: https://www.example-analytics.com/collect?v=2 WARC-Date: 2026-09-06T02:14:31.882Z Content-Type: application/warc-fields Content-Length: 112 action: deny pattern: *://*.example-analytics.com/* method: POST resourceType: XHR
deny は取り込みが起こすすべてのリクエストに届くので、そのレコードは、
ページのどの部分がリクエストを出したかに関わらず書かなければなりません
(MUST)。producer がほかには記録しない部分のリクエストも
含みます。
withheld request のレコードはレスポンスも本文も持ちません。主張しているのは、 ページがその URL を要求したこと、そしてこのプロファイルの producer が それについて何をするか決めたこと —— リクエストが失敗したという意味でも、 オリジンが何も返さなかったという意味でもありません。オリジンに到達 できなかった取り込みは、これをこの形で報告しては MUST NOT。
no-body の policy はこの種のレコードを生みません。
request / response のレコードは通常どおり書かれ、欠けている本文は
方針で省いたものとして報告されます —— 上限で落としたものとしては報告されません
(completeness を参照)。
withheld request のレコードは、パッケージの CDXJ 索引に現れることを 要求されません。これを探す読み手は WARC を読みます。
取り込みは 1 つのページを記録します。取り込みの間、producer はページに新しい
top-level browsing context —— ウィンドウやタブ —— を与えてはなりません
(MUST NOT)。誰が求めても同じです: ページ自身の script、
その中のフレーム、取り込みのリクエストが運んだ script。ページへの答えは、browser が
popup を止めたときと同じです: window.open は null を返し、
新しいウィンドウを target にした link や form は何も開きません。
ページが求めた 1 回ごとに、パッケージは WARC の metadata レコード ——
以下 window not opened のレコード —— を MUST
含みます。その WARC-Target-URI は、そのウィンドウが読み込むはずだった
URL を browser が知らせたとおりに MUST: ページが名指し
しなければ about:blank、form が求めたなら送る本文を除いた action の URL
です。application/warc-fields の本文は次を MUST
含みます:
| Field | 必須 | 説明 |
|---|---|---|
window | はい | not-opened。この版は、ほかの値を定めません。 |
windowName | いいえ | ページがウィンドウに付けた名前。付けなければ _blank。
ウィンドウを名指ししない遷移(修飾キーつきの click など)で求めたときは
置きません。 |
target | いいえ | 何が求めたか: page、または iframe(browser が
ページと別に走らせるフレーム。多くは別サイトのもの)。取り込みが述べないときは
置きません。 |
WARC/1.1 WARC-Type: metadata WARC-Target-URI: https://shop.example/campaign?from=top WARC-Date: 2026-10-01T02:14:31.882Z Content-Type: application/warc-fields Content-Length: 54 window: not-opened windowName: _blank target: page
ウィンドウの文書のリクエストは送られていないので、その URL について、取り込みを行う
機械から出たものはありません。このレコードは、urlPolicies の項目が URL に
当たるかどうかに関わらず書き、action を持ちません。これは
withheld request のレコードではなく、読み手はそれとして数えてはなりません
(MUST NOT)。
それでもウィンドウが開いた取り込みは、パッケージを作ってはなりません (MUST NOT)。ウィンドウのリクエストはパッケージに現れない まま相手のオリジンに届いており、パッケージは何も開かなかった取り込みとして自らを 記述することになるからです。
browser がウィンドウを開くのは、user activation —— 人の click や打鍵の痕跡 —— を
持つページに対してだけです。BrowserHive はそれをページに与えません。ページで評価する
コードは、リクエストが運ぶ behaviors と preload の script を含めて、activation なしで
走ります。ウィンドウを断るのはこれです。それらの script が、window.open・
Fullscreen API・clipboard への書き込みのような gesture の要る API を使えないのも、
同じ理由です。
window not opened のレコードは、パッケージの CDXJ 索引に現れることを要求されません。 これを探す読み手は WARC を読みます。
storage member
browserhive:capture は、取り込んだ origin が保持していた web
storage を記録する storage という member を
持たなければならない(MUST)。この member が記録するのは、その storage の
存在と形です。保存されていた値を
含んではならない(MUST NOT)。
禁じているのはこの member であって、パッケージではありません。値そのものは
別のエントリ storage/origins.jsonl に入り、値を求められた取り込みの
パッケージがそれを運びます。 を参照。
分けているのは、web storage が bearer token や session 識別子を日常的に持ち、
それらが WARC の payload のどこにも現れないからです ——
既定で記録すれば、パッケージはそれを行く先々へ運ぶことになります。
どちらをパッケージが持っているかは valuesRecorded が述べます。
web storage を記録するのは、後から復元できないからです。ネットワークを一度も
渡らないため、どの WARC レコードもこれを保持していません ——
localStorage から描画するページは、アーカイブが持っていない
データから描画しています。この member が無いと、読み手はそうしたページと
「本当に見せるものが無かったページ」を区別できません。
completeness は助けになりません ——
あれが報告するのはパッケージが保持していない body であり、
storage は一度も body になっていないからです。
{
"browserhive:capture": {
"storage": {
"profile": "browserhive:storage/2",
"stage": "after-behaviors",
"valuesRecorded": false,
"origins": [
{
"origin": "https://www.example.co.jp",
"local": { "keys": 2, "bytes": 32, "digest": "sha256:b58f31fdc511fb94c356ae7f6d64ac71e5bf056584653c3f3ec61a1f08bfaa0f" },
"session": { "keys": 1, "bytes": 13, "digest": "sha256:8ce272dea9fc6d7902b23f10411da49a7fb2e3c954ba9f1580cc087292eaafc2" }
},
{ "origin": "https://ads.example.net", "unreadable": true }
]
}
}
}
storage は profile、stage、
valuesRecorded、origins を
持たなければならない(MUST)。
profile —— この member が従う規則。本仕様のこの版では値は
"browserhive:storage/2" でなければならない(MUST)。
記録する内容や digest の計算方法を変えた producer は、この値を
変えなければならない(MUST)。
stage —— 取り込みのどの時点で storage を読んだか。この版では値は
"after-behaviors" でなければならない(MUST)。
stage が規範なのは、内容が変わるからです ——
ページは動作しながら storage に書き込むので、behavior が走る前に読んだものと
後に読んだものは、別の状態を表します。
valuesRecorded —— このパッケージが、下で定義するディレクトリに
保存されていた値も併せて運んでいるかどうか。これは「値を求められた」ことを
述べるのであって、「すべての origin の値が在る」ことではありません ——
valuesOversize と印の付いた origin の値は在りません。
を参照。
origins —— 取り込みが観測できた origin ごとに 1 つの項目を持つ配列。
各項目は origin を持たなければならない(MUST) ——
値は [[WACZ]] の enclosing document と同じ直列化形式、すなわち scheme・host、
および既定でない場合の port です。
web storage は origin ごとに区切られており、ページは日常的に自分以外の origin の
文書を埋め込みます —— 広告、計測のフレーム、埋め込みのプレイヤー。それらは
それぞれ自分の storage を持ち、ページ自身の origin からは見えません。
したがってこれは「そのページの storage」の記録ではありません ——
取り込みが観測できた origin ごとに 1 項目であり、
第三者の origin の項目が在ることは異常ではなく想定どおりです。
そもそも storage を持てない origin —— about:blank の文書や
data: URL のような opaque origin —— は記録するものが無いので
現れません。
各項目は、local と session の両方を持つか、あるいは
unreadable という単一の member を値 true で持つかの
いずれかでなければならない(MUST)。後者は「その origin の storage を
取り込みが読めなかった」という意味です。これは storage が空だった origin
—— "keys": 0 の area として書かれる —— とは別です。
同じ形で記録すると、この member が取り除こうとしている曖昧さが戻ってきます。
ここの項目と storage/origins.jsonl の行は、1 つの記録の
2 つの半分です。項目はその origin の storage が
どう見えたかを述べ —— 鍵がいくつ、何バイト、digest は何 ——
常に在ります。行は何が入っていたかを述べ、値を求められたときにだけ
在ります。どちらをパッケージが持っているかは valuesRecorded が
述べます。値を求められてもなお行を持たない origin は 2 通りあります ——
行に入れるものが無かった(unreadable として記録)か、
行に入れるものが大きすぎた(valuesOversize として記録)か。
その 2 つを区別するのが項目で、だからこそ項目のほうが常に書かれます。
local と session はそれぞれ以下を
持たなければならない(MUST)。
keys —— その area が保持していた鍵の数。bytes —— その area の合計の大きさ。すべての鍵と値の
UTF-8 表現で数えます。browser 自身の quota 会計が使う
UTF-16 コード単位ではありません。
digest —— "sha256:" に続けて、その area の SHA-256 を
小文字の 16 進で。計算は、鍵をコードポイント順に並べ、各要素が
key U+0000 value U+0000 を UTF-8 で寄与する形で行います。
並べ方と区切りは規範です —— これを決めないと同じ内容が別の digest になり、
取り込みをまたいで比較するという digest の目的が失われます。
digest を値へのコミットメントとして扱ってはならない(MUST NOT)。 値はしばしばエントロピーが低く —— フラグ、テーマ名、時刻 —— そのような digest は推測で容易に逆算できます。
項目は valuesOversize を値 true で
持ってもよい(MAY)。これは「この origin の保存値は
大きすぎたため運ばれなかった」こと、したがって「この項目がその origin について
パッケージが持つすべてである」ことを述べます。
unreadable と並べてはならず(MUST NOT) ——
読めなかった storage には、超えるべき大きさがありません ——
valuesRecorded が false のときにも
置いてはならない(MUST NOT)。そこではどの origin の値も
もともと運ばれる予定が無く、この member は誰も下していない判断を
報告することになります。
valuesOversize を持つ項目は、それでも local と
session を持たなければならない(MUST)。
ここが unreadable との違いで、意図的なものです ——
取り込みはこの origin を読めています。鍵がいくつあり、
どれだけ大きかったかを知っています。その数字が member の隣に在れば、
読み手は「値が欠けていること」と「どれだけ欠けているか」の両方を知り、
上限を上げて撮り直すかどうかを判断できます。area を落として項目を書くのは、
すでに手元にある計測を捨てることです。
{
"origin": "https://heavy.example",
"local": { "keys": 1, "bytes": 2129905, "digest": "sha256:1aeb74b512b17f6fc43ade8575d452df142ade5d1515a4afcaf05dd199688c38" },
"session": { "keys": 0, "bytes": 0, "digest": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" },
"valuesOversize": true
}
storage directory
[[WACZ]] は、archive・indexes・pages を避け、
datapackage.json の resources に列挙する限り、追加の
ファイルやディレクトリを認めています。適合するパッケージは、その許可を
使ってもよい(MAY)。
適合するパッケージは storage/origins.jsonl というエントリを
持ってもよい(MAY)。存在する場合、他のファイルと同様に
resources で宣言しなければならず(MUST)、
JSON Lines でなければならない(MUST) —— 1 行 1 つの JSON
オブジェクトで、各オブジェクトは 1 つの origin が保持していた web storage を
記録します。
このエントリが存在する場合、valuesRecorded は
true でなければならない(MUST)。存在しない場合、
valuesRecorded は false でなければならない(MUST)。
したがって読み手は、アーカイブ本体を開かずに、そのパッケージが保存されていた値を
運んでいるかどうかを判別できます。
{
"profile": "browserhive:storage/2",
"origin": "https://www.example.co.jp",
"takenAt": "2026-09-06T01:22:14.881Z",
"stage": "after-behaviors",
"areas": {
"local": { "feed.cache": "[{\"id\":1}]", "ui.theme": "dark" },
"session": { "nav.scroll": "820" }
}
}
各行は profile、origin、[[RFC3339]] のタイムスタンプと
しての takenAt、stage、areas を
持たなければならない(MUST)。areas は
local と session を
持たなければならない(MUST) ——
それぞれ、area が保持していたとおりに鍵を値へ写像するオブジェクトです。
storage で unreadable または
valuesOversize として記録された origin は、
ここに行を持ってはならない(MUST NOT)。
したがってエントリは 1 行も持たないことがありえます(MAY) ——
値を求められたが、どの origin の値も運べなかった場合です。これは
エントリが不在であること(=そもそも値を求められなかった)とは
別の主張です。
[[WACZ]] は、archive・indexes・pages を
避け、datapackage.json の resources に列挙する限り、
追加のファイルやディレクトリを認めています。適合するパッケージは、その許可を
使ってもよい(MAY)。
適合するパッケージは accessibility/axtree.jsonl というエントリを
持ってもよい(MAY)。存在する場合、他のファイルと同様に
resources で宣言しなければならず(MUST)、
JSON Lines でなければなりません
(MUST)—— 1 行 1 つの JSON オブジェクトで、各
オブジェクトは、取り込んだページに対して browser が計算したアクセシビリティ
ツリーのスナップショット 1 枚を記録します。
document が withheld を持つパッケージは、このエントリを含んでは
なりません(MUST NOT)。この木は描画されたページから読むので、
方針がその文書の本文を archive に入れなかったときも、文書の文字を運んでしまいます。
この木を記録するのは、後から作り直せないからです。DOM とレイアウト
ツリーの両方から作られ、対応する DOM ノードを持たないノード(CSS の
生成内容、リストの記号)を含み、aria-owns がノードの親を付け替える
ため、その形は文書の木とは異なります。将来の browser で archive を再生しても、
別の木ができます。tls を参照ではなく記録している理由と同じです。
各行は profile・url・takenAt・
stage・tree を含まなければなりません
(MUST)。
profile —— その行が従う刈り込み規則。本仕様のこの版では、値は
"browserhive:axtree/1" でなければなりません
(MUST)。刈り込み規則を変える producer は、この値も
変えなければなりません(MUST)—— 読み手が、何が
捨てられたのかを言えるようにするためです。url —— そのスナップショットを読んだ文書の URL。document の
url と等しくなければなりません(MUST)。takenAt —— 撮った時刻。[[RFC3339]] のタイムスタンプ。stage —— 取り込みのどの時点で撮ったか。この版では値は
"after-behaviors" でなければなりません
(MUST)。時点が規範的なのは、木が変わる
からです。 autoscroll は lazy load を発火させるので、behavior の
前に撮った木と後に撮った木は、別のページを記述しています。tree —— ノードの配列。次節を参照。
各行はさらに devicePixelRatio・viewport・
nodes を持つべきです(SHOULD)——
順に、そのスナップショットがどのパスのものか、効いていた viewport、そして
刈り込む前のノード数。最後の 1 つによって、読み手はどれだけ
捨てられたのかを見ることができます。
{
"profile": "browserhive:axtree/1",
"url": "https://www.iana.org/",
"takenAt": "2026-08-22T02:55:56.759Z",
"stage": "after-behaviors",
"devicePixelRatio": 1,
"viewport": { "width": 1280, "height": 800 },
"nodes": 255,
"tree": [
{
"role": "RootWebArea",
"name": "Internet Assigned Numbers Authority",
"children": [
{ "role": "heading", "name": "Domain Names", "level": 2 },
{ "role": "image", "name": "logo",
"url": "https://www.iana.org/_img/iana-logo-header.svg" }
]
}
]
}
読みやすさのため複数行で示しています。ファイル中では、各スナップショットは ちょうど 1 行を占めます。
すべてのノードは role を持たなければなりません
(MUST)。ノードは role・
name・level・url・checked・
expanded・disabled・required・
value・children 以外の member を持ってはなりません
(MUST NOT)。
このうち 2 つは、他より重い意味を持ちます:
level —— 見出しの深さ。これが無いと heading の
ノードを互いに区別できず、この木が記録するために在る文書構造そのものが
失われます。url —— 画像やリンクが指す先。ノードと archive/ に
記録されたレスポンスを結ぶのはこれで、「ここに図があった」から「その図の
バイト列」へ辿れるようになります。
値が無い・空・false の member は書いてはなりません
(MUST NOT)。全ノードに既定値を書くと、それが囲んでいる
中身より嵩が大きくなります。
browser が ignored と印を付けたノードは tree に現れてはなりません
(MUST NOT)。その子は残し、最も近い残されたノードの
下に付けなければなりません(MUST)。
これが、隠されたテキストを記録に持ち込まないための仕掛けです。
aria-hidden や display: none で外された部分木は
ignored なノードなので、そもそもこのファイルに届きません。したがって読み手は、
この木を「見えていたもの」の記述として扱えます —— WARC の
本文だけからは支えられない、より強い主張です。
role が generic・none・InlineTextBox の
ノードも tree に現れてはならず(MUST NOT)、
その子は同じように残さなければなりません(MUST)。
これらの role はそれ自体では意味を持たず、残すと木の深さが倍になるだけで、
読み手が使えるものは何も増えません。
viewport の外にあることを理由にノードを落としてはなりません (MUST NOT)。エージェントにとっては妥当な判断です —— 見えないものは操作できないので。しかし、誰もスクロールしなかった部分を捨てた アーカイブは、そのページの記録ではありません。
[[WACZ]] は、archive・indexes・pages を
避け、datapackage.json の resources に列挙する限り、
追加のファイルやディレクトリを認めています。適合するパッケージは、その許可を
使ってもよい(MAY)。
適合するパッケージは、取り込みが保存の前にページ上で走らせたコードを運ぶ
behaviors の下のエントリを持ってもよい(MAY)。
存在する場合、各エントリは他のファイルと同様に resources で
宣言しなければなりません(MUST)。
コードを記録するのは、後から取り戻せないからです。producer が同梱するコードは
少なくとも build が固定していますが、リクエストが運んできたコードは
リクエストと共に来て、応答を捨てた時点で失われます —— しかも、それが走った名前は
提供した側が決めたものなので、同じ名前で中身の違うコードが走った 2 回は
他の手段では区別が付きません。tls を参照ではなく記録している理由と
同じです。
適合するパッケージは behaviors/runtime.js というエントリを
持ってもよい(MAY)。存在する場合、それは producer が
ページの中で評価したソースを、バイト単位でそのまま
持たなければなりません(MUST)。
producer がそのようなソースを評価したときは在らなければならず (MUST)、そうでなければ在ってはなりません(MUST NOT)。 したがってその存在は「ページ上でコードが走った」ことを述べます —— その後どの behavior が何かをしたかどうかとは無関係に。
要求しているのは「バイト単位でそのまま」であって、読みやすさではありません。 runtime を minify する producer は minify された形を記録します —— それがページの受け取ったものだからです。人が読める形が要る読み手には build があり、それがパッケージを作ったソフトウェアを名指しします。
適合するパッケージは behaviors/custom.jsonl というエントリを
持ってもよい(MAY)。存在する場合、それは
JSON Lines で
なければなりません(MUST) ——
1 行に 1 つの JSON オブジェクトで、各オブジェクトは取り込みのリクエストと共に
届いた behavior を 1 つ記録します。
{
"profile": "browserhive:behaviors/1",
"id": "example.com:gallery",
"source": "class { static id = \"example.com:gallery\"; … }"
}
各行は profile・id・source を
持たなければなりません(MUST)。
profile — その行が従う規則。本仕様のこの版では値は
"browserhive:behaviors/1" で
なければなりません(MUST)。
id — その behavior が登録された名前。
settings.behaviors のうち origin が
"custom" である項目は、すべてここに行を
持たなければなりません(MUST)。
逆は成り立ちません —— 対応する項目を持たない行が
在ってもよい(MAY)。behavior はページに置かれた
うえで、何もしないことを選べるためです。
source — 届いたままのコード。バイト単位でそのまま。
producer がリクエスト由来の behavior をページに置いたときは 在らなければならず(MUST)、そうでなければ 在ってはなりません(MUST NOT)。したがってその不在は 「このパッケージはリクエストから来たコードを持たない」ことを述べます。
条件は「置いたか」であって「走ったか」ではありません —— runtime と同じ条件です。ページに置かれた コードは、そのあと何を決めたにせよ、そこで走っています。作用したコードだけを 記録するアーカイブは、実行されたコードを取りこぼします。
behavior の名前は、それを提供した側が決めるもので、何の制約もありません ——
現に使われている名前には : も / も含まれます。
それをエントリのパスに使うと、名前がパッケージの中の置き場所を
決めてしまいます。ファイル 1 つ・behavior 1 つにつき 1 行という形は、
名前を経路ではなく値のままに保ちます。
読み手は behaviors の中身を実行してはなりません (MUST NOT)。あれは producer が何を走らせたかの記録であって、保存されたページの 一部ではありません。パッケージを replay するのに、それらを再び走らせることを 求めるものは何もありません。
これを明記するのは、適合するパッケージの中で中身が実行可能なエントリが これだけであることと、パッケージを replay する読み手は既に実行の context を 開いているためです。
適合するパッケージは preload の下に項目を持って
よい(MAY)。取り込んだページ自身の script より
前に走らせるために、リクエストが持ち込んだコードです。在るときは、
他のファイルと同じく resources で申告し
なければならない(MUST)。
behaviors のコードは、ページが読み込まれた後に、主フレームで 1 度だけ走り、 何を判断したかを報告できます。preload のコードは、ページ自身の script より前に、 サブフレームを含むすべてのフレームで、遷移のたびに走り、報告する手段を持ちません。 「このコードは何を見えたか、何回走ったか」という問いへの答えが両者で違うので、 1 つの一覧に混ぜたパッケージは、どちらの問いにも答えられなくなります。
preload のスクリプトを走らせた適合パッケージは、それらを
preload/scripts.jsonl に、1 行 1 つの JSON オブジェクトとして、
登録した順に記録しなければならない(MUST)。
各行は profile(文字列 "browserhive:preload/1")・
id(持ち込まれた名前)・source(評価されたとおりの
スクリプト)を持たなければならない(MUST)。
id が、パッケージの中での項目の名前を決めては
ならない(MUST NOT)。それは行の中の値です。
名前は送った側が決めたもので、制約がありません。経路に見える名前は、 そうでなければ項目の置き場所を決めてしまいます。リクエストが持ち込む behavior と 同じ規則で、理由も同じです。
読み手は preload の中身を実行してはならない (MUST NOT)。behaviors と同じく、producer が走らせたものの記録であって、 アーカイブされたページの一部ではありません。
preload/scripts.jsonl が無いことと、settings の
preload が空であることは、合わせて「そのようなコードは走らなかった」と
述べます。読み手はこの 2 つを 1 つの主張として扱わ
なければならない(MUST)——
ファイルを持つのに member を欠く(またはその逆の)パッケージは適合しません。
この節の要求は [[WACZ]] に由来しません。既存の WACZ 読み手が実際に行っていることに由来し、 これらを無視したパッケージは、基底の仕様を満たしていても実務上は読めません。
datapackage.json の profile member は
"data-package" でなければならない。読み手はこの値で
分岐し、知らない値を拒むため、本プロファイルを識別する用途には使えません。
を参照。
datapackage.json の config と metadata を、本仕様が
定める値を運ぶために使ってはならない。読み手がこれらに意味を
割り当てています。
response レコードのペイロードは、交信を運んだプロトコルの版に
関わらず HTTP/1.1 形でなければならない。
.gz で終わらないパスに格納され
なければならない。読み手は拡張子で位置を特定し、
展開を行いません。
ここに記すのは、パッケージの作成者がこれらを満たせるようにするためです。それぞれが なぜ存在するかは実装史の問題であり、本仕様の外で文書化されています。
tls は来歴の証明ではありません。証明書は公開情報で、誰でも取得して複製を提示できます。
これらのフィールドを持っていることは、誰が要求に応答したかについて何も言いません。保存する
価値があるのはもっと狭い 2 つです —— issuer は接続が傍受されたときに変わり、
有効期間は取り込みが主張する時刻と突き合わせられます。そしてどちらも、書き留めなければ
取り込みの後には残りません。
completeness が主張するのは不在であって、正しさではありません。このパッケージが 実証的に保持していない本文を報告します。保持している本文がオリジンの配信したものである、 とは主張しません。
パッケージは completeness に URL を、tls に host 名を記録します。取り込んだ ページが URL に個人データを載せていた場合、それらの URL はアーカイブとともに旅をします。
アクセシビリティツリーは、browser が要素に対して計算した名前を運びます。それは
ページの可視テキストから作られます。文書の本文が WARC の本文に入っているなら、
そのテキストはそこに既に入っています。しかも刈り込み規則によって、
露出は本文より狭くなります: ignored なノードを除くので、aria-hidden や
display: none で隠されたテキストは、レスポンス本文には在っても、この木には
現れません。方針が文書の本文を archive に入れなかったなら、そのテキストは本文に
入っていないので、パッケージは木もページの文字と題も持ちません
(document を参照)。
この決まりは文書の単位で働きます。描画されたページには、副資源を通って届いた中身 —— スクリプトが出した要求への応答や、frame の文書 —— が入りえます。方針がそうした本文の どれかを archive に入れなくても、残された文書のページ由来の記録からは、その中身は 締め出されません。その中身を完全に締め出したい要求者は、文書ごと締め出す必要があります。
storage は、パッケージが既に保持している以上のものを晒しません。 記録するのは鍵の数・バイト数・digest です。値は記録せず、digest は値では ありません —— ただし上に書いたとおり、エントロピーの低い内容に対する digest は 隠し場所にもなりません。
storage/origins.jsonl を併せて持つパッケージは、
より多くを晒します。web storage は bearer token、
セッション識別子、未送信の下書きを日常的に保持しており、そのどれも WARC の
payload には現れません。この profile の中で、取り込みがネットワークから
一度も受け取っていない資料を適合パッケージが運びうる場所は、ここだけです。
producer は既定でこのディレクトリを出力しないべきである(SHOULD)。
読み手は valuesRecorded によって、アーカイブ本体を開かずに
どちらであるかを判別できます。
browser は記録を行った build を名指しし、これは必須です。origin へ別の身元を
提示する producer — たとえば User-Agent を差し替えた場合 — でも、
この member は変わりません。サーバへ送るものと、パッケージが報告するものは別です。
build の版を秘匿したい運用者は、適合するパッケージがそれを開示することを知っておく
必要があります。