Trust and Sensitivity
Every memory record carries a sensitivity field. Every retrieval request carries a TrustContext that declares the maximum sensitivity the caller is allowed to access. Records above the threshold are filtered out or returned in redacted form.
Sensitivity levels
Sensitivity is an ordered scale from least to most restricted:
| Level | Value | Numeric rank | Meaning |
|---|---|---|---|
| Public | public | 0 | Freely shareable content |
| Low | low | 1 | Minimal sensitivity |
| Medium | medium | 2 | Moderately sensitive content |
| High | high | 3 | Highly sensitive, elevated trust required |
| Hyper | hyper | 4 | Maximum protection |
The numeric rank is used internally to compare a record's sensitivity against the caller's MaxSensitivity:
// From pkg/retrieval/trust.go
var sensitivityOrder = map[schema.Sensitivity]int{
schema.SensitivityPublic: 0,
schema.SensitivityLow: 1,
schema.SensitivityMedium: 2,
schema.SensitivityHigh: 3,
schema.SensitivityHyper: 4,
}
TrustContext fields
// From pkg/retrieval/trust.go
type TrustContext struct {
// MaxSensitivity is the maximum sensitivity level the requester may access.
MaxSensitivity schema.Sensitivity
// Authenticated indicates whether the requester has been authenticated.
Authenticated bool
// ActorID identifies who is making the retrieval request.
ActorID string
// Scopes lists the scopes the requester is allowed to access.
// An empty slice means all scopes are allowed.
Scopes []string
}
MaxSensitivitySensitivityrequiredThe highest sensitivity level the caller may read in full. Records at this level and below are returned with their payload intact.
AuthenticatedboolWhether the requester has been authenticated via API key or other mechanism. Currently recorded in the trust context but does not independently gate access — MaxSensitivity is the primary control.
ActorIDstringIdentifier for the requesting actor (user ID, agent name, service account). Recorded in audit entries for traceability.
Scopes[]stringOptional list of allowed scopes. If non-empty, only records whose scope field matches one of these values are returned. Records with an empty scope are unscoped and bypass this check.
Retrieval behavior by level
The FilterByTrust function in pkg/retrieval/filter.go applies two rules per record:
- Full access — record sensitivity ≤
MaxSensitivity: returned with full payload. - Redacted access — record sensitivity is exactly one level above
MaxSensitivity: returned with metadata only (no payload, no provenance, no audit log). - Denied — record sensitivity is two or more levels above
MaxSensitivity: not returned at all.
// From pkg/retrieval/filter.go
func FilterByTrust(records []*schema.MemoryRecord, trust *TrustContext) []*schema.MemoryRecord {
result := make([]*schema.MemoryRecord, 0, len(records))
for _, r := range records {
if trust.Allows(r) {
result = append(result, r)
} else if trust.AllowsRedacted(r) {
result = append(result, Redact(r))
}
}
return result
}
Redacted records
A redacted record retains the following fields:
ID,Type,Sensitivity,Confidence,SalienceScope,Tags,CreatedAt,UpdatedAt
The following fields are cleared:
Payload(set tonil)Provenance(empty)AuditLog(empty)Relations(empty)
This gives callers enough metadata to know a record exists and its classification without exposing sensitive content.
Example: graduated access
If a caller sets MaxSensitivity: medium:
| Record sensitivity | Returned as |
|---|---|
public | Full record |
low | Full record |
medium | Full record |
high | Redacted (metadata only) |
hyper | Not returned |
Scope filtering
When a TrustContext includes a non-empty Scopes list, records with a non-empty scope must match at least one value in that list. Records with an empty scope are considered unscoped and are always included regardless of the Scopes filter.
// From pkg/retrieval/trust.go
if len(tc.Scopes) > 0 && record.Scope != "" {
found := false
for _, s := range tc.Scopes {
if s == record.Scope {
found = true
break
}
}
if !found {
return false
}
}
Code examples
- Go
- TypeScript
- Python
resp, _ := m.RetrieveGraph(ctx, &retrieval.RetrieveGraphRequest{
TaskDescriptor: "fix build error",
Trust: &retrieval.TrustContext{
MaxSensitivity: schema.SensitivityMedium,
Authenticated: true,
ActorID: "build-agent",
Scopes: []string{"project-acme"},
},
MemoryTypes: []schema.MemoryType{
schema.MemoryTypeEntity,
schema.MemoryTypeCompetence,
schema.MemoryTypeSemantic,
},
RootLimit: 10,
NodeLimit: 25,
MaxHops: 1,
})
for _, node := range resp.Nodes {
r := node.Record
fmt.Printf("Found: %s (type=%s, confidence=%.2f)\n", r.ID, r.Type, r.Confidence)
}
const graph = await client.retrieveGraph("database operations", {
trust: {
max_sensitivity: Sensitivity.MEDIUM,
authenticated: true,
actor_id: "ts-agent",
scopes: []
},
memoryTypes: ["entity", "semantic", "competence"],
rootLimit: 10,
nodeLimit: 25,
maxHops: 1
});
graph = client.retrieve_graph(
task_descriptor="database operations",
trust=TrustContext(max_sensitivity=Sensitivity.MEDIUM, authenticated=True),
memory_types=["entity", "semantic", "competence"],
root_limit=10,
node_limit=25,
max_hops=1,
)
Retrieval without a trust context
Passing a nil trust context to RetrieveGraph, lower-level Retrieve, or RetrieveByID returns ErrNilTrust. All retrieval calls must include a trust context.
// From pkg/retrieval/retrieval.go
if req.Trust == nil {
return nil, ErrNilTrust
}
Never omit the trust context in production code. Failing to supply one causes the request to be rejected rather than returning all records.
Layered retrieval order
When no MemoryTypes filter is specified, records are queried in this canonical order before trust filtering is applied:
working → entity → semantic → competence → plan_graph → episodic
This ordering ensures task-critical context, entity anchors, and high-value knowledge are surfaced before raw experience.