PII Storage, Encryption, and In-Browser Decryption

Prev Next

Summary

Darwinium can write event data into an S3 bucket in your own AWS account, an option known as "bring your own bucket." Using it keeps your data within an AWS account and region you choose, supporting data residency, privacy, and compliance requirements. It means Darwinium's own infrastructure never needs to store or even process your customer PII in either clear or encrypted form.

Event data is written to your bucket twice:

  1. where PII is encrypted
  2. where PII is irreversibly anonymised, using one-way hashing

The encrypted data can only be decrypted using a private key that's unique to your node, held by Darwinium in an isolated HashiCorp Vault instance. There's no way to infer the original plaintext PII from the anonymised, one-way hashed form.

Decryption happens inside the Darwinium Portal, only when a user who's been granted the "view PII" permission opens a view that requires seeing PII attributes. Darwinium staff can never be granted this permission. With in-browser decryption (IBD) enabled, that decryption is performed locally in the user's browser rather than on a Darwinium server, so Darwinium never stores or processes PII in the clear, not even momentarily.

Where your data lives

Bring-your-own-bucket is an optional deployment model. When enabled, event data is written to an S3 bucket you own and control, in a region of your choice.

Darwinium accesses the bucket via two cross-account IAM roles, assumed using an external ID rather than static credentials:

  • Write-only (s3:PutObject) - used by edge or API outpost components to deliver event data
  • Read-only (s3:GetObject, s3:ListBucket) - used by Darwinium's portal and data services when a query is made

Data is organized into three top-level folders, sharded by the first five characters of the event ID:

  • encrypted/ - encrypted version of each event
  • hashed/ - anonymized version of each event
  • metadata/ - schema/metadata

Encrypted

Each node has its own public/private keypair, generated by Darwinium and stored in Vault. Only the public key ever leaves Vault, and bundled into the edge or API outpost component for that node. Those are the components that encrypt and anonymise the data within your domain.

Data can be encrypted and anonymised within your domain regardless of event decisioning method:

  • Edge workers perform processing for CDN events (Cloudflare workers, AWS lambdas, Linode).
  • API outpost (API Gateway + Lambda, deployed in your AWS account) handle that same processing for EventAPI server-to-server calls.

Those components are where rules requiring the clear PII (for example, regex matching) are ran too, before the encryption and anonymisation.

The data is encrypted with the node's public key using HPKE (Hybrid Public Key Encryption) and written direct from that processing component to the encrypted/ location of your bucket. Darwinium's implementation uses the P256 KEM, SHA-256 for key derivation, and AES-256-GCM as the AEAD cipher.

Anonymized

The Edge or API outpost also performs a hash of PII attributes before sending to Darwinium's backend for decisioning. Those are included in the anonymized event data record written by Darwinium backend to hashed/.

The hashing uses SHA-256, then multiplying that hash by a Curve25519 point and encoding the result in canonical Ristretto form, using a key held privately in Darwinium's infrastructure.

This behaves like a one-way encryption of the hash rather than a plain hash: it lets Darwinium search for an exact PII value without ever handling plaintext, and resists rainbow-table attacks, since reversing it needs the private key as well as the hash. The result means hashed/ values can't be turned back into the original PII.

This anonymized form is what Darwinium staff see, and it's also what any customer user without the "view PII" permission sees. Neither can retrieve the encrypted/ copy or turn it into plaintext; only a user holding that specific permission can.

Keys

Private keys are stored in HashiCorp Vault, sealed with an AWS KMS key. Darwinium is responsible for automatically generating those and can rotate upon request, but no individual at Darwinium can access this vault.

Unlocking one to use for decryption requires:

  • A signed JWT with a claim explicitly granting "read PII" for that specific node
  • A bound mTLS certificate (a Darwinium service converts the mTLS digest into a Vault authentication credential, so a valid JWT alone isn't enough)

Which users can be issued a PII-read claim is governed by Darwinium Portal role-based permissions, and all access is logged in the portal's audit trail.

Decryption

Decryption only happens in one place: the Darwinium Portal, when a customer user who holds the "view PII" permission for that node opens an investigation and requests to see a PII field.

That request must carry a signed JWT with an explicit "read PII" claim for the node, validated before anything is decrypted. Darwinium staff cannot obtain that claim.

Without in-browser decryption (legacy), decryption happens server-side: Vault unlocks the private key, a Darwinium data access service decrypts the requested data in memory, and the resulting plaintext is sent to the user's browser over TLS 1.3.

In Browser Decryption (IBD)

With IBD enabled, the decryption step itself moves client side and only the requesting user's browser ever produces plaintext PII.

  1. Browser fetches non-sensitive event data (including event IDs) as normal.
  2. Browser sends event IDs plus its signed JWT (read-PII claim) to S3-reading component, which validates the JWT and returns the matching encrypted records, still encrypted.
  3. Each record includes an HPKE-encapsulated key. The browser sends these, with the same JWT, to the decryption service.
  4. The decryption service validates the JWT, then unwraps the encapsulated keys using the node's private key and returns the resulting shared secrets, not plaintext.
  5. The browser uses those shared secrets to decrypt the event content locally.

Image

At no point does a Darwinium server hold decrypted PII. The private key stays in Vault throughout, and the only thing the decryption service returns is a shared secret that's useless without the encrypted data the browser already fetched separately.

IBD applies across the portal (investigations, event detail views, identifier lookups, incident queues, entity relationship graphs, journeys, analytics dashboards), and exports follow the same flow. Rollout is controlled per node via a feature flag.