Private Aggregation API の基礎

Private Aggregation API の主なコンセプト

このドキュメントの対象者

Private Aggregation API を使用すると、クロスサイト データにアクセスできるワークレットから集計データを収集できます。ここで説明するコンセプトは、Shared Storage API と Protected Audience API 内でレポート機能を作成するデベロッパーにとって重要です。

  • クロスサイト測定用のレポート システムを構築しているデベロッパー
  • マーケターデータ サイエンティスト、その他の概要レポートの利用者は、これらのメカニズムを理解することで、最適化された概要レポートを取得するための設計上の決定を下すことができます。

主な用語

このドキュメントを読む前に、重要な用語とコンセプトを理解しておくと役立ちます。ここでは、これらの用語について詳しく説明します。

  • 集計キー(バケットとも呼ばれます)は、事前に決定されたデータポイントのコレクションです。たとえば、ブラウザが国名をレポートする位置情報データのバケットを収集することが考えられます。集計キーには複数のディメンションを含めることができます(国やコンテンツ ウィジェットの ID など)。
  • 集計可能な値は、集計キーに収集された個々のデータポイントです。フランスのユーザーがコンテンツを閲覧した人数を測定する場合は、France が集計キーのディメンションとなり、1viewCount が集計可能な値となります。
  • 集計可能レポートは、ブラウザ内で生成され、暗号化されます。Private Aggregation API の場合、これには単一のイベントに関するデータが含まれます。
  • 集計サービスは、集計可能レポートのデータを処理して概要レポートを作成します。
  • 概要レポートは集計サービスの最終出力であり、ノイズを含む集計されたユーザーデータと詳細なコンバージョン データが含まれています。
  • ワークレットは、特定の JavaScript 関数を実行し、リクエスト元に情報を返すことができるインフラストラクチャの一部です。ワークレット内では JavaScript を実行できますが、外部ページとのやり取りや通信はできません。

プライベート アグリゲーションのワークフロー

集計キーと集計可能な値を使用して Private Aggregation API を呼び出すと、ブラウザは集計可能なレポートを生成します。レポートは、レポートをバッチ処理するサーバーに送信されます。バッチ処理されたレポートは、後で集計サービスによって処理され、概要レポートが生成されます。

データはクライアントからコレクタに流れ、そこから集計サービスに流れて概要レポートが生成されます。
クライアントからコレクタへのデータフロー。
  1. Private Aggregation API を呼び出すと、クライアント(ブラウザ)が集計可能レポートを生成してサーバーに送信し、収集します。
  2. サーバーはクライアントからレポートを収集し、集計サービスに送信するバッチにまとめます。
  3. 十分なレポートが収集されたら、それらをバッチ処理して、信頼できる実行環境で実行されている集計サービスに送信し、概要レポートを生成します。

このセクションで説明するワークフローは、Attribution Reporting API と似ています。ただし、アトリビューション レポートは、異なるタイミングで発生するインプレッション イベントとコンバージョン イベントから収集されたデータを関連付けます。プライベート アグリゲーションは、単一のクロスサイト イベントを測定します。

集計キー

集計キー(略して「キー」)は、集計可能な値が蓄積されるバケットを表します。1 つ以上のディメンションをキーにエンコードできます。ディメンションは、ユーザーの年齢層や広告キャンペーンのインプレッション数など、より詳細な分析を行う対象となる側面を表します。

たとえば、複数のサイトに埋め込まれているウィジェットがあり、そのウィジェットを見たユーザーの国を分析したいとします。「ウィジェットを見たユーザーのうち、X 国のユーザーは何人ですか?」などの質問に答える場合。この質問に関するレポートを作成するには、ウィジェット ID と国 ID の 2 つのディメンションをエンコードする集計キーを設定します。

