> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tokenfactory.nebius.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Prepare inputs, then verify with networking disabled

> Save prepared inputs, verify them without a guest network interface, and inspect both operations using Python.

Prepare files in one operation, save the resulting image, then verify those files in a new operation with networking disabled. This recipe uses Python to call the REST API, where `networking.enabled` controls the guest network interface. The setting defaults to `true` and must be chosen for each operation.

## Prerequisites

Complete [Set up access](/sandboxes/start/set-up-access). Export `NEBIUS_API_KEY`, `NEBIUS_PROJECT_ID`, and an immutable `IMAGE_UUID` containing `/bin/sh` and the `cat`, `mkdir`, `test`, and `printf` commands. In a new local directory, create a Python project:

```bash theme={null}
uv init --bare --python 3.12
uv add "httpx==0.28.1"
```

The client keeps the API credential locally. It is not passed to guest code.

## Follow the workflow

1. Prepare `/fixture/expected.txt` with networking enabled and `disposable=False`.
2. Keep the returned image UUID. It supplies the prepared files to the next operation.
3. Run verification from that image with `networking=False` and `disposable=True`.

The verification checks the file contents and confirms that `/sys/class/net` contains no interface other than loopback. It does not depend on reaching an external host. Saving the preparation image carries the files into the second operation; it does not carry the earlier network setting.

## Run the complete example

Save the program as `verify_no_network.py` and run `uv run python verify_no_network.py`. Start with `main()`: it describes the two operations. The `run_operation()` helper submits each request once, prints its ID, and polls with a 90-second deadline. Each guest command has a 30-second execution limit.

Expected output includes two operation IDs, the saved checkpoint UUID, and:

```text theme={null}
fixture-ok network-disabled
```

<Accordion title="Complete program">
  ```python theme={null}
  import base64
  import os
  import time

  import httpx

  PREPARE_INPUTS = r"""
  mkdir -p /fixture
  printf 'expected-v1\n' > /fixture/expected.txt
  """

  VERIFY_OFFLINE = r"""
  test "$(cat /fixture/expected.txt)" = expected-v1
  for interface in /sys/class/net/*; do
      test "${interface##*/}" = lo
  done
  printf 'fixture-ok network-disabled\n'
  """


  def main():
      with httpx.Client(
          base_url=os.getenv(
              "CONTREE_BASE_URL", "https://api.tokenfactory.nebius.com/sandboxes"
          ).rstrip("/") + "/",
          headers={
              "Authorization": f"Bearer {os.environ['NEBIUS_API_KEY']}",
              "Project": os.environ["NEBIUS_PROJECT_ID"],
          },
          timeout=15,
      ) as client:
          prepared, _ = run_operation(
              client,
              os.environ["IMAGE_UUID"],
              PREPARE_INPUTS,
              networking=True,
              disposable=False,
          )
          checkpoint = prepared.get("result_image_uuid")
          if not checkpoint:
              raise RuntimeError("Preparation returned no saved image")
          print(f"Checkpoint: {checkpoint}", flush=True)

          verified, output = run_operation(
              client,
              checkpoint,
              VERIFY_OFFLINE,
              networking=False,
              disposable=True,
          )
          if (
              output != "fixture-ok network-disabled\n"
              or verified.get("result_image_uuid")
          ):
              raise RuntimeError("Verification output or disposable result was unexpected")
          print(output, end="")


  def run_operation(client, image, command, *, networking, disposable):
      # Submit once. A transport error can leave the submission outcome uncertain.
      response = client.post(
          "v1/instances",
          json={
              "image": image,
              "command": "set -e\n" + command,
              "shell": True,
              "networking": {"enabled": networking},
              "disposable": disposable,
              "timeout": 30,
              "truncate_output_at": 1048576,
          },
      )
      response.raise_for_status()
      if response.status_code != 201:
          raise RuntimeError(f"Unexpected create status: {response.status_code}")
      operation_id = response.json()["uuid"]
      print(f"Operation: {operation_id}", flush=True)
      deadline = time.monotonic() + 90
      while True:
          remaining = deadline - time.monotonic()
          if remaining <= 0:
              raise TimeoutError(f"Inspect operation {operation_id} before retrying")
          response = client.get(f"v1/operations/{operation_id}", timeout=min(15, remaining))
          response.raise_for_status()
          operation = response.json()
          status = operation["status"]
          if status == "SUCCESS":
              break
          if status not in {"PENDING", "ASSIGNED", "EXECUTING"}:
              raise RuntimeError(f"Operation {operation_id} ended as {status}")
          retry_after = response.headers.get("Retry-After", "1")
          delay = max(1, int(retry_after)) if retry_after.isdecimal() else 1
          time.sleep(max(0, min(delay, deadline - time.monotonic())))

      result = operation["metadata"]["result"]
      state = result["state"]
      stdout = decode_output(result["stdout"])
      stderr = decode_output(result["stderr"])
      if (
          state["exit_code"] != 0
          or state.get("timed_out")
          or state.get("signal") not in (None, -1)
      ):
          raise RuntimeError(f"Process failed in {operation_id}: {state}; stderr={stderr}")
      return operation, stdout


  def decode_output(stream):
      if stream.get("truncated"):
          raise RuntimeError("Output was truncated; increase the output limit")
      if stream["encoding"] == "base64":
          return base64.b64decode(stream["value"], validate=True).decode("utf-8")
      if stream["encoding"] in {"ascii", "utf-8", "utf8"}:
          return stream["value"]
      raise ValueError(f"Unsupported output encoding: {stream['encoding']}")


  if __name__ == "__main__":
      main()
  ```
</Accordion>

The preparation checkpoint remains subject to image retention. The verification returns no saved image because it is disposable. To adapt the recipe, replace `PREPARE_INPUTS` and `VERIFY_OFFLINE` while keeping the saved-image check and the network setting on the verification operation.

If a request fails or polling reaches its deadline, inspect the printed operation ID before resubmitting. Polling failure does not cancel accepted work. If submission fails before an ID is returned, resolve that uncertain outcome before repeating the POST. The remote execution limit still bounds an accepted workload.

A failed verification may indicate a missing or incorrect fixture, or an unexpected guest interface. Keep the operation and image IDs with the redacted process result when investigating. Disabling networking applies to the whole operation, including its subprocesses. Use it when verification can run entirely offline.

See [Configure networking and secrets](/sandboxes/guides/configure-networking-and-secrets) and the [spawn API reference](/api-reference/sandboxes/instances/spawn-a-new-container-instance).
