emitSignal method
void
emitSignal(
- String signalName, {
- dynamic data,
- String? sourceModuleId,
- ModuleIdentityToken? identityToken,
Emit a named signal with identity verification and rate limiting.
Implementation
void emitSignal(
String signalName, {
dynamic data,
String? sourceModuleId,
ModuleIdentityToken? identityToken,
}) {
final effectiveSourceId =
sourceModuleId ?? identityToken?.moduleId ?? 'unknown';
// Verify identity if token is provided
if (identityToken != null && sourceModuleId != null) {
if (!identityToken.verify(sourceModuleId)) {
AirLogger.security(
'Identity verification failed for signal emission',
context: {'expected': sourceModuleId, 'got': identityToken.moduleId},
);
AirAudit().log(
type: AuditType.securityViolation,
action: 'identity_spoofing_attempt',
moduleId: identityToken.moduleId,
context: {'attemptedId': sourceModuleId, 'signal': signalName},
severity: AuditSeverity.critical,
success: false,
);
return;
}
}
// Check rate limit
final rateLimitKey = '$effectiveSourceId:signal:$signalName';
if (!_checkRateLimit(rateLimitKey)) {
AirAudit().log(
type: AuditType.securityViolation,
action: 'signal_rate_limit_exceeded',
moduleId: effectiveSourceId,
context: {'signal': signalName},
severity: AuditSeverity.medium,
success: false,
);
return;
}
// Check permission to emit
if (!PermissionChecker().checkPermission(
effectiveSourceId,
Permission.eventEmit,
resource: signalName,
)) {
return;
}
AirLogger.debug(
'Emitting signal "$signalName"',
context: {'source': effectiveSourceId},
);
// Verify module identity and log the interaction for auditing.
AirAudit().log(
type: AuditType.moduleInteraction,
action: 'signal_emitted',
moduleId: effectiveSourceId,
context: {'signal': signalName, 'hasData': data != null},
);
// Add to history
_signalHistory.add(
SignalHistoryEntry(
name: signalName,
data: data,
sourceModuleId: sourceModuleId,
),
);
if (_signalHistory.length > _maxHistorySize) {
_signalHistory.removeAt(0);
}
final subs = _signalSubscriptions[signalName];
if (subs == null || subs.isEmpty) {
AirLogger.debug('No subscribers for signal "$signalName"');
return;
}
// Create copy of active subscribers to iterate safely, then clean up cancelled/expired
final activeSubs = subs
.where((s) => !s.isCancelled && !s.isExpired)
.toList();
subs.removeWhere((s) => s.isCancelled || s.isExpired);
for (final subscription in activeSubs) {
if (subscription.subscriberModuleId != null) {
// Record interaction. If source is unknown, we use the target itself to create a "bloom" effect
SecureServiceRegistry().recordInteraction(
ModuleInteraction(
sourceId: sourceModuleId ?? subscription.subscriberModuleId!,
targetId: subscription.subscriberModuleId!,
type: InteractionType.event,
detail: signalName,
),
);
}
try {
(subscription.callback as void Function(dynamic))(data);
} catch (e) {
AirLogger.error(
'Error in signal subscriber ${subscription.id}',
error: e,
);
}
}
notifyListeners();
}