Private Aggregation API に提供されるキーは、複数のディメンションで構成される BigInt です。この例では、ディメンションはウィジェット ID と国 ID です。ウィジェット ID は 1234 のように最大 4 桁で、各国はアルファベット順に番号が割り当てられているとします。たとえば、アフガニスタンは 1、フランスは 61、ジンバブエは 195 です。したがって、集計可能なキーは 7 桁になります。最初の 4 文字は WidgetID 用に予約され、最後の 3 文字は CountryID 用に予約されます。

キーがウィジェット ID 3276 を見たフランス(国 ID 061)のユーザー数を表しているとします。集計キーは 3276061 です。

集計キー
ウィジェット ID 国 ID
3276 061

集計キーは、SHA-256 などのハッシュ メカニズムで生成することもできます。たとえば、文字列 {"WidgetId":3276,"CountryID":67} をハッシュ化して、42943797454801331377966796057547478208888578253058197330928948081739249096287nBigInt 値に変換できます。ハッシュ値が 128 ビットを超える場合は、切り捨てて、許容されるバケット値の最大値である 2^128−1 を超えないようにすることができます。

Shared Storage ワークレット内では、ハッシュの生成に役立つ crypto モジュールと TextEncoder モジュールにアクセスできます。ハッシュの生成について詳しくは、MDN の SubtleCrypto.digest() をご覧ください。

次の例は、ハッシュ値からバケットキーを生成する方法を示しています。

async function convertToBucket(data) {
  // Encode as UTF-8 Uint8Array
  const encodedData = new TextEncoder().encode(data);

  // Generate SHA-256 hash
  const hashBuffer = await crypto.subtle.digest('SHA-256', encodedData);

  // Truncate the hash
  const truncatedHash = Array.from(new Uint8Array(hashBuffer, 0, 16));

  // Convert the byte sequence to a decimal
  return truncatedHash.reduce((acc, curr) => acc * 256n + BigInt(curr), 0n);
}

const data = {
  WidgetId: 3276,
  CountryID: 67
};

const dataString = JSON.stringify(data);
const bucket = await convertToBucket(dataString);

console.log(bucket); // 126200478277438733997751102134640640264n

集計可能な値

集計可能な値は、多くのユーザーにわたってキーごとに合計され、概要レポートの概要値の形式で集計された分析情報が生成されます。

ここで、先ほどの質問「ウィジェットを見たユーザーのうち、フランスのユーザーは何人ですか?」に戻ります。この質問に対する回答は、「ウィジェット ID 3276 を見たユーザーのうち、約 4,881 人がフランスのユーザーです」のようになります。集計可能な値はユーザーごとに 1 で、「4, 881 人のユーザー」は、その集計キーのすべての集計可能な値の合計である集計値です。

集計キー 集計可能な値
ウィジェット ID 国 ID 視聴回数
3276 061 1

この例では、ウィジェットを表示したユーザーごとに値を 1 ずつ増やしています。実際には、集計可能な値をスケーリングして、信号対雑音比を改善できます。

予算に対する割合

Private Aggregation API の各呼び出しは、コントリビューションと呼ばれます。ユーザーのプライバシーを保護するため、個人から収集できる投稿の数には上限があります。

すべての集計キーの集計可能な値を合計した値は、貢献度予算よりも小さくなければなりません。予算はワークレットのオリジンごと、1 日ごとに設定され、Protected Audience API ワークレットと Shared Storage ワークレットで別々に設定されます。1 日のデータには、過去 24 時間のローリング ウィンドウが使用されます。新しい集計可能レポートを作成すると予算を超過する場合、レポートは作成されません。

貢献予算はパラメータ L1 で表され、1 日 10 分あたり 216(65,536)に設定され、バックストップは 220(1,048,576)です。これらのパラメータの詳細については、説明をご覧ください。

貢献予算の値は任意ですが、ノイズはそれに合わせてスケーリングされます。この予算を使用して、要約値の信号対雑音比を最大化できます(詳しくは、ノイズとスケーリングのセクションをご覧ください)。

