Skip to content

Blind Indexes

Blind indexes solve a core problem: if all log data is encrypted, you cannot filter or search it. Volidator uses HMAC-SHA256 to produce a deterministic token for each actor and action value. These tokens are searchable without revealing the original value.

// Pseudocode — this runs inside the SDK before encryption
const actorBlindIndex = HMAC_SHA256(key: encryptionKey, data: actorValue);

The same input always produces the same output with the same key. This makes the index deterministic: every log event for actor: "user_123" produces the same actorBlindIndex, which the ingestion worker can use to look up all logs for that actor.

When you log an event, the SDK computes blind indexes for the searchable fields and sends them along with the encrypted payload:

{
"actorBlindIndex": "a3f9c8e1...",
"actionBlindIndex": "77b2e4d0...",
"targetBlindIndex": "c3b8a9f2...", // Optional
"tenantBlindIndex": "99f2e1a3...", // Optional
"encryptedPayload": "base64..."
}

Volidator indexes these hashed values to efficiently locate and return matching encrypted rows without ever having the ability to inspect the underlying plaintext.

What an attacker can learn from a blind index

Section titled “What an attacker can learn from a blind index”

A blind index is a hash. Without the encryption key, an attacker cannot reverse it to the original value. However, blind indexes are deterministic, so an attacker with the key could confirm whether a specific value appears in the index.

The security assumption is: the encryption key remains secret. If it is compromised, the blind indexes and ciphertext are both compromised.

Blind indexes only support exact-match lookups. You cannot do prefix search, range queries, or full-text search on encrypted fields. If you need those, store them in a separate unencrypted column and pass them in metadata with the appropriate redactKeys configuration.