Skip to content
Back to archipelago.banuacoder.com

Shared Packages

shared_consent Free ​

Tier
Freeversion 0.1.5
Command
archipelago generate shared_consent
Generates
  • shared/consent_api
  • shared/consent_impl

Telemetry consent gate and PII scrubbing packages (shared/consent_api, shared/consent_impl), wired into bootstrap.dart before dependency injection.

What it is ​

Every consent-gated feature asks one ConsentGate before it sends or keeps anything. The gate fails closed: every purpose is denied until the app records a decision. flutter_modular_monorepo generates this brick, so its projects already contain both packages and the bootstrap call. This brick is the only source of the consent packages.

Run it by hand only on projects created before consent shipped. app_blackbox_recorder generates it for you when shared/consent_api is missing.

Variables ​

None.

Usage ​

bash
archipelago generate shared_consent --output <project-root>

What the hook does ​

  • Adds shared/consent_api and shared/consent_impl to the workspace.
  • Adds consent_impl to the app's pubspec.yaml.
  • Inserts await ConsentInitializer.initialize(GetIt.I); before await initializeInjector() in apps/<app>/lib/bootstrap.dart, with the consent_impl import. It skips a bootstrap that already calls it.
  • If bootstrap.dart has no initializeInjector( line, it warns and you add the call by hand.
  • Records shared_consent in archipelago.yaml.

Record a decision ​

dart
await getIt<ConsentGate>().record(
  purpose: ConsentPurpose.diagnostics,
  granted: true,
);

The four purposes (analytics, diagnostics, marketing, targeting) are independent. In debug builds, --dart-define=CONSENT_DEBUG_GRANT_ALL=true grants all of them for QA. Do not seed a granted state in code.

ConsentPurpose is a closed enum for app-level purposes. Grants to a mini-app are keyed by an open subject string (miniapp:<publisherId>/<miniAppId>) and a scope key, so they have their own types in the same package:

TypeRole
ScopedConsentDecisionOne decision for one subject and scope (UTC time, source, manifest version, server record id).
ScopedConsentStorePersistence port. SharedPreferencesScopedConsentStore keeps one JSON list under archipelago.consent.scoped.v1, apart from the purpose record.
ScopedConsentGateisGranted(subject, scope), decisionsFor, subjects, changes, recordAll, putAll, revokeSubject. PersistedScopedConsentGate is the implementation. Fail-closed.
ScopedConsentSyncPort to the server record. The default NoopScopedConsentSync keeps consent on the device (isAvailable is false).

ConsentInitializer.initialize registers ScopedConsentGate and ScopedConsentSync in the global GetIt next to ConsentGate. Pass scopedSync: to plug in a backend, or scopedStore: to swap storage. Nothing reads them unless the partner mini-app SDK is present.

Recording is two steps. Push to the server first, then record locally with the returned id. If the push throws, record nothing, because a grant the server does not know cannot produce a token:

dart
final recordId = await getIt<ScopedConsentSync>().push(subject, {'profile.name': true}, manifestVersion);
await getIt<ScopedConsentGate>().recordAll(subject, {'profile.name': true}, policyVersion: manifestVersion, recordId: recordId);

revokeSubject records a refusal for every scope of the subject. The refusals stay as audit records and the subject leaves subjects. Decisions pulled from the server go in through putAll and overwrite a stale local grant. Without a real sync, scopes that need a server record (identity) cannot be granted.

You do not usually call these by hand. webview_sdk_advanced ships the partner consent UI on top of them (PartnerConsentController, PartnerConsentSheet and the settings pages); see Partner consent.