Guía de inicio rápido para la implementación del almacenamiento compartido y la agregación privada

Este documento es una guía de inicio rápido para usar Shared Storage y Private Aggregation. Deberás comprender ambas APIs, ya que Shared Storage almacena los valores y Private Aggregation crea los informes agregables.

Público objetivo: Proveedores de tecnología publicitaria y medición.

API de Shared Storage

Para evitar el seguimiento entre sitios, los navegadores comenzaron a particionar todas las formas de almacenamiento, incluido el almacenamiento local, las cookies, etcétera. Sin embargo, hay casos de uso en los que se requiere almacenamiento sin particiones. La API de Shared Storage proporciona acceso de escritura ilimitado en diferentes sitios de nivel superior con acceso de lectura que preserva la privacidad.

El almacenamiento compartido se restringe al origen del contexto (el llamador de sharedStorage).

Shared Storage tiene un límite de capacidad por origen, y cada entrada está limitada a una cantidad máxima de caracteres. Si se alcanza el límite, no se almacenarán más entradas. Los límites de almacenamiento de datos se describen en el explicador de Shared Storage.

Cómo invocar Shared Storage

Las tecnologías publicitarias pueden escribir en el almacenamiento compartido con JavaScript o un encabezado de respuesta. La lectura desde el almacenamiento compartido solo se produce dentro de un entorno aislado de JavaScript llamado worklet.

  • Uso de JavaScript: Las tecnologías publicitarias pueden realizar funciones específicas de Shared Storage, como establecer, agregar y borrar valores fuera de un worklet de JavaScript. Sin embargo, las funciones como la lectura de Shared Storage y la realización de Private Aggregation deben completarse a través de un worklet de JavaScript. Los métodos que se pueden usar fuera de un worklet de JavaScript se encuentran en Proposed API Surface - Outside the worklet.

    Los métodos que se usan en el worklet durante una operación se pueden encontrar en Proposed API Surface - In the worklet.

  • Cómo usar encabezados de respuesta

    Al igual que con JavaScript, solo se pueden realizar funciones específicas, como establecer, agregar y borrar valores en Shared Storage, con encabezados de respuesta. Para trabajar con Shared Storage en un encabezado de respuesta, se debe incluir Shared-Storage-Writable: ?1 en el encabezado de la solicitud.

    Para iniciar una solicitud desde el cliente, ejecuta el siguiente código, según el método que elijas:

    • Usa fetch()

      fetch("https://a.example/path/for/updates", {sharedStorageWritable: true});
      
    • Cómo usar una etiqueta iframe o img

      <iframe src="https://a.example/path/for/updates" sharedstoragewritable></iframe>
      
    • Usa un atributo IDL con una etiqueta iframe o img

      let iframe = document.getElementById("my-iframe");
      iframe.sharedStorageWritable = true;
      iframe.src = "https://a.example/path/for/updates";
      

Puedes encontrar más información en Shared Storage: Response Headers.

Cómo escribir en Shared Storage

Para escribir en Shared Storage, llama a sharedStorage.set() desde dentro o fuera de un worklet de JavaScript. Si se llama desde fuera del worklet, los datos se escriben en el origen del contexto de navegación desde el que se realizó la llamada. Si se llama desde dentro del worklet, los datos se escriben en el origen del contexto de navegación que cargó el worklet. Las claves establecidas tienen una fecha de vencimiento de 30 días a partir de la última actualización.

El campo ignoreIfPresent es opcional. Si está presente y se establece en true, la clave no se actualiza si ya existe. La fecha de vencimiento de la clave se renueva a 30 días a partir de la llamada a set(), incluso si la clave no se actualiza.