貢献予算について詳しくは、説明をご覧ください。また、貢献度予算も参照してください。

レポートあたりの貢献度の上限

呼び出し元によって上限が異なる場合があります。共有ストレージの場合、これらの上限はオーバーライド可能なデフォルトです。現時点では、Shared Storage API の呼び出し元に対して生成されるレポートは、レポートあたり 20 件の投稿に制限されています。一方、Protected Audience API の呼び出し元は、レポートあたり 100 件の投稿に制限されます。これらの上限は、埋め込むことができる投稿の数とペイロードのサイズのバランスを取るために選択されました。

共有ストレージの場合、単一の run() オペレーションまたは selectURL() オペレーション内で行われたコントリビューションは、1 つのレポートにバッチ処理されます。Protected Audience では、オークション内の単一のオリジンによる貢献はバッチ処理されます。

パディングありの投稿

投稿は、パディング機能でさらに変更されます。ペイロードをパディングすることで、集計可能レポートに埋め込まれた実際の貢献数に関する情報が保護されます。パディングにより、ペイロードは null コントリビューション(値が 0)で補強され、固定長に達します。

集計可能レポート

ユーザーが Private Aggregation API を呼び出すと、ブラウザは集計可能なレポートを生成します。このレポートは、後で集計サービスによって処理され、サマリー レポートが生成されます。集計可能なレポートは JSON 形式で、暗号化された貢献リストが含まれています。各貢献は {aggregation key, aggregatable value} ペアです。集計可能レポートは、最大 1 時間のランダムな遅延を伴って送信されます。

投稿は暗号化され、集計サービスの外部では読み取ることができません。集計サービスがレポートを復号し、概要レポートを生成します。ブラウザの暗号鍵とアグリゲーション サービスの復号鍵は、鍵管理サービスとして機能するコーディネーターによって発行されます。コーディネーターは、サービス イメージのバイナリ ハッシュのリストを保持し、呼び出し元が復号鍵を受け取ることが許可されていることを確認します。

デバッグモードが有効になっている集計可能レポートの例:

  "aggregation_service_payloads": [
    {
      "debug_cleartext_payload": "omRkYXRhgaJldmFsdWVEAAAAgGZidWNrZXRQAAAAAAAAAAAAAAAAAAAE0mlvcGVyYXRpb25paGlzdG9ncmFt",
      "key_id": "2cc72b6a-b92f-4b78-b929-e3048294f4d6",
      "payload": "a9Mk3XxvnfX70FsKrzcLNZPy+00kWYnoXF23ZpNXPz/Htv1KCzl/exzplqVlM/wvXdKUXCCtiGrDEL7BQ6MCbQp1NxbWzdXfdsZHGkZaLS2eF+vXw2UmLFH+BUg/zYMu13CxHtlNSFcZQQTwnCHb"
    }
  ],
  "debug_key": "777",
  "shared_info": "{\"api\":\"shared-storage\",\"debug_mode\":\"enabled\",\"report_id\":\"5bc74ea5-7656-43da-9d76-5ea3ebb5fca5\",\"reporting_origin\":\"https://localhost:4437\",\"scheduled_report_time\":\"1664907229\",\"version\":\"0.1\"}"

集計可能レポートは chrome://private-aggregation-internals ページで確認できます。

Private Aggregation API の内部ページ
Private Aggregation API の内部ページ

テストのために、[選択したレポートを送信] ボタンを使用して、レポートをサーバーにすぐに送信できます。

集計可能レポートを収集してバッチ処理する

ブラウザは、次の既知のパスを使用して、Private Aggregation API の呼び出しを含むワークレットのオリジンに集計可能レポートを送信します。

  • 共有ストレージの場合: /.well-known/private-aggregation/report-shared-storage
  • Protected Audience の場合: /.well-known/private-aggregation/report-protected-audience

