これは BrowserHive WACZ Profile 1.8.0 の日本語訳です。正典は 英語原典であり、判断の根拠にする場面ではそちらを確認してください。 訳と原典が食い違った場合は原典が優先します。

本仕様は [[WACZ]] のプロファイルです。WACZ パッケージの書き方を制約し、BrowserHive が その中に記録する追加の member を定めます。アーカイブだけを手にした読み手が、その取り込みが 何を取得し何を取得できなかったか、どのような設定の下で走ったかを判断できるようにするためです。

これは作業中の草案です。BrowserHive が現在書き出しているものを記述しています。同プロジェクト 以外での位置づけを持たず、[[WACZ]] の発行者から承認されたものでもありません。

この版は preload を足しました。コードの出どころが 2 つ目になります —— リクエストが持ち込み、遷移の前に、すべてのフレームで 走るスクリプト。 読み込み後に主フレームで 1 度だけ走る behaviors とは、走る時も場所も回数も違います。 あわせて settings に必須の preload member を足しました —— それら自身は何も報告できない ので、何が持ち込まれたかはパッケージが述べるしかありません。 新しい member が必須なので、1.7.0 に対して書かれたパッケージは 1.8.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 —— がそれぞれの URL で公開され続けるのはこの理由によりますが、 BrowserHive はもうそれらを出力しません。

取り込みは、ページをいつ撮ってよいかを決めなければならず、どの決め方も賭けです —— 固定の間は、もう揃っていたページから時間を奪い、揃っていないページには足りません。 合図で終える待ちは、合図より先に期限に当たることがあります。この版まで、パッケージは そのどれが起きたかを言えませんでした。これはパッケージを証拠として読む人にとって 重要です。「ページはこう見えていた」と「ページはまだ読み込み中に、こう見えていた」の 違いは、パッケージの他のどこからも取り戻せません —— 記録が言うのは届いたものであって、 取り込みが先へ進んだときにまだ向かっていたものではないからです。

Introduction

[[WACZ]] のパッケージは、何が取り込まれたかを述べます。しかし何が抜けたかは述べず、 どのような条件下で取り込みが走ったかも述べません。それらは取り込み時にしか知り得ず、 取り込みを行ったプロセスが終了した時点で失われます。

この喪失が問題になるのは、旅をするのがアーカイブの方だからです。3 年後にパッケージを開いた 読み手には、ログも API の応答も、それを作った機械も残っていません。「ここに欠けているものは あるか」にパッケージ自身が答えられないなら、他の誰も答えられません。

そこで本プロファイルは、パッケージが取り込みについての自身の申告を運ぶことを要求します —— どの本文が欠けていてそれはなぜか、どの上限が効いていたか、どのリクエストを送らず どのリクエストを送って保持しなかったか、記録の前にページから何を取り除いたか、 各オリジンがどの証明書を提示したか。これらは [[WACZ]] の意味を何ひとつ変えません。変えずに、そのままでは取り戻せないものを 記録します。

Conformance

本仕様の適合対象はパッケージ、すなわちファイル 1 つです。適合はファイルの性質で あって、それを書くソフトウェアの性質ではありません。ある実装が、あるときは適合する パッケージを、別のときは適合しないパッケージを書くことはあり得ます。本仕様はそのどちらで あるかについて何も述べません。

適合するパッケージは、適合する [[WACZ]] パッケージです。本仕様は要求を追加します。 取り除くことも、緩めることもしません。

本仕様における MUST(〜しなければならない)、 MUST NOT(〜してはならない)、 SHOULD(〜すべきである)、 MAY(〜してもよい)は [[RFC2119]] の記述に従って解釈されます。

JSON の member 名は大文字と小文字を区別します。timestamp を保持すると記した member の値は、 UTC の [[RFC3339]] date-time でなければならない。

The browserhive:capture member

適合するパッケージの datapackage.json は、値が JSON オブジェクトである browserhive:capture という名前の member を持た なければならない。

この member を config または metadata の中に置いては ならない。これらの名前は [[WACZ]] の読み手にとって意味を持ちます ( を参照)。

ここで定義されていない member が browserhive:capture に存在しても よい。読み手は認識しない member を無視し なければならない。

Conformance declaration

browserhive:capture は、そのパッケージが満たすと主張する本仕様の版の URL を 値とする conformsTo という member を持たなければならない。

"browserhive:capture": {
  "conformsTo": "https://uraitakahito.github.io/browserhive-specs/wacz-profile/1.8.0/"
}

