Warning

This is not meant for novice users. This is for those who (1) really like sandboxed apps, (2) like to tweak the permissions of their apps, and (3) are comfortable using the command line.

And of course, don’t trust strange scripts on the internet without at first reading them and understanding them.

About

This script uses flatpak as the sandbox, which is convenient as it allows you to use existing tools like Flatseal to configure the sandbox.

I built this because I like my apps sandboxed. I will mostly use it for playing games. This will let me tailor the permissions the games need perfectly, ie no network for single player games and network for multiplayer games.

And to answer the question of “Why not Steam/Heroic/Bottles/Lutris”. There’s a few reasons. This script keeps games sandboxed from each other, is lighterweight, and was made for my own enjoyment.

I’ve also been working on a bubblewrap version that reuses host binaries and libraries which I may share later. It’s lighter weight and in theory more secure (less shared with host, better for browsers thanks to having access to unprivileged namespaces).

But anyways…

How to use

There are two files. The script, called “sandbox”, and the template flatpak manifest, called “sandbox.yml”.

To run the script, manually install “sandbox.yml” to ~/.local/share/sandbox/sandbox.yml. Or, you can edit the script and update the variable TEMPLATE_PATH to where ever you want.

Then, to create a new sandbox, you just need to run ./sandbox app-name.

The script roughly does the follow

  1. Makes sure you entered a valid app name
  2. Makes sure you have flatpak-builder installed (will install it if not, also note the script uses a user remote)
  3. Builds an empty flatpak that depends on Freedesktop 25.08 runtime
  4. Installs the flatpak

The first time the app is run (flatpak run my.custom.app-name), a script will be created inside the sandboxed home called “start”. On subsequent launches, this is executable that will be run.

Say if I wanted to play a Windows game, I would set the start script to umu ./path/to/game.exe.

But it can do anything. I also tested with the firefox .tar.xz and it worked without issue.

The script

sandbox.yml

app-id: domain.publisher.Name
runtime: org.freedesktop.Platform
runtime-version: '25.08'
sdk: org.freedesktop.Sdk
command: launcher
finish-args:
  - --persist=.

modules:
  - name: launcher-setup
    buildsystem: simple
    build-commands:
      - mkdir -p /app/bin
      # verify if 'start' exists. If not, create a default Hello World script.
      - |
        sh -c 'cat > /app/bin/launcher <<EOF
        #!/bin/sh
        TARGET="\$HOME/start"
        
        if [ ! -f "\$TARGET" ]; then
          echo "Creating default start script at \$TARGET..."
          echo "#!/bin/sh" > "\$TARGET"
          echo "echo Hello World from the sandbox!" >> "\$TARGET"
          chmod +x "\$TARGET"
        fi
        
        exec "\$TARGET" "\$@"
        EOF'
      - chmod +x /app/bin/launcher

sandbox:

#!/bin/bash

# bash safety options
# -e exits on failure
# -u exits on unknown variables
# -o pipefail exits on failed pipe
set -euo pipefail

################################################################################
### Usage Clause ###
####################

