Skip to content

feat: let a caller supply the mycelium key and ip seed when deploying a vm - #1548

Open
mik-tf wants to merge 3 commits into
developmentfrom
development_mik_mycelium_pin
Open

feat: let a caller supply the mycelium key and ip seed when deploying a vm#1548
mik-tf wants to merge 3 commits into
developmentfrom
development_mik_mycelium_pin

Conversation

@mik-tf

@mik-tf mik-tf commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Description

A VM's mycelium address is determined by two values: the mycelium key on its network, and the ip seed on the machine. tfcmd deploy vm generates both on every run and offers no way to supply them, so a VM that is canceled and deployed again always comes back on a different address.

That matters wherever something outside the deployment refers to a VM by its address — a peer that has it pinned, a record that outlives the machine, or a replacement expected to take over from the one it replaces. zos.Mycelium.Key already documents the intent:

Key is the key of the mycelium peer in the mycelium node associated with this network. It's provided by the user so it can be later moved to other nodes without losing the key.

The grid client supports it on both paths — ZNetLight.MyceliumKeys and VMLight.MyceliumIPSeed, and their classic equivalents — and the values reach the wire. Only the command line had no way to express them.

This adds --mycelium-key and --mycelium-seed to deploy vm. Omitting them generates a fresh value exactly as before, so existing callers are unaffected.

This was verified against mainnet rather than reasoned about. Three deployments were made with one fixed key and seed pair, on dedicated nodes that advertise zmachine-light and not classic zmachine. Between each one the VM was canceled and its address confirmed unreachable, so every deployment was genuinely new — each created fresh contract ids:

deployment node mycelium address reachable
first 8076 (DE) 5c5:681f:d60b:505:ff0f:9f4f:753f:5521 4/4 ping, 111 ms
canceled and redeployed 8076 (DE) same 4/4 ping, 138 ms
redeployed on a different node 8074 (FI) same 4/4 ping, 146 ms

The third case is the one that matters: a different machine in a different country answered on the address its predecessor had. All six contracts were canceled by id afterwards.

Changes

  • grid-cli/cmd/deploy_vm.go
    • new --mycelium-key (hex, 32 bytes) and --mycelium-seed (hex, 6 bytes) flags on deploy vm
    • parseMyceliumIdentity decodes and validates them; empty means generate, which is the prior behaviour
    • the flags are marked required-together, and the rule is also stated in the parsing function so it does not depend on the call site — either value alone still changes the address, which would leave a caller with a VM that is not where they expect it
    • a key or seed of the wrong length, or one that is not hex, is refused with the expected length named
    • supplying an identity while --mycelium is disabled is refused rather than silently ignored
  • grid-cli/internal/cmd/deploy.go
    • buildNetwork and buildNetworkLight accept an optional key and fall back to RandomMyceliumKey when it is empty
    • DeployVM and DeployVMLight thread it through
    • DeployKubernetesCluster passes none and generates as before, its command line having no way to express one
  • grid-cli/cmd/deploy_vm_test.go — unit tests for the parsing and validation rules
  • grid-cli/docs/vm.md — the two flags, plus a short section on keeping an address across deployments

Related Issues

None filed upstream — happy to open one if you would prefer the discussion there.

Checklist

  • Tests included
  • Build pass
  • Documentation
  • Code format and docstring

mik-tf added 2 commits August 3, 2026 10:00
… a vm

A vm's mycelium address is determined by two values: the mycelium key on its
network, and the ip seed on the machine. `deploy vm` generates both on every
run and offers no way to supply them, so a deployment that is destroyed and
rebuilt always comes back on a different address.

That matters wherever the address is depended on from outside the deployment.
The type carrying the network key already documents the intent — it is
provided by the user so it can later be moved to other nodes without losing
the key — but nothing on the command line could act on it.

`deploy vm` now accepts `--mycelium-key` (hex, 32 bytes) and
`--mycelium-seed` (hex, 6 bytes). Both paths honour them: the light network
and machine as well as the classic ones. Omitting them generates a fresh
value exactly as before, so every existing caller is unaffected.

They have to be supplied together, and cobra enforces that alongside the
parsing function, because either one on its own still moves the address and
would leave a caller with a machine that is not where they expect it. A key
or seed of the wrong length, or one that is not hex, is refused with the
expected length named. Supplying an identity while mycelium is disabled is
refused rather than quietly ignored.

The kubernetes deploy path passes no key and generates as it did before, its
command line having no way to express one.