これらのエンドポイントでは、クライアントから送信された集計可能レポートを受信するコレクタとして機能するサーバーを運用する必要があります。

その後、サーバーはレポートをバッチ処理し、そのバッチを集計サービスに送信します。集計可能レポートの暗号化されていないペイロード(shared_info フィールドなど)で利用可能な情報に基づいてバッチを作成します。理想的には、バッチには 100 件以上のレポートを含める必要があります。

バッチ処理は、毎日または毎週行うように設定できます。この戦略は柔軟性があり、ボリュームの増加が予想される特定のイベント(インプレッション数の増加が予想される日など)のバッチ処理戦略を変更できます。バッチには、同じ API バージョン、レポート送信元、スケジュール レポート時刻のレポートを含める必要があります。

フィルタ ID

Private Aggregation API と Aggregation Service を使用すると、フィルタリング ID を使用して、より詳細なレベルで測定値を処理できます。たとえば、大きなクエリで結果を処理するのではなく、広告キャンペーンごとに処理できます。

プライベート アグリゲーションとアグリゲーション サービスのフィルタリング ID。
Private Aggregation と Aggregation Service のフィルタリング ID。

今すぐこの機能を使用するには、現在の実装に適用するおおまかな手順を以下に示します。

共有ストレージの手順

フローで Shared Storage API を使用している場合:

  1. 新しい共有ストレージ モジュールを宣言して実行する場所を定義します。次の例では、モジュール ファイルに filtering-worklet.js という名前を付け、filtering-example に登録しています。

    (async function runFilteringIdsExample () {
    await window.sharedStorage.worklet.addModule('filtering-worklet.js');
    await window.sharedStorage.run('filtering-example', {
      keepAlive: true,
      privateAggregationConfig: {
        contextId: 'example-id',
        filteringIdMaxBytes: 8 // optional
      }
    }});
    })();
    

    filteringIdMaxBytes はレポートごとに構成可能であり、設定されていない場合はデフォルトで 1 になります。このデフォルト値は、ペイロード サイズが不必要に大きくなり、ストレージと処理の費用が増加するのを防ぐためのものです。詳しくは、柔軟な貢献度に関する説明をご覧ください。

  2. filtering-worklet.js では、共有ストレージ ワークレット内の privateAggregation.contributeToHistogram(...) に貢献度を渡すときに、フィルタリング ID を指定できます。

    // Within  filtering-worklet.js
    class FilterOperation {
      async run() {
        let contributions = [{
          bucket: 1234n,
          value: 56,
          filteringId: 3n // defaults to 0n if not assigned, type bigint
        }];
    
        for (const c of contributions) {
          privateAggregation.contributeToHistogram(c);
        }
        
    }
    });
    
    register('filtering-example', FilterOperation);
    
  3. 集計可能レポートは、エンドポイント /.well-known/private-aggregation/report-shared-storage を定義した場所に送信されます。Aggregation Service ジョブ パラメータに必要な変更については、ID のフィルタリング ガイドをご覧ください。

バッチ処理が完了してデプロイされた集計サービスに送信されると、フィルタされた結果が最終的な概要レポートに反映されます。

Protected Audience の手順

フローで Protected Audience API を使用している場合:

  1. 現在の Protected Audience の実装では、Private Aggregation にフックするために次の設定を行うことができます。共有ストレージとは異なり、フィルタリング ID の最大サイズを構成することはまだできません。デフォルトでは、フィルタリング ID の最大サイズは 1 バイトで、0n に設定されます。これらは、Protected Audience レポート関数reportResult()generateBid() など)で設定されることに注意してください。

    const contribution = {
      ...
      filteringId: 0n
    };
    
    privateAggregation.contributeToHistogram(contribution);
    
  2. 集計可能レポートは、エンドポイント /.well-known/private-aggregation/report-protected-audience を定義した場所に送信されます。バッチ処理が完了してデプロイされた集計サービスに送信されると、フィルタされた結果が最終的な概要レポートに反映されます。Attribution Reporting API と Private Aggregation API の説明と、最初の提案は以下のとおりです。

