Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
95d37df
feat(settings): resolve the gcp project and zone
NovusEdge Sep 8, 2026
1991587
feat(gce): authenticate with application default credentials
NovusEdge Sep 8, 2026
a1a7487
feat(iso): name each catalog entry's GCE image
NovusEdge Sep 8, 2026
3fd7ada
feat(gce): compute deadlines and carry them in labels
NovusEdge Sep 8, 2026
4c60361
feat(cloudinit): make the seed provider-aware
NovusEdge Sep 8, 2026
a5a4d35
merge: gce client, labels and deadlines
NovusEdge Sep 8, 2026
b256e12
merge: gce image reference and provider-aware seed
NovusEdge Sep 8, 2026
13c0601
feat(gce): build the insert request and the operator-scoped firewall …
NovusEdge Sep 8, 2026
d5af490
feat(gce): implement the Provider over Compute Engine
NovusEdge Sep 8, 2026
7869c03
fix(gce): resolve the guest name to a catalog image, not an entry ID
NovusEdge Sep 8, 2026
11fc6d2
fix(gce): stop using the retired Google Domains checkip endpoint
NovusEdge Sep 8, 2026
5f2524d
test(gce): recorded-transport tests for the compute REST calls
NovusEdge Sep 8, 2026
e431b75
feat(cli): add the WHERE column to stoat ls
NovusEdge Sep 8, 2026
da0dccf
feat(cli): show a VM's provider in stoat get
NovusEdge Sep 8, 2026
4f6141c
fix(tui): edit's running check asks the provider, not qemu
NovusEdge Sep 8, 2026
806a020
fix(tui): refuse to type a console password on a non-qemu VM
NovusEdge Sep 8, 2026
9f9b07f
docs: document the WHERE column and get's provider block
NovusEdge Sep 8, 2026
8e2760d
fix(core): register gce and expose its facts through Provider
NovusEdge Sep 9, 2026
e380c70
feat(cli): show gce project, deadlines and machine facts
NovusEdge Sep 9, 2026
16065c7
fix(tui): resolve edit's running state off the View path, show provid…
NovusEdge Sep 9, 2026
fff4cb0
feat(gce): let the operator pin the firewall source range
NovusEdge Sep 9, 2026
e443788
fix(core): actually enforce the capability declarations
NovusEdge Sep 9, 2026
c849df3
feat(gce): make the run-time limit configurable, defaulting to a day
NovusEdge Sep 9, 2026
1221bf8
fix(gce): make the provider work against the real API
NovusEdge Sep 9, 2026
9a8bf4a
test: add the gce live gate, and stop naming a command that does not …
NovusEdge Sep 9, 2026
b59fb41
fix(gce): satisfy the linter, and stop impersonating through a deprec…
NovusEdge Sep 9, 2026
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
33 changes: 26 additions & 7 deletions docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,20 @@ Lists every VM directory under the data root, plus any directory whose `vm.toml`

```
$ stoat ls
NAME MODE STATE CPUS RAM SSH
work live running 4 4096 2222
scratch disk stopped 2 2048 2223
oldvm - broken - - - unexpected token near line 4
NAME MODE STATE WHERE CPUS RAM SSH
work live running local 4 4096 2222
cloudy cloud running gce 2 4096 22
scratch disk stopped local 2 2048 2223
oldvm - broken - - - - unexpected token near line 4
```

