Guia de início rápido para implementação do armazenamento compartilhado e da agregação particular

Este documento é um guia de início rápido para usar o Armazenamento compartilhado e a API Private Aggregation. Você precisa entender as duas APIs porque o Shared Storage armazena os valores e a Private Aggregation cria os relatórios agregáveis.

Público-alvo:tecnologias de publicidade e provedores de medição.

API Shared Storage

Para evitar o rastreamento entre sites, os navegadores começaram a particionar todas as formas de armazenamento, incluindo armazenamento local, cookies e assim por diante. Mas há casos de uso em que o armazenamento não particionado é necessário. A API Shared Storage oferece acesso de gravação ilimitado em diferentes sites de nível superior com acesso de leitura que preserva a privacidade.

O armazenamento compartilhado é restrito à origem do contexto (o autor da chamada de sharedStorage).

O armazenamento compartilhado tem um limite de capacidade por origem, e cada entrada é limitada a um número máximo de caracteres. Se o limite for atingido, nenhuma outra entrada será armazenada. Os limites de armazenamento de dados estão descritos na explicação sobre o armazenamento compartilhado.

Invocar o armazenamento compartilhado

As adtechs podem gravar no armazenamento compartilhado usando JavaScript ou um cabeçalho de resposta. A leitura do armazenamento compartilhado só ocorre em um ambiente JavaScript isolado chamado worklet.

  • Usando JavaScript: as adtechs podem realizar funções específicas do armazenamento compartilhado, como definir, anexar e excluir valores fora de um worklet JavaScript. No entanto, funções como leitura do armazenamento compartilhado e execução de agregação privada precisam ser concluídas por um worklet JavaScript. Os métodos que podem ser usados fora de um worklet JavaScript estão em Superfície da API proposta: fora do worklet.

    Os métodos usados no worklet durante uma operação podem ser encontrados em Superfície da API proposta: no worklet.

  • Como usar cabeçalhos de resposta

    Assim como no JavaScript, apenas funções específicas, como definir, anexar e excluir valores no armazenamento compartilhado, podem ser feitas usando cabeçalhos de resposta. Para trabalhar com o armazenamento compartilhado em um cabeçalho de resposta, Shared-Storage-Writable: ?1 precisa ser incluído no cabeçalho da solicitação.

    Para iniciar uma solicitação do cliente, execute o seguinte código, dependendo do método escolhido:

    • Como usar o fetch()

      fetch("https://a.example/path/for/updates", {sharedStorageWritable: true});
      
    • Como usar uma tag iframe ou img

      <iframe src="https://a.example/path/for/updates" sharedstoragewritable></iframe>
      
    • Usar um atributo IDL com uma tag iframe ou img

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

Para mais informações, consulte Armazenamento compartilhado: cabeçalhos de resposta.

Como gravar no armazenamento compartilhado

Para gravar no armazenamento compartilhado, chame sharedStorage.set() de dentro ou de fora de um worklet JavaScript. Se for chamada de fora do worklet, os dados serão gravados na origem do contexto de navegação de onde a chamada foi feita. Se for chamado de dentro do worklet, os dados serão gravados na origem do contexto de navegação que carregou o worklet. As chaves definidas têm uma data de validade de 30 dias a partir da última atualização.

O campo ignoreIfPresent é opcional. Se estiver presente e definido como true, a chave não será atualizada se já existir. A validade da chave é renovada para 30 dias a partir da chamada set(), mesmo que a chave não seja atualizada.

Se o armazenamento compartilhado for acessado várias vezes no mesmo carregamento de página com a mesma chave, o valor da chave será substituído. É uma boa ideia usar sharedStorage.append() se a chave precisar manter o valor anterior.

  • Usando JavaScript

    Fora do 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'}
    

    Da mesma forma, dentro do worklet:

    sharedStorage.set('myKey', 'myValue1', { ignoreIfPresent: true });
    
  • Como usar cabeçalhos de resposta

    Também é possível gravar no armazenamento compartilhado usando cabeçalhos de resposta. Para fazer isso, use Shared-Storage-Write no cabeçalho da resposta com os seguintes comandos:

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

    Vários itens podem ser separados por vírgulas e combinar set, append, delete e clear.

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

Como anexar um valor

É possível anexar um valor a uma chave usando o método "append". Se a chave não existir, chamar append() vai criar a chave e definir o valor. Isso pode ser feito usando JavaScript ou um cabeçalho de resposta.

  • Usando JavaScript

    Para atualizar valores de chaves atuais, use sharedStorage.append() de dentro ou de fora do 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 anexar dentro do worklet:

    sharedStorage.append('myKey', 'myValue1');
    
  • Como usar cabeçalhos de resposta

    Assim como ao definir um valor no armazenamento compartilhado, você pode usar o Shared-Storage-Write no cabeçalho da resposta para transmitir o par de chave-valor.

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

