Documentation
adrs/038-permission-discovery-service-references.md
ADR 038: Permission Discovery Requires Service References in AppHost
Status
Accepted
Context
The Identity component needs to discover permissions from all other components by calling their /.well-known/permissions endpoints. This discovery happens via the PermissionSyncService, which is triggered manually via POST /permissions/sync or can be configured to run automatically on startup.
The Problem
When attempting to call other component endpoints from Identity, we encountered DNS resolution failures:
System.Net.Http.HttpRequestException: nodename nor servname provided, or not known (catalog:443)
Despite Aspire being specifically designed for service discovery, Identity couldn't resolve component service names like catalog, spatial, etc.
Investigation
The issue was that Aspire's service discovery works through environment variables that are injected when one component explicitly references another. The environment variables follow the format:
services__{sourceComponentName}__{endpointName}__{index}=http://localhost:PORT
These environment variables are only injected when you call WithReference() on the component builder in the AppHost.
Key Insight: In Dynaplex, components are registered in the ComponentRegistry, and by default they only reference their dependencies (via DependsOn()). The Identity component has minimal dependencies (core-data, prism, events), but it needs to discover ALL components for permission synchronization - including components it doesn't depend on (catalog, spatial, transport, workflow, iot, bbu, etc.).
Decision
Every AppHost must explicitly configure Identity to reference all components that expose permissions, using WithReference() without WaitFor().
Pattern
// After all components are registered
var identity = registry.Get("identity");
// Add references to all components (for service discovery)
// NOTE: Use WithReference() WITHOUT WaitFor() to avoid circular dependencies
if (registry.TryGet("catalog", out var catalog))
{
identity.Component.WithReference(catalog.Component);
}
if (registry.TryGet("spatial", out var spatial))
{
identity.Component.WithReference(spatial.Component);
}
// ... repeat for all components ...
Why WithReference() Without WaitFor()
WithReference(): Injects service discovery environment variables (enables DNS resolution)WaitFor(): Creates startup ordering dependency (wait for component to be ready)
We use WithReference() alone because:
- Components already have correct startup order via
DependsOn()in their registrations - Adding
WaitFor()creates circular dependencies (e.g., catalog depends on identity, but identity waiting for catalog = deadlock) - We only need the environment variables for service discovery, not startup coordination
Why Not Use DependsOn()?
We cannot add all components as dependencies of Identity because:
- It would create massive circular dependencies (most components depend on Identity for auth)
- Identity doesn't actually depend on these components - it just needs to discover them
- The permission discovery is optional and best-effort (components can fail to respond)
Consequences
Positive
- ✅ Service discovery works correctly - Identity can resolve all component service names
- ✅ Permission synchronization succeeds for all running components
- ✅ No circular dependencies or startup deadlocks
- ✅ Components can be added/removed from AppHost without breaking Identity startup
Negative
- ⚠️ Each AppHost must remember to add these WithReference() calls
- ⚠️ Adding a new component requires updating the permission discovery block
- ⚠️ The pattern is not obvious - developers might forget this step
Mitigation
- Document pattern clearly in this ADR and reference documentation
- Add comments in AppHost code explaining why this is needed
- Create a checklist for adding new components
- Consider future enhancement: automatic discovery of all registered components
Implementation Notes
Required for Every AppHost
Any project that uses the Identity component and needs permission discovery must include this pattern. Examples:
Acsis.Dynaplex.Projects.BbuRfidAcsis.Dynaplex.Projects.AssetTrakClassic
Components to Reference
Reference ALL components that might expose permissions:
- core-data
- system-environment
- spatial
- catalog
- transport
- workflow
- events
- iot
- bbu
- Any new components added to the system
Error Symptoms
If this pattern is not followed, you'll see:
- DNS resolution errors: "nodename nor servname provided, or not known (component-name:443)"
- Empty permissions table in Identity database
- Permission-based UI elements missing (buttons, menu items)
- Permission sync endpoint returning incomplete results
References
- PermissionSyncService.cs - The service that performs discovery
- Microsoft .NET Aspire Service Discovery - Official docs
- Aspire WithReference API
Related ADRs
- ADR-036: Database Project Separation - Explains component structure
- ADR-007: Aspire Microservices Migration - Context for service discovery