-
Notifications
You must be signed in to change notification settings - Fork 8
Description
We haven't taken a stab in writing at this yet but with the SynthID work going on I took a stab at requirements. We'll get feedback from stakeholders such as publishers, SSPs, DSPs, Rowena, Tony, Hill, Shailley and some others on this first, but here's a start. Note the usage of the "Metadata" Fastly feature which is actually quite cool and useful here:
`Metadata is arguably the most underutilized feature of Fastly's KV Store. You can attach up to 2048 bytes of UTF-8 string data to any entry—no schema required. This could be JSON, timestamps, flags, or even assembly code (if that's your thing).
Why is this powerful? Because metadata is accessible without streaming the body. You can make decisions based on metadata alone, saving bandwidth and processing time.`
MD file below:
SyntheticID KV Store Schema
Overview
This schema defines the structure for storing user identity and behavioral data in Fastly KV Store, using the SyntheticID as the key.
Storage Architecture
- Key: SyntheticID (format:
{64-hex-hmac}.{6-alphanumeric-suffix}) - Value: JSON payload (up to 25MB, see schema below)
- Metadata: Fast-access fields (up to 2048 bytes, see metadata schema)
Metadata Schema (2048 bytes max)
Optimized for fast access without streaming the full value:
{
"schema_version": "1.0",
"last_updated": "2026-02-15T10:30:00Z",
"consent_status": "granted",
"jurisdiction": "EU",
"sync_status": "active",
"ttl_days": 90
}Metadata Fields
| Field | Type | Description |
|---|---|---|
schema_version |
string | Schema version for backward compatibility |
last_updated |
ISO 8601 | Last modification timestamp |
consent_status |
enum | One of: granted, denied, pending, expired |
jurisdiction |
string | Geographic region (e.g., EU, US-CA, US-NY) |
sync_status |
enum | One of: active, stale, error, disabled |
ttl_days |
number | Days until expiration |
Value Schema (JSON Payload)
{
"version": "1.0",
"created_at": "2026-01-15T08:22:00Z",
"updated_at": "2026-02-15T10:30:00Z",
"identifiers": {
"synthetic_id": "a1b2c3...xyz.Ab12c3",
"universal_ids": {
"uid2": {
"token": "AgAAAAVW...",
"expires_at": "2026-02-20T10:30:00Z",
"status": "active"
},
"id5": {
"id": "ID5*xxx...",
"link_type": "deterministic",
"expires_at": "2026-03-15T10:30:00Z"
},
"liveramp": {
"ats": "ramp:xxx...",
"envelope": "envelope-v3:xxx..."
},
"criteo": {
"onetag_id": "criteo-xxx..."
},
"sharedid": {
"id": "shared-xxx...",
"third_party_cookie": "xxx..."
}
},
"first_party": {
"email_sha256": "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8",
"phone_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"crm_id": "CRM-12345",
"loyalty_id": "LOYAL-67890",
"authenticated_user_id": "user-abc-123"
},
"cross_device": {
"device_graph_id": "device-cluster-xxx",
"linked_devices": ["device-1", "device-2"],
"confidence_score": 0.92
}
},
"partner_identifiers": {
"dsp_buyers": {
"thetradedesk": {
"buyer_id": "TTD-buyer-xxx",
"cookie_value": "xxx...",
"last_synced": "2026-02-15T09:00:00Z"
},
"google_dv360": {
"buyer_id": "DV360-xxx",
"cookie_value": "xxx...",
"last_synced": "2026-02-15T09:00:00Z"
},
"amazon_advertising": {
"buyer_id": "amzn-xxx",
"cookie_value": "xxx...",
"last_synced": "2026-02-15T09:00:00Z"
}
},
"ssp_identifiers": {
"pubmatic": {
"user_id": "PM-xxx",
"cookie_value": "xxx...",
"last_synced": "2026-02-15T09:00:00Z"
},
"magnite": {
"user_id": "MAG-xxx",
"cookie_value": "xxx...",
"last_synced": "2026-02-15T09:00:00Z"
},
"openx": {
"user_id": "OX-xxx",
"cookie_value": "xxx...",
"last_synced": "2026-02-15T09:00:00Z"
}
},
"data_partners": {
"permutive": {
"user_id": "perm-xxx",
"segment_ids": ["seg-123", "seg-456", "seg-789"],
"last_synced": "2026-02-15T09:00:00Z"
},
"liveramp": {
"abilitec_id": "lr-xxx",
"last_synced": "2026-02-15T09:00:00Z"
},
"lotame": {
"panorama_id": "lotame-xxx",
"last_synced": "2026-02-15T09:00:00Z"
}
}
},
"consent": {
"tcf": {
"version": "2.2",
"consent_string": "CPXxRfAPXxRfAAfKABENATEIAACAAAAAAAAAAAAAAAAA.YAAAAAAAAAA",
"purpose_consents": {
"1": true,
"2": true,
"3": false,
"4": true,
"5": false,
"6": true,
"7": true,
"8": true,
"9": false,
"10": true
},
"vendor_consents": {
"755": true,
"10": true
},
"timestamp": "2026-02-15T10:30:00Z",
"cmp_id": 123,
"cmp_version": 1
},
"gpp": {
"string": "DBABMA~CPXxRfAPXxRfAAfKABENATEIAACAAAAAAAAAAAAAAAAA.YAAAAAAAAAA",
"sections": ["tcfeuv2", "uspv1"],
"timestamp": "2026-02-15T10:30:00Z"
},
"ccpa": {
"us_privacy_string": "1YNN",
"opt_out": false,
"timestamp": "2026-02-15T10:30:00Z"
},
"custom": {
"analytics_consent": true,
"personalization_consent": true,
"advertising_consent": true,
"timestamp": "2026-02-15T10:30:00Z"
},
"coppa": {
"is_child": false,
"age_gate_passed": true,
"age": null
}
},
"segments": {
"behavioral": {
"categories": [
{
"id": "IAB1",
"name": "Arts & Entertainment",
"confidence": 0.85,
"last_observed": "2026-02-15T10:30:00Z"
},
{
"id": "IAB3",
"name": "Business",
"confidence": 0.72,
"last_observed": "2026-02-14T15:20:00Z"
}
],
"intent_signals": {
"purchase_intent": {
"category": "automotive",
"score": 0.78,
"recency_days": 3
},
"research_intent": {
"category": "travel",
"score": 0.65,
"recency_days": 1
}
}
},
"contextual": {
"recent_content": [
{
"url_path": "/tech/ai-news",
"categories": ["technology", "ai"],
"timestamp": "2026-02-15T10:25:00Z"
},
{
"url_path": "/sports/football",
"categories": ["sports", "football"],
"timestamp": "2026-02-15T09:15:00Z"
}
]
},
"private": {
"permutive_segments": [
"seg-high-value-user",
"seg-frequent-visitor",
"seg-newsletter-subscriber"
],
"custom_segments": [
"vip-member",
"early-adopter"
]
},
"lookalike": {
"models": [
{
"model_id": "lal-premium-buyers",
"score": 0.88,
"updated_at": "2026-02-15T00:00:00Z"
}
]
}
},
"attributes": {
"keywords": [
"tech-enthusiast",
"sports-fan",
"early-adopter",
"newsletter-subscriber"
],
"lifecycle": {
"stage": "engaged",
"first_seen": "2025-11-20T14:30:00Z",
"last_seen": "2026-02-15T10:30:00Z",
"session_count": 47,
"page_view_count": 283,
"days_active": 87,
"avg_session_duration_seconds": 420
},
"engagement": {
"recency_days": 0,
"frequency_30d": 18,
"monetary_value": null,
"engagement_score": 0.82,
"content_affinity": {
"technology": 0.75,
"sports": 0.60,
"business": 0.45
}
},
"propensity": {
"subscription_likelihood": 0.68,
"churn_risk": 0.15,
"conversion_likelihood": 0.54
}
},
"technical": {
"fingerprint": {
"user_agent_hash": "sha256:xxx...",
"ip_geolocation": {
"country": "US",
"region": "CA",
"city": "San Francisco",
"dma": 807,
"postal_code": "94105"
},
"platform": {
"device_type": "desktop",
"os": "macOS",
"os_version": "14.2",
"browser": "Chrome",
"browser_version": "120.0"
}
},
"environment": {
"app_version": null,
"sdk_version": null,
"is_mobile_app": false,
"is_web": true
}
},
"quality": {
"data_completeness_score": 0.87,
"last_validated": "2026-02-15T10:30:00Z",
"error_flags": [],
"data_sources": [
"first_party_direct",
"uid2_sync",
"permutive_enrichment"
],
"confidence_level": "high"
},
"sync_metadata": {
"collective_sync": {
"participant_publishers": ["pub-123", "pub-456"],
"last_broadcast": "2026-02-15T09:00:00Z",
"last_received": "2026-02-15T08:45:00Z",
"shared_segments": ["seg-premium-audience"],
"opt_in": true
},
"partner_sync_status": {
"uid2": {
"last_sync": "2026-02-15T09:00:00Z",
"next_sync": "2026-02-16T09:00:00Z",
"status": "success"
},
"id5": {
"last_sync": "2026-02-15T09:00:00Z",
"next_sync": "2026-02-16T09:00:00Z",
"status": "success"
}
}
}
}Schema Design Principles
1. Versioning
- Top-level
versionfield for schema evolution - Metadata
schema_versionfor quick compatibility checks - Allows backward-compatible changes
2. Timestamps
- ISO 8601 format for all timestamps
- Track creation, updates, sync times
- Enable time-based queries and cleanup
3. Consent-First
- Comprehensive consent tracking (TCF, GPP, CCPA, custom)
- Purpose-level granularity
- COPPA compliance flags
4. Partner Integration
- Structured namespacing for DSPs, SSPs, data partners
- Sync status tracking
- Easy to add new partners
5. Privacy & Quality
- No raw PII (only hashed identifiers)
- Quality scoring and validation tracking
- Data source attribution
Usage Patterns
Fast Metadata Access
// Check consent without reading full payload
let metadata = kv_store.get_metadata(synthetic_id)?;
if metadata.consent_status != "granted" {
return Err("No consent");
}Full Profile Read
let value = kv_store.get(synthetic_id)?;
let profile: SyntheticIdProfile = serde_json::from_str(&value)?;Atomic Updates with Generation Markers
// Read with generation
let (value, generation) = kv_store.get_with_generation(synthetic_id)?;
let mut profile: SyntheticIdProfile = serde_json::from_str(&value)?;
// Update specific fields
profile.partner_identifiers.dsp_buyers.insert("new_dsp", buyer_data);
profile.updated_at = Utc::now();
// Write with generation check (fails if modified)
kv_store.put_with_generation(
synthetic_id,
&serde_json::to_string(&profile)?,
generation
)?;TTL-Based Expiration
// Set 90-day TTL for GDPR compliance
kv_store.put_with_ttl(
synthetic_id,
&serde_json::to_string(&profile)?,
90 * 24 * 60 * 60 // 90 days in seconds
)?;Size Considerations
- Typical size: 5-15 KB per profile
- Heavy user: 20-50 KB (many segments, partners)
- Maximum: 25 MB (KV Store limit)
- Metadata: ~200-400 bytes (well under 2048 limit)
Future Enhancements
- Compression: Use gzip for values >10KB
- Sharding: Split very large profiles across multiple keys
- Caching: Use metadata timestamps for cache invalidation
- Archival: Move stale profiles to cheaper storage
- Analytics: Aggregate statistics without PII