Si se accede al almacenamiento compartido varias veces en la misma carga de página con la misma clave, se reemplaza el valor de la clave. Es una buena idea usar sharedStorage.append() si la clave debe mantener el valor anterior.

  • Uso de JavaScript

    Fuera del worklet:

    window.sharedStorage.set('myKey', 'myValue1', { ignoreIfPresent: true });
    // Shared Storage: {'myKey': 'myValue1'}
    window.sharedStorage.set('myKey', 'myValue2', { ignoreIfPresent: true });
    // Shared Storage: {'myKey': 'myValue1'}
    window.sharedStorage.set('myKey', 'myValue2', { ignoreIfPresent: false });
    // Shared Storage: {'myKey': 'myValue2'}
    

    Del mismo modo, dentro del worklet:

    sharedStorage.set('myKey', 'myValue1', { ignoreIfPresent: true });
    
  • Cómo usar encabezados de respuesta

    También puedes escribir en el almacenamiento compartido con encabezados de respuesta. Para ello, usa Shared-Storage-Write en el encabezado de la respuesta junto con los siguientes comandos:

    Shared-Storage-Write : set;key="myKey";value="myValue";ignore_if_present
    
    Shared-Storage-Write : set;key="myKey";value="myValue";ignore_if_present=?0
    

    Se pueden separar varios elementos con comas y combinar set, append, delete y clear.

    Shared-Storage-Write :
    set;key="hello";value="world";ignore_if_present, set;key="good";value="bye"
    

Cómo agregar un valor

Puedes agregar un valor a una clave existente con el método append. Si la clave no existe, llamar a append() crea la clave y establece el valor. Esto se puede lograr con JavaScript o un encabezado de respuesta.

  • Uso de JavaScript

    Para actualizar los valores de las claves existentes, usa sharedStorage.append() desde dentro o fuera del worklet.

    window.sharedStorage.append('myKey', 'myValue1');
    // Shared Storage: {'myKey': 'myValue1'}
    window.sharedStorage.append('myKey', 'myValue2');
    // Shared Storage: {'myKey': 'myValue1myValue2'}
    window.sharedStorage.append('anotherKey', 'hello');
    // Shared Storage: {'myKey': 'myValue1myValue2', 'anotherKey': 'hello'}
    

    Para agregar datos dentro del worklet, haz lo siguiente:

    sharedStorage.append('myKey', 'myValue1');
    
  • Cómo usar encabezados de respuesta

    De manera similar a la configuración de un valor en Shared Storage, puedes usar Shared-Storage-Write en el encabezado de respuesta para pasar el par clave-valor.

    Shared-Storage-Write : append;key="myKey";value="myValue2"
    

Actualización por lotes de valores

Puedes llamar a sharedStorage.batchUpdate() desde dentro o fuera de un worklet de JavaScript y pasar un array ordenado de métodos que especifiquen las operaciones elegidas. Cada constructor de métodos acepta los mismos parámetros que el método individual correspondiente para establecer, agregar, borrar y borrar.

Puedes llamar a batchUpdate() desde JavaScript o usar un encabezado de respuesta:

  • Uso de JavaScript

    Los métodos de JavaScript que se pueden usar con batchUpdate() incluyen los siguientes:

    • SharedStorageSetMethod(): Escribe un par clave-valor en el almacenamiento compartido.
    • SharedStorageAppendMethod(): Anexa un valor a una clave existente en el almacenamiento compartido o escribe un par clave-valor si la clave aún no existe.
    • SharedStorageDeleteMethod(): Borra un par clave-valor del almacenamiento compartido.
    • SharedStorageClearMethod(): Borra todas las claves del almacenamiento compartido.
    sharedStorage.batchUpdate([
    new SharedStorageSetMethod('keyOne', 'valueOne'),
    new SharedStorageAppendMethod('keyTwo', 'valueTwo'),
    new SharedStorageDeleteMethod('keyThree'),
    new SharedStorageClearMethod()
    ]);
    
  • Cómo usar encabezados de respuesta

    Shared-Storage-Write : set;key=keyOne;value=valueOne, append;key=keyTwo;value=valueTwo,delete;key=keyThree,clear
    

El uso de encabezados de respuesta realiza batchUpdate() para todos los métodos del encabezado.

Cómo leer desde Shared Storage

Solo puedes leer desde el almacenamiento compartido dentro de un worklet.

await sharedStorage.get('mykey');

El origen del contexto de navegación desde el que se cargó el módulo del worklet determina de quién se lee el almacenamiento compartido.

Cómo borrar contenido de Shared Storage