宣言をここに置き、datapackage.json の profile member に置かないのは、 そちらがこの用途に使えないためです。

読み手は browserhive:capture が存在するというだけで適合を推定しては ならない。conformsTo が無ければ、そのパッケージが どの版に向けて書かれたかは不明です。

build

browserhive:capture は、パッケージを作ったソフトウェアを機械可読な形で 識別する build member を持たなければならない。

Member型必須意味
versionstringはいリリースの識別子。
revisionstringはいビルド元のソース revision。
buildTimestringはいソフトウェアがビルドされた時刻。
"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型必須意味
signaturestringはい "required" または "none"。この取り込みが署名なしで 出されてよかったか。
viewportobjectはい member width と height。ともに整数、単位は CSS ピクセル。
devicePixelRatiosarray of numberはい ページを読み込んだときの device pixel ratio を、読み込んだ順に。要素数が そのまま読み込みの回数。
sessionstringはい その取り込みのために作られ、終われば捨てられる browser context で 走ったのか、取り込みをまたいで使い回される context で走ったのか。 前の取り込みが残した状態にパッケージが依存しうるかどうかを、 読み手はこれで判定します。
behaviorsarray of objectはい 読み込み後に走った behavior。走った順のまま。 各項目は id(その behavior が走った名前)と origin("builtin"・"site"・ "custom" のいずれか)を持ちます。自前の behavior を 持たない producer は、すべての項目に "custom" と書きます。 behaviors ディレクトリを参照。
preloadarray of objectはい 遷移の前に走らせるために持ち込まれたスクリプト。登録した順のまま —— 報告したものではありません(それら自身は何も報告できないため)。 各項目は id(持ち込まれた名前)を持ちます。空の配列は 「1 本も持ち込まれなかった」と述べます。 preload ディレクトリを参照。
limitsobjectはい この取り込みに効いていた本文の上限。member maxResponseBytes と maxTaskBytes。ともに整数で、単位はバイト。
urlPoliciesarray of objectはい 効いていた URL パターンのそれぞれに対して取り込みが何をしたかを、 評価した順に。urlPolicies を参照。
contentTypePoliciesarray of objectはい 効いていたメディア型のそれぞれに対して取り込みが何をしたか。 contentTypePolicies を参照。
acceptLanguagestringいいえ 取り込みが設定したときにのみ存在する。オリジンが返すものが変わるため。
"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型必須意味
bodylessUrlsarray of stringはい 304 としてしか現れなかった URL。本文が wire を渡っていないので、 アーカイブは保持し得ません。
truncatedUrlsarray of stringはい サイズ上限に達して取り込みが本文を落とした URL。
completebooleanはい 両方の配列が空のとき、かつそのときに限り 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、および要求者が設定した content-type フィルタで省かれた本文。 最後のものを報告しても、読み手が既に決めたこと以上を何も伝えません。

coverage

browserhive:capture は、取り込みがページのどこまで下れたかを記述する coverage member を持ってもよい。

Member型必須意味
scrollExhaustedbooleanはい スクロールがページの終端ではなく段数の上限で止まったとき true —— つまりその下にあるものが一度も要求されていないとき。
scrollStepsnumberはい実際に進んだ段数。
scrolledPxnumberはい 到達した深さの概算。段数 × 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型必須意味
strategystringはい どの合図を、どの順で見たか。この版が定めるのは "fully-loaded" —— load、次に limits.minMs の間を置き、それから network・cpu・dom を並べて見る。
endedBystringはい 見ていた合図がすべて期限の前に満ちたとき "quiet"、そうでなければ "deadline"。
waitedMsnumberはい navigation が返ってから待ちが終わるまでのミリ秒。
devicePixelRationumberはい この観測がどの読み込みのものか。devicePixelRatios の要素の 1 つで、 複数あるときは最後のもの。
limitsobjectはい 効いていた上限。どれも数値で、minMs、maxMs(期限)、 quietMs(静止の合図がそれぞれ満たすべき窓)、 networkConcurrency(静かとみなしてよい要求の数)。
loadobjectはい atMs: document が読み込み終わったのを見た時刻。
networkobjectはい quietAtMs: in-flight の要求が limits.networkConcurrency 以下のまま limits.quietMs 続いた時刻。
cpuobjectはい quietAtMs: long task —— メインスレッドを 50 ms 以上握ったタスク —— が limits.quietMs の間走らなかった時刻。
domobjectはい 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型必須意味
selectorsobject の配列はい 効いていた選択子。評価した順のまま。 member は framework と selector で、どちらも文字列。
heuristicobjectはい どの選択子も名指ししていない要素の除去を支配していた閾値。 member は enabled (boolean)・ minViewportCoverageRatio (number)・ minZIndex (number)。
framework文字列または nullいいえ 最初に一致した項目の framework。閾値だけが発火したときは "heuristic"。何も取り除かなかったときは null。
removedSelectors文字列の配列いいえ 要素が取り除かれた選択子。selectors の部分集合で、評価した順。
unusableSelectors文字列の配列いいえ ページを読む側が評価できなかった selectors の項目。 これらが何にも当たらなかったのは選択子として成立していなかったからであって、 「当たらなかった」とは別の主張。
removedOverlaysnumberいいえ 選択子ではなく閾値によって取り除かれた要素の数。
devicePixelRationumberいいえ どの読み込みの観測か。devicePixelRatios の項目のいずれか。
unreadabletrueいいえ 除去を試みたが遂行できなかったときに在る。
"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 です。

