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

# Metrics And Timeouts

> Inspect machine usage and configure timeout behavior.

## Metrics

<CodeGroup>
  ```python Python SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  from nullspace import Machine

  with Machine.create(template="base") as machine:
      for metric in machine.get_metrics():
          print(metric)
  ```

  ```typescript TypeScript SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  import { Machine } from "nullspace";

  const machine = await Machine.create({ template: "base" });
  for (const metric of await machine.getMetrics()) {
    console.log(metric);
  }
  ```

  ```bash CLI theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  nullspace machine metrics mch_123
  ```

  ```bash HTTP API theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  curl "${NULLSPACE_API_URL}/v1/machines/mch_123/metrics" \
    -H "Authorization: Bearer ${NULLSPACE_API_KEY}"
  ```
</CodeGroup>

For streaming updates, use [Monitor](../observability/monitor).

## Timeout

Set a timeout at create time or update it while the machine is running. Each
client below is an equivalent alternative for the same setting. Note that the
SDKs and CLI take a relative duration (seconds) while the HTTP API takes
`timeout_ms`.

<CodeGroup>
  ```python Python SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  machine = Machine.create(template="base", timeout=600)
  machine.set_timeout(120, timeout_action="destroy")
  ```

  ```typescript TypeScript SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  const machine = await Machine.create({ template: "base", timeout: 600 });
  await machine.setTimeout(120, { timeoutAction: "destroy" });
  ```

  ```bash CLI theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  nullspace machine timeout set mch_123 --seconds 120 --action destroy
  ```

  ```bash HTTP API theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  curl -X POST "${NULLSPACE_API_URL}/v1/machines/mch_123/timeout" \
    -H "Authorization: Bearer ${NULLSPACE_API_KEY}" \
    -H "Content-Type: application/json" \
    -d '{ "timeout_ms": 120000, "timeout_action": "destroy" }'
  ```
</CodeGroup>

`set_timeout` is a refresh: each call resets the deadline to the new window from
now. The endpoint echoes the effective lease back so you can schedule the next
refresh without guessing:

* `timeout_ms` — the window now in effect
* `timeout_at` — the absolute deadline (RFC 3339)
* `remaining_ms` — milliseconds left until `timeout_at`, clamped at 0
* `timeout_action` — what fires at the deadline

The SDK `set_timeout` / `setTimeout` return this lease, and the same
`timeout_at` / `remaining_ms` appear on `MachineInfo` (from `get`/`list`):

<CodeGroup>
  ```python Python SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  lease = machine.set_timeout(120, timeout_action="destroy")
  print(lease.remaining_ms, lease.timeout_at)
  ```

  ```typescript TypeScript SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  const lease = await machine.setTimeout(120, { timeoutAction: "destroy" });
  console.log(lease.remainingMs, lease.timeoutAt);
  ```

  ```bash HTTP API theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  # 200 response body:
  # { "timeout_ms": 120000, "timeout_at": "2026-06-19T12:02:00Z",
  #   "remaining_ms": 120000, "timeout_action": "destroy" }
  ```
</CodeGroup>

There are two related timeout knobs:

* `on_timeout` is create-time lifecycle policy. Use `"destroy"` or `"pause"`.
* `timeout_action` is used by `set_timeout()`. Use `"destroy"` or `"hibernate"`.

The default timeout behavior destroys the machine when it expires. Use
`on_timeout="pause"` at create time when persistence-capable deployments should
hibernate the machine instead. The TypeScript SDK does not expose `autoResume`
or a `pause` timeout policy on create, so configure pause-on-timeout with
auto-resume from the Python SDK, CLI, or HTTP API:

<CodeGroup>
  ```python Python SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  machine = Machine.create(
      template="base",
      timeout=600,
      on_timeout="pause",
      auto_resume=True,
  )
  ```

  ```bash CLI theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  nullspace machine create --template base --timeout 600 --on-timeout pause --auto-resume
  ```

  ```bash HTTP API theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  curl -X POST "${NULLSPACE_API_URL}/v1/machines" \
    -H "Authorization: Bearer ${NULLSPACE_API_KEY}" \
    -H "Content-Type: application/json" \
    -d '{
      "template": "base",
      "timeout_ms": 600000,
      "lifecycle": { "on_timeout": "pause" },
      "auto_resume": true
    }'
  ```
</CodeGroup>

`auto_resume=True` is valid only with pause/hibernate timeout behavior. Updating
an auto-resumable machine to destroy-on-timeout is rejected so paused alias
routing cannot become inconsistent.

## Keepalive while in use

When an agent loop needs to hold a machine open for as long as work is running,
use the opt-in keepalive helper instead of hand-rolling a refresh loop. It
refreshes the lease immediately, then on an interval (default: half the timeout
window), and stops cleanly when the block exits.

<CodeGroup>
  ```python Python SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  with machine.keep_alive(timeout_secs=300):
      run_long_agent_loop(machine)   # lease refreshed in the background
  # refresher stopped here; the normal timeout takes over again
  ```

  ```python Async Python SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  async with machine.keep_alive(timeout_secs=300):
      await run_long_agent_loop(machine)
  ```

  ```typescript TypeScript SDK theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
  await using _keepalive = await machine.keepAlive({ timeoutSecs: 300 });
  await runLongAgentLoop(machine);   // disposed (stopped) on scope exit
  ```
</CodeGroup>

Keepalive is a client-side convenience built on `set_timeout`; the server
contract stays an explicit refresh. Pass `refresh_every_secs` /
`refreshEverySecs` to control the cadence.

## No session ceiling

Refreshing a timeout has **no upper bound**: there is no per-tier maximum
session length and no cap on how long or how often you can extend a lease. A
machine lives exactly as long as you keep it alive (and bills only for the time
it is running or paused). This is deliberate — long-lived agents and human
sessions never hit an artificial wall and never need special-case handling. The
only timeout that matters is the one you set.

## Capacity and limits

During private beta, machine capacity is intentionally limited. Treat create
timeouts as recoverable and make scripts idempotent with explicit cleanup.

## Related

* [Monitor stream](../observability/monitor)
* [Auto-resume](./auto-resume)
* [Lifecycle events](../observability/lifecycle-events)
* [Environment variables](../reference/env-vars)