Atualização em lote de valores

É possível chamar sharedStorage.batchUpdate() de dentro ou de fora de um worklet JavaScript e transmitir uma matriz ordenada de métodos que especificam as operações escolhidas. Cada construtor de método aceita os mesmos parâmetros que o método individual correspondente para definir, adicionar, excluir e limpar.

É possível chamar batchUpdate() do JavaScript ou usar um cabeçalho de resposta:

  • Usando JavaScript

    Os métodos JavaScript que podem ser usados com batchUpdate() incluem:

    • SharedStorageSetMethod(): grava um par de chave-valor no armazenamento compartilhado.
    • SharedStorageAppendMethod(): adiciona um valor a uma chave existente no armazenamento compartilhado ou grava um par de chave-valor se a chave ainda não existir.
    • SharedStorageDeleteMethod(): exclui um par de chave-valor do armazenamento compartilhado.
    • SharedStorageClearMethod(): limpa todas as chaves no armazenamento compartilhado.
    sharedStorage.batchUpdate([
    new SharedStorageSetMethod('keyOne', 'valueOne'),
    new SharedStorageAppendMethod('keyTwo', 'valueTwo'),
    new SharedStorageDeleteMethod('keyThree'),
    new SharedStorageClearMethod()
    ]);
    
  • Como usar cabeçalhos de resposta

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

Usar cabeçalhos de resposta realiza batchUpdate() para todos os métodos no cabeçalho.

Como fazer a leitura do armazenamento compartilhado

Só é possível ler do armazenamento compartilhado em um worklet.

await sharedStorage.get('mykey');

A origem do contexto de navegação de onde o módulo do worklet foi carregado determina de quem o armazenamento compartilhado é lido.

Excluir do armazenamento compartilhado

É possível fazer exclusões do armazenamento compartilhado usando JavaScript de dentro ou fora do worklet ou usando cabeçalhos de resposta com delete(). Para excluir todas as chaves de uma vez, use clear() em qualquer uma das opções.

  • Usando JavaScript

    Para excluir do armazenamento compartilhado fora do worklet:

    window.sharedStorage.delete('myKey');
    

    Para excluir do armazenamento compartilhado de dentro do worklet:

    sharedStorage.delete('myKey');
    

    Para excluir todas as chaves de uma só vez de fora do worklet:

    window.sharedStorage.clear();
    

    Para excluir todas as chaves de uma vez de dentro do worklet:

    sharedStorage.clear();
    
  • Como usar cabeçalhos de resposta

    Para excluir valores usando cabeçalhos de resposta, também é possível usar Shared-Storage-Write no cabeçalho de resposta para transmitir a chave a ser excluída.

    delete;key="myKey"
    

    Para excluir todas as chaves usando cabeçalhos de resposta:

    clear;
    

Como ler grupos de interesse da API Protected Audience do armazenamento compartilhado

É possível ler os grupos de interesse da API Protected Audience em um worklet do armazenamento compartilhado. O método interestGroups() retorna uma matriz de objetos StorageInterestGroup, incluindo os atributos AuctionInterestGroup e GenerateBidInterestGroup.

O exemplo a seguir mostra como ler os grupos de interesse do contexto de navegação e algumas operações possíveis que podem ser realizadas nos grupos de interesse recuperados. Duas operações possíveis usadas são encontrar o número de grupos de interesse e encontrar o grupo com a maior contagem de lances.

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);
}

A origem do contexto de navegação de onde o módulo worklet foi carregado determina a origem dos grupos de interesse que estão sendo lidos por padrão. Para saber mais sobre a origem padrão do worklet e como mudá-la, consulte a seção Execução do armazenamento compartilhado e da agregação privada no tutorial da API Shared Storage.

Opções

Todos os métodos modificadores do armazenamento compartilhado aceitam um objeto de opções opcional como o último argumento.

withLock

A opção withLock é opcional. Se especificada, essa opção instrui o método a adquirir um bloqueio para o recurso definido usando a API Web Locks antes de continuar. Um nome de bloqueio é transmitido ao solicitar o bloqueio. O nome representa um recurso cujo uso é coordenado em várias guias,trabalhadores ou códigos na origem.

A opção withLock pode ser usada com os seguintes métodos modificadores do armazenamento compartilhado:

  • set
  • append
  • excluir
  • limpar
  • atualização em lote

É possível definir o bloqueio usando JavaScript ou um cabeçalho de resposta:

  • Usando JavaScript

    sharedStorage.set('myKey', 'myValue', { withLock: 'myResource' });
    
  • Como usar cabeçalhos de resposta

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

