Policy Engine
The Policy Engine governs the data lifecycle of every strand. Retention policies cryptographically tombstone records when they age out — provable deletion, not silent erasure. Field-level ACL policies strip sensitive fields from API responses without touching the stored data. Both are enforced by the database layer, not application code.
- ▸Define max_age_secs per policy
- ▸Background agent tombstones expired records every 5 min
- ▸TOMBSTONE nucleotide = cryptographic proof of deletion
- ▸Most restrictive rule wins when multiple apply
- ▸Define denied_fields per policy
- ▸Applied inline at every read response boundary
- ▸Stored strand data is never modified
- ▸All matching policies union their denied_fields
Retention Policies
A retention policy defines how long records survive before the enforcement agent writes a TOMBSTONE nucleotide against them. The enforcement cycle runs every 5 minutes. Multiple retention policies are applied with the most restrictive rule winning — if two policies match and one says 30 days while the other says 90, the 30-day rule applies.
Create a retention policy
{
"name": "GDPR 90-day",
"max_age_secs": 7776000,
"classification_tag": null,
"priority": 10
}
// → { "id": "a1b2c3d4…", "name": "GDPR 90-day", "max_age_secs": 7776000,
// "priority": 10, "created_at_ms": 1747003200000, ... }List / get / delete
{
"retention": [
{ "id": "a1b2…", "name": "GDPR 90-day", "max_age_secs": 7776000, "priority": 10, ... }
],
"acl": [...]
}// GET → 200 RetentionPolicy | 404 if not found // DELETE → 204 No Content
Field-Level ACL Policies
An ACL policy names the payload fields that must be stripped before any API response leaves the agent. The stripping happens at every read boundary — GET /v1/records/:hash, GET /v1/strand/records, POST /v1/query, and POST /v1/query/semantic. The stored strand data is never modified; only the payload_b64 in the response is re-encoded without the denied keys. The content hash in the response still reflects the original unmasked data.
Create an ACL policy
{
"name": "Redact PII",
"denied_fields": ["ssn", "dob", "credit_card"],
"principal": "*",
"priority": 5
}
// → { "id": "f7e8d9c0…", "name": "Redact PII", "denied_fields": ["ssn","dob","credit_card"],
// "principal": "*", "priority": 5, "created_at_ms": 1747003200000 }
// → 400 Bad Request if denied_fields is emptyList / get / delete
// GET → 200 AclPolicy | 404 if not found // DELETE → 204 No Content
Conflict Resolution Hierarchy
When multiple policies apply to the same record or response, the conflict resolution rules determine the effective behavior. Call GET /v1/policies/hierarchy to see the computed effective order at any time.
{
"resolution_rules": [
"ACL policies are additive: denied_fields from all matching rules are unioned",
"Retention policies are restrictive: most limiting max_age_secs wins",
"Higher priority number = checked first; ties broken by creation order (oldest first)"
],
"effective_order": [
{ "kind": "retention", "id": "a1b2…", "name": "GDPR 90-day", "priority": 10, "description": "Tombstone after 7776000s" },
{ "kind": "acl", "id": "f7e8…", "name": "Redact PII", "priority": 5, "description": "Redact: ssn, dob, credit_card" }
]
}Python
pip install sapixdb-policy
import asyncio
from sapixdb_policy import PolicyClient
async def main():
async with PolicyClient("http://localhost:7475") as pol:
# Retention: tombstone records older than 90 days (GDPR right-to-erasure)
gdpr = await pol.gdpr_retention(90, priority=10)
print(f"Created retention policy: {gdpr.id} ({gdpr.max_age_secs}s)")
# Shorter window for high-sensitivity records
ccpa = await pol.create_retention(
"CCPA 30-day",
max_age_secs=30 * 86_400,
priority=20, # checked first — stricter wins
)
# Field-level ACL: strip PII from every API response
pii = await pol.redact_fields(
["ssn", "dob", "credit_card", "bank_account"],
name="PII Redaction",
priority=5,
)
print(f"ACL policy: {pii.id} fields={pii.denied_fields}")
# Inspect the effective policy order
h = await pol.hierarchy()
print("\nResolution rules:")
for rule in h.resolution_rules:
print(f" • {rule}")
print("\nEffective order (highest priority first):")
for entry in h.effective_order:
print(f" [{entry.kind:<10}] {entry.name:<20} priority={entry.priority}")
print(f" {entry.description}")
# Remove the GDPR policy
await pol.delete_retention(gdpr.id)
print(f"\nDeleted policy {gdpr.id}")
asyncio.run(main())PolicyClient API
sapixdb-agent, SapixClient exposes raw-dict wrappers — policy_list(), policy_hierarchy(), policy_create_retention(), policy_get_retention(), policy_delete_retention(), policy_create_acl(), policy_get_acl(), policy_delete_acl(). Use sapixdb-policy for typed Pydantic models and the gdpr_retention() / redact_fields() helpers.