Consent Grain
What is a Consent grain?
A Consent grain records an explicit permission grant or withdrawal by a data subject, scoped to a specific purpose and jurisdiction. Consent grains are the compliance backbone of AI memory in Areev’s context database, providing an immutable, auditable record of who gave (or revoked) permission for what, when, and under which legal basis.
For GDPR, CCPA, LGPD, and other privacy regulations, the AI agent memory system requires verifiable proof that data processing was authorized. Consent grains provide this with DID-based identity, purpose-bounded scoping, and jurisdiction tracking. Because grains in the autonomous memory engine are immutable, consent records cannot be silently modified or deleted — ensuring the audit trail remains intact for regulatory review.
The text representation used for embedding and BM25 indexing describes the consent action: "did:example:john grants did:example:acme" for grants, or "did:example:john withdraws did:example:acme" for withdrawals. This lets you search consent records by data subject or grantee DID.
| Field | Type | Required | Description |
|---|---|---|---|
subject_did | string | yes | DID of the data subject granting or withdrawing consent |
user_id | string | yes | Owner user ID (auto-populated from auth identity in HTTP; required in SDK) |
grantee_did | string | no | DID of the entity receiving the permission |
scope | string | no | Purpose or scope the consent covers (e.g., "marketing-emails") |
is_withdrawal | boolean | no | true if this is a withdrawal of prior consent (default: grant) |
basis | string | no | Legal basis (e.g., "GDPR Art. 6(1)(a)", "CCPA opt-in") |
jurisdiction | string | no | Applicable jurisdiction (e.g., "EU", "US-CA", "BR") |
prior_consent | string | no | Hash of the prior consent grain this one supersedes |
witness_dids | string[] | no | DIDs of witnesses to the consent action |
Plus all common fields (confidence, tags, namespace, etc.).
Note:
prior_consentandwitness_didsare not currently persisted when you create a consent grain through the public/addor/batch-addingest path — both fields are silently dropped and will not appear on the stored grain. Do not build consent logic that depends on reading these back. Track consent lifecycle usingsubject_did,scope,grantee_did, andis_withdrawalinstead (see below).
How do I record a consent grant?
Pass subject_did and the scope, basis, and jurisdiction through the /batch-add endpoint with grain_type: "consent". Both subject_did and user_id are required. When using the HTTP API, user_id is auto-populated from the authenticated caller’s identity if not explicitly provided. Include basis and jurisdiction for regulatory compliance.
The scope field defines what the consent covers — for example, "marketing-emails", "analytics", or "model-training". Keep scopes specific and consistent across your system so that consent checks can match exact scope strings. The basis field records the legal authority for processing (e.g., "GDPR Art. 6(1)(a)" for explicit consent, "CCPA opt-in" for California Consumer Privacy Act).
The grantee_did identifies the entity receiving the permission — if omitted, the consent applies to the system operator by default.
import areev
db = areev.open("./data")
h = db.add("consent", {
"subject_did": "did:example:john",
"grantee_did": "did:example:acme-corp",
"scope": "marketing-emails",
"basis": "GDPR Art. 6(1)(a)",
"jurisdiction": "EU"
})
POST /api/memories/default/batch-add
Content-Type: application/json
{
"grains": [
{
"grain_type": "consent",
"fields": {
"subject_did": "did:example:john",
"grantee_did": "did:example:acme-corp",
"scope": "marketing-emails",
"basis": "GDPR Art. 6(1)(a)",
"jurisdiction": "EU"
}
}
]
}
How do I record a consent withdrawal?
Record a withdrawal as a new consent grain with is_withdrawal set to true, carrying the same subject_did, scope, and grantee_did as the original grant. Because consent grains are immutable and timestamped, the grant and the withdrawal together form an auditable record of when consent was given and when it was revoked.
Match grants to withdrawals by the subject_did + scope + grantee_did combination rather than by a stored back-reference: prior_consent is not persisted on the ingest path, so a withdrawal cannot rely on linking to the original grant by hash. For compliance audits, the time-ordered sequence of grant and withdrawal grains for a given combination provides the history needed to demonstrate lawful processing under GDPR Article 7 or equivalent regulations — you can show when consent was granted, under which basis, and when it was withdrawn.
Withdrawal takes effect immediately. Once a withdrawal grain exists, any system checking consent for the given subject_did + scope + grantee_did combination will find the withdrawal and should halt the associated data processing. Your application code is responsible for performing this check before processing.
grant_hash = db.add("consent", {
"subject_did": "did:example:john",
"grantee_did": "did:example:acme-corp",
"scope": "marketing-emails",
"basis": "GDPR Art. 6(1)(a)",
"jurisdiction": "EU"
})
withdrawal_hash = db.add("consent", {
"subject_did": "did:example:john",
"grantee_did": "did:example:acme-corp",
"scope": "marketing-emails",
"is_withdrawal": True,
"jurisdiction": "EU"
})
POST /api/memories/default/batch-add
Content-Type: application/json
{
"grains": [
{
"grain_type": "consent",
"fields": {
"subject_did": "did:example:john",
"grantee_did": "did:example:acme-corp",
"scope": "marketing-emails",
"is_withdrawal": true,
"jurisdiction": "EU"
}
}
]
}
How do I query Consent grains?
Search by data subject DID, grantee DID, scope, or jurisdiction using the standard /recall endpoint with grain_type: "consent".
For compliance checks, query by subject_did to find all consent records for a data subject. This surfaces both grants and withdrawals, letting your application determine the current consent status for each scope. To check a specific permission, search for the combination of subject_did, scope, and grantee_did and look at the most recent grain — if it is a withdrawal, consent is not active.
For bulk compliance audits, query by jurisdiction to find all consent records under a specific regulatory regime. Combine with tags like "needs-review" to flag consent records that require periodic re-confirmation under regulations that mandate renewal (e.g., some LGPD interpretations).
# Find all consent records for a data subject
results = db.recall(query="did:example:john", grain_type="consent")
# Find all marketing consent grants
results = db.recall(query="marketing-emails", grain_type="consent")
POST /api/memories/default/recall
Content-Type: application/json
{"query": "did:example:john", "grain_type": "consent", "limit": 20}
When should I use Consent vs. State?
Use Consent for data-subject permission records that carry legal weight — who granted what, under which legal basis, in which jurisdiction. Use State for general-purpose configuration or session snapshots without compliance semantics.
Consent grains have purpose-built fields for DID-based identity, legal basis, jurisdiction, and withdrawal tracking. A State grain storing consent information would lose these structured fields and would not integrate with Areev’s compliance verification checks. The structured fields on Consent grains enable deterministic compliance queries that would require fragile JSON parsing on State grains.
If you need to track configuration preferences that have no regulatory implications (e.g., UI theme, notification settings), use State. If the preference involves personal data processing rights, use Consent.
Related
- Grain Types: Overview of all 10 OMS grain types and shared fields
- State: For general-purpose snapshots without compliance semantics
- Belief: For knowledge claims derived from consent status
- Event: For recording the moment consent was given or withdrawn