Skip to content

Manage user consent and opt-out

Grant and revoke a user’s consent categories, set their opt-out preferences and read workspace consent and enforcement stats.

  • Enterprise installed (enterprise.enabled: true). The routes are served in dev mode, so a license is not required. See Licensing & Features.
  • A Workspace with spec.privacy set. This provisions the privacy-api.
  • The user’s pseudonymous id (virtualUserId) if you call privacy-api directly. The service stores whatever key it receives, so a raw user id records state for a subject that matches no session or memory, and returns no error.
  • Access to the privacy-api Service from inside the cluster, and a ServiceAccount token it accepts.

If you build an application, call the dashboard’s workspace routes instead of privacy-api. They pseudonymise the user id for you and proxy to privacy-api with identical bodies. See Workspace routes for the paths and how they scope the user.

The steps below call privacy-api directly. Every route, body and response field is in the Privacy API reference.

  1. Forward the Service. It is named privacy-<workspace> and lives in the workspace namespace.

    Terminal window
    kubectl port-forward -n <workspace-namespace> svc/privacy-<workspace> 8080:8080
  2. Mint a token for a ServiceAccount in the workspace namespace, with the audience omnia-session-api. The examples use $SA_TOKEN and http://localhost:8080.

    Terminal window
    export SA_TOKEN=$(kubectl create token default -n <workspace-namespace> --audience omnia-session-api)

Six consent categories exist. memory:identity, memory:location and memory:health need an explicit grant before memory writes in that category succeed. The other three need none. See Consent categories.

  1. Grant categories. The service applies grants first, then revocations.

    Terminal window
    curl -sS -X PUT \
    http://localhost:8080/api/v1/privacy/preferences/<virtual-user-id>/consent \
    -H "Authorization: Bearer $SA_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"grants": ["memory:identity", "memory:location"]}'

    The response is the resulting state (illustrative values):

    {
    "grants": ["memory:identity", "memory:location"],
    "defaults": ["memory:context", "memory:history", "memory:preferences"],
    "denied": ["memory:health"]
    }
  2. Revoke a category.

    Terminal window
    curl -sS -X PUT \
    http://localhost:8080/api/v1/privacy/preferences/<virtual-user-id>/consent \
    -H "Authorization: Bearer $SA_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"revocations": ["memory:location"]}'

    privacy-api pushes the revocation to the workspace’s memory-api instances, which then apply the MemoryPolicy consentRevocation.action to memories already stored in that category. Each grant and revocation also writes a row to the privacy audit hub.

Opt-out suppresses recording at a scope (all, workspace or agent) instead of per category.

  1. Set an opt-out. target names the workspace or agent, and is omitted for all.

    Terminal window
    curl -sS -X POST http://localhost:8080/api/v1/privacy/opt-out \
    -H "Authorization: Bearer $SA_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"virtualUserId": "<virtual-user-id>", "scope": "workspace", "target": "my-workspace"}'

    A successful call returns 204.

  2. Remove it by sending the same body with DELETE.

    Terminal window
    curl -sS -X DELETE http://localhost:8080/api/v1/privacy/opt-out \
    -H "Authorization: Bearer $SA_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"virtualUserId": "<virtual-user-id>", "scope": "workspace", "target": "my-workspace"}'

Both stats routes need the workspace query parameter. Enforcement stats filter on the Workspace UID, not its name.

  1. Read the Workspace UID.

    Terminal window
    export WS_UID=$(kubectl get workspace my-workspace -o jsonpath='{.metadata.uid}')
  2. Read the stats.

    Terminal window
    curl -sS "http://localhost:8080/api/v1/privacy/enforcement-stats?workspace=$WS_UID" \
    -H "Authorization: Bearer $SA_TOKEN"
    curl -sS "http://localhost:8080/api/v1/privacy/consent/stats?workspace=$WS_UID" \
    -H "Authorization: Bearer $SA_TOKEN"

    The consent stats route requires the parameter but ignores its value.

  1. Read the user’s consent state. It returns the same grants, defaults and denied fields as the write.

    Terminal window
    curl -sS \
    http://localhost:8080/api/v1/privacy/preferences/<virtual-user-id>/consent \
    -H "Authorization: Bearer $SA_TOKEN"
  2. Read the user’s opt-outs. A 404 means the user has no stored preferences.

    Terminal window
    curl -sS http://localhost:8080/api/v1/privacy/preferences/<virtual-user-id> \
    -H "Authorization: Bearer $SA_TOKEN"
  3. Confirm that opt-out is enforced in the workspace.

    Terminal window
    kubectl get workspace my-workspace \
    -o jsonpath='{.status.conditions[?(@.type=="OptOutEnforced")]}'

    True with reason PrivacyEnforced means privacy-api is serving. See the OptOutEnforced condition for the False reasons.

The body names a category outside the six registered ones. The response reads unknown consent category: <value>. Use a value from Consent categories.

Sessions show [OPTED_OUT] content or memories stop appearing

Section titled “Sessions show [OPTED_OUT] content or memories stop appearing”

If the user has opted out, the sentinel and the missing memories are expected. If they have not, privacy-api may be unavailable: while spec.privacy is set and privacy-api cannot be resolved, session-api and memory-api treat every subject as opted out. Check the OptOutEnforced condition. PrivacyUnavailable means privacy-api is not serving yet. The services re-resolve it periodically and recover without a restart.

To alert on an outage, watch omnia_session_api_content_suppressed_total{reason="privacy-unavailable"}. The user-opted-out series is ordinary traffic. See Behaviour when privacy-api cannot answer.

The Workspace has no spec.privacy, so opt-out is off and all writes are recorded. Set spec.privacy and confirm the Workspace reports status.privacyURL.