Puedes realizar eliminaciones desde el almacenamiento compartido con JavaScript desde dentro o fuera del worklet, o bien con encabezados de respuesta con delete(). Para borrar todas las claves a la vez, usa clear() desde cualquiera de los dos.

  • Uso de JavaScript

    Para borrar datos del almacenamiento compartido desde fuera del worklet, haz lo siguiente:

    window.sharedStorage.delete('myKey');
    

    Para borrar datos del almacenamiento compartido desde el worklet, haz lo siguiente:

    sharedStorage.delete('myKey');
    

    Para borrar todas las claves a la vez desde fuera del worklet, haz lo siguiente:

    window.sharedStorage.clear();
    

    Para borrar todas las claves a la vez desde el interior del worklet, haz lo siguiente:

    sharedStorage.clear();
    
  • Cómo usar encabezados de respuesta

    Para borrar valores con encabezados de respuesta, también puedes usar Shared-Storage-Write en el encabezado de respuesta para pasar la clave que se borrará.

    delete;key="myKey"
    

    Para borrar todas las claves con encabezados de respuesta, haz lo siguiente:

    clear;
    

Cómo leer grupos de interés de Protected Audience desde el almacenamiento compartido

Puedes leer los grupos de interés de Protected Audience desde un worklet de Shared Storage. El método interestGroups() devuelve un array de objetos StorageInterestGroup, incluidos los atributos AuctionInterestGroup y GenerateBidInterestGroup.

En el siguiente ejemplo, se muestra cómo leer los grupos de interés del contexto de navegación y algunas operaciones posibles que se podrían realizar en los grupos de interés recuperados. Dos posibles operaciones que se utilizan son encontrar la cantidad de grupos de interés y encontrar el grupo de interés con el recuento de ofertas más alto.

async function analyzeInterestGroups() {
  const interestGroups = await interestGroups();
  numIGs = interestGroups.length;
  maxBidCountIG = interestGroups.reduce((max, cur) => { return cur.bidCount > max.bidCount ? cur : max; }, interestGroups[0]);
  console.log("The IG that bid the most has name " + maxBidCountIG.name);
}

El origen del contexto de navegación desde el que se cargó el módulo del worklet determina el origen de los grupos de interés que se leen de forma predeterminada. Para obtener más información sobre el origen predeterminado del worklet y cómo cambiarlo, consulta la sección Ejecución de Shared Storage y Private Aggregation en la guía de la API de Shared Storage.

Opciones

Todos los métodos de modificación de Shared Storage admiten un objeto de opciones opcional como el último argumento.

withLock

La opción withLock es opcional. Si se especifica, esta opción indica al método que adquiera un bloqueo para el recurso definido con la API de Web Locks antes de continuar. Se pasa un nombre de bloqueo cuando se solicita el bloqueo. El nombre representa un recurso para el que el uso se coordina en varias pestañas,trabajadores o código dentro del origen.

La opción withLock se puede usar con los siguientes métodos de modificador de Shared Storage:

  • set
  • append
  • borrar
  • borrar
  • actualización por lotes

Puedes establecer el bloqueo con JavaScript o un encabezado de respuesta:

  • Uso de JavaScript

    sharedStorage.set('myKey', 'myValue', { withLock: 'myResource' });
    
  • Cómo usar encabezados de respuesta

    Shared-Storage-Write : set;key="myKey";value="myValue",options;with_lock="myResource"
    

Los bloqueos de Shared Storage se particionan según el origen de los datos. Los bloqueos son independientes de los que se obtienen con el método LockManager, independientemente de si se encuentran en un contexto window o worker. Sin embargo, comparten el mismo alcance que los bloqueos obtenidos con request() dentro del contexto de SharedStorageWorklet.

Si bien el método request() permite varias opciones de configuración, los bloqueos adquiridos dentro del almacenamiento compartido siempre cumplen con los siguientes parámetros de configuración predeterminados:

  • mode: "exclusive": No se pueden mantener otros bloqueos con el mismo nombre de forma simultánea.
  • steal: false: Los bloqueos existentes con el mismo nombre no se liberan para satisfacer otras solicitudes.
  • ifAvailable: false: Las solicitudes esperan de forma indefinida hasta que el bloqueo esté disponible.
Cuándo usar withLock

