Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
bf0d215
Add Arch Linux PKGBUILD for the full elp stack (daemon + CLI + GUI)
filippo-ferrando Sep 11, 2026
c77a5f4
Fix build-arch.sh PKGBUILD templating with a '#' in the git ref source
filippo-ferrando Sep 12, 2026
4edabeb
Fix PKGBUILD pkgver()/CMake versioning for a tagless checkout
filippo-ferrando Sep 12, 2026
c2b71e7
Add missing meson/flex/libcap-ng/attr makedepends for the vendored QE…
filippo-ferrando Sep 12, 2026
555f35f
Add python-distlib makedepend for QEMU's mkvenv bootstrap
filippo-ferrando Sep 12, 2026
689c9d6
Use an annotated git tag so CMake's versioning.cmake can describe it
filippo-ferrando Sep 12, 2026
1965912
Use v1.0.0-dev as the local dev tag, not v0.0.0-dev
filippo-ferrando Sep 12, 2026
b340ed6
Add rust makedepend for the rxx/ cargo build
filippo-ferrando Sep 12, 2026
3bffbc0
Declare libkeybinder3 (AUR) as a runtime dependency, document the AUR…
filippo-ferrando Sep 12, 2026
9b9af0f
Relax two -Werror warnings that only newer toolchains trigger
filippo-ferrando Sep 12, 2026
68d82eb
Silence a GCC 16 false-positive -Werror=array-bounds in elp_api
filippo-ferrando Sep 12, 2026
86108a9
Declare elpd.service as a proper local source, not a bare sibling file
filippo-ferrando Sep 12, 2026
9020bff
Fix namcap-found packaging defects: license, deps, RPATH leaks, debug…
filippo-ferrando Sep 12, 2026
6812a85
Add the "intent" feature: named groups of instances (daemon, RPC, CLI)
filippo-ferrando Sep 12, 2026
f7e5588
Actually populate intent/intent_role metadata (was a no-op until now)
filippo-ferrando Sep 12, 2026
db611c9
Add "elp intent add" to add members to an existing intent
filippo-ferrando Sep 12, 2026
39708fa
Rework GUI intent support: dedicated management page, dropdown-only p…
filippo-ferrando Sep 12, 2026
49dcc58
Make IntentMemberLaunchSink generic over the outer reply/request types
filippo-ferrando Sep 12, 2026
e155d78
Allow empty-member intents; carry service_id through intent members
filippo-ferrando Sep 12, 2026
3961a5d
Add intent picker to the Catalogue/Deploy dialog
filippo-ferrando Sep 12, 2026
c777ef4
Let LLM sessions be true intent members, not just VM instances
filippo-ferrando Sep 12, 2026
bb5819d
Package elp-api as its own systemd service
filippo-ferrando Sep 12, 2026
fe7be3d
Add v1 migration feature: elp migrate, mDNS host discovery, GUI
filippo-ferrando Sep 12, 2026
c65f3f8
Enable AUTOMOC in src/platform for MdnsService's Q_OBJECT machinery
filippo-ferrando Sep 12, 2026
fb023c1
Set AUTOMOC as an explicit target property too, not just the variable
filippo-ferrando Sep 12, 2026
5946bee
Add --identity to elp migrate, for ssh key auth as elpd runs as root
filippo-ferrando Sep 12, 2026
900426a
Add a per-known-host default identity file, with a file-picker field
filippo-ferrando Sep 12, 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
53 changes: 52 additions & 1 deletion BUILD.linux.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,57 @@
# Build instructions for Linux

## Environment Setup
## Arch Linux

Build and package the full stack (`elpd` daemon, `elp` CLI, `elp` GUI) as an
installable Arch package:

```
sudo pacman -S devtools
./scripts/build-arch.sh
```

