a devlog on machines & languages

18 PonderCode Devlog 5

The experiment worked! And it has implications for Adamant.

Part A: Dependency Resolution

As part of the brainstorming, I created a simple Python script where I hardcoded a set of binaries as part of a list and then used ldd to resolve their dependency graph. This graph was then used to build a shell script that executed Bubblewrap. This is the heart of the script:

def generate_sand_script(apps):
    """Constructs the bwrap shell script string."""
    bind_mounts = []
    libraries = set()

    # 1. Process the requested apps
    for app in apps:
        app_path = get_app_path(app)
        if app_path:
            bind_mounts.append(f"  --ro-bind {app_path} {app_path}")
            # Get dependencies for this app
            deps = get_dependencies(app_path)
            for dep in deps:
                libraries.add(dep)
        else:
            print(f"Warning: Command '{app}' not found in PATH.")

    # 2. Sort libraries for a clean, deterministic output
    sorted_libs = sorted(list(libraries))

    # 3. Build the script template
    script = [
        "#!/bin/sh",
        "set -euo pipefail",
        'REAL_HOME="$HOME"',
        'REAL_PROJECT_DIR="$(pwd)"',
        'FAKE_HOME="/home/user"',
        'FAKE_PATH="/usr/bin"',
        "",
        "",
    ]

    exec = [
        "exec bwrap --clearenv --unshare-all --die-with-parent",
        "  --tmpfs /",
        "  --tmpfs /tmp",
    ]

    # Add app bind mounts
    for bm in bind_mounts:
        exec.append(bm)

    # Add library bind mounts
    for lib in sorted_libs:
        exec.append(f"  --ro-bind {lib} {lib}")

    # Add symlinks and environment setup
    exec.extend(
        [
            "  --proc /proc",
            "  --dev /dev",
            "  --symlink /usr/lib /lib",
            "  --symlink /usr/lib64 /lib64",
            "  --symlink /usr/bin /bin",
            '  --dir "$FAKE_HOME"',
            '  --setenv HOME "$FAKE_HOME"',
            '  --setenv PATH "$FAKE_PATH"',
            '  --chdir "$FAKE_HOME"',
            '  "$@"',
        ]
    )

    return "\n".join(script) + " \\\n".join(exec)

I took this and incorporated it into the app. Now, the user does not have to manually map paths into the sandbox. They can provide a list of binaries and the system will resolve them at runtime and prepare the sandbox accordingly.

Part B: Sockets + Proxy

The app initializes the sandbox with socat running a proxy on 127.0.0.1:8080 inside the sandbox. It also maps a socket path into the sandbox. socat intercepts the requests from curl and other applications and the app uses the whitelist in the <profile>.hosts key to decide if the connection can be permitted or not. An important limitation: client applications must support HTTP_PROXY + HTTPS_PROXY envars.

This exact mechanism, A + B, can be used for Adamant!

PonderCode is mostly done. I will continue this series in the future when I take a couple of days to iron out various bugs and streamline the codebase.