QNTX implements a configurable bounded storage strategy to prevent unbounded database growth. The system enforces limits, evicts oldest attestations when exceeded, and logs eviction events for observability.
All limits configurable via am.toml. When exceeded, the oldest attestations for that constraint are deleted and the event is logged to storage_events.
For bulk ingestion, self-certifying ASIDs are required. Without them, the 64-actor limit causes silent data loss once exceeded.
Using the same actor for all ingestion hits the 64-entity limit. Instead, use each attestation's own ASID as its actor — this bypasses the limit entirely.
// Self-certifying: ASID vouches for itself
asid, _ := id.GenerateASID(entity.ID, "processed", context, "")
attestation := &types.As{
ID: asid,
Actors: []string{asid}, // Self-certifying
// ...
}
Shared actors are appropriate for authority-based claims ("github@oauth") or source tracking, but be aware of the 64-actor limit.
[database.bounded_storage]
actor_context_limit = 16 # attestations per (actor, context) pair
actor_contexts_limit = 64 # contexts per actor
entity_actors_limit = 64 # actors per entity (subject)
Zero or negative values fallback to defaults (16/64/64).
The db symbol palette command opens the database glyph showing:
Evictions are broadcast in real-time via WebSocket (storage_eviction).
All eviction events are logged to storage_events with event type, actor/context/entity, deletion count, limit value, and eviction details (JSON).
# Query enforcement events
qntx db stats --limit 20
# Or directly
sqlite3 ~/.qntx/db/sqlite.db \
"SELECT event_type, COUNT(*) FROM storage_events GROUP BY event_type"
Enforcement runs through Rust's single rusqlite connection (avoids dual-driver SQLite corruption). Uses a half-bound threshold: triggers at limit + limit/2, evicts down to limit. For actor_context_limit=16, this means enforcement triggers at 24 and evicts 8 at once, amortizing cost.
RustBackedStore.CreateAttestation()
→ Rust FFI: INSERT attestation
→ Rust: enforce_limits()
├─ actor_context_limit: if count > 24, evict oldest down to 16
├─ actor_contexts_limit: if contexts > 96, evict least-used down to 64
└─ entity_actors_limit: if actors > 96, evict least-recent down to 64
Each path: load batch → distill into sigma (Σ) → delete originals → insert sigma
(each logs to storage_events)
Evicted attestations are distilled into a sigma (Σ) — a compressed aggregate attestation — before deletion. Sigmas are normal attestations that participate in future enforcement, enabling recursive meta-distillation. See ADR-020: Attestation Distillation for the full design.