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

  • AmbiguousProps@lemmy.today
    link
    fedilink
    English
    arrow-up
    2
    arrow-down
    1
    ·
    edit-2
    18 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
      11 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

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

        Why are you just copy and pasting the same comment now? You don’t need to actually spam me. I’m only going to reply to one of these comments.

        When this was last brought up to you, two people said it wasn’t working for their apps: https://lemmy.cafe/comment/15393162

        You ignored them, conveniently. I can also attest that it doesn’t show for Boost a lot of the time, especially when you repost memes that are just images. And before you say it, no, it’s not because I’m defederated with those instances - Lemmy.today isn’t defederated with a single instance. And even if that weren’t the case, you should still link back to the original post to give proper credit, to at least let users know it wasn’t your content.

        You’re not adding friction. You’re basically a freebooter. You don’t care about giving people credit, you appear to want the credit. I don’t understand why this is such a difficult concept for you to understand when multiple people have told you that the cross post button doesn’t appear for them. Probably because you’d rather claim the content as your own with no acknowledgment that it was sourced from someone else. You seem to only be doing it because it adds friction for you: you can’t stand to give a post five seconds of your time because you have to freeboot content as fast as possible, for god knows why. You’d rather look like a spam network than ever give people links to the original post.

        And yes, it does do nesting of posts. Because it is working as intended. It clearly shows that you weren’t the original author. But no, you’d rather mask that as much as you can get away with. You know what I do when I cross post something multiple times? I un-nest it once if it’s nested twice. It’s literally that easy. Oh no… you have to delete a few “>”!! That’s so hard for you, I’m so sorry.

        I don’t care if you hate ML. I hate ML as an instance too. However, I’m not about to steal content from ML users just because I hate the ML admins. You are defeating the purpose of federation and feed curation, and basically acting like a reddit poweruser. You want to put your motivations and ideals onto others, without them even knowing that you’re doing it. Not only are you freebooting, you’re masking your behavior and usually not acknowledging that the post isn’t yours in the comments.

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

          I find your propensity to misrepresent, distort and lie distasteful and find it hilarious that you’re trying to take some higher ground lmao

          I didn’t ignore anyone I literally tried to correct the problem and then replied and explained to them what was probably going on, my reply is in your link. That case was that weird image proxy problem that was rare that I mentioned

          And also, didn’t answer my question and conveniently dodged it, what app specifically doesn’t show the crosspost section?

          As to the rest of your post, I’ve already explained to you multiple times

          1. I attribute OC. Attribution does not require a link back.
          2. The vast majority of what i crosspost are links to news articles, blog posts, videos etc.
          3. What are images or direct posts e.g. memes are just reposts of a repost of a repost and even when they aren’t, it’s a meme, when have memes ever been required to be attributed? On any platform?
          4. I do explain when applicable where I got something when asked about it, prove that I didn’t
          • AmbiguousProps@lemmy.today
            link
            fedilink
            English
            arrow-up
            1
            arrow-down
            1
            ·
            edit-2
            7 hours ago

            I’m not the one that needs the higher ground here, so it’s pointless to say I’m trying to take the higher ground. I’m not the one freebooting content, OC or not. I always properly link posts, not only to give credit for creating or finding the content, but also so that people know there is more discussion elsewhere, including those with screen readers. I also only post from one account, so that people can block my posts if they please. You rapidly switch between accounts, further masking your freebooting behaviors. I mention this not to take the high ground, but to show you that it’s possible and easy to simply provide links in your reposts. I don’t even really care that you repost, I care more about how you’re reposting, which is probably the worst, most user-unfriendly way of doing it that I’ve seen since reddit.

            I didn’t dodge it. There are comments in my link that you continue to ignore. I’m not going to do the reading for you, especially since I already went back and provided you the link which you clearly visited. Because of your laziness or ignorance of the fact that Lemmy’s cross post functionality is buggy at best, it looks like you’re the only one posting to those users. You know what would fix the weird image linking issue? Properly attributing your cross post. It’s not hard, it’s not difficult, and it’s not time consuming. It’s only difficult if you’re reposting hundreds of posts like a spam network, which is exactly what you seem like to users that don’t know any better.

            1. You attribute it, but you don’t link to the original post. This is the entire problem. I know, because you’ve done it to me. I had no idea until I literally looked up my username one day, and saw a post from you “attributing” it to me. I had no clue that this was posted, and missed out on providing explanations. Further, people thanked you for the post, not the original author. You didn’t reply to them to say you’re welcome, but you also didn’t reply to them saying that it was another user’s OC. I honestly don’t give a fuck about you attributing me for my personal case, but it’s mind boggling that you do not see how it could rub someone the wrong way. I only spoke up about it once you did it for the hundredth time on your 14th account, because it’s fucking annoying to see.
            2. Yes, but you don’t connect conversations about these articles together properly for those with apps that do not properly load your cross post. Further, these freebooted cross posts vastly outnumber the articles you find on your own. It would be one thing if you did what most people posting do, and use an RSS feed to post articles, and just happened to post the same article as someone else. But no, roughly 5 minutes after someone posts the article, you post it, because it came up in your feed. This is exactly the issue. I would block you, but you’ll just pop up somewhere else and then claim that you “only” use 5 accounts in a failing effort to boost smaller instances. So, I’d rather comment with the screenshots of your million accounts every time you post the bullshit copypasta about why you “crosspost” ml content (you should probably change that copypasta to say “why I freeboot ML content”, which would be accurate to what you actually do. The ironic thing there is that every time you mention ML in any fashion, it defeats the purpose of not linking there in the first place. Oh, also, feel free to block me. But good luck, since you’ve got a few dozen accounts. I’ll still comment the same since I’m not going to even bother blocking you.
            3. Some people link back to where they found it, and that’s good. You don’t even bother posting where you found it when it’s on the fediverse, and that’s bad. There’s a difference, no matter how you try to pretend there isn’t. You’re hurting the few users on the fediverse when you do that.
            4. I don’t need to prove anything, you just never mention it with the hopes that no one will notice and that works for you 95% of the time. Nothing to prove when no one notices. But when people do notice, like in this thread, I’ll happily show them just how many accounts you’ve created, as stated previously. Also, you seriously want me to go through your dozens of accounts with thousands of posts to “prove” this to you? Probably by design, you make that very difficult to do.