Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 30 additions & 10 deletions packages/showcase/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,42 @@ Its intended purposes are:

## Searchable encryption

Searchable encryption allows queries over encrypted data so long as the data has enabled the appropriate encrypted search configuration for a column.

The following searchable encryption strategies are supported:
Searchable encryption allows queries over encrypted data so long as the column's
type declares the required capability.

**EQL v3 model β€” self-configuring domain types.** Unlike EQL v2 (which used an
opaque `eql_v2_encrypted` type plus an `eql_v2.add_search_config` call per
column), EQL v3 gives each `(token type Γ— capability)` combination its own
Postgres domain type over `jsonb`. The column type alone declares both the
encryption and what can be searched β€” there is no separate config call. The
capability groups below map to v3 domain-name suffixes:

| Capability (v2 name) | v3 domain suffix | Operations |
|-----------------------|-------------------------------------------|------------|
| match | `_match` (e.g. `eql_v3_text_match`) | `LIKE`/`ILIKE`, `@@` |
| ore / comparison | `_ord` / `_ord_ore` (e.g. `eql_v3_integer_ord`) | `<` `<=` `=` `<>` `>` `>=`, `MIN`/`MAX` |
| unique / equality | `_eq` (e.g. `eql_v3_text_eq`) | `=` `<>` |
| ste_vec (encrypted JSON) | `json_search` (`eql_v3_json_search`) | `->` `->>` `@>` `<@`, `jsonb_path_*` |

The showcase uses **`eql_v3_json_search`** (the ste_vec/encrypted-JSON domain)
for its encrypted columns.

### match

Provides support for text search operations over encrypted data. Enables use of the SQL `LIKE`, `NOT LIKE`, `ILIKE`, `NOT ILIKE` keywords on encrypted text.
Text search over encrypted text (`_match` domains). Enables `LIKE`, `NOT LIKE`,
`ILIKE`, `NOT ILIKE` and the `@@` fuzzy-match operator.

Operators:

- `~~` (same as `LIKE`)
- `!~~` (same as `NOT LIKE`)
- `~~*` (same as `ILIKE`)
- `!~~*` (same as `NOT LIKE`)
- `@@` (fuzzy match)

### ore

Provides support for compare & equality operators on encrypted data.
Compare & equality operators on encrypted scalars (`_ord` / `_ord_ore` domains).

- `<` (less than)
- `<=` (less than or equal)
Expand All @@ -42,13 +60,15 @@ Provides support for compare & equality operators on encrypted data.
- `>` (greater than)
- `>=` (greater than or equal)

This implies that the built-in SQL functions `MIN` and `MAX` work on encrypted columns when they have ORE enabled.
This implies that the built-in SQL functions `MIN` and `MAX` work on encrypted
columns typed as an ordering domain.

### unique

Provides support for equality testing of encrypted columns (`=`).
Equality testing of encrypted columns (`=`) via an `_eq` domain.

This strategy is poorly named because it does NOT imply that there is a unique constraint.
This capability is historically called "unique" but does NOT imply a unique
constraint.

### ste_vec

Expand All @@ -70,7 +90,7 @@ Functions:
- `jsonb_array_elements`
- `jsonb_array_elements_text`

When an `ste_vec` is created for a JSON document it allows the following operations to be performed:
An `eql_v3_json_search` (ste_vec) column allows the following operations to be performed:

- Containment operations (`@>` & `<@`)
- Fields or array elements extracted using `->` or `json_query_path`
Expand All @@ -95,7 +115,7 @@ Encrypted columns can only be passed as arguments to a SQL function if the value

For example, the SQL `AVG` function cannot be used on encrypted numeric values. But the SQL `MIN` and `MAX` functions can be used on an encrypted value that has an ORE index.

**IMPORTANT: CAST operations cannot work on encrypted data** because casting would require decryption of the encrypted data within the database, which is impossible. When a column has an `ste_vec` configuration, comparison and ordering operations work directly on the encrypted values without requiring CAST operations.
**IMPORTANT: CAST operations cannot work on encrypted data** because casting would require decryption of the encrypted data within the database, which is impossible. When a column is typed as an `eql_v3_json_search` (ste_vec) domain, comparison and ordering operations work directly on the encrypted values without requiring CAST operations.

