Official IntelliToggle provider for the OpenFeature Dart Server SDK. Enables secure feature flag evaluation with OAuth2 authentication, multi-tenant isolation, and real-time updates.
Note: This provider targets server-side Dart applications. Flutter support is available but secondary to server-side functionality.
- OpenFeature compliance - Boolean, String, Integer, Double, and Object flag evaluation
- OAuth2 authentication - Client credentials flow with tenant isolation
- Advanced targeting - Multi-context evaluation with rules engine
- Real-time updates - Streaming configuration changes and webhooks
- Local development - InMemoryProvider for testing without external dependencies
- OREP server - Remote evaluation protocol for distributed systems
Compatible with Dart 3.9.2 and above.
dependencies:
openfeature_dart_server_sdk: ^0.0.11
openfeature_provider_intellitoggle: ^0.0.5dart pub getIntelliToggle uses OAuth2 Client Credentials for secure API access.
curl -X POST https://api.intellitoggle.com/api/oauth2/clients \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TENANT_TOKEN" \
-H "X-Tenant-ID: YOUR_TENANT_ID" \
-d '{
"name": "My Server App",
"scopes": ["flags:read", "flags:evaluate"]
}'Response:
{
"clientId": "client_1234567890",
"clientSecret": "cs_1234567890_secret"
}INTELLITOGGLE_CLIENT_ID=client_1234567890
INTELLITOGGLE_CLIENT_SECRET=cs_1234567890_secret
INTELLITOGGLE_TENANT_ID=your_tenant_id
INTELLITOGGLE_API_URL=https://api.intellitoggle.comimport 'package:openfeature_dart_server_sdk/openfeature_dart_server_sdk.dart';
import 'package:openfeature_provider_intellitoggle/openfeature_provider_intellitoggle.dart';
void main() async {
// Initialize provider with OAuth2 client secret
final provider = IntelliToggleProvider(
sdkKey: 'YOUR_CLIENT_SECRET',
options: IntelliToggleOptions.production(),
);
// Set as global provider
await OpenFeatureAPI().setProvider(provider);
// Create client
final client = IntelliToggleClient(
FeatureClient(
metadata: ClientMetadata(name: 'my-service'),
hookManager: HookManager(),
),
);
// Evaluate flags
final enabled = await client.getBooleanValue(
'new-api-endpoint',
false,
targetingKey: 'user-123',
evaluationContext: {
'role': 'admin',
'plan': 'enterprise',
'region': 'us-east',
},
);
print('Feature enabled: $enabled');
}final multiContext = client.createMultiContext({
'user': {
'targetingKey': 'user-123',
'role': 'admin',
'plan': 'enterprise',
},
'organization': {
'targetingKey': 'org-456',
'tier': 'premium',
'industry': 'fintech',
},
});
final config = await client.getObjectValue(
'feature-config',
{},
evaluationContext: multiContext.attributes,
);final deviceContext = client.createContext(
targetingKey: 'device-789',
kind: 'device',
customAttributes: {
'os': 'linux',
'version': '5.4.0',
'datacenter': 'us-west-2',
},
);
final threshold = await client.getIntegerValue(
'rate-limit',
1000,
evaluationContext: deviceContext.attributes,
);provider.events.listen((event) {
switch (event.type) {
case IntelliToggleEventType.ready:
print('Provider ready');
break;
case IntelliToggleEventType.configurationChanged:
print('Flags updated: ${event.data?['flagsChanged']}');
break;
case IntelliToggleEventType.error:
print('Error: ${event.message}');
break;
}
});// Set context for all evaluations
OpenFeatureAPI().setGlobalContext(
OpenFeatureEvaluationContext({
'environment': 'production',
'service': 'api-gateway',
'version': '2.1.0',
})
);final options = IntelliToggleOptions.production(
baseUri: Uri.parse('https://api.intellitoggle.com'),
timeout: Duration(seconds: 10),
pollingInterval: Duration(minutes: 5),
);final options = IntelliToggleOptions.development(
baseUri: Uri.parse('http://localhost:8080'),
timeout: Duration(seconds: 5),
);final options = IntelliToggleOptions(
timeout: Duration(seconds: 15),
enablePolling: true,
pollingInterval: Duration(minutes: 2),
enableStreaming: true,
maxRetries: 5,
enableLogging: true,
);final provider = InMemoryProvider();
// Set test flags
provider.setFlag('feature-enabled', true);
provider.setFlag('api-version', 'v2');
provider.setFlag('rate-limits', {
'requests_per_minute': 1000,
'burst_size': 50,
});
await OpenFeatureAPI().setProvider(provider);
// Evaluate flags
final client = IntelliToggleClient(FeatureClient(
metadata: ClientMetadata(name: 'test'),
hookManager: HookManager(),
));
final enabled = await client.getBooleanValue('feature-enabled', false);
print('Feature enabled: $enabled');
// Update flags at runtime
provider.setFlag('feature-enabled', false);Start remote evaluation server:
dart run bin/orep_server.dartEnvironment variables:
OREP_HOST=0.0.0.0
OREP_PORT=8080
OREP_AUTH_TOKEN=secure-token-123Test evaluation:
curl -X POST http://localhost:8080/v1/flags/my-flag/evaluate \
-H "Authorization: Bearer secure-token-123" \
-H "Content-Type: application/json" \
-d '{
"defaultValue": false,
"type": "boolean",
"context": {
"targetingKey": "user-123",
"role": "admin"
}
}'Future<String> getAccessToken() async {
final response = await http.post(
Uri.parse('https://api.intellitoggle.com/oauth/token'),
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'grant_type=client_credentials'
'&client_id=$clientId'
'&client_secret=$clientSecret'
'&scope=flags:read flags:evaluate',
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
return data['access_token'];
}
throw Exception('OAuth2 failed: ${response.body}');
}Future<Map<String, dynamic>> evaluateFlag({
required String flagKey,
required String projectId,
required Map<String, dynamic> context,
}) async {
final token = await getAccessToken();
final response = await http.post(
Uri.parse('$baseUrl/api/flags/projects/$projectId/flags/$flagKey/evaluate'),
headers: {
'Authorization': 'Bearer $token',
'X-Tenant-ID': tenantId,
'Content-Type': 'application/json',
},
body: jsonEncode(context),
);
if (response.statusCode == 200) {
return jsonDecode(response.body);
}
throw Exception('Evaluation failed: ${response.body}');
}try {
final result = await client.getBooleanValue('my-flag', false);
} on FlagNotFoundException {
// Flag doesn't exist
} on AuthenticationException {
// Invalid credentials
} on ApiException catch (e) {
// API error with status code
print('API error: ${e.code}');
} catch (e) {
// General error
}final options = IntelliToggleOptions(
maxRetries: 3,
retryDelay: Duration(seconds: 1),
timeout: Duration(seconds: 10),
);final hook = ConsoleLoggingHook();
// Add globally
OpenFeatureAPI().addHooks([hook]);
// Add to specific client
final hookManager = HookManager();
hookManager.addHook(hook);class AnalyticsHook extends Hook {
@override
Future<void> after(HookContext context) async {
// Track flag usage
analytics.track('flag_evaluated', {
'flag_key': context.flagKey,
'result': context.result,
});
}
}For Flutter applications, IntelliToggle provides UI state management and direct API integration:
// Add to pubspec.yaml
dependencies:
flutter_dotenv: ^5.2.1
provider: ^6.1.5
// Basic Flutter service
class IntelliToggleService extends ChangeNotifier {
Future<String> getAccessToken() async {
// OAuth2 implementation
}
Future<Map<String, dynamic>> evaluateFlag({
required String projectId,
required String flagKey,
required Map<String, dynamic> context,
}) async {
// Direct API calls
}
}Flutter apps use direct HTTP API calls rather than the OpenFeature provider pattern. See the sample Flutter app for complete implementation.
| Option | Description | Default |
|---|---|---|
baseUri |
API endpoint | https://api.intellitoggle.com |
timeout |
Request timeout | 10 seconds |
enablePolling |
Poll for config changes | true |
pollingInterval |
Polling frequency | 5 minutes |
enableStreaming |
Real-time updates | false |
maxRetries |
Retry attempts | 3 |
enableLogging |
Debug logging | false |
// Production optimized
IntelliToggleOptions.production()
// Development optimized
IntelliToggleOptions.development(){
'targetingKey': 'user-123',
'kind': 'user',
'role': 'admin',
'plan': 'enterprise'
}{
'kind': 'multi',
'user': {
'targetingKey': 'user-123',
'role': 'admin'
},
'organization': {
'targetingKey': 'org-456',
'plan': 'enterprise'
}
}targetingKey- Required identifier for targetingkind- Context type (user,organization,device,multi)anonymous- Anonymous context flagprivateAttributes- Attributes to exclude from logs
POST /v1/flags/{flagKey}/evaluate- Evaluate flagGET /v1/provider/metadata- Provider info
POST /v1/provider/seed- Seed test flagsPOST /v1/provider/reset- Clear all flagsPOST /v1/provider/shutdown- Shutdown provider
All endpoints require Bearer token:
curl -H "Authorization: Bearer changeme-token" ...Production deployments require HTTPS endpoints. HTTP only allowed for localhost.
- Client secrets are never logged or exposed
- Tokens have configurable TTL (default: 60 minutes)
- Automatic token refresh with 10-minute buffer
All requests include X-Tenant-ID header for multi-tenancy:
headers: {
'Authorization': 'Bearer $token',
'X-Tenant-ID': '$tenantId',
}We welcome contributions! See CONTRIBUTING.md for guidelines.
- Website: https://intellitoggle.com
- Docs & CLI: https://sdks.aortem.io/intellitoggle
- API Reference: https://api.intellitoggle.com/docs
- GitHub: https://github.com/aortem/intellitoggle-openfeature-provider-dart-server