Epic: #287 - Docker Compose Topology Domain Model Refactoring
Related Plan: docs/refactors/plans/docker-compose-topology-domain-model.md
Overview
Move port exposure logic from Tera templates to the domain layer, following the same pattern used for networks in Phase 1-2. This creates type-safe port definitions with descriptions that render as inline YAML comments for sysadmin self-documentation.
Background
This phase was originally part of the refactoring plan (see PORT-01 through PORT-11 rules in the plan) but was not included in the initial 5-PR strategy. The port exposure rules identified in the plan are currently scattered between Rust code and Tera templates, creating the same dual-source-of-truth problem that was solved for networks.
Problem Statement
Current State
Port exposure logic is split across multiple locations:
- Template conditionals - Complex
{%- if %} blocks for TLS-based port exposure
- Rust filtering -
http_tracker_ports_without_tls computed in context builder
- Hardcoded ports - Caddy (80, 443), Prometheus (9090), MySQL (none)
Problems
- Logic in templates - Port exposure decisions based on TLS configuration
- No cross-service validation - Cannot detect host port conflicts across services
- Missing documentation - Rendered YAML lacks context for what each port does
- Existing validation is tracker-only -
TrackerConfig validates internal socket conflicts but not Docker Compose host port conflicts
Desired State
services:
tracker:
ports:
# BitTorrent UDP announce (external)
- "6969:6969/udp"
# HTTP tracker announce (no TLS)
- "7070:7070"
# REST API for stats/whitelist (no TLS)
- "1212:1212"
Port Rules from Original Plan
These rules (PORT-01 through PORT-11) will be moved to domain:
| Rule ID |
Rule Description |
| PORT-01 |
Tracker needs ports section if UDP OR HTTP without TLS OR API without TLS |
| PORT-02 |
UDP ports always exposed (no TLS) |
| PORT-03 |
HTTP ports WITHOUT TLS exposed directly |
| PORT-04 |
HTTP ports WITH TLS NOT exposed (Caddy handles) |
| PORT-05 |
API exposed only when no TLS |
| PORT-06 |
API NOT exposed when TLS |
| PORT-07 |
Grafana 3000 exposed only without TLS |
| PORT-08 |
Grafana 3000 NOT exposed with TLS |
| PORT-09 |
Caddy always exposes 80, 443, 443/udp |
| PORT-10 |
Prometheus 9090 on localhost only |
| PORT-11 |
MySQL no exposed ports |
Tasks
P3.1: Create Port Domain Types
P3.2: Extend ServiceTopology with Ports
P3.3: Add Cross-Service Port Validation
P3.4: Update Template and Context
Acceptance Criteria
References
Epic: #287 - Docker Compose Topology Domain Model Refactoring
Related Plan: docs/refactors/plans/docker-compose-topology-domain-model.md
Overview
Move port exposure logic from Tera templates to the domain layer, following the same pattern used for networks in Phase 1-2. This creates type-safe port definitions with descriptions that render as inline YAML comments for sysadmin self-documentation.
Background
This phase was originally part of the refactoring plan (see PORT-01 through PORT-11 rules in the plan) but was not included in the initial 5-PR strategy. The port exposure rules identified in the plan are currently scattered between Rust code and Tera templates, creating the same dual-source-of-truth problem that was solved for networks.
Problem Statement
Current State
Port exposure logic is split across multiple locations:
{%- if %}blocks for TLS-based port exposurehttp_tracker_ports_without_tlscomputed in context builderProblems
TrackerConfigvalidates internal socket conflicts but not Docker Compose host port conflictsDesired State
Port Rules from Original Plan
These rules (PORT-01 through PORT-11) will be moved to domain:
Tasks
P3.1: Create Port Domain Types
Protocolenum (or reuse fromdomain/tracker)PortBindingstruct with description supportPortDefinitioncontext type for template renderingP3.2: Extend ServiceTopology with Ports
ports: Vec<PortBinding>toServiceTopologyP3.3: Add Cross-Service Port Validation
DockerComposeTopology::validate_port_uniqueness()TopologyError::PortConflictvariantP3.4: Update Template and Context
PortDefinitionwithbinding()anddescription()for templateDockerComposeContextto use derived portsdocker-compose.yml.teraport sectionsAcceptance Criteria
TrackerConfigvalidation (different scopes)References