Machine: lifecycle & state methods

Generated Markdown for references/fact_vm_lifecycle.md.

Open book page Back to the skill graph

# Machine: lifecycle & state methods

| Method | Returns | Notes |
| --- | --- | --- |
| `m.name()` | `string` |  |
| `m.kind()` | `string` | `"vm"` or `"container"` — reported so a script can \*say\* what it has, never so it has to branch to drive it |
| `m.start()` / `m.stop()` / `m.stop_force()` / `m.restart()` | `Result[unit, string]` | stop = graceful ladder (agent → ACPI → kill) |
| `m.poweroff()` | `Result[unit, string]` | Clean QMP `quit` — exits QEMU flushing block caches. The only safe seal for guests with no ACPI (DOS, Win 3.x), where a force-kill can drop qcow2 writes |
| `m.state()` | `string` | one of `"stopped"` / `"starting"` / `"running"` / `"stopping"` |
| `m.is_ready()` | `bool` | The **sticky** ready flag — once set it stays set while QEMU runs, so it does not drop across a guest reboot |
| `m.agent_answering()` | `bool` | The **live** agent probe, ungated — goes false while the guest is down or mid-reboot. Use this to watch a reboot you requested from inside the guest |
| `m.wait_ready(timeout_secs: int)` | `Result[unit, string]` | Block until agent responds |
| `m.wait_shutdown(timeout_secs: int)` | `Result[unit, string]` | Block until powered off |
| `m.ip()` | `Result[string, string]` | Primary NIC IPv4 (DHCP lease / agent) |
| `m.ip_nic(nic: int)` | `Result[string, string]` | By NIC index (0-based) |
| `m.is_healthy()` | `bool` | Latest healthcheck verdict; a machine declaring none counts healthy once ready |
| `m.logs(lines: int)` | `Result[string, string]` | Tail of the console log — a container's captured stdout/stderr, a VM's serial log |
| `m.logins()` | `List[Login]` | The identities the machine declares with `login {}` (§19.2). Read from the lab file, so it answers on a stopped machine |
| `m.as_login(selector: string)` | `Result[Machine, string]` | A second handle onto the same machine, whose guest work runs as that **login** — a label, the account name, or the family floor (`SYSTEM`/`root`). Fails naming the machine on a selector nothing matches |
| `m.as_account(user: string, password: string)` | `Result[Machine, string]` | The same, for an account the lab file does not declare, or one whose declared secret has been rotated |

`Login` carries `label`, `user`, `password: Option[string]`, `elevated` and
`default`. Reading it is what lets a provision script create **exactly** the
account the lab file declares, instead of the password living in two places
that drift. The secret crosses as written — `none` where the author declared
none, never an empty string. `elevated` and `default` cross \*resolved\*, so a
lone login reports `default == true` without the lab file saying so.


`as_login` / `as_account` are the wscript rung of §19.2's precedence ladder,
resolved exactly as `vmlab exec --user/--password` resolves it. They return a
handle rather than switching this one, so an identity is said once and every
call on the result — `exec`, `copy_to`, `terminal` — carries it. That is what
lets a `provision {}` step address a login's home directory \*\*before that
account has ever logged on\*\* (§19.8), which is the only route for anything
that must land as the developer rather than as the machine. A `playbook {}`
has no rung here at all.


Inside a machine's **own** first-boot script the readiness pair is special-cased:
`is_ready()` / `wait_ready()` mean "does the agent answer right now", because the
sticky flag is deliberately withheld until that script returns. Everywhere else
they report full readiness, and `agent_answering()` is how you get the live
signal.


## Related

- [Machine]../references/entity_vm_api.md

- [Lab]../references/entity_lab_api.md

[← Back to SKILL.md]../SKILL.md