Files
millerson-overlay.nix/packages/marmel/package.nix
T
alex 51dd95c19c fix(marmel): allow /nix/store in the Landlock sandbox
The upstream ruleset allow-lists FHS paths only (/usr, /bin, /lib, /opt,
/etc, /var), and every executable here resolves below the store: /bin/sh
is a symlink into /nix/store/<hash>-bash-5.3p9/bin/sh. Landlock matches
the resolved target rather than the symlinked view, so the harness
re-entry (`marmel --internal-sandbox-exec`) cannot exec the shell and
every shell tool call comes back as "Failed to exec shell in sandbox:
Permission denied (os error 13)" with exit 1. The sandbox is on by
default, so the only workaround on a Nix host is MARMEL_DISABLE_SANDBOX=1,
which drops the protection altogether.

The patch adds "/nix/store" to the read-only+execute list, guarded by
exists() like its neighbours. Store paths are world-readable by design,
so the added reach is nothing a local user did not already have. Checked
against the built binary: sandboxed exec works, git runs, workspace
writes work, ~/.ssh and /root stay blocked.

Tested: nix build .#marmel passes, lib suite 966 passed / 0 failed with
one filter. test_internal_sandbox_cross_directory_rename no longer needs
a skip and passes; test_internal_sandbox_exec_dev_null_and_dns stays
skipped for its own reason, the no-network build sandbox has no
/etc/resolv.conf. Temporary downstream fix, drop it once upstream takes
the store path (nix-overlay-5ml).
2026-10-16 16:19:18 +03:00

96 lines
3.7 KiB
Nix

{
lib,
rustPlatform,
fetchFromGitHub,
cacert,
}:
rustPlatform.buildRustPackage rec {
pname = "marmel";
# Upstream publishes no git tags and no GitHub releases, and Cargo.toml still
# says 1.0.0, so the version is meaningless and the source is tracked by
# branch head. Bump with nix-update (see passthru.updateScript).
version = "1.0.0";
src = fetchFromGitHub {
owner = "Na1w";
repo = "marmel";
rev = "3f238ff217ee2e1033c937c840bd2d33ec57abc4";
hash = "sha256-ijSsUAJom/uIiE09PM1kfXqNVCRqnipAQo72KXlr8sQ=";
};
cargoHash = "sha256-etqW3xcxkiNfkiPxl/Emt5pQCkNnoIhHFkX0Zqfs4rc=";
nativeCheckInputs = [ cacert ];
patches = [
./session-boot-trust-store.patch
# Temporary downstream fix, upstream has no NixOS-aware allow-list yet.
# Adds "/nix/store" to the read-only+execute list of the Landlock ruleset, so the
# sandbox re-entry can exec `sh` on a Nix host at all. Drop when upstream lands it
# (nix-overlay-5ml).
./landlock-nix-store.patch
];
# The test suite builds `reqwest::Client`s, and rustls rejects construction
# outright when no system trust store is found ("No CA certificates were
# loaded from the system"). rustls-native-certs probes only SSL_CERT_FILE,
# SSL_CERT_DIR and a fixed list of /etc dirs (/etc/ssl, /etc/pki/tls, ...),
# none of which exist in the build sandbox, so point the tests at nixpkgs'
# bundle. Build-time only; the installed binary still uses the host's trust
# store at runtime.
preCheck = ''
export SSL_CERT_FILE=${cacert}/etc/ssl/certs/ca-bundle.crt
'';
# Serialise the test harness. The lib suite shares process-global worker
# registries and steer/abort buses (src/orchestrator/workers.rs,
# src/orchestrator/bus.rs, src/orchestrator/preemption.rs), so the
# manager-loop tests fail nondeterministically when the harness runs them on
# parallel threads: rebuilding one unchanged derivation yielded 3 failures,
# then 1 failure, then 0 with serial threads. Drop this flag once upstream
# makes that state per-instance.
#
# Only the DNS sandbox-exec test is skipped now. It re-execs `sh` through
# `marmel --internal-sandbox-exec` and then reads /etc/resolv.conf, which the
# no-network build sandbox does not provide. landlock-nix-store.patch fixed the
# exec half for both sandbox-exec tests: with it, the cross-directory rename
# test runs for real (no SKIP printed by `integration_host`) and the lib suite
# passes at 966 tests with that single filter.
cargoTestFlags = [
"--"
"--test-threads=1"
"--skip"
"harness::sandbox::tests::test_internal_sandbox_exec_dev_null_and_dns"
];
# Role prompts are embedded with `include_str!`, so the binary needs no
# runtime data files. The annotated example configs are the de-facto
# first-run documentation, since a backend URL and model are mandatory.
postInstall = ''
install -Dm644 marmel.toml.example $out/share/doc/${pname}/examples/marmel.toml.example
install -Dm644 marmel.toml.cloud $out/share/doc/${pname}/examples/marmel.toml.cloud
'';
passthru = {
category = "AI Coding Agents";
updateScript = [
"nix-update"
"--flake"
".#marmel"
"--version=branch=main"
];
};
meta = {
description = "Autonomous agentic coding assistant with Manager + specialist subagent orchestration over any OpenAI-compatible LLM backend";
homepage = "https://github.com/Na1w/marmel";
# Upstream README states MIT, but the repository ships no LICENSE file and
# Cargo.toml has no license field.
license = lib.licenses.mit;
sourceProvenance = with lib.sourceTypes; [ fromSource ];
mainProgram = "marmel";
platforms = lib.platforms.unix;
};
}