sqlite_loom_suite 0.1.0 copy "sqlite_loom_suite: ^0.1.0" to clipboard
sqlite_loom_suite: ^0.1.0 copied to clipboard

Cross-application local coordination and durable shared data for sqlite_loom suites.

sqlite_loom_suite #

sqlite_loom_suite is an optional companion to sqlite_loom for related applications from one publisher that need to coordinate durable local state on the same device.

It provides suite and app identities, namespaced JSON values, a durable at-least-once event journal, voluntary app registration, compatibility negotiation, expiring leases, and redacted diagnostics. Initialization is explicit and dependency-injectable; there is no global suite singleton.

This is local-device infrastructure. It is not network synchronization, distributed consensus, installed-app discovery, secure secret storage, encryption at rest, or guaranteed background messaging.

Architecture #

Platform Transport Storage owner Status
iOS / macOS Direct App Group file Every entitled app opens the same file Supported
Android Signature-permission ContentProvider broker One configured host app Supported
Windows packaged Direct PublisherCacheFolder Same-publisher packaged apps Supported
Windows unpackaged Explicit local path Application/operator Supported with caller-managed ACLs
Linux Explicit local path Application/operator Supported with caller-managed owner/group/mode
Web None Explicitly unsupported

Direct transports keep the SQLite database, -wal, and -shm in one local directory, enable foreign keys and WAL, and use a bounded busy timeout. Android clients never receive a database path or arbitrary SQL access. Native notifications are wake hints only; SQLite plus the durable journal are authoritative and polling is always available.

The suite database is normally additional to each app's existing private database. Keep app-specific records private and copy only intentionally shared state into the suite store. Moving an existing domain database into an App Group is an explicit application migration: every app then gains access to the whole file and must share compatible schema/migration ownership. This package does not relocate an application's database automatically.

Never place the database on iCloud Drive, OneDrive-synchronized folders, Dropbox, NFS, SMB, or another network filesystem. SQLite WAL requires all processes to be on one host and does not work over network filesystems; see SQLite WAL documentation.

Install #

dependencies:
  sqlite_loom_suite: ^0.1.0

The package targets sqlite_loom: ^0.4.0.

Minimal direct setup #

final client = await SuiteClient.open(
  SuiteConfiguration(
    suiteId: SuiteId('example.publisher.shared'),
    appId: SuiteAppId('example.publisher.app_a'),
    appName: 'Example App A',
    appVersion: '1.0.0',
    role: SuiteRole.host,
    appleAppGroup: 'group.example.publisher.shared',
  ),
);

// Always close with the application lifecycle.
await client.close();

Linux and unpackaged Windows callers pass an absolute databasePath or inject DirectSuiteTransport(pathResolver: ...). Packaged Windows callers pass windowsPublisherCacheFolder. Android clients use AndroidBrokerTransport; see the host/client guide.

Deployment guardrails #

Prefer the platform-specific factories (SuiteConfiguration.appleAppGroup, .androidHost, .androidClient, .windowsPublisherCache, and .desktop) so incompatible role and transport combinations are not assembled by hand. For production suites, declare the same deployment contract in every app:

final deployment = SuiteDeploymentManifest(
  schemaOwnerAppId: SuiteAppId('example.publisher.host'),
  expectedApps: [
    SuiteExpectedApp(
      appId: SuiteAppId('example.publisher.host'),
      protocol: const SuiteProtocolRange(minimum: 1, maximum: 1),
    ),
    SuiteExpectedApp(
      appId: SuiteAppId('example.publisher.client'),
      protocol: const SuiteProtocolRange(minimum: 1, maximum: 1),
    ),
  ],
);

Direct transports automatically run SuitePreflight before opening. Android clients inspect the installed provider and reject missing, non-exported, URI-granting, or non-signature-protected brokers before the handshake. Suite metadata permanently records a random installation identity and the first declared schema owner; conflicting owners fail closed.