tls

browserhive:capture は、各 HTTPS 接続について browser が見たものを記録する tls member を持ってもよい。存在する場合、 hosts と chains を持たなければならない。

tls.hosts

host を鍵とするオブジェクト。各値は以下の member を持つオブジェクトか、null。

Member型必須意味
protocolstringはいネゴシエートされた TLS の版。
cipherstringはいネゴシエートされた cipher。
subjectstringはいsubject の common name。browser が報告したそのまま。
sanarray of stringはい その証明書が有効な host 名の一覧 (subjectAltName)。SAN を持たない証明書では空。
issuerstringはいissuer の common name。
validFromstringはい証明書の有効期間の開始。
validTostringはいその終了。
chainRefstringいいえ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 を記録しています。上の規定がそれぞれどう現れるかを見てください。

chains の値は base64 符号化された DER です。例では読みやすさのため途中で 切ってありますが、実際には 1 通あたり 1–2 KB 程度の文字列になります。

URL and media type policies

取り込みは、一部のリクエストを他と違う扱いにすることがあります —— 送らない、記録しない、本文なしで記録する。 browserhive:capture.settings は何をしたかを MUST 記録します。取り込みの設定に手が届かない読み手が、 そのどれであったかを判定できるようにするためです。

urlPolicies

urlPolicies は順序付きの配列です。各項目は 2 つの member を持つ オブジェクト:

MemberType必須説明
patternstringはい 効いていたパターン。取り込みが設定されたとおりの綴りで。
actionstringはい 下の値のいずれか。
actionリクエストの送信パッケージが持つもの
denyしない リクエストの記録のみ。その URL について取り込みを行った機械から 出たものは無く、それを通じて取り込みを知ったオリジンも在りません。
no-archiveする リクエストの記録のみ。オリジンは応答しましたが、パッケージは その応答を保持しません。
no-bodyする request / response のレコード。ただし response の本文は含みません。

この配列は空のときでも存在して 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" }
]

Records of withheld requests

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いいえ ページが何を取りに行っていたか。取り込みが知っている場合に。
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

withheld request のレコードはレスポンスも本文も持ちません。主張しているのは、 ページがその URL を要求したこと、そしてこのプロファイルの producer が それについて何をするか決めたこと —— リクエストが失敗したという意味でも、 オリジンが何も返さなかったという意味でもありません。オリジンに到達 できなかった取り込みは、これをこの形で報告しては MUST NOT。

no-body の policy はこの種のレコードを生みません。 request / response のレコードは通常どおり書かれ、欠けている本文は 他の欠けている本文と同じ形で報告されます。

withheld request のレコードは、パッケージの CDXJ 索引に現れることを 要求されません。これを探す読み手は WARC を読みます。

The 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 }
      ]
    }
  }
}
  

Members

storage は profile、stage、 valuesRecorded、origins を 持たなければならない(MUST)。

Origin entries

各項目は 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)。

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
}
    

The 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 の値も運べなかった場合です。これは エントリが不在であること(=そもそも値を求められなかった)とは 別の主張です。

The accessibility directory

[[WACZ]] は、archive・indexes・pages を 避け、datapackage.json の resources に列挙する限り、 追加のファイルやディレクトリを認めています。適合するパッケージは、その許可を 使ってもよい(MAY)。