The `STATE` column is colored (green `running`, red `broken`) when [color is enabled](#scripting). `-q`/`--quiet` is accepted but has no effect on `ls`'s output.
`WHERE` names the execution surface: `local` for a qemu VM, or the provider's own name (`gce`) otherwise. The `STATE` column is colored (green `running`, red `broken`) when [color is enabled](#scripting). `-q`/`--quiet` is accepted but has no effect on `ls`'s output.

A gce VM within an hour of its run-time limit or an operator-requested stop gets a line under the table, and the same line prints to stderr for every command that touches it:

```
cloudy: stops in 42m (run-time limit). extend with: stoat gce extend cloudy 4h
```

`--project` filters the list to VMs the `stoat.toml` in the current directory declares. It refuses outside a project.

Expand Down Expand Up @@ -138,13 +145,14 @@ A VM `stoat.toml` declares but that does not exist yet shows state `missing`. An

## `stoat get <name>`

Prints one VM's fields as `key: value` lines: name, os, mode, backend, state, cpus, ram, disk, share, ssh port, ssh user, recipes, forwards, display, plus an `error:` line when the VM is broken.
Prints one VM's fields as `key: value` lines: name, os, mode, provider, backend, state, cpus, ram, disk, share, ssh port, ssh user, recipes, forwards, display, plus an `error:` line when the VM is broken.

```
$ stoat get work
name: work
os: alpine
mode: live
provider: qemu
backend: apkovl
state: running
cpus: 4
Expand All @@ -160,6 +168,8 @@ display: a qemu window

`display` is the only line here that is not a `vm.toml` field. See [`stoat up`](#stoat-up-name) for what it means and why the answer changes. It is omitted entirely for a broken VM, whose `vm.toml` supplies neither of the facts the answer depends on.

A non-qemu VM gets `gcp project:`, `zone:`, `machine type:`, `address:` and `expires:` lines right after `provider:`, before `backend:`. `expires` names the nearer of the run-time limit and any operator-requested stop.

**Exit codes:** 0 on success; 1 if the VM can't be loaded.

## `stoat create <name> --image=IMAGE`
Expand All @@ -172,7 +182,16 @@ created work (alpine, live, ssh port 2222)
start it with: stoat up work
```

Flags: `--image` (required; catalog id or a path to your own image), `--os`, `--backend` (override what a bring-your-own image's filename would otherwise infer), `--mode` (`live` or `disk`; only meaningful for the alpine iso, every other image has one mode), `--ram` (MB), `--cpus`, `--disk` (absolute size, e.g. `8G`), `--share` (host directory to expose), `--console-password` (`random` generates one), `--recipes` (comma-separated or repeated), `--set recipe.param=value` (set a non-secret recipe parameter), `--secret recipe.param` (read a secret from the environment or prompt), `--agent-access` (`none`, `observe`, `manage`, or `exec`; default `manage`, controls MCP guest access). The hidden `--allow-exec` flag remains as a compatibility alias: true maps to `exec`, false to `manage`.
Flags: `--image` (required; catalog id or a path to your own image), `--os`, `--backend` (override what a bring-your-own image's filename would otherwise infer), `--mode` (`live` or `disk`; only meaningful for the alpine iso, every other image has one mode), `--ram` (MB), `--cpus`, `--disk` (absolute size, e.g. `8G`), `--share` (host directory to expose), `--console-password` (`random` generates one), `--recipes` (comma-separated or repeated), `--set recipe.param=value` (set a non-secret recipe parameter), `--secret recipe.param` (read a secret from the environment or prompt), `--agent-access` (`none`, `observe`, `manage`, or `exec`; default `manage`, controls MCP guest access), `--provider` (`qemu`, the default, or `gce`), `--gcp-project` and `--gcp-zone` (honored only with `--provider gce`; each falls back to `config.toml`, then gcloud's active configuration). The hidden `--allow-exec` flag remains as a compatibility alias: true maps to `exec`, false to `manage`.

A `--provider gce` create prints a second line naming where it landed and where the project and zone came from:

```
$ stoat create cloudy --image ubuntu-24.04 --provider gce
created cloudy (ubuntu, cloud, ssh port 22)
gcp project engrammic, zone europe-west4-a, from ~/.stoat/config.toml
start it with: stoat up cloudy
```

`create` (alias `new`) refuses at project scope: `a stoat.toml is present; declare the VM there and run stoat up, or pass --global`. `--global` creates the VM outside the project.

Expand Down
9 changes: 8 additions & 1 deletion docs/reference/json.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,15 @@ VM {"name":"work","os":"alpine","mode":"cloud","backend":"cloudinit",
"forwards":[{"host_port":8080,"guest_port":80}],
"allow_exec":false,"agent_access":"manage","display":"vnc",
"error":"only on a broken VM",
"project":"/home/u/myrepo","key":"dev","project_missing":false}
"project":"/home/u/myrepo","key":"dev","project_missing":false,
"provider":"gce","gcp_project":"engrammic","gcp_zone":"europe-west4-a",
"machine_type":"e2-medium","address":"34.12.221.212",
"hard_deadline":"2026-09-08T22:14:00Z","soft_deadline":""}
```

`provider` is omitted for a qemu VM (the empty `core.VM.Provider`), and carries the provider's own name (`"gce"`) otherwise. `gcp_project`, `gcp_zone`, `machine_type` and `address` are omitted for qemu and for a gce VM whose details call failed. `hard_deadline` and `soft_deadline` are RFC3339, each omitted when that deadline does not apply.

```json
VMStatus {"name":"work",...VM fields...,"health":"ok","recipes_detail":[
{"name":"xfce","applied":true,"version":"1.2","at":"...",
"health":"unknown","params":{},"outputs":{}}]}
Expand Down
30 changes: 27 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
module github.com/novusedge/stoat

go 1.26
go 1.26.0

require (
charm.land/bubbles/v2 v2.2.1
charm.land/bubbletea/v2 v2.0.9
charm.land/huh/v2 v2.0.3
charm.land/lipgloss/v2 v2.0.6
charm.land/log/v2 v2.0.0
cloud.google.com/go/compute v1.67.0
github.com/BurntSushi/toml v1.6.0
github.com/alecthomas/kong v1.16.1
github.com/charmbracelet/x/ansi v0.11.8
Expand All @@ -16,13 +17,18 @@ require (
github.com/modelcontextprotocol/go-sdk v1.7.0
github.com/pelletier/go-toml/v2 v2.4.3
golang.org/x/sys v0.47.0
google.golang.org/api v0.297.0
gopkg.in/yaml.v3 v3.0.1
)

require (
cloud.google.com/go/auth v0.23.2 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.9.0 // indirect
github.com/anchore/go-lzo v0.1.0 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/catppuccin/go v0.2.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/charmbracelet/colorprofile v0.4.3 // indirect
github.com/charmbracelet/harmonica v0.2.0 // indirect
github.com/charmbracelet/ultraviolet v0.0.0-20260811164956-006e29f97886 // indirect
Expand All @@ -35,9 +41,15 @@ require (
github.com/djherbis/times v1.6.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/elliotwutingfeng/asciiset v0.0.0-20260129054604-cfde2086bc57 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logfmt/logfmt v0.6.1 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/jsonschema-go v0.4.3 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.20 // indirect
github.com/googleapis/gax-go/v2 v2.24.0 // indirect
github.com/klauspost/compress v1.18.5 // indirect
github.com/lucasb-eyer/go-colorful v1.4.1 // indirect
github.com/mattn/go-runewidth v0.0.27 // indirect
Expand All @@ -50,12 +62,24 @@ require (
github.com/segmentio/asm v1.1.3 // indirect
github.com/segmentio/encoding v0.5.4 // indirect
github.com/sirupsen/logrus v1.9.4 // indirect
github.com/stretchr/testify v1.11.1 // indirect
github.com/ulikunitz/xz v0.5.15 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect
go.opentelemetry.io/otel v1.44.0 // indirect
go.opentelemetry.io/otel/metric v1.44.0 // indirect
go.opentelemetry.io/otel/trace v1.44.0 // indirect
golang.org/x/crypto v0.55.0 // indirect
golang.org/x/exp v0.0.0-20260727155853-b88d891fe743 // indirect
golang.org/x/oauth2 v0.35.0 // indirect
golang.org/x/net v0.58.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sync v0.22.0 // indirect
golang.org/x/text v0.41.0 // indirect
golang.org/x/time v0.15.0 // indirect
google.golang.org/genproto v0.0.0-20260715232425-e75dac1f907d // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260715232425-e75dac1f907d // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260819154853-08b0e4226688 // indirect
google.golang.org/grpc v1.83.2 // indirect
google.golang.org/protobuf v1.36.12 // indirect
)
Loading