How it works
Where a public widget comes from, and why it asks
Anybody can publish a package with widgets in a git repository, and you can install it with one command:
devmachine packages install https://example.com/alice/tools.git@v1
This page says what that brings onto your computer and your machines, and why some of its widgets ask before they run.
Three kinds of package
Every widget comes from a package, and the package says how far its
widgets are trusted (trust in devmachine widgets list --format json):
- official — the pinned packages release. You chose it with
packages pin, and every change to it is reviewed before a release. - local — a package you wrote in
<config>/packages/. - third-party — a package installed from a git address. Its folder
has a
.devmachine-source.ymlsaying where it came from and which commit.
What waits for you
A third-party widget that runs something — a command, a prompt, a
session, or a package provider — shows Allow and Deny in the app
before it runs, the same as a widget an agent wrote in a
board. A widget that only reads the
app’s own data or a web address does not ask.
What you approve covers the widget’s source, filled in, and the package’s
commit. packages update that moves to a new commit asks again, because
the code behind the widget changed.
Official and local widgets do not ask: you chose that release, or you wrote the package.
What installing does, and what it does not
packages install fetches one commit, checks it, and shows you what it
brings before writing anything: its widgets and which run code, the
commands of its entrypoint, its providers, every executable file, every
task file, every other file of the role that decides what those tasks do
(meta/, library/, templates/, vars/ and the like) and the
credentials it asks for. It refuses:
- a name the pinned release has, because a package of yours always wins over the release’s and would replace it on every machine;
- a name you already have in
<config>/packages/, your own or one installed before (packages updateis how a package from a git address changes); - a file name or a line of what it brings holding a character that moves
or hides text in a terminal, such as ESC or a Unicode bidirectional
override: such a name could erase the lines above the prompt, the
“runs as root” warning included, and you would answer a question you
cannot read. A file name that is not UTF-8 is refused for the same
reason. The prompt also prints any such character as an escape, in
case one gets past, and so does every error that quotes the
repository: git’s output, a problem in its
package.yml, a link’s name; - a link that leads outside the package, which would copy a file from your computer to your machines;
- a package that does not validate.
Installing touches no machine. Adding it to a machine is the step that
matters: packages add <name> --machine <m> and sync run its Ansible
tasks there as root, like every package’s. The widget approval does not
cover that. Read its tasks before you add it; install lists them.
When the release gains the same name later
install checks the name against the release pinned at that moment. A
later packages pin can bring a release that has a package of the same
name. Your copy would then win on every sync and replace the official
one without a word, so sync refuses instead, before it connects. It
does not pick one for you: the two packages share a name, not an
author. Take the third-party one off (packages rm, then packages remove), or pin an earlier release.
Why there is no signature
Nothing checks who wrote a third-party package. A signature would say who published it, not whether its code is safe, and you would still have to decide. So the CLI makes the decision visible instead: it shows what the package brings and asks, and its widgets that run code ask again in the app. A list of trusted publishers may come later.
Why a package provider runs through the CLI
A widget that reads a package’s own command, such as
devmachine-app/stats, runs devmachine run --package <package> --machine <m> --no-log -- <command> --<key> <value>. The CLI already
knows the machine’s address, account and key, and refuses any command the
package does not list in commands. The app never builds a shell line:
each value of the widget’s with arrives as one word.