Los bloqueos son útiles en situaciones en las que puede haber varios worklets ejecutándose de forma simultánea (p.ej., varios worklets en una página o varios worklets en diferentes pestañas), cada uno de los cuales observa los mismos datos. En ese caso, es una buena idea encapsular el código del worklet pertinente con un bloqueo para garantizar que solo un worklet procese informes a la vez.

Otra situación en la que los bloqueos son útiles es cuando hay varias claves que deben leerse juntas en un worklet y su estado debe sincronizarse. En ese caso, se deben encapsular las llamadas a get con un bloqueo y asegurarse de adquirir el mismo bloqueo cuando se escriban esas claves.

Orden de los seguros

Debido a la naturaleza de los bloqueos web, es posible que los métodos de modificación no se ejecuten en el orden que definiste. Si la primera operación requiere un bloqueo y se retrasa, la segunda operación puede comenzar antes de que finalice la primera.

Por ejemplo:

// This line might pause until the lock is available.
sharedStorage.set('keyOne', 'valueOne', { withLock: 'resource-lock' });

// This line will run right away, even if the first one is still waiting.
sharedStorage.set('keyOne', 'valueTwo');
Ejemplo de modificación de varias claves

La opción withLock con batchUpdate() garantiza la exclusión mutua con otras operaciones simultáneas que adquieren el mismo bloqueo. Solo puedes aplicar la opción withLock para batchUpdate() a todo el lote. Si se aplica withLock a cualquier objeto de método individual dentro del lote, se arroja una excepción.

En este ejemplo, se usa un bloqueo para garantizar que las operaciones de lectura y eliminación dentro del worklet se realicen juntas, lo que evita la interferencia desde fuera del worklet.

En el siguiente ejemplo de modify-multiple-keys.js, se establecen valores nuevos para keyOne y keyTwo con modify-lock y, luego, se ejecuta la operación modify-multiple-keys desde el worklet:

// modify-multiple-keys.js
sharedStorage.batchUpdate([
    new SharedStorageSetMethod('keyOne', calculateValueFor('keyOne')),
    new SharedStorageSetMethod('keyTwo', calculateValueFor('keyTwo'))
], { withLock: 'modify-lock' });

const modifyWorklet = await sharedStorage.createWorklet('modify-multiple-keys-worklet.js');
await modifyWorklet.run('modify-multiple-keys');

Luego, dentro de modify-multiple-keys-worklet.js, puedes solicitar el bloqueo con navigator.locks.request() para leer y modificar las claves según sea necesario.

// modify-multiple-keys-worklet.js
class ModifyMultipleKeysOperation {
  async run(data) {
    await navigator.locks.request('modify-lock', async (lock) => {
      const value1 = await sharedStorage.get('keyOne');
      const value2 = await sharedStorage.get('keyTwo');

      // Do something with `value1` and `value2` here.

      await sharedStorage.delete('keyOne');
      await sharedStorage.delete('keyTwo');
    });
  }
}
register('modify-multiple-keys', ModifyMultipleKeysOperation);

Cambio de contexto