# make sure argument is provided for APP_NAME
if (( $# != 1)); then
    echo "Error: too few or too many arguments"
    echo "Usage: $0 app-name"
    exit 1
fi

################################################################################
### Configuration ###
#####################

# app details
DOMAIN="my"
PUBLISHER="custom"
APP_NAME="$1"

# flatpak manifest template location
TEMPLATE_PATH="$HOME/.local/share/sandbox/sandbox.yml"

################################################################################
### Functions ###
#################

# make sure flatpak version of flatpak-builder is installed
# installs it if necessary
check_dependencies() {
    if ! flatpak list --app | grep -q org.flatpak.Builder; then
        echo "flatpak-builder is not installed, installing now..."
        flatpak --user remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
        flatpak --user install --noninteractive flathub org.flatpak.Builder
    fi
}

# make sure a string only contains letters, numbers, and dashes
validate_string() {
    local input="$1"
    local var_name="$2"
    if [[ ! "$input" =~ ^[a-zA-Z0-9-]+$ ]]; then
        echo "Error: $var_name ('$input') contains invalid characters."
        echo "Allowed: letters, numbers, and dashes only."
        exit 1
    fi
}

cleanup() {
    # check if WORK_DIR exists to avoid errors if mktemp failed
    if [[ -d "$WORK_DIR" ]]; then
        echo "Cleaning up temporary directory..."
        rm -rf "$WORK_DIR"
    fi
}

################################################################################
### Execution ###
#################

# ensure flatpak-builder is installed on host or as flatpak
check_dependencies

# validate DOMAIN, PUBLISHER, and APP_NAME
validate_string "$DOMAIN" "DOMAIN"
validate_string "$PUBLISHER" "PUBLISHER"
validate_string "$APP_NAME" "APP_NAME"

# finalize app ID
APP_ID="$DOMAIN.$PUBLISHER.$APP_NAME"

# create temporary directory in current directory
WORK_DIR=$(mktemp -d -p "$PWD")

# check if WORK_DIR was created
if [[ ! "$WORK_DIR" || ! -d "$WORK_DIR" ]]; then
    echo "Error: could not create temp dir"
    exit 1
fi

# run cleanup function on successful exit or on failure
trap cleanup EXIT

# copy flatpak manifest to WORK_DIR
cp "$(realpath "$TEMPLATE_PATH")" "$WORK_DIR/$APP_ID.yml"

# edit the app ID of the flatpak manifest
sed -i "s/domain.publisher.Name/$APP_ID/g" "$WORK_DIR/$APP_ID.yml"

# build the flatpak
flatpak run --filesystem="$WORK_DIR" org.flatpak.Builder \
    --user \
    --force-clean \
    --repo="$WORK_DIR/$PUBLISHER" \
    "$WORK_DIR/build_dir" \
    "$WORK_DIR/$APP_ID.yml"

# install the flatpak
flatpak --user install --noninteractive --reinstall "$WORK_DIR/$PUBLISHER" "$APP_ID"

Developer @ashx64@lemmy.world

    • cm0002@lemmings.worldOP
      link
      fedilink
      arrow-up
      1
      arrow-down
      2
      ·
      18 hours ago
      Why am I cross-posting .ml content?

      I cross-post from .ml to the nearest relevant non-.ml comm to reduce the influence of .ml comms and indirectly, the instance as a whole, to make it an easier decision for other instance admins to defederate because one key reason I identified that admins don’t want to defederate is because .ml still has some very large comms and some niche comms.

      Megathread on the issue

      Some highlights from the link:

      "Don’t worry guys, the Uyghur Genocide was REALLY just birth control! ~dessalines, .ml admin, dev https://lemmy.world/post/30580167

      “See! nobody died IN Tiananmen Square, just AROUND it, so it doesn’t count!!” ~ Davel, .ml admin https://lemmy.world/post/30673342

      .ml admin, Nutomics continued transphobia https://lemmy.world/post/29222558 The original transphobic Comment from Nutomic: https://lemmy.world/post/18236068

      “NK is actually good and anything counter to that is Western propaganda!” ~dessalines, .ml admin, dev https://lemmy.world/post/31595035

      General negative sentiment to other instances who haven’t “seen the way” yet ~davel, .ml admin https://lemmy.world/post/27426510

      “If you don’t support Russia then you just don’t understand geopolitics” ~dessalines, .ml admin, dev https://lemmy.world/post/27352415

      And so so much documentation on clear heavy handed censorship and bias also on the link. So much I can’t even put them all here because this comment would be really long.

      I believe the behavior of its admins (the main admins are Lemmy devs) does harm to the overall growth of the Lemmy-verse and maybe even the Thrediverse (since Lemmy kinda kicked off the Thrediverse) because of its association with the devs of Lemmy and their insistence to use .ml as their personal political platform to spread harmful propaganda

      On the outside, bringing up Lemmy frequently leads to comments like “Lemmy? Isn’t that the place with a bunch of tankies?” Or “Tried Lemmy, but found it full of pro Russia crap so I left”. The best way forward from that I see is to either widely defederate from .ml like the rest of the Triad, or pressure them to put a fair and unbiased as possible admin team.

        • cm0002@lemmings.worldOP
          link
          fedilink
          arrow-up
          1
          arrow-down
          1
          ·
          17 hours ago

          Why are you so concerned with my vote? I’m not the only one downvoting when you post them lmao

          • AmbiguousProps@lemmy.today
            link
            fedilink
            English
            arrow-up
            3
            arrow-down
            1
            ·
            10 hours ago

            It’s pretty depressing that you downvote those who do what you fail to do: link to the original post instead of basically stealing their content. It is very confusing to me why this is so difficult to understand and fix, it’s literally built in to most apps, such as boost.

            Yet, you downvote the people who rightfully link to the OP. And no, linking the author (like you’ve done with one of my posts in the past) does not count. I didn’t know that you stole reposted one of my posts until months later, for example.

              • AmbiguousProps@lemmy.today
                link
                fedilink
                English
                arrow-up
                2
                arrow-down
                1
                ·
                edit-2
                8 hours ago

                I’m unfortunately going to repeat myself twice in this post (since you did), these do not work in all frontends so it looks like you’re just stealing content. Posting the link directly in the post will always work, 100%. A link to the original author is not the same as a link to the original post, and cross post functionality in Lemmy is buggy at best.

                If your problem is linking to ML, then you are defeating the point by your own logic, since according to you, all Lemmy frontends automatically add the cross post link to ML to your posts. You just shouldn’t rip posts from ML if you have a problem with merely linking to the OP and giving the original author proper credit.

                It makes it worse if you’re actually using Boost to do these reposts, because that means that you’re actively stripping the automatic cross post link out.

                • cm0002@lemmings.worldOP
                  link
                  fedilink
                  arrow-up
                  1
                  ·
                  edit-2
                  2 hours ago

                  I have done this for many months now, across thousands of crossposts, I know the crossposting system. It isn’t buggy and that menu is available on most web clients and apps from the default Lemmy UI to tesseract to photon to alexdrite to boost and voyager to jerboa. There’s the rare occasional hiccup, but it’s mostly due to fighting these stupid image proxying rules some instances do. Certainly doesn’t happen frequently enough to justify a direct link, which does in fact harm these efforts

                  Show me an app that doesn’t

                  If your problem is linking to ML, then you are defeating the point by your own logic, since according to you, all Lemmy frontends automatically add the cross post link to ML to your posts. You just shouldn’t rip posts from ML if you have a problem with merely linking to the OP and giving the original author proper credit.

                  It’s in a sub menu, adding just enough friction

                  It makes it worse if you’re actually using Boost to do these reposts, because that means that you’re actively stripping the automatic cross post link out.

                  I actually don’t, it’s too slow for my work flow

                  But, I do actively strip it from my own posts that I’m crossposting. Why? Because I despise it, it’s clear it’s just a left over from before the crossposting system was robust and the clients had their own dedicated menu for it. And it causes stupid shit like this to the posts:

                  Link

                  Link

                  Link

                  Actual body text

          • commie@lemmy.dbzer0.com
            link
            fedilink
            arrow-up
            2
            arrow-down
            1
            ·
            16 hours ago

            because you reposted it without liking to the op. it’s like you are trying to stop people from engaging with the author

            • AmbiguousProps@lemmy.today
              link
              fedilink
              English
              arrow-up
              2
              arrow-down
              1
              ·
              10 hours ago

              They never properly cross post content. Sometimes they’ll add the author of the post, but it would be much better to properly link to the original post. I’ve tried and failed to explain this to them in the past. This, along with their dozens of accounts, makes them seem like a spam network that steals content from other users. It’s giving reddit karma farming, except here karma isn’t supposed to matter. Note that I don’t think they are a spam network, but this user behaves like one and refuses to ever make changes to the way they post.

              They claim to be “supporting smaller instances” simply by posting with accounts from said instances, even though most people don’t care to look at instances of users. In reality, they’re giving these instances a bad reputation since they look like they’re supporting a spammer.

              Every time this is brought up, they post the same BS spoiler that doesn’t ever acknowledge the root of their distasteful behavior, and downvote those who call them out. It’s super fucking annoying.

              • cm0002@lemmings.worldOP
                link
                fedilink
                arrow-up
                1
                arrow-down
                1
                ·
                edit-2
                9 hours ago

                They never properly cross post content.

                Meanwhile, in the land of reality, my last 6 .ml crossposts on this account of time of commenting:

                • AmbiguousProps@lemmy.today
                  link
                  fedilink
                  English
                  arrow-up
                  2
                  arrow-down
                  1
                  ·
                  edit-2
                  8 hours ago

                  As has been explained to you before by me and many, many others: this functionality does not always work as expected, and a bunch of apps are missing it entirely. That is the reality.

                  Do you know what would always work?

                  A link to the OP directly in the post. Just like you did for the author, but for the actual post. If you’re using Boost, why are you removing the cross post text that is automatically included for this very reason? It really makes it seem like you just want to take others posts and claim them as your own.

                  This would also work for the meme reposts you do, by the way.

                  • cm0002@lemmings.worldOP
                    link
                    fedilink
                    arrow-up
                    1
                    ·
                    2 hours ago

                    I have done this for many months now, across thousands of crossposts, I know the crossposting system. It isn’t buggy and that menu is available on most web clients and apps from the default Lemmy UI to tesseract to photon to alexdrite to boost and voyager to jerboa. There’s the rare occasional hiccup, but it’s mostly due to fighting these stupid image proxying rules some instances do. Certainly doesn’t happen frequently enough to justify a direct link, which does in fact harm these efforts.

                    Show me an app that doesn’t do it

                    It’s in a sub menu, adding just enough friction

                    A link to the OP directly in the post. Just like you did for the author, but for the actual post.

                    Adding the author is for attribution and the tag maintains the friction, a user has a direct link to the author and it’s up to them to seek it out, I will not be the one to give them easy access

                    If you’re using Boost, why are you removing the cross post text that is automatically included for this very reason? It really makes it seem like you just want to take others posts and claim them as your own.

                    I actually don’t, it’s too slow for my work flow

                    But, I do actively strip it from my own posts that I’m crossposting. Why? Because I despise it, it’s clear it’s just a left over from before the crossposting system was robust and the clients had their own dedicated menu for it. And it causes stupid shit like this to the posts:

                    Link

                    Link

                    Link

                    Actual body text

              • commie@lemmy.dbzer0.com
                link
                fedilink
                arrow-up
                2
                arrow-down
                1
                ·
                15 hours ago

                they posted it elsewhere, and there is no reason to expect they’d see any feedback posted here.