xdg-desktop-portal
xdg-desktop-portal copied to clipboard
Allow a flatpak to discover/call another flatpak
Hi!
GIMP has this nifty new feature for opening RAW images, which is we use third-party advanced RAW software for the job. The idea is: why bother reimplementing a shitty 3-slider RAW developer as a plugin which will mostly be a toy when there are really good complete ones, and several being Free Software, like darktable or rawtherapee. So now when someone tries to open a RAW in GIMP, we detect installed darktable and rawtherapee and redirect the call to the user's favorite RAW software (if both are installed, one can choose one's favorite in GIMP's preferences). When the editing stops on this third-party software, it automatically sends back the result to GIMP for further editing.
Unfortunately this feature stopped working with flatpak. Flatpaked GIMP don't see DT/RT installed by the system packaging, nor installed as flatpak.
Let's say we don't mind not detecting non-flatpak DT/RT. After all, flatpak is a sandbox and one of the goal is to shield the system. But couldn't there be an API to detect other flatpaks and being able to run them? I see there is a darktable flatpak for instance. It would be awesome if we could detect it within GIMP flatpak and run org.darktable.Darktable.
You can open URIs but it doesn't work for local file://
's so that is probably the best way to expose that. It does create an easy sandbox escape but the user will have to explicitly allow it.. don't know if that makes it ok.
How would it work exactly? I'm not sure I understand your workaround.
For info, right now (out of flatpak), we discover the third-party app in a Linux OS by simply checking its availability in the path.
You let the host handle file associations and you would just pass it the raw file.
Not all RAW software will work well with GIMP (currently only darktable and rawtherapee). There is a coded layer between GIMP and the RAW software. This specific feature has been closely created together by developers of all 3 projects (GIMP, darktable and RawTherapee) so that there is a back-and-forth interaction/connection.
In particular if we just let file association handle the file, once the user is done developing the RAW, the third party software would not give back the result to GIMP. And that also means we can't just give the file to the RAW software in a common way. GIMP actually runs a command with specific options. Here is the workflow currently:
- the photographer opens a RAW file in GIMP;
- GIMP automatically opens the RAW file in DT/RT;
- the photographer tweaks/develops the image in DT/RT;
- when finished, the photographer closes DT/RT (no need to save/export anything);
- GIMP automatically loads the result of the developed RAW;
- the photographer can continue working on the image in GIMP.
So no, we can't just let the host handle file association. The point is not to redirect the file towards some other software and not hear about it anymore. That feature would make no sense at all (if that's what we want to do, we'd just say we don't support RAW images). The point is acknowledging that the RAW development is better done in a specialized software, but further image editing are better done in GIMP. This is basically a workflow where various more specialized software work together seamlessly and are connected, sharing images and work-in-progress at different steps, like a "Free Software graphics suite" or something.
Well, thats just not how sandboxes work: they isolate applications from each other and from the host system. If you want deep integration, you can ship the auxiliary software as part of your flatpak, and have it run in the same sandbox. Otherwise, you may need an api between the two apps to facilitate this sort of interaction. A d-bus api would be much easier to integrate in the existing tooling than launching the app with special options.
Ok we'll discuss this. It may indeed be better than a flatpak-specific communication protocol. I opened this on our side: https://bugzilla.gnome.org/show_bug.cgi?id=791362
This is basically a portal question, so moving it over to xdg-desktop-portal
Given an application-specific D-Bus API, and --talk-name=com.example.Foo
in the app which needs to be able to tell Foo to do things, that app can tell whether Foo is installed by either trying to call a method on it (which would fail if it's not installed) or calling ListActivatableNames
on org.freedesktop.DBus
(the bus daemon itself, not the portal) to see whether it's present. The other half of this would be a “please install this app” portal.
The status quo is that, if you don't mind being GNOME specific and your app can talk to org.gnome.Software
, you can tell it to show the details page for an app:
org.gtk.Actions.Activate("details", [GLib.Variant("(ss)", ("system/flatpak/flathub/desktop/org.gnome.dfeet/stable", ""))], {})
Or to install it (without user confirmation):
Activate("install", [GLib.Variant("(su)", ("system/flatpak/flathub/desktop/org.wesnoth.Wesnoth/stable", 1))], {})
Aside from the GNOME-specificity, there are some big caveats:
- You have to know the remote name and branch name for the app
- You can't tell when the app has been installed (or cancelled) without polling
I imagine that a better interaction would be (on GNOME):
- App calls
InstallApp(window_id, "com.example.Foo", {"preferred-remote": "org.flathub.Stable"})
- If Foo is not installed:
- User gets a cut-down version of the GNOME Software details page as a dialog, where they can choose to install Foo or cancel
- Once they do one or the other,
Response
fires
- If Foo is already installed:
-
Response
fires at once? Unfortunately, this would allow any application to test for what other applications are installed, which has been deemed undesirable in the past, though at least the user would be spammed with dialogs for each app that's not installed so it would be visible…
-
- Assuming success, App can now activate and talk to Foo over D-Bus
Seem plausible? @manuq can you comment from the point of view of wanting an API like this?
One thought was whether we could instead add a hint to OpenURI
/ OpenFile
of the preferred application to use to open something, and in the case where that app is not installed, the portal could offer to install it.
@wjt yes, your description matches exactly what an ideal portal API would look like.
- Ability to check if another app is installed from a flatpaked app.
- Ability to offer the user to install the app if not installed (through gnome-software in GNOME).
- Continue the original app flow once the other app is installed.
And I agree, making this a user-facing portal mitigates the sandbox flaw of discovering other apps.
Regarding app A start talking to app B, our use-case only needs app B installed. We talk to gnome-shell to launch the other app with the org.gnome.Shell.AppLauncher.Launch()
DBus method, and if that doesn't work we try org.gtk.Application.Activate()
.
I think sneaking this into OpenURI might not work for all cases - you might not have a uri / file to open with the app, but instead a D-Bus request to make, or sth.
The overall plan sounds good to me.
If we are concerned about discovering installed apps, we could add a separate permission for that.
One thing that I'm really not sure about is how you'd solve that original workflow without either questions that the user can't answer naturally, or a lack of question that would mean that too much is opened.
What would the UI look like for those apps? How do you trigger those links/API calls between applications?
From afar, it looks like a souped-up "OpenURI" with a signal back when editing is done, an "Edit In..." call, something which Android's Intent can already do.
Should this be part of a wider "Sharing" mechanism between apps instead? Or do we really want to poke holes just between those applications and no others?
Or do we really want to poke holes just between those applications and no others?
On that, applications from the same domain on iOS have more open permissions between themselves, so that Google's YouTube application can offer to open a location in Google's Maps application rather than in the native/default maps application. I don't know if there's a mechanism to do that for arbitrary applications though.
Or do we really want to poke holes just between those applications and no others?
On that, applications from the same domain on iOS have more open permissions between themselves, so that Google's YouTube application can offer to open a location in Google's Maps application rather than in the native/default maps application. I don't know if there's a mechanism to do that for arbitrary applications though.
It looks like Universal Links allows one iOS app to open another by attempting to opening a URL. A special file on the web server is checked at the time the target app is installed to determine whether it allows other apps to make it open a given URL.
Notably, if the app is not installed, attempting to open a supported URL will open the URL in Safari; it won't take the user to the app's detail page in the App Store. The rationale is the user always gets an immediate, meaningful view of the resource without being prompted to install the app. But the website can have a clear "install the companion app for a better experience" link.
The Android approach is more flexible and respectful of the user's wishes though, so it seems like a better model for us. Eg, if there's an OpenStreetMap-based app that claims to support URLs like "https://maps.google.com/*", the user can set the default handler. My understanding is, on iOS, only the controller of maps.google.com is allowed to determine which (at most one) app is allowed to handle each URL regex starting with its domain.
Though I guess the original topic is best supported by a custom URI. The specific apps that support it could register as supporting that scheme and the calling app won't have to hard-code their names or URL structures. Opening a generic URI is a bit different than a service-specific URL though and only the former is appropriate for passing arbitrary local data.
Sorry if the last comment was a bit of a divergence :)
https://lists.freedesktop.org/archives/xdg/2014-January/013068.html - Problem 3 here is somewhat relevant.
I see 3 use cases:
- Actually communication between apps via dbus or some other ipc.
- Basically a safer version of flatpak-spawn flatpak run
- Share a file/socket between 2 app (The orginal question).
Number 2 is needed for browser native messaging host system like firefox's. It could also allow run extensions that just provide binary run with another set of permissions. Inkscape has a cli and sandboxed latex will help alot.
Just piling onto the discussion here. Other cross-flatpak dependencies (already packaged) are that the OpenShot app leverages both Blender and Inkscape.
One of the nice things about Flatpak is that the packaged apps don't need to be flatpak aware. Seems like many of the suggestions fundamentally want to change how programs are executed under linux. If an app has a preferences dialog to specify the path to another executable, why shouldn't I be allowed to type "flatpak run my.app.Here app
The simple solution, of course, is to build flatpaks with combinations of apps. Need access to another app? Bundle it. This also gives the package maintainer control over the version bundled.
I think no adaption is required, you just need some patches to have – as you said – some script in a bin
dir that then executes the flatpak run
command. Patches, to make applications work better in flatpaks, are already used in many apps today on Flathub e.g., so this is not a problem IMHO.
I don't believe you can currently call flatpak from within flatpak. Check out the following and tell me what I'm missing:
[bnordgren@mine Title]$ flatpak ps
Instance PID Application Runtime
2257365655 327395 org.openshot.OpenShot org.kde.Platform
[bnordgren@mine Title]$ flatpak enter 2257365655 bash
[📦 org.openshot.OpenShot ~]$ flatpak ps
bash: flatpak: command not found
Need to have a command that runs before you can point an app to the command you want to run.
No you cannot, but if this was implemented, the application inside the flatpak would not need to be adjusted, you would likely only need to put in some wrapper scripts for the used binaries.
Lets raise this to flatpak to handle fine grained permission to flatpak(for all flapak commands, usefull fo flatseal software centre and this case) and also permission to expose ipc stored in $XDG_RUNTIME/app.
Randomly casting about the internet, I learned about flatpak-spawn, which in line with my example up above should work like:
[bnordgren@mine]$ flatpak run --command=bash org.openshot.OpenShot
[📦 org.openshot.OpenShot]$ flatpak-spawn --host flatpak run org.blender.Blender
Read prefs: /home/bnordgren/.var/app/org.blender.Blender/config/blender/2.91/config/userpref.blend
/run/user/1000/gvfs/ non-existent directory
found bundled python: /app/blender/2.91/python
DEBUG:BlenderGIS-master.core.checkdeps:GDAL Python binding unavailable
DEBUG:BlenderGIS-master.core.checkdeps:PyProj unavailable
DEBUG:BlenderGIS-master.core.checkdeps:Pillow unavailable
DEBUG:BlenderGIS-master.core.checkdeps:ImageIO Freeimage plugin available
That opened the standard blender gui. Tweak the command line to put in the required command line arguments, and viola. From the man page (guess it still pays to RTFM):
Unlike other flatpak commands, flatpak-spawn is available to applications inside the sandbox. It runs COMMAND outside the sandbox, either in another sandbox, or
on the host.
flatpak-spawn uses the Flatpak portal to create a copy the sandbox it was called from, optionally using tighter permissions and the latest version of the app and
runtime.
@bnordgren That looks interesting, though I can't seem to get it to work myself. With --host
I get the error Portal call failed: org.freedesktop.DBus.Error.ServiceUnknown
. I also tried with --sandbox
but with that I get bwrap: execvp flatpak: No such file or directory
. Does using this require a special permission?
Not that I know of. I just did that as a regular user on fedora 33 silverblue. My user account is in sudo/wheel, as it's the only non-root account on the system, but I didn't actually use any elevated privileges as far as I know. I'm what you might call a remedial flatpak user, not an expert. Others may have more insight.
@Taiko2k flatpak-spawn's man page literally mentions you need to provide D-Bus access to org.freedesktop.Flatpak
, granted this unto itself probably barely makes any sense to anyone unfamiliar with D-Bus and talk permissions in Flatpak.
Depending on the Flatpak you're trying to flatpak-spawn from you may already have talk access to that interface. That's probably why yours and @bnordgren's results differed.
Try testing flatpak-spawn from any sandbox using the following:
flatpak run --talk-name=org.freedesktop.Flatpak --command=sh com.flatpak.Choice
.
You may then make the changes permanent by registering the permission with flatpak-override. Hope this helps.
@kon14 Just out of curiosity, it looks to me that once these talk permissions are established, flatpak-spawn
essentially acts like a get out of jail free card for any process in the sandbox. While useful in this case, it sure seems that this defeats at least the process-containment aspect of a sandbox. Is this accurate, or are there other controls which would prevent me from running arbitrary commands on the host (like sudo rm -rf /*
) from within the sandbox?
We definitely need fine grain permissions on flatpak-spawn. maybe we should have a sandbox flatpak command in a flatpak. flatpak-spawn flatpak override is a security time bomb. most people just need flatpak run or a few host commands. May be take queques from polkit
Just wanted to bring up my browser integration use-case and the associated pain-points..
I have installed org.mozilla.firefox (for DRM stuff) and com.microsoft.Teams on Alpine Linux desktop (as google's DRM plugin windivine and msteams are proprietary software with no musl binaries .. obviously).
I received an email with invitation link to a Teams meeting.
If I click on the link in sandboxed friefox, the page it redirects to has an iframe with src value that looks like msteams:/l/meetup-join/19....
(long string). The page itself prompts to open msteam but naturally it is not discoverable in firefox sandbox.
Insult to the injury, there is currently no way to sign in using an invitation link in their desktop app..
So my workaround is to HTML decode that src value with quotes "msteams:/l/meetup...."
and launch teams from suckless terminal:
$ flatpak run \
--env=DBUS_SESSION_BUS_ADDRESS='unix:abstract=/tmp/dbusLcsUc61GBB,guid=c1005d826f37b49af839644f60bc4df0' \
-vvv com.microsoft.Teams "msteams:/l/meetup-join....."
# that --env thingy is yet another workaround for https://github.com/flathub/com.microsoft.Teams/issues/61
what an ideal portal API would look like.
- Ability to check if another app is installed from a flatpaked app.
There may be a fingerprinting risk here if flatpak apps can identify what other applications are installed on your system
It may be slightly better if they can only tell which protocols / message-types are handled and not exactly which apps are handling them?
An alternative with a worse UX could be to completely disallow any introspection of other protocols/apps, with the suggestion that every app that needs to call out to another one should blindly attempt it and also display instructions on-screen for users to install a recommended app if nothing happens (or, instead of instructions, a flatpak-managed button that streamlines the installation)?
Behavioural advertising has really ruined things for everyone here, and now we need to consider ways to block cross-app tracking :S
@kasperk81
I have installed org.mozilla.firefox (for DRM stuff) and com.microsoft.Teams on Alpine Linux desktop (as google's DRM plugin windivine and msteams are proprietary software with no musl binaries .. obviously).
I have a similar setup with Firefox, Discord, Slack, Zoom, and a whole bunch of similar apps all installed via flatpak, and I am able to click links in Firefox that result in being handled over in the Slack and other flatpak apps just fine
So, I believe the issue you've encountered is specific to Microsoft Teams and should be reported to them?
The issue, however, is still a good example of how easy it is for app developers to improperly register protocol handlers, etc, so maybe there's a documentation or other outreach fix that can be done here?