This example uses examples/python/functions/machine_function_composition to show Machines and Functions working together in one workflow:
  • create a customer Machine
  • invoke a deployed Function from inside that machine over the public API
  • retain a failed Function run and connect to its backing machine for debugging

Setup

uv pip install "nullspace-sdk[cli]"
export NULLSPACE_API_KEY=ns_live_...
export NULLSPACE_API_URL=https://api.your-nullspace-domain
cd examples/python/functions/machine_function_composition

Run

python run.py
The script deploys function.py, creates a customer machine, runs a DNS check inside the machine, invokes the Function from that machine, then starts a retained failing run and connects to the retained machine with Function.connect_machine(...). Expected output includes:
deployed machine-function-composition-...
created customer machine mch_...
machine DNS check exit=0
machine-invoked function run fnrun_... status=succeeded
connected retained function machine mch_...
The script destroys the customer machine, destroys the retained Function machine, and deletes the Function in finally cleanup.

Key Pieces

Invoke the Function from inside the customer machine:
invoked = machine.commands.run(
    "python3",
    ["-c", machine_invoker()],
    envs={
        "NULLSPACE_API_URL": api_url,
        "NULLSPACE_API_KEY": os.environ["NULLSPACE_API_KEY"],
        "NULLSPACE_FUNCTION_ID": function.id,
        "SOURCE_MACHINE_ID": machine.id,
    },
)
Connect to a retained Function run:
terminal = wait_for_run(function, retained_id)
retained_machine = function.connect_machine(terminal)
probe = retained_machine.commands.run(
    "cat",
    ["retained-marker.txt"],
    cwd="/workspace/project",
)
retained_machine.kill()
Use command environment variables for short-lived credentials passed into the machine process. Do not print API keys or copy raw bearer values into evidence logs.