Signed-by: mik-tf <mik-tf@noreply.invalid>
Records the two new flags alongside the other optional ones, and adds a short
section on why they exist: a mycelium address depends on the network key and
the machine's ip seed, both of which are generated per deployment unless
supplied, so a redeployed vm normally moves.

Notes the two constraints a user will otherwise meet by trial: the flags have
to be given together, since either alone still changes the address, and the
key is private material worth storing deliberately if a redeployment is
intended.

Signed-by: mik-tf <mik-tf@noreply.invalid>
@mik-tf

mik-tf commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

Two checks are red here — test (grid-client) and test (tfrobot) — and neither is caused by this PR. Flagging it so it does not read as a regression.

Both fail inside setup(), which validates the account balance while constructing the client against the dev network:

account contains 0.000050 tft, min fee is 2 tft
        grid-client/deployer/validation.go:97

The nil pointer panic that follows is downstream of that error rather than a separate defect. Written up with the suggested fix in #1549.

Three things that place it outside this branch:

  • this PR touches four files and all of them are under grid-cli/; grid-client/ and tfrobot/ are byte-identical to development
  • development is already failing on the same suites at f8d1d856, the commit this branched from — for example runs 30789727006 and 30734892734
  • checking out development with none of these changes reproduces both failures locally

The checks covering the changed code are green: test (grid-cli), Run Lint (grid-cli), build (grid-cli) and build-docker (grid-cli).

Signed-by: mik-tf mik-tf@noreply.invalid

…d line

The mycelium key is a credential: whoever holds it can answer at the address
it defines. `--mycelium-key` takes the value directly, which puts it on the
command line, where it is readable by anything that can list processes for as
long as the deploy runs. That is a poor place for a value whose whole purpose
is to let a rebuilt machine reclaim an address.

`deploy vm` now also accepts `--mycelium-key-env`, which names an environment
variable holding the hex key and reads it from there. The two are mutually
exclusive, and supplying neither still generates a key exactly as before, so
every existing caller is unaffected.

The ip seed deliberately gets no equivalent. It selects an address within the
network the key defines, so knowing it grants nothing, and a second way to
pass a value that is not secret would be cost without benefit.

Errors on this path name the variable and never its contents, and are returned
rather than logged, because an error sink that ships elsewhere is not somewhere
a caller can predict. A variable that is unset and one that is empty are
reported separately: both are caller mistakes, and the alternative to naming
them is falling back to generating a key, which hands back a machine on an
address the caller did not choose and believes it did.

Cobra's requirement that the key and seed be given together is now expressed
only in the parsing function. The grammar became "either a key or a variable
naming one, together with a seed", which the flag-group helpers cannot state;
the parsing function already carried the rule independently and is tested for
it directly.

Signed-by: mik-tf <mik-tf@noreply.invalid>
@mik-tf

mik-tf commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

A third commit has landed on this branch — flagging it here rather than letting it turn up in a re-read.

It adds --mycelium-key-env, which names an environment variable holding the hex key instead of taking the key on the command line.

The reason is that the key is a credential rather than a parameter: an address is a pure function of it, so whoever holds it can answer at that address. A value passed as an argument is readable from the process table by anything on the machine for as long as the command runs, and a deploy runs for minutes. That is not something later care can undo, which is why it seemed worth fixing at the same time as the flags that make the key worth supplying at all.

Details, so the diff is easy to read:

  • the new flag and --mycelium-key are mutually exclusive, and supplying neither still generates a key, so every existing caller is unaffected;
  • the ip seed deliberately gets no equivalent — it selects an address within the network the key defines, so knowing it grants nothing, and a second way to pass a non-secret would be cost without benefit;
  • errors on this path name the variable and never its contents, and are returned rather than logged, since the logger here ships errors off the machine;
  • an unset variable and an empty one are reported separately. Both are caller mistakes, and the alternative to naming them is falling back to generating a key, which hands back a machine on an address the caller did not choose and believes it did.

One change worth calling out because it touches existing behaviour: the requirement that key and seed be supplied together now lives only in parseMyceliumIdentity rather than also in MarkFlagsRequiredTogether. The grammar became "either a key, or a variable naming one, together with a seed", which the flag-group helpers cannot express. The parsing function already stated the rule independently and is tested for it directly, so the constraint is unchanged in effect.

Happy to split this into its own PR stacked on top if you would rather review the original two commits alone — just say so and I will move it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant