diff --git a/crates/cli/src/program.rs b/crates/cli/src/program.rs index 6cc9bfe00..478687bf7 100644 --- a/crates/cli/src/program.rs +++ b/crates/cli/src/program.rs @@ -16,6 +16,12 @@ pub enum ProgramCommands { /// backend at all. Your program will simply execute without being verified. #[arg(long)] dev: Option, + + /// On Linux, run the support Docker container with `--network=host` instead of rewriting + /// callback URLs to `host.local`. Avoids opening Docker's bridge network in the firewall. + /// Has no effect in `--dev` mode (no Docker). Not useful on macOS Docker Desktop. + #[arg(long, default_value_t = false)] + linux_network_host_mode: bool, }, /// Compile the program code @@ -46,8 +52,16 @@ pub enum ProgramCacheCommands { pub async fn execute(command: ProgramCommands, config: &AppConfig) -> Result<()> { match command { - ProgramCommands::Start { dev } => { - e3_support_scripts::program_start(config.program().clone(), dev).await? + ProgramCommands::Start { + dev, + linux_network_host_mode, + } => { + e3_support_scripts::program_start( + config.program().clone(), + dev, + linux_network_host_mode, + ) + .await? } ProgramCommands::Compile { dev } => { e3_support_scripts::program_compile(config.program().clone(), dev).await? diff --git a/crates/support-scripts/ctl/container b/crates/support-scripts/ctl/container index 60ec5c7e9..296808fee 100755 --- a/crates/support-scripts/ctl/container +++ b/crates/support-scripts/ctl/container @@ -40,22 +40,44 @@ else TTY_FLAGS="" fi +# Peel networking mode before forwarding remaining args into the container. +LINUX_NETWORK_HOST_MODE=0 +INNER_ARGS=() +for arg in "$@"; do + if [[ "$arg" == "--linux-network-host-mode" ]]; then + LINUX_NETWORK_HOST_MODE=1 + else + INNER_ARGS+=("$arg") + fi +done +set -- "${INNER_ARGS[@]}" + if docker ps -q -f name="$CONTAINER_NAME" | grep -q .; then echo "Running exec $IMAGE..." docker exec $TTY_FLAGS "$CONTAINER_NAME" bash -c "$*" else echo "Running start $IMAGE..." - # --network=host does not work on macos for allowing the container to access - # the local machine. `--add-host...` is adding host.local to the hosts file - # in the docker container we can then replace localhost and 127.0.0.1 - # from the input callback url so calls redirect to gateway. - # This should in theory be crossplatform - # However on linux the user must allow incoming connections from Docker's bridge network 172.17.0.0/16 through their firewall. + # Default (cross-platform): `--add-host=host.local:host-gateway` plus rewriting + # localhost/127.0.0.1 in callback URLs so the container can reach the host. + # On Linux that requires allowing Docker's bridge network (172.17.0.0/16) through + # the firewall. `--linux-network-host-mode` uses Docker `--network=host` instead + # so localhost callbacks work without rewrite or firewall changes. + # Note: `--network=host` is not useful on macOS Docker Desktop. + DOCKER_NET_ARGS=(--add-host=host.local:host-gateway -p 13151:13151) + DOCKER_ENV_ARGS=() + if [[ "$LINUX_NETWORK_HOST_MODE" == "1" ]]; then + echo "Using Docker --network=host (linux network host mode)" + # Keep host.local→127.0.0.1 so older support images that still rewrite + # localhost callbacks keep working under host networking. + DOCKER_NET_ARGS=(--network=host --add-host=host.local:127.0.0.1) + DOCKER_ENV_ARGS=(-e INTERFOLD_SKIP_LOCALHOST_REWRITE=1) + fi + docker run $TTY_FLAGS --rm \ --name "$CONTAINER_NAME" \ --platform linux/amd64 \ - --add-host=host.local:host-gateway \ - -p 13151:13151 \ + "${DOCKER_NET_ARGS[@]}" \ + "${DOCKER_ENV_ARGS[@]}" \ -v "$(pwd)/.interfold/generated/contracts:/app/contracts:rw" \ -v "$(pwd)/tests:/app/tests" \ -v "$(pwd)/.interfold/caches/target:/app/target" \ diff --git a/crates/support-scripts/ctl/start b/crates/support-scripts/ctl/start index 08eec573b..dd3b361ac 100755 --- a/crates/support-scripts/ctl/start +++ b/crates/support-scripts/ctl/start @@ -1,6 +1,7 @@ #!/usr/bin/env bash unset RISC0_DEV_MODE RPC_URL PRIVATE_KEY PINATA_JWT PROGRAM_URL BOUNDLESS_ONCHAIN +LINUX_NETWORK_HOST_MODE=0 while [[ $# -gt 0 ]]; do case $1 in @@ -28,6 +29,10 @@ while [[ $# -gt 0 ]]; do BOUNDLESS_ONCHAIN="$2" shift 2 ;; + --linux-network-host-mode) + LINUX_NETWORK_HOST_MODE=1 + shift + ;; *) echo "Unknown argument: $1" exit 1 @@ -62,4 +67,8 @@ if [[ -n "$BOUNDLESS_ONCHAIN" ]]; then CONTAINER_ARGS+=("--boundless-onchain" "$BOUNDLESS_ONCHAIN") fi -exec "$SCRIPT_DIR/container" "${CONTAINER_ARGS[@]}" \ No newline at end of file +if [[ "$LINUX_NETWORK_HOST_MODE" == "1" ]]; then + CONTAINER_ARGS+=("--linux-network-host-mode") +fi + +exec "$SCRIPT_DIR/container" "${CONTAINER_ARGS[@]}" diff --git a/crates/support-scripts/src/lib.rs b/crates/support-scripts/src/lib.rs index 241e6c8a1..1dd0698ed 100644 --- a/crates/support-scripts/src/lib.rs +++ b/crates/support-scripts/src/lib.rs @@ -22,8 +22,14 @@ pub async fn program_compile(program_config: ProgramConfig, is_dev: Option ProgramSupport::new(program_config, is_dev).compile().await } -pub async fn program_start(program_config: ProgramConfig, is_dev: Option) -> Result<()> { - ProgramSupport::new(program_config, is_dev).start().await +pub async fn program_start( + program_config: ProgramConfig, + is_dev: Option, + linux_network_host_mode: bool, +) -> Result<()> { + ProgramSupport::new(program_config, is_dev) + .start(linux_network_host_mode) + .await } /// Upload the compiled program to Pinata IPFS diff --git a/crates/support-scripts/src/program.rs b/crates/support-scripts/src/program.rs index 52e486637..5b6246397 100644 --- a/crates/support-scripts/src/program.rs +++ b/crates/support-scripts/src/program.rs @@ -42,10 +42,10 @@ impl ProgramSupportApi for ProgramSupport { ProgramSupport::Risc0(s) => s.compile().await, } } - async fn start(&self) -> Result<()> { + async fn start(&self, linux_network_host_mode: bool) -> Result<()> { match self { - ProgramSupport::Dev(s) => s.start().await, - ProgramSupport::Risc0(s) => s.start().await, + ProgramSupport::Dev(s) => s.start(linux_network_host_mode).await, + ProgramSupport::Risc0(s) => s.start(linux_network_host_mode).await, } } diff --git a/crates/support-scripts/src/program_dev.rs b/crates/support-scripts/src/program_dev.rs index 7f2f22d35..5d22879ec 100644 --- a/crates/support-scripts/src/program_dev.rs +++ b/crates/support-scripts/src/program_dev.rs @@ -26,7 +26,12 @@ impl ProgramSupportApi for ProgramSupportDev { run_bash_script(&cwd, &script, &[]).await?; Ok(()) } - async fn start(&self) -> Result<()> { + async fn start(&self, linux_network_host_mode: bool) -> Result<()> { + if linux_network_host_mode { + eprintln!( + "warning: --linux-network-host-mode has no effect in --dev mode (Docker is not used)" + ); + } let cwd = env::current_dir()?; let script = cwd.join(".interfold/support/dev/start"); ensure_script_exists(&script).await?; diff --git a/crates/support-scripts/src/program_risc0.rs b/crates/support-scripts/src/program_risc0.rs index 611b7e344..ac98e4240 100644 --- a/crates/support-scripts/src/program_risc0.rs +++ b/crates/support-scripts/src/program_risc0.rs @@ -25,7 +25,7 @@ impl ProgramSupportApi for ProgramSupportRisc0 { } /// Run the docker container start script - async fn start(&self) -> Result<()> { + async fn start(&self, linux_network_host_mode: bool) -> Result<()> { let cwd = env::current_dir()?; let script = cwd.join(".interfold/support/ctl/start"); ensure_script_exists(&script).await?; @@ -39,6 +39,10 @@ impl ProgramSupportApi for ProgramSupportRisc0 { risc0_config.risc0_dev_mode.to_string(), ]; + if linux_network_host_mode { + args.push("--linux-network-host-mode".into()); + } + // Boundless support if let Some(boundless) = &risc0_config.boundless { args.extend_from_slice(&[ diff --git a/crates/support-scripts/src/traits.rs b/crates/support-scripts/src/traits.rs index 2145e6a4d..1235311d0 100644 --- a/crates/support-scripts/src/traits.rs +++ b/crates/support-scripts/src/traits.rs @@ -10,6 +10,6 @@ use async_trait::async_trait; #[async_trait] pub trait ProgramSupportApi { async fn compile(&self) -> Result<()>; - async fn start(&self) -> Result<()>; + async fn start(&self, linux_network_host_mode: bool) -> Result<()>; async fn upload(&self) -> Result<()>; } diff --git a/crates/support/README.md b/crates/support/README.md index 96b0eaa36..9a1430039 100644 --- a/crates/support/README.md +++ b/crates/support/README.md @@ -142,6 +142,15 @@ interfold program start This starts the Docker container running `e3-support-app` on port 13151. If Boundless config is present, it will submit proofs to the Boundless market. Otherwise it falls back to dev mode. +On Linux, if Docker bridge networking requires firewall rules for host callbacks, use: + +```bash +interfold program start --linux-network-host-mode +``` + +This runs the container with `--network=host` so `localhost` callback URLs work without +`host.local` rewriting. Not needed in `--dev` mode (no Docker) and not useful on macOS Docker Desktop. + ### Step 6: Submit an E3 Request The E3 request is submitted on-chain by the instigator (e.g., CRISP coordination server): diff --git a/crates/support/app/src/main.rs b/crates/support/app/src/main.rs index 74a068a7f..ddbb8cef0 100644 --- a/crates/support/app/src/main.rs +++ b/crates/support/app/src/main.rs @@ -132,9 +132,18 @@ async fn handle_compute(req: web::Json) -> ActixResult