fasq_security

Pub Version Repository Issues

Security plugin for FASQ (Flutter Async State Query).

Provides enterprise-grade security features for FASQ including encryption, secure storage, and persistence.

Current Version: 0.3.1

📚 Documentation

For full documentation and API reference, visit:
https://shafi.dev/fasq/security

✨ Features

  • 🔒 Encryption: AES-GCM encryption with 256-bit keys.
  • 🛡️ Secure Storage: Platform-specific secure key storage (Keychain/Keystore).
  • 💾 Persistence: Encrypted SQL persistence using Drift.
  • ⚡ Performance: Isolate-based encryption for large data sets.

📦 Installation

dependencies:
  fasq: ^0.5.1
  fasq_security: ^0.3.1

🚀 Quick Start

Use the unified bootstrap for secure persistence and offline sync:

import 'package:flutter/widgets.dart';
import 'package:fasq_security/fasq_security.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final fasq = await Fasq.initialize(
    scope: FasqDataScope.anonymous(),
    persistence: const QueryPersistence.secure(),
  );

  runApp(FasqProvider(runtime: fasq, child: const MyApp()));
}

Fasq.initialize owns resources it creates and close is idempotent:

await fasq.close();

The runtime automatically maps Flutter resumed events to foreground replay and paused events to an optional best-effort background wake-up. Mobile platform execution is not guaranteed; startup and reconnect replay remain the correctness path.

With OfflineSync.custom, the supplied DurableOutboxStore is borrowed and owns its persistence security. Construct FileDurableOutbox with an OutboxEncryption adapter when encrypted custom file persistence is required. If opening an outbox fails, FasqRecoveryException.recovery exposes a safe typed recovery code and message after partial bootstrap resources are closed.

Manual QueryClient, DefaultSecurityPlugin, and durable queue composition remain available for advanced integrations.

FasqProvider lives in core fasq. This package supplies one secure FasqRuntime implementation; applications and other packages can provide their own implementation without depending on fasq_security.

Read the durable offline sync release notes for migration guidance and the compatibility boundary for legacy queue APIs.

Durable mutations without queue boilerplate

Only mutations that must survive an offline restart need a durable handle. Online-only work keeps the normal core API:

MutationBuilder(
  mutationFn: api.updateProfile,
  builder: (context, state, mutate) => ProfileForm(onSave: mutate),
)

Define a durable mutation manually when code generation is not useful:

final addTodo = DurableMutation<Todo, AddTodo>.define(
  key: const FasqMutationKey<Todo, AddTodo>(
    namespace: 'todos',
    name: 'add',
  ),
  codec: JsonMutationCodec<AddTodo>(
    encoder: (value) => value.toJson(),
    decoder: (payload) => AddTodo.fromJson(
      Map<String, Object?>.from(payload as Map),
    ),
  ),
  execute: api.addTodo,
);

Register the same handle during bootstrap and reference its typed key in the core MutationBuilder(mutationKey: ...) API. FasqProvider resolves the executor and durable queue from the bootstrapped runtime. With fasq_serializer_generator, annotate a one-argument Future<T> function with @FasqMutation; the generated ...Durable handle removes the key, codec, and registration code. The annotated function remains the only executor used online and during replay. Durable handles use write-ahead execution: online work is persisted first and replayed immediately; offline work remains queued.

For advanced queue composition, DurableMutationDefinition remains available from fasq (and is re-exported by fasq_security). Most applications should use DurableMutation instead.

🔐 Secure Queries

Mark specific queries as secure. Their data will be encrypted in memory/disk and cleared when the app goes to the background (configurable).

QueryBuilder<String>(
  queryKey: 'auth-token',
  queryFn: () => api.login(),
  options: QueryOptions(
    isSecure: true,                // Enable security
    maxAge: Duration(minutes: 15), // Enforce expiry
  ),
  builder: (context, state) {
    // state.data is available only when authenticated
    return Text('Token: ${state.data}');
  },
)

📄 License

MIT

Libraries

fasq_security