適合するパッケージは accessibility/axtree.jsonl というエントリを 持ってもよい(MAY)。存在する場合、他のファイルと同様に resources で宣言しなければならず(MUST)、 JSON Lines でなければなりません (MUST)—— 1 行 1 つの JSON オブジェクトで、各 オブジェクトは、取り込んだページに対して browser が計算したアクセシビリティ ツリーのスナップショット 1 枚を記録します。

この木を記録するのは、後から作り直せないからです。DOM とレイアウト ツリーの両方から作られ、対応する DOM ノードを持たないノード(CSS の 生成内容、リストの記号)を含み、aria-owns がノードの親を付け替える ため、その形は文書の木とは異なります。将来の browser で archive を再生しても、 別の木ができます。tls を参照ではなく記録している理由と同じです。

Snapshot members

各行は profile・url・takenAt・ stage・tree を含まなければなりません (MUST)。

各行はさらに 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 行を占めます。

Node shape

すべてのノードは role を持たなければなりません (MUST)。ノードは role・ name・level・url・checked・ expanded・disabled・required・ value・children 以外の member を持ってはなりません (MUST NOT)。

このうち 2 つは、他より重い意味を持ちます:

値が無い・空・false の member は書いてはなりません (MUST NOT)。全ノードに既定値を書くと、それが囲んでいる 中身より嵩が大きくなります。

Pruning

browser が ignored と印を付けたノードは tree に現れてはなりません (MUST NOT)。その子は残し、最も近い残されたノードの 下に付けなければなりません(MUST)。

これが、隠されたテキストを記録に持ち込まないための仕掛けです。 aria-hidden や display: none で外された部分木は ignored なノードなので、そもそもこのファイルに届きません。したがって読み手は、 この木を「見えていたもの」の記述として扱えます —— WARC の 本文だけからは支えられない、より強い主張です。

role が generic・none・InlineTextBox の ノードも tree に現れてはならず(MUST NOT)、 その子は同じように残さなければなりません(MUST)。 これらの role はそれ自体では意味を持たず、残すと木の深さが倍になるだけで、 読み手が使えるものは何も増えません。

viewport の外にあることを理由にノードを落としてはなりません (MUST NOT)。エージェントにとっては妥当な判断です —— 見えないものは操作できないので。しかし、誰もスクロールしなかった部分を捨てた アーカイブは、そのページの記録ではありません。

The behaviors directory

[[WACZ]] は、archive・indexes・pages を 避け、datapackage.json の resources に列挙する限り、 追加のファイルやディレクトリを認めています。適合するパッケージは、その許可を 使ってもよい(MAY)。

適合するパッケージは、取り込みが保存の前にページ上で走らせたコードを運ぶ behaviors の下のエントリを持ってもよい(MAY)。 存在する場合、各エントリは他のファイルと同様に resources で 宣言しなければなりません(MUST)。

コードを記録するのは、後から取り戻せないからです。producer が同梱するコードは 少なくとも build が固定していますが、リクエストが運んできたコードは リクエストと共に来て、応答を捨てた時点で失われます —— しかも、それが走った名前は 提供した側が決めたものなので、同じ名前で中身の違うコードが走った 2 回は 他の手段では区別が付きません。tls を参照ではなく記録している理由と 同じです。

Runtime

適合するパッケージは behaviors/runtime.js というエントリを 持ってもよい(MAY)。存在する場合、それは producer が ページの中で評価したソースを、バイト単位でそのまま 持たなければなりません(MUST)。

producer がそのようなソースを評価したときは在らなければならず (MUST)、そうでなければ在ってはなりません(MUST NOT)。 したがってその存在は「ページ上でコードが走った」ことを述べます —— その後どの behavior が何かをしたかどうかとは無関係に。

要求しているのは「バイト単位でそのまま」であって、読みやすさではありません。 runtime を minify する producer は minify された形を記録します —— それがページの受け取ったものだからです。人が読める形が要る読み手には build があり、それがパッケージを作ったソフトウェアを名指しします。

Request-supplied behaviors

適合するパッケージは 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)。

producer がリクエスト由来の behavior をページに置いたときは 在らなければならず(MUST)、そうでなければ 在ってはなりません(MUST NOT)。したがってその不在は 「このパッケージはリクエストから来たコードを持たない」ことを述べます。

条件は「置いたか」であって「走ったか」ではありません —— runtime と同じ条件です。ページに置かれた コードは、そのあと何を決めたにせよ、そこで走っています。作用したコードだけを 記録するアーカイブは、実行されたコードを取りこぼします。