Os bloqueios do armazenamento compartilhado são particionados pela origem dos dados. Os bloqueios são independentes de qualquer bloqueio obtido usando o método request() do LockManager, seja em um contexto window ou worker. No entanto, eles compartilham o mesmo escopo que os bloqueios obtidos usando request() no contexto SharedStorageWorklet.

Embora o método request() permita várias opções de configuração, os bloqueios adquiridos no armazenamento compartilhado sempre obedecem às seguintes configurações padrão:

  • mode: "exclusive": nenhum outro bloqueio com o mesmo nome pode ser mantido simultaneamente.
  • steal: false: os bloqueios atuais com o mesmo nome não são liberados para acomodar outras solicitações.
  • ifAvailable: false: as solicitações aguardam indefinidamente até que o bloqueio fique disponível.
Quando usar withLock

Os bloqueios são úteis em cenários em que pode haver vários worklets em execução simultânea (por exemplo, vários worklets em uma página ou em guias diferentes), cada um analisando os mesmos dados. Nesse cenário, é uma boa ideia envolver o código do worklet relevante com um bloqueio para garantir que apenas um worklet esteja processando relatórios por vez.

Outra situação em que os bloqueios são úteis é quando há várias chaves que precisam ser lidas juntas em um worklet e o estado delas precisa ser sincronizado. Nesse caso, é necessário envolver as chamadas get com um bloqueio e adquirir o mesmo bloqueio ao gravar nessas chaves.

Ordem dos bloqueios

Devido à natureza dos bloqueios da Web, os métodos modificadores podem não ser executados na ordem definida. Se a primeira operação exigir um bloqueio e for atrasada, a segunda poderá começar antes da conclusão da primeira.

Por exemplo:

// 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');
Exemplo de modificação de várias chaves

A opção withLock com batchUpdate() garante a exclusão mútua com outras operações simultâneas que adquirem o mesmo bloqueio. Você só pode aplicar a opção withLock para batchUpdate() em todo o lote. Aplicar withLock a qualquer objeto de método individual no lote gera uma exceção.

Este exemplo usa um bloqueio para garantir que as operações de leitura e exclusão no worklet aconteçam juntas, evitando interferências de fora do worklet.

O exemplo de modify-multiple-keys.js a seguir define novos valores para keyOne e keyTwo com modify-lock e executa a operação modify-multiple-keys do 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');

Em seguida, dentro do modify-multiple-keys-worklet.js, você pode solicitar o bloqueio usando navigator.locks.request() para ler e modificar as chaves conforme necessário.

// 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);

Troca de contexto

Os dados do armazenamento compartilhado são gravados na origem (por exemplo, https://example.adtech.com) do contexto de navegação de onde a chamada foi originada.

Quando você carrega o código de terceiros usando uma tag <script>, ele é executado no contexto de navegação do incorporador. Portanto, quando o código de terceiros chama sharedStorage.set(), os dados são gravados no armazenamento compartilhado do incorporador. Quando você carrega o código de terceiros em um iframe, ele recebe um novo contexto de navegação, e a origem dele é a do iframe. Portanto, a chamada sharedStorage.set() feita do iframe armazena os dados no armazenamento compartilhado da origem do iframe.

Contexto próprio

Se uma página própria tiver um código JavaScript de terceiros incorporado que chame sharedStorage.set() ou sharedStorage.delete(), o par de chave-valor será armazenado no contexto próprio.

Dados armazenados em uma página própria com JavaScript de terceiros incorporado.
O diagrama mostra dados armazenados em uma página própria com JavaScript de terceiros incorporado.

Contexto de terceiros

O par de chave-valor pode ser armazenado no contexto da adtech ou de terceiros criando um iframe e chamando set() ou delete() no código JavaScript de dentro do iframe.

Dados armazenados no contexto de adtech ou de terceiros.
O diagrama mostra dados armazenados em um contexto de tecnologia de publicidade ou de terceiros.

API Private Aggregation

Para medir dados agregáveis armazenados no Shared Storage, use a API Private Aggregation.

Para criar um relatório, chame contributeToHistogram() em um worklet com um bucket e um valor. O bucket é representado por um número inteiro sem sinal de 128 bits, que precisa ser transmitido para a função como um BigInt. O valor é um número inteiro positivo.

Para proteger a privacidade, o payload do relatório, que contém o agrupamento e o valor, é criptografado em trânsito e só pode ser descriptografado e agregado usando o serviço de agregação.

O navegador também limita as contribuições que um site pode fazer para uma consulta de saída. Especificamente, o orçamento de contribuição limita o total de todos os relatórios de um único site para um determinado navegador em um período específico em todos os agrupamentos. Se o orçamento atual for excedido, um relatório não será gerado.

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

Execução do armazenamento compartilhado e da agregação privada

Por padrão, ao usar o armazenamento compartilhado com createWorklet(), a origem da partição de dados será a origem do contexto de navegação de invocação, não a origem do próprio script do worklet.

Para mudar o comportamento padrão, defina a propriedade dataOrigin ao chamar createWorklet.

  • dataOrigin: "context-origin": (padrão) os dados são armazenados no armazenamento compartilhado da origem do contexto de navegação de invocação.
  • dataOrigin: "script-origin": os dados são armazenados no armazenamento compartilhado da origem do script do worklet. É necessário ativar esse modo.
  • dataOrigin: "https://custom-data-origin.example": os dados são armazenados no armazenamento compartilhado de uma origem de dados personalizada. É necessário ativar esse modo e ter o consentimento do proprietário da origem de dados personalizada, conforme detalhado em Origem de dados personalizada.
sharedStorage.createWorklet(scriptUrl, {dataOrigin: "script-origin"});

Para ativar, ao usar "script-origin" ou uma origem personalizada, o endpoint do script precisa responder com o cabeçalho Shared-Storage-Cross-Origin-Worklet-Allowed. Para solicitações entre origens, o CORS também precisa estar ativado.

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

Também é possível executar scripts de origem cruzada usando um iframe de terceiros. Nesse caso, as ações do armazenamento compartilhado estarão no contexto de navegação de terceiros.

Como usar um iframe entre origens

Um iframe é necessário para invocar o worklet de armazenamento compartilhado.

No iframe do anúncio, carregue o módulo worklet chamando addModule(). Para executar o método registrado no arquivo worklet sharedStorageWorklet.js, chame sharedStorage.run() no mesmo JavaScript do iframe de anúncio.

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

No script do worklet, você precisará criar uma classe com um método run assíncrono e register para ser executado no iframe do anúncio. 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);

