Skip to content

Dart / Flutter SDK

Auto-generated Dart client for Bella Baxter. Works in Flutter apps, Dart CLI tools, and Shelf servers.

Installation

yaml
# pubspec.yaml
dependencies:
  bella_baxter: ^1.0.0
sh
dart pub get

Quick Start

dart
import 'package:bella_baxter/bella_baxter.dart';

// Recommended: read config from environment (injected by `bella exec`)
final client = BellaClient.fromEnv();

final secrets = await client.pullSecrets(
  projectSlug: 'my-project',
  environmentSlug: 'production',
);
print(secrets['DATABASE_URL']);

Or construct explicitly:

dart
final client = BellaClient(BellaClientOptions(
  baseUrl: Platform.environment['BELLA_BAXTER_URL']!,
  apiKey: Platform.environment['BELLA_BAXTER_API_KEY']!,
));

final secrets = await client.pullSecrets(
  projectSlug: 'my-project',
  environmentSlug: 'production',
);

Flutter Integration

dart
// lib/main.dart
void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final client = BellaClient(BellaClientOptions(
    baseUrl: const String.fromEnvironment('BELLA_BAXTER_URL'),
    apiKey: const String.fromEnvironment('BELLA_BAXTER_API_KEY'),
  ));

  final secrets = await client.pullSecrets(
    projectSlug: 'my-project',
    environmentSlug: 'production',
  );

  runApp(MyApp(secrets: secrets));
}

Full Flutter sample

Dart Shelf Server

dart
// Inject secrets before starting the server
final client = BellaClient.fromEnv();
final secrets = await client.pullSecrets(
  projectSlug: 'my-project',
  environmentSlug: 'production',
);

final handler = Pipeline()
    .addMiddleware(secretsMiddleware(secrets))
    .addHandler(_router);

await io.serve(handler, 'localhost', 8080);

Full Shelf sample

Zero-Knowledge Encryption (ZKE)

By default the SDK generates a fresh P-256 keypair per request (ephemeral E2EE). With ZKE you supply a persistent device key — the server audits which host fetched each secret and the SDK caches the wrapped DEK.

Generate your device key once:

sh
bella auth setup   # stores in OS keychain; copy the PKCS#8 PEM

Load the key bytes and pass them in:

dart
import 'dart:convert';
import 'dart:typed_data';
import 'package:bella_baxter/bella_baxter.dart';

// Decode PKCS#8 PEM → raw DER bytes
Uint8List _pemToBytes(String pem) {
  final b64 = pem
      .replaceAll('-----BEGIN PRIVATE KEY-----', '')
      .replaceAll('-----END PRIVATE KEY-----', '')
      .replaceAll(RegExp(r'\s'), '');
  return Uint8List.fromList(base64.decode(b64));
}

final keyPem = Platform.environment['BELLA_BAXTER_PRIVATE_KEY'];

final client = BellaClient(BellaClientOptions(
  baseUrl: Platform.environment['BELLA_BAXTER_URL']!,
  apiKey: Platform.environment['BELLA_BAXTER_API_KEY']!,
  privateKey: keyPem != null ? _pemToBytes(keyPem) : null,
  onWrappedDekReceived: (project, env, wrappedDek, leaseExpires) {
    debugPrint('DEK for $project/$env expires $leaseExpires');
  },
));

If privateKey is null the SDK falls back to ephemeral E2EE — fully backward-compatible.

BellaClientOptions Reference

OptionDefaultDescription
baseUrlhttps://api.bella-baxter.ioBella API base URL
apiKeybax-... consumer key (mutually exclusive with accessToken)
accessTokenShort-lived JWT (injected by bella exec in SSO mode)
appClientnullSent as X-App-Client header; falls back to BELLA_BAXTER_APP_CLIENT env var
connectTimeout10sHTTP connection timeout
receiveTimeout30sHTTP receive timeout
cachenullOptional SecretCache for offline-first fallback
privateKeynullPKCS#8 DER bytes for ZKE; null = ephemeral E2EE

Typed Secrets

sh
bella secrets generate dart

Generates a typed AppSecrets class with final fields.

All Samples

SamplePatternLink
01-dart-dotenvbella pull → dotenv DartGitHub
02-process-injectbella exec -- dart run main.dartGitHub
03-dart-cliSDK in Dart CLI toolGitHub
04-dart-shelfSDK in Shelf serverGitHub
05-flutter-appSDK in Flutter appGitHub

Released under the ELv2 License.