集計サービスのフィルタリング ID のガイドに進むか、Attribution Reporting API のセクションに進んで、詳細な説明をご覧ください。

集計サービス

このサービスは TEE で実行され、集計可能レポートを復号し、ノイズを追加して最終的な概要レポートを作成します。
サービスは TEE で実行され、集計可能レポートを復号し、ノイズを追加して最終的な概要レポートを作成します。

集計サービスは、コレクタから暗号化された集計可能レポートを受け取り、概要レポートを生成します。コレクタで集計可能レポートをバッチ処理する方法について詳しくは、バッチ処理ガイドをご覧ください。

このサービスは、データ完全性、データ機密性、コード完全性について一定の保証を提供する高信頼実行環境(TEE)で実行されます。コーディネーターが TEE とともに使用される仕組みについて詳しくは、コーディネーターの役割と目的をご覧ください。

概要レポート

概要レポートでは、収集したデータにノイズが追加された状態で確認できます。特定のキーセットの概要レポートをリクエストできます。

概要レポートには、JSON 辞書形式の Key-Value ペアのセットが含まれています。各ペアには次のものが含まれます。

  • bucket: 集計キー(バイナリ数値文字列)。使用される集計キーが「123」の場合、バケットは「1111011」になります。
  • value: 特定の測定目標の要約値。ノイズが追加された、利用可能なすべての集計可能レポートから合計されます。

次に例を示します。

[
  {"bucket":` `"111001001",` `"value":` `"2558500"},
  {"bucket":` `"111101001",` `"value":` `"3256211"},
  {"bucket":` `"111101001",` `"value":` `"6536542"},
]

ノイズとスケーリング

ユーザーのプライバシーを保護するため、集計サービスは、概要レポートがリクエストされる度に、それぞれの概要の値に 1 回ノイズを付加します。ノイズ値は、ラプラス確率分布からランダムに導かれます。ノイズの付加方法を直接制御することはできませんが、測定データのノイズの影響に対して働きかけることはできます。

ノイズ分布は、集計可能なすべての値の合計に関係なく同じです。したがって、集計可能な値が高いほど、ノイズの影響は少なくなると考えられます。

たとえば、ノイズ分布の標準偏差が 100 で、ゼロを中心としているとします。収集された集計可能レポート値(または「集計可能値」)が 200 のみの場合、ノイズの標準偏差は集計値の 50% になります。ただし、集計可能な値が 20,000 の場合、ノイズの標準偏差は集計値の 0.5% にしかなりません。そのため、集計可能な値 20,000 の方が SN 比がはるかに高くなります。

そのため、集計可能な値にスケーリング ファクタを掛けることで、ノイズを減らすことができます。スケーリング ファクタは、指定された集計可能値をどの程度スケーリングするかを表します。

ノイズは集計値に関係なく一定です。
集計値に関係なくノイズ定数。

スケーリング係数を大きくして値をスケールアップすると、相対ノイズが減少します。ただし、これにより、すべてのバケットのすべての貢献度の合計が貢献度予算の上限に達するのも早くなります。スケーリング係数の定数を小さくして値をスケーリングすると、相対ノイズが増加しますが、予算上限に達するリスクが軽減されます。

集計可能な値を貢献度予算にスケーリングします。
集計可能な値を貢献予算にスケーリングします。

適切なスケーリング ファクタを計算するには、貢献予算をすべてのキーの集計可能な値の最大合計で割ります。

詳細については、貢献予算のドキュメントをご覧ください。

意見交換とフィードバックの提供

Private Aggregation API は現在、活発な議論の途上にあり、今後変更される可能性があります。この API をお試しになり、フィードバックがございましたら、ぜひお聞かせください。