When generating tests, it is important that Claude understands the fundamental limitations of EQL so that it does not generate test cases or example code that can never work.

Expand Down
2 changes: 1 addition & 1 deletion packages/showcase/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "showcase"
version.workspace = true
edition.workspace = true
description = "Healthcare data model demonstrating EQL v2 searchable encryption with realistic encrypted application patterns"
description = "Healthcare data model demonstrating EQL v3 searchable encryption with realistic encrypted application patterns"

[dependencies]
serde = { version = "1.0", features = ["derive"] }
Expand Down
24 changes: 9 additions & 15 deletions packages/showcase/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# EQL v2 JSONB Operations Showcase
# EQL v3 JSONB Operations Showcase

A comprehensive demonstration of EQL v2's JSONB support for searchable encryption with healthcare data.
A comprehensive demonstration of EQL v3's JSONB support for searchable encryption with healthcare data.

## Table of Contents

Expand Down Expand Up @@ -31,7 +31,7 @@ A comprehensive demonstration of EQL v2's JSONB support for searchable encryptio

## Overview

This showcase demonstrates EQL v2's comprehensive support for JSONB operations on encrypted data. All examples use a realistic healthcare database with encrypted patient information, showcasing how applications can query complex nested data while maintaining searchable encryption.
This showcase demonstrates EQL v3's comprehensive support for JSONB operations on encrypted data. All examples use a realistic healthcare database with encrypted patient information, showcasing how applications can query complex nested data while maintaining searchable encryption.

**Key Features:**
- βœ… All JSONB operators work with encrypted data
Expand All @@ -45,21 +45,15 @@ This showcase demonstrates EQL v2's comprehensive support for JSONB operations o
The healthcare database includes:

```sql
-- Patients table with encrypted PII
-- Patients table with encrypted PII.
-- EQL v3 uses self-configuring domain types: `eql_v3_json_search` is the
-- searchable encrypted-JSON (SteVec) domain, so the column type alone declares
-- the encryption and its searchability β€” no `add_search_config` call is needed.
CREATE TABLE patients (
id uuid,
pii eql_v2_encrypted, -- Complex nested JSONB with medical data
pii eql_v3_json_search, -- Complex nested JSONB with medical data
PRIMARY KEY(id)
);

-- EQL search configuration for patient data
SELECT eql_v2.add_search_config(
'patients',
'pii',
'ste_vec',
'jsonb',
'{"prefix": "patients/pii"}'
);
```

## Test Data Structure
Expand Down Expand Up @@ -505,4 +499,4 @@ Examples:

⚠️ **Chained Operators**: The `->` operator cannot be chained on `ste_vec` encrypted columns. Use JSONPath functions like `jsonb_path_query_first()` for deep nested access instead.

This showcase proves that EQL v2 provides comprehensive JSONB support for encrypted data, enabling sophisticated healthcare applications while maintaining strong privacy protections.
This showcase proves that EQL v3 provides comprehensive JSONB support for encrypted data, enabling sophisticated healthcare applications while maintaining strong privacy protections.
20 changes: 3 additions & 17 deletions packages/showcase/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -492,25 +492,11 @@ pub async fn insert_test_data() {
}