behavior の名前は、それを提供した側が決めるもので、何の制約もありません —— 現に使われている名前には : も / も含まれます。 それをエントリのパスに使うと、名前がパッケージの中の置き場所を 決めてしまいます。ファイル 1 つ・behavior 1 つにつき 1 行という形は、 名前を経路ではなく値のままに保ちます。

Reading these entries

読み手は behaviors の中身を実行してはなりません (MUST NOT)。あれは producer が何を走らせたかの記録であって、保存されたページの 一部ではありません。パッケージを replay するのに、それらを再び走らせることを 求めるものは何もありません。

これを明記するのは、適合するパッケージの中で中身が実行可能なエントリが これだけであることと、パッケージを replay する読み手は既に実行の context を 開いているためです。

The preload directory

適合するパッケージは preload の下に項目を持って よい(MAY)。取り込んだページ自身の script より 前に走らせるために、リクエストが持ち込んだコードです。在るときは、 他のファイルと同じく resources で申告し なければならない(MUST)。

behaviors のコードは、ページが読み込まれた後に、主フレームで 1 度だけ走り、 何を判断したかを報告できます。preload のコードは、ページ自身の script より前に、 サブフレームを含むすべてのフレームで、遷移のたびに走り、報告する手段を持ちません。 「このコードは何を見えたか、何回走ったか」という問いへの答えが両者で違うので、 1 つの一覧に混ぜたパッケージは、どちらの問いにも答えられなくなります。

Supplied scripts

preload のスクリプトを走らせた適合パッケージは、それらを preload/scripts.jsonl に、1 行 1 つの JSON オブジェクトとして、 登録した順に記録しなければならない(MUST)。 各行は profile(文字列 "browserhive:preload/1")・ id(持ち込まれた名前)・source(評価されたとおりの スクリプト)を持たなければならない(MUST)。

id が、パッケージの中での項目の名前を決めては ならない(MUST NOT)。それは行の中の値です。

名前は送った側が決めたもので、制約がありません。経路に見える名前は、 そうでなければ項目の置き場所を決めてしまいます。リクエストが持ち込む behavior と 同じ規則で、理由も同じです。

Reading these entries

読み手は preload の中身を実行してはならない (MUST NOT)。behaviors と同じく、producer が走らせたものの記録であって、 アーカイブされたページの一部ではありません。

preload/scripts.jsonl が無いことと、settings の preload が空であることは、合わせて「そのようなコードは走らなかった」と 述べます。読み手はこの 2 つを 1 つの主張として扱わ なければならない(MUST)—— ファイルを持つのに member を欠く(またはその逆の)パッケージは適合しません。

Constraints inherited from replay

この節の要求は [[WACZ]] に由来しません。既存の WACZ 読み手が実際に行っていることに由来し、 これらを無視したパッケージは、基底の仕様を満たしていても実務上は読めません。

ここに記すのは、パッケージの作成者がこれらを満たせるようにするためです。それぞれが なぜ存在するかは実装史の問題であり、本仕様の外で文書化されています。

Security and privacy considerations

tls は来歴の証明ではありません。証明書は公開情報で、誰でも取得して複製を提示できます。 これらのフィールドを持っていることは、誰が要求に応答したかについて何も言いません。保存する 価値があるのはもっと狭い 2 つです —— issuer は接続が傍受されたときに変わり、 有効期間は取り込みが主張する時刻と突き合わせられます。そしてどちらも、書き留めなければ 取り込みの後には残りません。

completeness が主張するのは不在であって、正しさではありません。このパッケージが 実証的に保持していない本文を報告します。保持している本文がオリジンの配信したものである、 とは主張しません。

パッケージは completeness に URL を、tls に host 名を記録します。取り込んだ ページが URL に個人データを載せていた場合、それらの URL はアーカイブとともに旅をします。

アクセシビリティツリーは、browser が要素に対して計算した名前を運びます。それは ページの可視テキストから作られるもので、そのテキストは WARC の本文に 既に入っています —— つまりこの木を記録しても、パッケージが それまで持っていなかったものが増えるわけではありません。むしろ刈り込み規則に よって、露出は本文より狭くなります: ignored なノードを除くので、 aria-hidden や display: none で隠されたテキストは、 レスポンス本文には在っても、この木には現れません。

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 の版を秘匿したい運用者は、適合するパッケージがそれを開示することを知っておく 必要があります。