This renders [`packaging/archlinux/PKGBUILD.in`](packaging/archlinux/PKGBUILD.in)
against the current checkout and builds it in a clean, sandboxed chroot via
devtools' `extra-x86_64-build` (bootstrapped automatically on first run).
Pass `--no-sandbox` to build with a plain `makepkg` in the current
environment instead (you'll need every package listed in the rendered
PKGBUILD's `depends`/`makedepends` installed yourself). See
`./scripts/build-arch.sh --help` for all options.

The build compiles Qt6, gRPC, Boost, and QEMU from source via vcpkg (see
`vcpkg.json`) — but only the **first** time: the script bind-mounts a
persistent cache directory (`build/vcpkg-binary-cache` by default) into the
chroot and points vcpkg's binary cache at it, so later builds, even in a
freshly recreated chroot, reuse those binaries instead of recompiling them.
Delete that directory to force a full rebuild from scratch.

Install the resulting package with `pacman -U <package>.pkg.tar.zst`; this
installs `elpd`/`elp`/the GUI, desktop/icon/completion files, and two
systemd units you need to enable yourself (packages don't auto-enable
services on Arch):

```
sudo systemctl enable --now elpd elp-api
```

`elp-api` is the REST/gateway sidecar (`https://127.0.0.1:7777`) the GUI
uses for things like the catalogue's per-service gateway CA fetch — without
it running, deploying anything from the Catalogue fails with a connection-
refused error trying to reach `127.0.0.1:7777/ca.crt`.

Migrating an instance/intent to another host (`elp migrate`, or the GUI's
"Migrate" action) also needs:
- `avahi-daemon.service` running (`sudo systemctl enable --now avahi-daemon`)
for elpd to advertise/discover other hosts on the network. This is
optional — migration itself still works with a manually-added host (GUI's
Migration Hosts page, or `elp migrate --to user@host` directly); avahi is
only what populates the automatic "discovered on network" list.
- Working SSH access (key-based, no password prompt) from this machine to
the target as the user given in `user@host` — migration shells out to
`ssh`/`rsync` under the hood, using your own `~/.ssh/config`/agent, and
`elp` already installed with its daemon running on the target.

## Ubuntu / apt-based distributions

### Build dependencies

Expand Down
14 changes: 14 additions & 0 deletions data/cloud-init-yaml/cloud-init-postgres.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Reference copy of the "postgres" service template used by `elp intent
# create --service postgres`; the daemon embeds this content directly
# (src/daemon/intent_service_templates.cpp) rather than reading this file.
packages:
- postgresql

runcmd:
- |
echo "listen_addresses = '*'" >> /etc/postgresql/*/main/postgresql.conf
echo "host all all 0.0.0.0/0 md5" >> /etc/postgresql/*/main/pg_hba.conf
systemctl enable postgresql --now
systemctl restart postgresql

final_message: "postgres is up, after $UPTIME seconds"
12 changes: 12 additions & 0 deletions data/cloud-init-yaml/cloud-init-redis.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Reference copy of the "redis" service template used by `elp intent create
# --service redis`; the daemon embeds this content directly
# (src/daemon/intent_service_templates.cpp) rather than reading this file.
packages:
- redis-server

runcmd:
- |
sed -i 's/^bind 127.0.0.1.*/bind 0.0.0.0/' /etc/redis/redis.conf
systemctl enable redis-server --now

final_message: "redis is up, after $UPTIME seconds"
56 changes: 56 additions & 0 deletions include/multipass/intent_spec.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (C) Elemento.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

#pragma once

#include <boost/json.hpp>

#include <string>
#include <vector>

namespace multipass
{

// A named group of instances launched together (e.g. "test-app-1" made of a
// redis and a postgres instance). Membership is also mirrored into each
// member instance's own VMSpecs.metadata ("intent"/"intent_role"); this
// registry is the source of truth for the group itself (name, ordered
// members, creation time), independent of any single member.
struct IntentSpec
{
struct Member
{
std::string role; // e.g. "redis", or a user-chosen label for inline members
std::string instance_name; // the launched VM instance's name, or an LLM session's instance_id
std::string kind{"vm"}; // "vm" or "llm"; defaults to "vm" for older persisted records

friend inline bool operator==(const Member&, const Member&) = default;
};

std::string name;
std::vector<Member> members;
std::string creation_timestamp; // ISO-8601

friend inline bool operator==(const IntentSpec&, const IntentSpec&) = default;
};

void tag_invoke(const boost::json::value_from_tag&,
boost::json::value& json,
const IntentSpec& spec);
IntentSpec tag_invoke(const boost::json::value_to_tag<IntentSpec>&, const boost::json::value& json);

} // namespace multipass
84 changes: 84 additions & 0 deletions include/multipass/mdns_service.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright (C) Elemento.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

#pragma once

#include <QObject>

#include <memory>
#include <string>

namespace multipass
{

// Advertised/discovered over the "_elp._tcp" mDNS service type. host_name/host_os/host_arch
// mirror DaemonInfoReply's own fields (see daemon_info's handler); a peer's label is its mDNS
// service instance name (not necessarily its host_name).
struct MdnsHostInfo
{
std::string label;
std::string address; // resolved IP (or hostname, if resolution only gets that far)
std::string host_name;
std::string host_os;
std::string host_arch;
std::string backend;
};

struct MdnsAdvertisement
{
std::string label; // mDNS service instance name for this daemon; default: host_name
std::string host_name;
std::string host_os;
std::string host_arch;
std::string backend;
};

// Advertises this elpd as "_elp._tcp" on the local network and browses for other instances
// of it, so the GUI's migration screen can offer a live list of candidate hosts alongside the
// manually-added "known hosts" list (list_network_hosts merges both — see Daemon::migrate's
// own doc comment on why migration itself doesn't talk to a peer's elpd directly: this service
// is purely informational/discovery, migration itself goes over SSH to the target's own CLI).
class MdnsService : public QObject
{
Q_OBJECT
public:
~MdnsService() override = default;

// Begins advertising + browsing. Safe to call once; browsing/advertising continue until
// this object is destroyed. Never throws — failures (no mDNS daemon reachable, etc.) are
// logged and leave this a permanently-inert no-op rather than taking elpd down with it.
virtual void start() = 0;

signals:
// Always emitted on this object's own thread; connect with an auto/queued connection to
// observe from Daemon's thread (see mdns_service_linux.cpp/mdns_service_macos.cpp — both
// run their platform library's event loop on a dedicated worker thread).
void host_discovered(multipass::MdnsHostInfo info);
void host_removed(std::string label);

protected:
MdnsService();
};

// Returns a platform-appropriate implementation (Avahi on Linux, Bonjour/dns_sd on macOS), or
// a permanently-inert no-op elsewhere (e.g. Windows) so callers never need to branch on
// platform themselves.
std::unique_ptr<MdnsService> make_mdns_service(MdnsAdvertisement advertisement);

} // namespace multipass

Q_DECLARE_METATYPE(multipass::MdnsHostInfo)
7 changes: 7 additions & 0 deletions include/multipass/vm_specs.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ struct VMSpecs
// Marketplace service template id (e.g. caddy_ca_v1). Stored outside QEMU
// metadata so backend metadata refreshes cannot wipe it.
std::string service_id;
// The image alias/cloud-init this instance was originally launched with
// (as given to LaunchRequest, not the resolved local VMImage/YAML::Node
// this becomes once prepared) — otherwise discarded after launch, but
// needed to redefine the same instance elsewhere (migration).
std::string image;
std::string cloud_init_user_data;
std::string remote_name;

friend inline bool operator==(const VMSpecs& a, const VMSpecs& b) = default;
};
Expand Down
Loading
Loading