@@ -36,8 +36,73 @@ use crate::paths::{foc_devnet_config, foc_devnet_run_dir};
3636use crate :: run_id:: { create_latest_symlink, save_current_run_id} ;
3737use crate :: version_info:: write_version_file;
3838pub use eth_acc_funding:: constants:: FEVM_ACCOUNTS_PREFUNDED ;
39+ use std:: net:: ToSocketAddrs ;
3940use std:: path:: { Path , PathBuf } ;
40- use tracing:: { info, warn} ;
41+ use tracing:: { error, info, warn} ;
42+
43+ /// Check that host.docker.internal resolves to 127.0.0.1.
44+ ///
45+ /// This is required for SP-to-SP fetch to work. The hostname must resolve to localhost
46+ /// so that URLs registered in the SP registry work from both the host and inside containers.
47+ ///
48+ /// On macOS with Docker Desktop, this works automatically.
49+ /// On Linux, users must add `127.0.0.1 host.docker.internal` to /etc/hosts.
50+ fn check_host_docker_internal ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
51+ info ! ( "Checking host.docker.internal resolution..." ) ;
52+
53+ // Try to resolve host.docker.internal:80 (port doesn't matter, just need DNS resolution)
54+ match "host.docker.internal:80" . to_socket_addrs ( ) {
55+ Ok ( mut addrs) => {
56+ // Check if any resolved address is 127.0.0.1
57+ let is_localhost = addrs. any ( |addr| {
58+ addr. ip ( ) . is_loopback ( )
59+ } ) ;
60+
61+ if is_localhost {
62+ info ! ( "✓ host.docker.internal resolves to localhost" ) ;
63+ Ok ( ( ) )
64+ } else {
65+ error ! ( "════════════════════════════════════════════════════════════════════" ) ;
66+ error ! ( "ERROR: host.docker.internal does not resolve to localhost (127.0.0.1)" ) ;
67+ error ! ( "════════════════════════════════════════════════════════════════════" ) ;
68+ error ! ( "" ) ;
69+ error ! ( "SP-to-SP fetch requires host.docker.internal to resolve to 127.0.0.1" ) ;
70+ error ! ( "so that registered SP URLs work from both host and containers." ) ;
71+ error ! ( "" ) ;
72+ error ! ( "To fix this, add the following line to /etc/hosts:" ) ;
73+ error ! ( "" ) ;
74+ error ! ( " 127.0.0.1 host.docker.internal" ) ;
75+ error ! ( "" ) ;
76+ error ! ( "You can do this with:" ) ;
77+ error ! ( " echo '127.0.0.1 host.docker.internal' | sudo tee -a /etc/hosts" ) ;
78+ error ! ( "" ) ;
79+ error ! ( "════════════════════════════════════════════════════════════════════" ) ;
80+ Err ( "host.docker.internal must resolve to 127.0.0.1" . into ( ) )
81+ }
82+ }
83+ Err ( _) => {
84+ error ! ( "════════════════════════════════════════════════════════════════════" ) ;
85+ error ! ( "ERROR: host.docker.internal does not resolve" ) ;
86+ error ! ( "════════════════════════════════════════════════════════════════════" ) ;
87+ error ! ( "" ) ;
88+ error ! ( "SP-to-SP fetch requires host.docker.internal to resolve to 127.0.0.1" ) ;
89+ error ! ( "so that registered SP URLs work from both host and containers." ) ;
90+ error ! ( "" ) ;
91+ error ! ( "Add the following line to /etc/hosts:" ) ;
92+ error ! ( "" ) ;
93+ error ! ( " 127.0.0.1 host.docker.internal" ) ;
94+ error ! ( "" ) ;
95+ error ! ( "You can do this with:" ) ;
96+ error ! ( " echo '127.0.0.1 host.docker.internal' | sudo tee -a /etc/hosts" ) ;
97+ error ! ( "" ) ;
98+ error ! ( "For GitHub Actions, add this step before running foc-devnet:" ) ;
99+ error ! ( " - run: echo '127.0.0.1 host.docker.internal' | sudo tee -a /etc/hosts" ) ;
100+ error ! ( "" ) ;
101+ error ! ( "════════════════════════════════════════════════════════════════════" ) ;
102+ Err ( "host.docker.internal must be resolvable" . into ( ) )
103+ }
104+ }
105+ }
41106
42107/// Stop any existing cluster before starting a new one.
43108fn stop_existing_cluster ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
@@ -411,6 +476,9 @@ pub fn start_cluster(
411476 run_id : String ,
412477 notest : bool ,
413478) -> Result < ( ) , Box < dyn std:: error:: Error > > {
479+ // Check host.docker.internal resolution first (required for SP-to-SP fetch)
480+ check_host_docker_internal ( ) ?;
481+
414482 stop_existing_cluster ( ) ?;
415483
416484 let ( volumes_dir, run_dir, run_id) =
0 commit comments