Como usar solicitações entre origens

O Shared Storage e a Private Aggregation permitem a criação de worklets de origem cruzada sem a necessidade de iframes de origem cruzada.

A página própria também pode invocar uma chamada createWorklet() para o endpoint JavaScript de origem cruzada. Você precisa definir a origem da partição de dados do worklet como a origem do script ao criar o worklet.

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

O endpoint JavaScript de origem cruzada precisa responder com os cabeçalhos Shared-Storage-Cross-Origin-Worklet-Allowed e observar que o CORS está ativado para a solicitação.

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

Os worklets criados com o createWorklet() terão selectURL e run(). O recurso addModule() não está disponível para isso.

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

Origem de dados personalizada

Quando o dataOrigin é definido como uma origem válida, o proprietário do dataOrigin precisa consentir com o processamento do armazenamento compartilhado para esse dataOrigin hospedando um arquivo JSON que lista a origem do script do worklet no caminho /.well-known/shared-storage/trusted-origins. O arquivo precisa ser uma matriz de objetos com as chaves scriptOrigin e contextOrigin. Os valores dessas chaves podem ser uma string ou uma matriz de strings.

Crie o arquivo trusted-origins usando as seguintes informações:

  • Contexto do autor da chamada
  • Origem e URL do script do worklet
  • Origem e proprietário dos dados

A tabela a seguir mostra como você pode construir o arquivo trusted-origins com base nessas informações:

Contexto do autor da chamada URL do script do worklet Origem dos dados Proprietário dos dados Arquivo JSON de origens confiáveis do proprietário da origem de dados
https://publisher.example https://publisher.example/script.js context-origin https://publisher.example JSON não é necessário
https://publisher.example https://ad.example/script.js script-origin https://ad.example JSON não é necessário
https://publisher.example https://cdn-ad.example/script.js https://ad.example https://ad.example
[{
  "scriptOrigin": "https://cdn-ad.example",
  "contextOrigin": "https://publisher.example"
}]
      
Qualquer pessoa que ligar https://cdn-ad.example/script.js https://ad.example https://ad.example
[{
  "scriptOrigin": "https://cdn-ad.example",
  "contextOrigin": "*"
}]
      
https://publisher-a.example, OR 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, OR 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 exemplo, o JSON a seguir pode ser hospedado em https://custom-data-origin.example/.well-known/shared-storage/trusted-origins e combinar todos os processadores permitidos de dados do armazenamento compartilhado para a origem 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óximas etapas

As páginas a seguir explicam aspectos importantes das APIs Shared Storage e Private Aggregation.

Depois de conhecer as APIs, comece a coletar os relatórios, que são enviados como uma solicitação POST para os seguintes endpoints como JSON no corpo da solicitação.

  • Relatórios de depuração: context-origin/.well-known/private-aggregation/debug/report-shared-storage
  • Relatórios - context-origin/.well-known/private-aggregation/report-shared-storage

Depois que os relatórios forem coletados, você poderá testar usando a ferramenta de teste local ou configurar o ambiente de execução confiável para o serviço de agregação e receber os relatórios agregados.

Envie feedback

Compartilhe seu feedback sobre as APIs e a documentação no GitHub.