Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions agent/CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Run from repo root via pnpm scripts — not raw cargo/nargo/hardhat.
| Integration tests | `pnpm test:integration [name]` (`--no-prebuild` to skip binary build) |
| Lint / format | `pnpm lint` · `pnpm format` / `pnpm format:check` |
| Build circuits | `pnpm build:circuits [--preset …] [--committee …]` (needs `nargo` + `bb`; `interfold noir setup` installs them) |
| Start the support app | `interfold program start` (`--linux-network-host-mode` uses Docker host networking and is Linux-only) |
| Generate Solidity verifiers | `pnpm generate:verifiers [--check\|--write]` |
| Circuit artifact cache | `pnpm store:circuits push\|pull` (orphan branch `circuit-artifacts`) |
| Consistency checks | `pnpm check:committee` · `check:docs` · `check:invariants` · `check:ciphernode bond` · `check:pnpm` · `check:size` |
Expand Down
18 changes: 16 additions & 2 deletions crates/cli/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ pub enum ProgramCommands {
/// backend at all. Your program will simply execute without being verified.
#[arg(long)]
dev: Option<bool>,

/// 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
Expand Down Expand Up @@ -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?
Expand Down
1 change: 1 addition & 0 deletions crates/support-scripts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ tokio.workspace = true
duct.workspace = true
async-trait.workspace = true
e3-config.workspace = true
tracing = { workspace = true }
114 changes: 90 additions & 24 deletions crates/support-scripts/ctl/container
Original file line number Diff line number Diff line change
Expand Up @@ -40,29 +40,95 @@ 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 [[ "$LINUX_NETWORK_HOST_MODE" == "1" && "$(uname -s)" != "Linux" ]]; then
echo "Error: --linux-network-host-mode needs Linux. Detected $(uname -s)." >&2
echo "Docker Desktop does not enable --network=host by default, and the container" >&2
echo "port would not be published. Start again without the flag." >&2
exit 1
fi

container_linux_network_host_mode() {
local network_mode skip_rewrite
network_mode=$(docker inspect -f '{{.HostConfig.NetworkMode}}' "$CONTAINER_NAME" 2>/dev/null) || return 1
skip_rewrite=$(docker inspect -f '{{range .Config.Env}}{{println .}}{{end}}' "$CONTAINER_NAME" 2>/dev/null \
| grep -c '^INTERFOLD_SKIP_LOCALHOST_REWRITE=1$' || true)
[[ "$network_mode" == "host" && "$skip_rewrite" -eq 1 ]]
}

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.
docker run $TTY_FLAGS --rm \
--name "$CONTAINER_NAME" \
--platform linux/amd64 \
--add-host=host.local:host-gateway \
-p 13151:13151 \
-v "$(pwd)/.interfold/generated/contracts:/app/contracts:rw" \
-v "$(pwd)/tests:/app/tests" \
-v "$(pwd)/.interfold/caches/target:/app/target" \
-v "$(pwd)/.interfold/caches/registry:/home/devuser/.cargo/registry" \
-v "$(pwd)/.interfold/caches/git:/home/devuser/.cargo/git" \
-v "$(pwd)/.interfold/caches/risc0-cache:/home/devuser/.risc0/cache" \
-v "$(pwd)/.interfold/caches/risc0-circuits:/home/devuser/.risc0/circuits" \
-v "${CACHE_PREFIX}-cargo-cache:/usr/local/cargo" \
"$IMAGE" bash -c "$*"
EXISTING_LINUX_NETWORK_HOST_MODE=0
if container_linux_network_host_mode; then
EXISTING_LINUX_NETWORK_HOST_MODE=1
fi

if [[ "$EXISTING_LINUX_NETWORK_HOST_MODE" != "$LINUX_NETWORK_HOST_MODE" ]]; then
echo "Existing container network mode does not match requested mode. Recreating container..." >&2
if ! docker stop "$CONTAINER_NAME" >/dev/null; then
echo "Error: failed to stop container $CONTAINER_NAME." >&2
exit 1
fi

container_id="$CONTAINER_NAME"
for ((attempt = 0; attempt < 50; attempt++)); do
if ! container_id=$(docker ps -aq --filter "name=^/${CONTAINER_NAME}$"); then
echo "Error: failed to check removal of container $CONTAINER_NAME." >&2
exit 1
fi
[[ -z "$container_id" ]] && break
sleep 0.1
done

if [[ -n "$container_id" ]]; then
echo "Error: container $CONTAINER_NAME was not removed within 5 seconds." >&2
exit 1
fi
else
echo "Running exec $IMAGE..."
docker exec $TTY_FLAGS "$CONTAINER_NAME" bash -c "$*"
exit $?
fi
fi

echo "Running start $IMAGE..."
# 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 \
"${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" \
-v "$(pwd)/.interfold/caches/registry:/home/devuser/.cargo/registry" \
-v "$(pwd)/.interfold/caches/git:/home/devuser/.cargo/git" \
-v "$(pwd)/.interfold/caches/risc0-cache:/home/devuser/.risc0/cache" \
-v "$(pwd)/.interfold/caches/risc0-circuits:/home/devuser/.risc0/circuits" \
-v "${CACHE_PREFIX}-cargo-cache:/usr/local/cargo" \
"$IMAGE" bash -c "$*"
11 changes: 10 additions & 1 deletion crates/support-scripts/ctl/start
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -62,4 +67,8 @@ if [[ -n "$BOUNDLESS_ONCHAIN" ]]; then
CONTAINER_ARGS+=("--boundless-onchain" "$BOUNDLESS_ONCHAIN")
fi

exec "$SCRIPT_DIR/container" "${CONTAINER_ARGS[@]}"
if [[ "$LINUX_NETWORK_HOST_MODE" == "1" ]]; then
CONTAINER_ARGS+=("--linux-network-host-mode")
fi

exec "$SCRIPT_DIR/container" "${CONTAINER_ARGS[@]}"
10 changes: 8 additions & 2 deletions crates/support-scripts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@ pub async fn program_compile(program_config: ProgramConfig, is_dev: Option<bool>
ProgramSupport::new(program_config, is_dev).compile().await
}

pub async fn program_start(program_config: ProgramConfig, is_dev: Option<bool>) -> Result<()> {
ProgramSupport::new(program_config, is_dev).start().await
pub async fn program_start(
program_config: ProgramConfig,
is_dev: Option<bool>,
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
Expand Down
6 changes: 3 additions & 3 deletions crates/support-scripts/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}

Expand Down
7 changes: 6 additions & 1 deletion crates/support-scripts/src/program_dev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
tracing::warn!(
"--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?;
Expand Down
6 changes: 5 additions & 1 deletion crates/support-scripts/src/program_risc0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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?;
Expand All @@ -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(&[
Expand Down
2 changes: 1 addition & 1 deletion crates/support-scripts/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<()>;
}
9 changes: 9 additions & 0 deletions crates/support/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,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):
Expand Down
25 changes: 17 additions & 8 deletions crates/support/app/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ async fn call_webhook(callback_url: &str, payload: &WebhookPayload) -> anyhow::R

println!("Sending webhook to: {}", callback_url);

let response = reqwest::Client::new()
.post(callback_url)
.json(payload)
.send()
.await?;
let client = reqwest::Client::builder()
.redirect(reqwest::redirect::Policy::none())
.build()
.map_err(|e| anyhow::anyhow!("failed to build webhook client: {e}"))?;
let response = client.post(callback_url).json(payload).send().await?;

println!("Webhook response status: {}", response.status());
if !response.status().is_success() {
Expand Down Expand Up @@ -161,9 +161,18 @@ async fn handle_compute(req: web::Json<ComputeRequest>) -> ActixResult<HttpRespo
.map_err(actix_web::error::ErrorBadRequest)?;

println!("fhe_inputs.params = {:?}", fhe_inputs.params);
let callback_url = callback_url
.replace("localhost", "host.local")
.replace("127.0.0.1", "host.local");
let callback_url = if std::env::var("INTERFOLD_SKIP_LOCALHOST_REWRITE")
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false)
{
// Docker --network=host: localhost in the container is the host.
callback_url
} else {
// Bridge networking: rewrite so callbacks reach the host via host-gateway.
callback_url
.replace("localhost", "host.local")
.replace("127.0.0.1", "host.local")
};

// Process computation in background
let background_e3_id = e3_id.clone();
Expand Down