Skip to main content

Consent and gating

The SDK enforces two policies before any memory operation touches a backend: capture gating (when writing) and injection gating (when reading). Both live in ContextManager and both consult the user's preferences through UserAccountsManager.

Gating is not a feature you turn on. It is the default path. AtomicMemorySDK.ingest() and AtomicMemorySDK.search() always go through it.

Capture gate (on ingest)

On every ingest(input, platform) call, ContextManager asks shouldCaptureFromPlatform(normalizedPlatform). If the user has disabled capture from that platform, the operation:

  • Does not reach the provider
  • Returns undefined
  • Emits a captureBlocked event with { platform, reason }

There is no error. Blocked capture is a normal, expected outcome, and applications should treat it as such.

Injection gate (on search and package)

search(request, site) and package(request, site) go through a different gate: evaluateInjectionGate(query, site). The gate returns { allowed: boolean }. When a site is blocked:

The key shape detail: a blocked search returns a SearchResultPage with results: [], not a thrown exception and not a distinct "blocked" object. Design UI around the empty-results path — show nothing, show a rationale, or fall back to a non-gated source; do not rely on a catch block.

search vs searchDirect

The SDK exposes two search entry points on purpose:

MethodGatesWhen to use
search(request, site)Yes — injection gateDefault. Every application call should use this unless you own the gate yourself.
searchDirect(request)NoCallers that already enforce their own injection policy (browser extensions that have inspected the page and decided injection is safe before asking the SDK).

Use search by default. searchDirect exists for callers that enforce their own injection gate — do not use it in general application code. If you are unsure which you need, you need search.

The isCapturePaused helper

The /consent subpath export ships one small helper: isCapturePaused(pausedUntil). It returns true when the given ISO timestamp is in the future. Applications that manage their own pause UX read the current pausedUntil from settings and check with this function before presenting capture controls.

import { isCapturePaused } from '@atomicmemory/atomicmemory-sdk/consent';

if (isCapturePaused(settings.pausedUntil)) {
// show "capture paused until X" UI
}

The SDK's built-in capture gate already consults pause state — this helper exists for apps that need to reflect that state in their own UI without reimplementing the check.

Designing around the gates

Two patterns work well:

  • Silent drop. Accept undefined from ingest and empty results from search as legitimate outcomes. Show user-facing affordances to reconfigure capture or injection rules.
  • Introspect before calling. For apps that want to disable UI affordances preemptively, read the user's preferences directly through UserAccountsManager.getPreferences() before displaying a "save to memory" button.

Either pattern is fine. The wrong pattern is wrapping every call in a try/catch expecting a block error — there isn't one.

Next