Dartvel, home
Docs
Features
Studio
Cloud
Compared

Search the site

GitHub
pub.dev

GETTING STARTED

Getting started
Existing Flutter apps
Existing Native apps
Run on your phone

APP

UI and styling
Routing
State
Accessibility
Keyboard shortcuts
Localization
Devices and desktop
Native device access
Media, 3D and XR

DATA

Data models
Forms
Search
Sync and offline
Import and export
Change capture
Database
Cache
File storage
Images
Privacy and erasure

BACKEND

Backend functions
Auth and sessions
Authorization
Queues and jobs
Workers and memory
Notifications and mail
Outbound HTTP
AI
Webhooks
GraphQL and OpenAPI
API keys and OAuth
Multi-tenancy
Billing and commerce
Modules

OPERATIONS

Edge security
Secrets and environments
Monitoring
Releases

SHIPPING

Build targets
Telegram Mini Apps
Static web hosting
Servers and deploying

REFERENCE

Testing
CLI reference
Coding agents

OPERATIONS

Secrets and environments

A backend secret that reaches client code stops the build, before it can ship in an app bundle.

A deploy stops when a secret its environment needs is missing.

ON THIS PAGE

Declare your secrets

Read a secret on the server

What the build refuses

Give a test its own secrets

Keep the application key in the OS keychain

Status

Declare your secrets

# pubspec.yaml
dartvel:
  secrets:
    PAYSTACK_SECRET: { required: [production, staging] }
    DATABASE_URL: { required: [production] }
    PUBLIC_MAPS_KEY: { scope: client, required: [production] }

Copy code to clipboard

A secret is backend-only unless it says scope: client, and a client secret must start with PUBLIC_. A name that breaks either rule stops the build.

required lists the environments it must be set in. dartvel deploy --environment production refuses to start without them.

Read a secret on the server

@DVBackendFunction(policy: 'Order.update')
Future<Map<String, Object?>> _refund(String reference) async {
  // Backend code has no DV facade, so it reads secrets through DVSecrets.
  final String key = const DVSecrets().get('PAYSTACK_SECRET'); // throws if unset
  final Response response = await const DVHttp().host('paystack').post(
    '/refund',
    json: <String, Object?>{'transaction': reference},
    headers: <String, String>{'authorization': 'Bearer $key'},
    idempotencyKey: 'refund-$reference',
  );
  return <String, Object?>{'status': response.status};
}

Copy code to clipboard

App code uses DV.Secrets. get throws when the name is unset, and maybeGet, getOr and has do not.

Values come from DVSecrets.configure, then the environment, then systemd credentials, then your .env files.

A value you have read is replaced with [redacted] in logs, crash reports and analytics events, once it is 8 characters long.

What the build refuses

DV-SECRETS-001

Means: Client code reads a backend secret

DV-SECRETS-002

Means: Code reads a name nobody declared

DV-SECRETS-003

Means: An env file has a PUBLIC_ variable nobody declared, which would be compiled into the app

Client code means lib/ outside your backend directory.

PUBLIC_ values are compiled into env.g.dart as Env.PUBLIC_NAME. Anyone with the app can read them, so keep real keys out.

Give a test its own secrets

test('a secret is supplied for one test and gone after it', () async {
  await DV.Test.withSecrets(<String, String>{'PAYSTACK_SECRET': 'sk_test_1'}, () {
    expect(DV.Secrets.get('PAYSTACK_SECRET'), 'sk_test_1');
  });
  expect(DV.Secrets.has('PAYSTACK_SECRET'), isFalse);
});

Copy code to clipboard

Keep the application key in the OS keychain

dartvel key generate
dartvel key status
dartvel key rotate

Copy code to clipboard

The key encrypts what your app stores on the device. generate refuses to replace a key that exists.

It is held by the Secret Service on Linux, the Keychain on macOS and iOS, DPAPI on Windows and the Keystore on Android.

rotate records the old and new fingerprints, so you can tell which key wrote what.

Status

Partial

Spec section: Secrets and Environments

Planned work and implementation limits

No Vault or cloud KMS adapter. A secrets manager feeds values in through DVSecrets.configure.

The build checks names written as literals only.

The Android Keystore and iOS Keychain paths have not been checked on real devices.

PREVIOUS Edge security Sign-in limits, WAF rules and query budgets
NEXT Monitoring Metrics, traces, crash reports, alerts and analytics
GitHub
pub.dev
npm
Acknowledgements
Privacy
Terms

FSL-1.1-MIT licensed. Built with Dartvel.

Dartvel is made by

SigmaDev Digital

To the bottom