steam-deck-utilities
steam-deck-utilities copied to clipboard
[Discussion] Support Distribution via Flatpak
More of a discussion - is there any interest in distributing the app via a flatpak method? The application would need some pretty hefty permissions due to the system modifications it is making, but I might be able to make an MVP if there is interest.
If viable, benefits would include an easier means of distribution for steam deck users via the Discover store.
I'm admittedly not an expert in Flatpaks, but from a short bit of research it appeared that even wide-open permissions wouldn't be permissive enough to write to sysfs and procfs. Am I incorrect on that?
I also need the permissions granted to be very granular, per function. In this case, I need the permissions to have the deck user's privilege for moving and symlinking files as to keep permissions correct without bulk-applying random permission bits, while simultaneously allowing for sudo only when needed for the tweaks.
Like I said, I'm definitely not a flatpak expert, but I'd be very interested to hear if these things are possible since I was under the impression that it was an all-or-nothing "least privilege", and unable to write to the correct filesystems.
I am also far from an expert on flatpak, but my thought would be to use flatpak-spawn to run the necessary commands on the host. I might try to mess around and whip up a prototype to see if it is viable.
Hmmm good catch, I didn't know about the --host parameter. Quite a few of the features would need that, almost the entire project, but I really look forward to your research!
Early results - I think this is possible.

Don't worry, that isn't really my password.
My standard user can't write to /usr/bin without sudo, so this shows I am able to affect host using sudo. The only annoying thing is that I can't seem to keep the sudo authorization between invocations of flatpak-spawn, but there may be something simple I am missing. However, this can be solved by just piping the password for each command requiring sudo. And yes, that is the GUI running within the flatpak.
Interesting, very interesting. I'm still curious to know if we can do all functionality this way without complicating the program an undue amount, but it's looking very promising. 🤔
Interesting, very interesting. I'm still curious to know if we can do all functionality this way without complicating the program an undue amount, but it's looking very promising. 🤔
My initial thought would be to keep this somewhat transparent would be to replace calls to exec.Command(...) with another function that is aware if it is in a flatpak. If within the flatpak, it would invoke the commands via a call to exec.Command(...) using the method outlined above - otherwise it would invoke commands as it is currently implemented. There is a little more overall complexity there, but it would allow for both flatpak and non-flatpak versions of the application that share a lot of logic.
I will try to hack on this more this weekend - I'll post a draft PR if I have positive findings.
Update: I have almost the entire app working within the flatpak - the big issue I am running into is getting setUnitValue(param string, value string) error working. Once that is done, we should be good to go. I am exploring another tool (https://github.com/1player/host-spawn) to make the code a little cleaner.
The prototype has a new function (still WIP, so the end product will likely look different):
func createCommand(command string, args ...string) *exec.Cmd {
if !isAppWithinFlatpak() {
return exec.Command(command, args...)
}
hostSpawnCmd := "host-spawn"
hostCommandArgs := []string{command}
hostCommandArgs = append(hostCommandArgs, args...)
fmt.Println("Executing command ", hostSpawnCmd, hostCommandArgs)
cmd := exec.Command(hostSpawnCmd, hostCommandArgs...)
if command == "sudo" {
stdin, err := cmd.StdinPipe()
if err != nil {
CryoUtils.ErrorLog.Println(err)
return nil
}
_, err = stdin.Write([]byte(CryoUtils.UserPassword + "\n"))
if err != nil {
CryoUtils.ErrorLog.Println(err)
return nil
}
}
return cmd
}
Functions that used exec.Command now look like
func disableSwap() error {
CryoUtils.InfoLog.Println("Disabling swap temporarily...")
_, err := createCommand("sudo", "swapoff", "-a").Output()
if err != nil {
return fmt.Errorf("error disabling swap")
}
return err
}