Metadata-Version: 2.4
Name: glyphvm
Version: 0.1.1
Summary: The Glyph VM command-line client, installed as glyph.
Project-URL: Homepage, https://glyph.host
License-Expression: MIT
License-File: LICENSE
Keywords: cli,cloud,glyph,virtual-machines
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.12
Requires-Dist: click>=8.1
Requires-Dist: httpx>=0.27
Requires-Dist: rich>=13.7
Requires-Dist: websockets>=13
Description-Content-Type: text/markdown

# glyphvm

The command-line client for [Glyph](https://glyph.host): create and manage
virtual machines, open a console or guest shell, and inspect networking and
billing. Install the **`glyphvm`** package and run **`glyph`**.

Requires Python 3.12 or newer on Linux or macOS. Windows users need WSL;
the interactive terminal implementation requires POSIX APIs. The internal
Python module remains `rune`, as do the existing `RUNE_*` environment variables
and configuration path.

## Install

```sh
uv tool install glyphvm
# Alternatively: pipx install glyphvm

glyph --version
glyph login
```

With an activated Python virtual environment, `python -m pip install glyphvm`
also installs the command. To upgrade an installation managed by uv, run
`uv tool upgrade glyphvm`.

## Configure

```sh
glyph login [--org acme]                          # against https://glyph.host
glyph login --base-url http://127.0.0.1:8000       # against a dev stack
glyph --base-url https://warden.example whoami    # override for one command
```

`login` shows a confirmation code and, on Enter, opens
`<url>/app/cli/authorize/?code=...` in your browser. Sign in
there if you are not, check the code matches your terminal, and confirm; the
CLI, which has been polling, receives your API token. `--no-browser` prints
the URL instead of opening it (over ssh, open it anywhere and type the code
if the link does not carry it); `--token <token>` skips the browser and
stores a token copied from the web UI's API token page. Either way `login`
verifies the token against `GET /api/v1/me/`, then writes
`~/.config/rune/config.toml` (mode 0600):

```toml
[default]
url = "https://warden.example"
token = "..."
org = "acme"
```

Resolution order, highest wins:

1. flags: `--base-url` (also accepted as `--url`), `--token`, `--org`
2. environment: `RUNE_URL`, `RUNE_TOKEN`, `RUNE_ORG`
3. config file (`$RUNE_CONFIG`, else `$XDG_CONFIG_HOME/rune/config.toml`,
   else `~/.config/rune/config.toml`)
4. the built-in default URL, `https://glyph.host`

If you belong to exactly one organization, `login` selects it automatically.

## Usage

The short spells (what the marketing site shows):

```sh
glyph conjure --name web-1   # a VM with defaults: type s-1 (1 vCPU, 1024 MiB, 10 GiB), debian-13
glyph shell web-1            # a root shell inside it
glyph console web-1          # its serial console
glyph banish web-1           # delete it
```

Every command that takes a VM accepts its **name or id**. A name is looked up
in the current organization and must be unique there.

The full set:

```sh
glyph whoami
glyph org list
glyph org use acme

glyph vm list
glyph types                   # instance types and their hourly prices per region under your plan
glyph vm create web-1 --type s-2 --image debian-12
glyph conjure --name web-1 [--type T --image X --region R]
glyph vm show <id>
glyph vm start <id>
glyph vm stop <id>
glyph vm pause <id>           # freeze the guest in place; resume picks up where it left off
glyph vm resume <id>
glyph vm rm <id> --yes
glyph vm console <id>         # attach to the serial console; ~. at line start detaches
glyph console <id-or-name>    # the same, one word shorter
glyph vm shell <id>           # a root login shell inside the guest; exit ends it
glyph shell <id-or-name>      # the same, one word shorter

glyph vm show <id>            # also shows the public addresses, rate limits and this month's egress

glyph net list                # the organization's networks
glyph net quota               # this month's internet egress per VM against the quota
glyph billing show            # pre-paid balance, burn rate, time left, quota
glyph billing ledger          # the ledger, newest first (top-ups happen on the web)

glyph --json vm list          # raw JSON for scripting
```

Mutating VM commands return as soon as warden has accepted the request
(HTTP 202); the VM's `state` catches up with `desired_state` asynchronously.

## Console

`glyph vm console <id>` asks warden for a short-lived ticket, opens a websocket
to the console gateway and puts your terminal in raw mode, so keystrokes go
straight to the VM's serial port (the guest's own tty handles echo). Type
`~.` at the start of a line to detach, as with ssh. Without a tty on stdin
(pipes, scripts) the bytes are forwarded as-is. Session events from the
gateway are printed to stderr as `[console] ...`.

## Shell

`glyph vm shell <id>` is a login session inside the guest, not the serial
line: ssh over vsock from the host to the sshd that a systemd ≥ 256 guest
binds there itself, as root, with a key the host gave the guest at boot. It
gets a pty sized to your terminal (there is no live resize yet), `exit` or
Ctrl-D ends it, and several people can be in the same VM at once. `~.` at
the start of a line still detaches. With input from a pipe there is no pty,
as with ssh: `echo 'uname -a' | glyph shell web-1` runs it and returns. A guest without sshd, or one whose
systemd is too old, gives `shell unavailable` (exit 1) with the reason.

Gateway close codes map to exit codes: `4401` ticket rejected → 2, `4404`
VM not found/running → 1, `4409` console busy → 1, `4502` host unreachable →
3, `4503` shell unavailable → 1, normal end → 0.

## Exit codes

| Code | Meaning |
|------|---------|
| 0    | success |
| 1    | request rejected (4xx/5xx other than 401), or no org selected |
| 2    | not logged in (401), or a usage error from click |
| 3    | warden unreachable / timed out |
| 4    | payment required (402): the organization's balance is too low, or its account is frozen |

## Development

From the repository root:

```sh
uv sync --project rune --locked
uv run --directory rune --locked glyph --help
uv run --directory rune --locked pytest --cov=rune
uv run --directory rune --locked ruff check rune tests scripts
uv run --directory rune --locked ruff format --check rune tests scripts
```

For a persistent installation from a checkout, use `uv tool install ./rune`
or `python -m pip install -e ./rune` in a virtual environment. Maintainers can
follow `docs/publishing-rune.md` in the repository for build and release steps.

## License

MIT. See the package's `LICENSE` file.