Los datos de Shared Storage se escriben en el origen (por ejemplo, https://example.adtech.com) del contexto de navegación desde el que se originó la llamada.

Cuando cargas el código de terceros con una etiqueta <script>, el código se ejecuta en el contexto de navegación del sitio incorporado. Por lo tanto, cuando el código de terceros llama a sharedStorage.set(), los datos se escriben en el Almacenamiento compartido del incorporador. Cuando cargas el código de terceros dentro de un iframe, el código recibe un nuevo contexto de navegación y su origen es el origen del iframe. Por lo tanto, la llamada sharedStorage.set() que se realiza desde el iframe almacena los datos en el almacenamiento compartido del origen del iframe.

Contexto propio

Si una página propia tiene código JavaScript de terceros incorporado que llama a sharedStorage.set() o sharedStorage.delete(), el par clave-valor se almacena en el contexto propio.

Datos almacenados en una página de origen con JavaScript de terceros incorporado.
En el diagrama, se muestran los datos almacenados en una página de origen con JavaScript de terceros incorporado.

Contexto de terceros

El par clave-valor se puede almacenar en el contexto de la tecnología publicitaria o de terceros creando un iframe y llamando a set() o delete() en el código de JavaScript desde el iframe.

Son los datos almacenados en el contexto de la tecnología publicitaria o de terceros.
El diagrama muestra los datos almacenados en un contexto de tecnología publicitaria o de terceros.

API de Private Aggregation

Para medir los datos agregables almacenados en Shared Storage, puedes usar la API de Private Aggregation.

Para crear un informe, llama a contributeToHistogram() dentro de un worklet con un bucket y un valor. El bucket se representa con un número entero de 128 bits sin signo que se debe pasar a la función como un BigInt. El valor es un número entero positivo.

Para proteger la privacidad, la carga útil del informe, que contiene el bucket y el valor, se encripta durante la transmisión y solo se puede desencriptar y agregar con el Servicio de agregación.

El navegador también limitará las contribuciones que un sitio puede hacer a una consulta de salida. Específicamente, el presupuesto de contribución limita el total de todos los informes de un solo sitio para un navegador determinado en un período determinado en todos los discretizaciones. Si se supera el presupuesto actual, no se generará un informe.

privateAggregation.contributeToHistogram({
  bucket: BigInt(myBucket),
  value: parseInt(myBucketValue)
});

Ejecución de Shared Storage y Private Aggregation

De forma predeterminada, cuando se usa el almacenamiento compartido con createWorklet(), el origen de la partición de datos será el origen del contexto de navegación de invocación, no el origen de la secuencia de comandos del worklet en sí.

Para cambiar el comportamiento predeterminado, establece la propiedad dataOrigin cuando llames a createWorklet.

  • dataOrigin: "context-origin": (Predeterminado) Los datos se almacenan en el almacenamiento compartido del origen del contexto de navegación que invoca.
  • dataOrigin: "script-origin": Los datos se almacenan en el almacenamiento compartido del origen del script del worklet. Se requiere habilitar la opción para usar este modo.
  • dataOrigin: "https://custom-data-origin.example": Los datos se almacenan en el almacenamiento compartido de un origen de datos personalizado. Se requiere la habilitación para activar este modo y el consentimiento del propietario del origen de datos personalizado, como se detalla en Origen de datos personalizado.
sharedStorage.createWorklet(scriptUrl, {dataOrigin: "script-origin"});

Para habilitar la opción, cuando se usa "script-origin" o un origen personalizado, el extremo de la secuencia de comandos debe responder con el encabezado Shared-Storage-Cross-Origin-Worklet-Allowed. Para las solicitudes de origen cruzado, también se debe habilitar CORS.

Shared-Storage-Cross-Origin-Worklet-Allowed : ?1
Access-Control-Allow-Origin: *

También puedes ejecutar secuencias de comandos de origen cruzado con un iframe de terceros, en cuyo caso las acciones de Shared Storage estarán en el contexto de navegación de terceros.

Uso de iframe de origen cruzado

Se necesita un iframe para invocar el worklet del almacenamiento compartido.

En el iframe del anuncio, carga el módulo del worklet llamando a addModule(). Para ejecutar el método registrado en el archivo de worklet sharedStorageWorklet.js, en el mismo iframe de anuncio de JavaScript, llama a sharedStorage.run().

const sharedStorageWorklet = await window.sharedStorage.createWorklet(
  'https://any-origin.example/modules/sharedStorageWorklet.js'
);
await sharedStorageWorklet.run('shared-storage-report', {
  data: { campaignId: '1234' },
});

En la secuencia de comandos del worklet, deberás crear una clase con un método run asíncrono y register para que se ejecute en el iframe del anuncio. Dentro de sharedStorageWorklet.js:

class SharedStorageReportOperation {
  async run(data) {
    // Other code goes here.
    bucket = getBucket(...);
    value = getValue(...);
    privateAggregation.contributeToHistogram({
      bucket,
      value
    });
  }
}
register('shared-storage-report', SharedStorageReportOperation);

Usa solicitudes de origen cruzado

El almacenamiento compartido y la agregación privada permiten la creación de worklets de origen cruzado sin necesidad de iframes de origen cruzado.

La página propia también puede invocar una llamada createWorklet() al extremo de JavaScript de origen cruzado. Cuando crees el worklet, deberás establecer el origen de la partición de datos del worklet como el origen del script.

async function crossOriginCall() {
  const privateAggregationWorklet = await sharedStorage.createWorklet(
    'https://cross-origin.example/js/worklet.js',
    { dataOrigin: 'script-origin' }
  );
  await privateAggregationWorklet.run('pa-worklet');
}
crossOriginCall();

El extremo de JavaScript de origen cruzado deberá responder con los encabezados Shared-Storage-Cross-Origin-Worklet-Allowed y tener en cuenta que CORS está habilitado para la solicitud.

Shared-Storage-Cross-Origin-Worklet-Allowed : ?1

Los worklets creados con createWorklet() tendrán selectURL y run(). addModule() no está disponible para esto.

class CrossOriginWorklet {
  async run(data){
    // Other code goes here.
    bucket = getBucket(...);
    value = getValue(...);
    privateAggregation.contributeToHistogram({
      bucket,
      value
    });
  }
}

Origen de datos personalizado

Cuando dataOrigin se establece en un origen válido, el propietario de dataOrigin debe dar su consentimiento para el procesamiento del almacenamiento compartido para ese dataOrigin alojando un archivo JSON que enumere el origen del script del worklet en la ruta de acceso /.well-known/shared-storage/trusted-origins. El archivo debe ser un array de objetos con las claves scriptOrigin y contextOrigin. Los valores de estas claves pueden ser una cadena o un array de cadenas.

Crea el archivo trusted-origins con la siguiente información:

  • Contexto de la persona que llama
  • Origen y URL del script del worklet
  • Origen y propietario de los datos

En la siguiente tabla, se muestra cómo podrías construir el archivo trusted-origins según esta información:

Contexto de la persona que llama URL del script del worklet Origen de los datos Propietario de los datos Archivo JSON de orígenes de confianza del propietario del origen de datos
https://publisher.example https://publisher.example/script.js context-origin https://publisher.example No se necesita JSON
https://publisher.example https://ad.example/script.js script-origin https://ad.example No se necesita JSON
https://publisher.example https://cdn-ad.example/script.js https://ad.example https://ad.example
[{
  "scriptOrigin": "https://cdn-ad.example",
  "contextOrigin": "https://publisher.example"
}]
      
Cualquier persona que llama https://cdn-ad.example/script.js https://ad.example https://ad.example
[{
  "scriptOrigin": "https://cdn-ad.example",
  "contextOrigin": "*"
}]
      
https://publisher-a.example, O https://publisher-b.example https://cdn-ad.example/script.js https://ad.example https://ad.example
[{
  "scriptOrigin": "https://cdn-ad.example",
  "contextOrigin": [
      "https://publisher-a.example",
      "https://publisher-b.example"
  ]
}]
      
https://publisher.example https://cdn-a-ad.example/script.js O https://cdn-b-ad.example/script.js https://ad.example https://ad.example
[{
  "scriptOrigin": [
    "https://cdn-a-ad.example",
    "https://cdn-b-ad.example"
  ],
  "contextOrigin": "https://publisher.example"
}]
      

Por ejemplo, el siguiente JSON se puede alojar en https://custom-data-origin.example/.well-known/shared-storage/trusted-origins y combinar todos los procesadores permitidos de datos de Shared Storage para el origen https://custom-data-origin.example.

[
  {
    "scriptOrigin": "https://script-origin.a.example",
    "contextOrigin": "https://context-origin.a.example"
  },
  {
    "scriptOrigin": "https://script-origin.b.example",
    "contextOrigin": [
      "https://context-origin.a.example",
      "https://context-origin.b.example"
    ]
}]

Próximos pasos

En las siguientes páginas, se explican aspectos importantes de las APIs de Shared Storage y Private Aggregation.

Una vez que conozcas las APIs, puedes comenzar a recopilar los informes, que se envían como una solicitud POST a los siguientes extremos como JSON en el cuerpo de la solicitud.

  • Informes de depuración:context-origin/.well-known/private-aggregation/debug/report-shared-storage
  • Informes:context-origin/.well-known/private-aggregation/report-shared-storage

Una vez que se recopilan los informes, puedes realizar pruebas con la herramienta de pruebas locales o configurar el entorno de ejecución de confianza para el servicio de agregación y obtener los informes agregados.

Comparte tus comentarios

Puedes compartir tus comentarios sobre las APIs y la documentación en GitHub.