pub async fn clear() {
// HAZARD!
//
// Deleting rows from the eql_v2_configuration table is not officially supported due to the risk of data loss.
//
let sql = r#"
DELETE
FROM public.eql_v2_configuration
WHERE
(data -> 'tables') ?| array[
'patients',
'patient_medications',
'patient_procedures'
];
"#;

// EQL v3 encrypted columns are self-configuring domain types, so there is no
// `eql_v2_configuration` table to clean up (as there was in EQL v2) β€” clearing
// the demo just truncates the tables.
let client = connect_with_tls(PROXY).await;

client.simple_query(sql).await.unwrap();

let tables = &[
"patient_medications",
"patient_procedures",
Expand Down
12 changes: 6 additions & 6 deletions packages/showcase/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* β•‘ β”‚ medications β”‚ β”‚ procedures β”‚ β”‚ patients β”‚ β•‘
* β•‘ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β•‘
* β•‘ β”‚ id (uuid) PK β”‚ β”‚ id (uuid) PK β”‚ β”‚ id (uuid) PK β”‚ β•‘
* β•‘ β”‚ name (text) β”‚ β”‚ name (text) β”‚ β”‚ pii (eql_v2_enc) β”‚ β•‘
* β•‘ β”‚ name (text) β”‚ β”‚ name (text) β”‚ β”‚ pii (json_search) β”‚ β•‘
* β•‘ β”‚ description β”‚ β”‚ description (text) β”‚ β”‚ β”‚ β•‘
* β•‘ β”‚ (text) β”‚ β”‚ code (text) β”‚ β”‚ Contains: β”‚ β•‘
* β•‘ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ procedure_type β”‚ β”‚ β€’ first_name β”‚ β•‘
Expand All @@ -23,7 +23,7 @@
* β•‘ β”‚ FK β†’ patients.id β”‚ β”‚ FK β†’ patients.id β”‚ β”‚ β”‚ β•‘
* β•‘ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β•‘
* β•‘ β”‚ medication β”‚ β”‚ procedure β”‚ β”‚ β”‚ β•‘
* β•‘ β”‚ (eql_v2_encrypted) β”‚ β”‚ (eql_v2_encrypted) β”‚ β”‚ β”‚ β•‘
* β•‘ β”‚ (eql_v3_json_search)β”‚ β”‚ (eql_v3_json_search)β”‚ β”‚ β”‚ β•‘
* β•‘ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β•‘
* β•‘ β”‚ Contains: β”‚ β”‚ Contains: β”‚ β”‚ β”‚ β•‘
* β•‘ β”‚ β€’ medication_id ────┼──── β€’ procedure_id ─────┼──────── β”‚ β•‘
Expand All @@ -43,7 +43,7 @@
* β•‘ β€’ All with CASCADE DELETE for referential integrity β”‚ β”‚ β•‘
* β•‘ β”‚ β”‚ β•‘
* β•‘ Encryption Details: β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β•‘
* β•‘ β€’ PII data in patients.pii is encrypted using EQL v2 β•‘
* β•‘ β€’ PII data in patients.pii is encrypted using EQL v3 β•‘
* β•‘ β€’ Junction tables store encrypted procedure/medication details β•‘
* β•‘ β€’ Foreign keys enforce referential integrity with CASCADE DELETE β•‘
* β•‘ β€’ Reference tables contain plaintext lookup data β•‘
Expand All @@ -66,7 +66,7 @@ use crate::{

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("🩺 Healthcare Database Showcase - EQL v2 Searchable Encryption");
println!("🩺 Healthcare Database Showcase - EQL v3 Searchable Encryption");
println!("============================================================");

trace();
Expand Down Expand Up @@ -148,7 +148,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("\nπŸŽ‰ === ALL TESTS COMPLETED SUCCESSFULLY! ===");
println!();
println!("πŸ”’ This comprehensive demonstration showcases:");
println!(" β€’ EQL v2 searchable encryption for sensitive patient data");
println!(" β€’ EQL v3 searchable encryption for sensitive patient data");
println!(" β€’ All supported JSONB operators: ->, ->>, @>, <@");
println!(" β€’ JSONB functions: jsonb_path_exists, jsonb_path_query_first, jsonb_path_query");
println!(" β€’ Comparison operations on extracted JSONB fields");
Expand All @@ -157,7 +157,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!(" β€’ Realistic medical data with nested objects, arrays, and mixed data types");
println!(" β€’ Secure querying of encrypted data while maintaining privacy");
println!();
println!("✨ EQL v2 provides comprehensive JSONB support for encrypted healthcare data!");
println!("✨ EQL v3 provides comprehensive JSONB support for encrypted healthcare data!");

Ok(())
}
Expand Down
6 changes: 3 additions & 3 deletions packages/showcase/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl Procedure {

/// Represents a patient in the healthcare system.
///
/// This struct demonstrates the use of EQL v2 encryption for protecting sensitive patient data.
/// This struct demonstrates the use of EQL v3 encryption for protecting sensitive patient data.
/// The patient's personally identifiable information (PII) is encrypted to ensure privacy and compliance
/// with healthcare regulations like HIPAA.
#[derive(Serialize)]
Expand Down Expand Up @@ -111,7 +111,7 @@ impl Patient {

/// Contains personally identifiable information for a patient.
///
/// This data is sensitive and must be encrypted when stored in the database. EQL v2 provides
/// This data is sensitive and must be encrypted when stored in the database. EQL v3 provides
/// searchable encryption, allowing healthcare providers to query patient data while maintaining
/// strong privacy protections. Enhanced fields are optional to support both basic and complex
/// patient records.
Expand Down Expand Up @@ -279,7 +279,7 @@ pub struct LabResults {
/// Represents a medication prescription for a patient.
///
/// This struct links patients to their prescribed medications and contains sensitive medical information
/// that requires encryption. The prescription details are stored using EQL v2 encryption to protect
/// that requires encryption. The prescription details are stored using EQL v3 encryption to protect
/// patient privacy while enabling necessary medical queries.
#[derive(Serialize)]
pub struct Prescription {
Expand Down
107 changes: 44 additions & 63 deletions packages/showcase/src/schema.sql
Original file line number Diff line number Diff line change
@@ -1,67 +1,48 @@
-- Patients table with encrypted PII
DROP TABLE IF EXISTS patients CASCADE;
CREATE TABLE patients (
id uuid,
pii eql_v2_encrypted,
PRIMARY KEY(id)
);
-- Patients table with encrypted PII.
--
-- EQL v3 uses self-configuring domain types: `eql_v3_json_search` is the
-- searchable encrypted-JSON (SteVec) domain, so the column type alone declares
-- the encryption and its searchability β€” there is no separate
-- `eql_v2.add_search_config` call as in EQL v2.
DROP TABLE IF EXISTS patients CASCADE;
CREATE TABLE patients (
id uuid,
pii eql_v3_json_search,
PRIMARY KEY(id)
);

SELECT eql_v2.add_search_config(
'patients',
'pii',
'ste_vec',
'jsonb',
'{"prefix": "patients/pii"}'
);
-- Medications reference table (plaintext)
DROP TABLE IF EXISTS medications CASCADE;
CREATE TABLE medications (
id uuid,
name text,
description text,
PRIMARY KEY(id)
);

-- Medications reference table (plaintext)
DROP TABLE IF EXISTS medications CASCADE;
CREATE TABLE medications (
id uuid,
name text,
description text,
PRIMARY KEY(id)
);
-- Procedures reference table (plaintext)
DROP TABLE IF EXISTS procedures CASCADE;
CREATE TABLE procedures (
id uuid,
name text,
description text,
code text,
procedure_type text,
PRIMARY KEY(id)
);

-- Procedures reference table (plaintext)
DROP TABLE IF EXISTS procedures CASCADE;
CREATE TABLE procedures (
id uuid,
name text,
description text,
code text,
procedure_type text,
PRIMARY KEY(id)
);
-- Patient medications junction table with encrypted details
DROP TABLE IF EXISTS patient_medications CASCADE;
CREATE TABLE patient_medications (
patient_id uuid,
medication eql_v3_json_search,
FOREIGN KEY (patient_id) REFERENCES patients(id) ON DELETE CASCADE
);

-- Patient medications junction table with encrypted details
DROP TABLE IF EXISTS patient_medications CASCADE;
CREATE TABLE patient_medications (
patient_id uuid,
medication eql_v2_encrypted,
FOREIGN KEY (patient_id) REFERENCES patients(id) ON DELETE CASCADE
);

SELECT eql_v2.add_search_config(
'patient_medications',
'medication',
'ste_vec',
'jsonb',
'{"prefix": "patient_medications/medication"}'
);

-- Patient procedures junction table with encrypted details
DROP TABLE IF EXISTS patient_procedures CASCADE;
CREATE TABLE patient_procedures (
patient_id uuid,
procedure eql_v2_encrypted,
FOREIGN KEY (patient_id) REFERENCES patients(id) ON DELETE CASCADE
);

SELECT eql_v2.add_search_config(
'patient_procedures',
'procedure',
'ste_vec',
'jsonb',
'{"prefix": "patient_procedures/procedure"}'
);
-- Patient procedures junction table with encrypted details
DROP TABLE IF EXISTS patient_procedures CASCADE;
CREATE TABLE patient_procedures (
patient_id uuid,
procedure eql_v3_json_search,
FOREIGN KEY (patient_id) REFERENCES patients(id) ON DELETE CASCADE
);
Loading