A file containing non-suite tables is rejected by default. Only a deliberate domain-database migration may set allowExistingDatabase: true; doing so gives every participating direct app access to the entire file. Known cloud/network paths and world-writable Unix directories are also rejected. The allowUnsafeSharedPath escape hatch is intended only for environments whose security is established outside the package.

Validate application manifests, entitlements, and desktop paths before a release:

dart run sqlite_loom_suite:sqlite_loom_suite_doctor \
  --project . \
  --apple-group group.example.publisher.shared \
  --android-authority example.publisher.shared.sqlite_loom_suite

See the deployment checklist.

Shared values #

final write = await client.values.write(
  'preferences',
  'accent',
  {'name': 'indigo'},
);

final wonRace = await client.values.write(
  'preferences',
  'accent',
  {'name': 'teal'},
  ifRevision: write.value!.revision,
);

await for (final value in client.values.watch('preferences', 'accent')) {
  print('revision ${value?.revision}'); // Do not print the stored value in production.
}

Values are JSON-compatible, size-limited, optionally expiring, and revisioned monotonically across the suite. Compare-and-set returns applied: false on a revision conflict. No raw SQL is exposed.

Durable events #

final published = await client.events.publish(
  type: 'document.changed',
  payload: {'documentId': '42'},
  target: SuiteEventTarget.apps({
    SuiteAppId('example.publisher.app_a'),
    SuiteAppId('example.publisher.app_b'),
  }),
  deduplicationKey: 'document-42-revision-7',
  timeToLive: const Duration(days: 1),
);

final subscription = client.events.subscribe(
  appId: client.configuration.appId,
  after: 0,
);
await for (final event in subscription.events) {
  await handle(event);
  await subscription.acknowledge(event);
}

Delivery is at least once. Persist the last processed sequence in application state when stronger restart behavior is needed, make handlers idempotent, and acknowledge only after successful processing. Suspended or terminated apps are subject to OS background limits and may not run until reopened.

App registration #

SuiteClient.open registers the current app. Registration only proves that an app ran and opted in; stale rows can survive uninstall.

final recentlySeen = await client.apps.list(
  seenWithin: const Duration(days: 30),
);
await client.apps.removeStale(const Duration(days: 90));

Leases #

final lease = await client.leases.acquire(
  'event-cleanup',
  'example.publisher.app_a/process-1',
  const Duration(minutes: 1),
);
if (lease != null) {
  try {
    await client.events.cleanup();
  } finally {
    await client.leases.release(lease);
  }
}

Leases recover after process death through expiration. They assume a reasonably consistent local wall clock and are not consensus or a fencing-token service.

Security #

Do not store credentials, refresh tokens, private keys, or other secrets in suite values/events. SQLite Loom does not encrypt the database, and this package deliberately does not market shared storage as a secret store. Apple Keychain Access Groups are a separate consumer concern. Android hosts must use a signature permission and the included provider performs a second same-signer check.

Payloads and values are excluded from database observations by default. Treat app IDs, declared capabilities, and non-sensitive metadata as claims within the platform publisher/signing boundary.

Guides #

Relationship to SQLite Loom #

This package uses the public SqliteLoomProject, migrations, SqliteLoom, connection configuration, invalidation, external-change monitoring, observers, and health helpers. It does not duplicate Loom's typed query layer or modify Loom's compact core. Advanced consumers may use client.loom only on direct transports; Android broker clients receive a typed failure because there is no safe local database instance.

License #

MIT © 2026 Omar Yacop.

0
likes
160
points
104
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Cross-application local coordination and durable shared data for sqlite_loom suites.

Repository (GitHub)
View/report issues
Contributing

Topics

#sqlite #database #ipc #persistence #coordination

License

MIT (license)

Dependencies

crypto, flutter, path, plugin_platform_interface, sqflite, sqflite_common, sqlite_loom

More

Packages that depend on sqlite_loom_suite

Packages that implement sqlite_loom_suite