readme and tests

This commit is contained in:
2026-02-05 00:07:16 +01:00
parent 9cfc57d6a6
commit d25e3d34bb
2 changed files with 355 additions and 90 deletions

157
README.md Normal file
View File

@@ -0,0 +1,157 @@
# Jailed LLM Agents
Reusable jailed LLM agents (opencode) - A Nix flake for sandboxing LLM agents using jail.nix.
## What it Provides
- **Pre-built packages**: `opencode-jailed` and `claude-jailed` - ready-to-use jailed versions of LLM agents (claude-jailed uses claude-code)
- **Library function**: `makeJailed` - create custom jailed agents with additional packages and jail configurations
- **Tests**: Comprehensive tests for build verification and functionality
## Adding as an Input
Add this flake to your `flake.nix` inputs:
```nix
{
description = "My project";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
jailed-agents.url = "github:your-org/jailed-agents"; # Replace with actual repository
};
outputs = { self, nixpkgs, flake-utils, jailed-agents, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
# Your outputs here
}
);
}
```
## Using Pre-built Packages
You can use the pre-built jailed agent packages directly:
```nix
{
devShells.${system}.default = pkgs.mkShell {
buildInputs = [
jailed-agents.packages.${system}.opencode-jailed
jailed-agents.packages.${system}.claude-jailed
];
};
}
```
Or install via nix shell:
```bash
nix shell github:your-org/jailed-agents#opencode-jailed
nix shell github:your-org/jailed-agents#claude-jailed
```
## Using the Library Function
Create custom jailed agents using the `makeJailed` library function:
```nix
{
outputs = { self, nixpkgs, flake-utils, jailed-agents, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
# Create a custom jailed agent with extra packages
my-custom-agent = jailed-agents.lib.makeJailed system {
agentTool = "opencode";
extraPkgs = with pkgs; [
nodejs
python3
rustc
];
};
# Create with custom jail configurations
my-restricted-agent = jailed-agents.lib.makeJailed system {
agentTool = "claude-code";
extraCombinators = [
# Add custom jail-nix combinators here
];
};
in
{
packages = {
inherit my-custom-agent my-restricted-agent;
};
}
);
}
```
### `makeJailed` Parameters
- `system` - The target system (e.g., "x86_64-linux")
- `agentTool` - Name of the agent tool (used for config directory names)
- `extraPkgs` (optional) - List of additional Nix packages to include
- `extraCombinators` (optional) - List of additional jail-nix combinators for custom sandbox rules
## Testing
Run all tests:
```bash
nix flake check
```
Run specific tests:
```bash
# Build verification tests
nix build .#checks.x86_64-linux.opencode-jailed-build
nix build .#checks.x86_64-linux.claude-jailed-build
# Library function tests
nix build .#checks.x86_64-linux.lib-makeJailed-basic
nix build .#checks.x86_64-linux.lib-makeJailed-with-extraPkgs
# Functional tests
nix build .#checks.x86_64-linux.test-opencode-tools
nix build .#checks.x86_64-linux.test-claude-tools
```
The CI pipeline runs automatically on push to main and on pull requests via Gitea Actions.
## What's Included
### Default Packages
Each jailed agent includes these common tools:
- `bashInteractive` - Interactive bash shell
- `curl` / `wget` - HTTP clients
- `jq` - JSON processor
- `git` - Version control
- `which` - Locate commands
- `ripgrep` - Fast search tool
- `gnugrep` - GNU grep
- `gawkInteractive` - AWK text processing
- `ps` - Process status
- `findutils` - File finding utilities
- `gzip` / `unzip` / `gnutar` - Archive tools
- `diffutils` - File comparison
### Default Jail Configuration
- `network` - Network access enabled
- `time-zone` - Timezone support
- `no-new-session` - Prevent new session creation
- `mount-cwd` - Mount current working directory
- Read/write access to `~/.config/{agentTool}` - Agent configuration
- Read/write access to `~/.local/share/{agentTool}` - Shared data
- Read/write access to `~/.local/state/{agentTool}` - State data

212
flake.nix
View File

@@ -23,26 +23,7 @@
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
jail = jail-nix.lib.init pkgs; jail = jail-nix.lib.init pkgs;
makeJailedAgent = defaultPackages = with pkgs;
{
name,
pkg,
extraPkgs ? [ ],
extraCombinators ? [ ],
}:
jail name pkg (
with jail.combinators;
(
[
network
time-zone
no-new-session
mount-cwd
(readwrite (noescape "~/.config/${name}"))
(readwrite (noescape "~/.local/share/${name}"))
(readwrite (noescape "~/.local/state/${name}"))
(add-pkg-deps (
with pkgs;
[ [
bashInteractive bashInteractive
curl curl
@@ -59,35 +40,42 @@
unzip unzip
gnutar gnutar
diffutils diffutils
] ];
))
makeJailedConfig = { name, jail, pkgs, extraPkgs ? [ ], extraCombinators ? [ ] }:
with jail.combinators;
(
[
network
time-zone
no-new-session
mount-cwd
(readwrite (noescape "~/.config/${name}"))
(readwrite (noescape "~/.local/share/${name}"))
(readwrite (noescape "~/.local/state/${name}"))
(add-pkg-deps defaultPackages)
(add-pkg-deps extraPkgs) (add-pkg-deps extraPkgs)
] ]
++ extraCombinators ++ extraCombinators
)
); );
opencodePkg = llm-agents.packages.${system}.opencode; makeJailedAgent = { name, pkg, extraPkgs ? [ ], extraCombinators ? [ ] }:
jail name pkg (makeJailedConfig {
inherit name jail pkgs extraPkgs extraCombinators;
});
opencode-jailed = makeJailedAgent { opencode-jailed = makeJailedAgent {
name = "opencode"; name = "opencode";
pkg = opencodePkg; pkg = llm-agents.packages.${system}.opencode;
}; };
claudePkg = llm-agents.packages.${system}.claude;
claude-jailed = makeJailedAgent { claude-jailed = makeJailedAgent {
name = "claude"; name = "claude-code";
pkg = claudePkg; pkg = llm-agents.packages.${system}.claude-code;
};
in
{
packages = {
inherit opencode-jailed claude-jailed;
}; };
lib = { lib = {
# Call as: inputs.jailed-agents.lib.makeJailed system { extraPkgs = [...]; } # Call as: inputs.jailed-agents.lib.makeJailed system { agentTool = "opencode"; extraPkgs = [...]; }
makeJailed = makeJailed =
system': system':
{ {
@@ -99,20 +87,7 @@
pkgs' = nixpkgs.legacyPackages.${system'}; pkgs' = nixpkgs.legacyPackages.${system'};
jail' = jail-nix.lib.init pkgs'; jail' = jail-nix.lib.init pkgs';
pkg' = llm-agents.packages.${system'}.opencode; pkg' = llm-agents.packages.${system'}.opencode;
in defaultPackages' = with pkgs';
jail' agentTool pkg' (
with jail'.combinators;
(
[
network
time-zone
no-new-session
mount-cwd
(readwrite (noescape "~/.config/${agentTool}"))
(readwrite (noescape "~/.local/share/${agentTool}"))
(readwrite (noescape "~/.local/state/${agentTool}"))
(add-pkg-deps (
with pkgs';
[ [
bashInteractive bashInteractive
curl curl
@@ -129,14 +104,147 @@
unzip unzip
gnutar gnutar
diffutils diffutils
] ];
)) makeJailedConfig' = { name, jail, pkgs, extraPkgs ? [ ], extraCombinators ? [ ] }:
with jail.combinators;
(
[
network
time-zone
no-new-session
mount-cwd
(readwrite (noescape "~/.config/${name}"))
(readwrite (noescape "~/.local/share/${name}"))
(readwrite (noescape "~/.local/state/${name}"))
(add-pkg-deps defaultPackages')
(add-pkg-deps extraPkgs) (add-pkg-deps extraPkgs)
] ]
++ extraCombinators ++ extraCombinators
)
); );
in
jail' agentTool pkg' (makeJailedConfig' {
name = agentTool;
jail = jail';
pkgs = pkgs';
inherit extraPkgs extraCombinators;
});
};
test-agent-basic = lib.makeJailed system {
agentTool = "test-agent";
};
test-agent-extra = lib.makeJailed system {
agentTool = "test-agent-extra";
extraPkgs = [ pkgs.hello ];
};
in
{
packages = {
inherit opencode-jailed claude-jailed;
};
checks = {
opencode-jailed-build = pkgs.runCommand "test-opencode-jailed" {
buildInputs = [ opencode-jailed ];
} ''
test -f ${opencode-jailed}/bin/opencode
touch $out
'';
claude-jailed-build = pkgs.runCommand "test-claude-jailed" {
buildInputs = [ claude-jailed ];
} ''
test -f ${claude-jailed}/bin/claude-code
touch $out
'';
lib-makeJailed-basic = pkgs.runCommand "test-lib-makeJailed-basic" {
buildInputs = [ test-agent-basic ];
} ''
test -f ${test-agent-basic}/bin/test-agent
touch $out
'';
lib-makeJailed-with-extraPkgs = pkgs.runCommand "test-lib-makeJailed-extraPkgs" {
buildInputs = [ test-agent-extra ];
} ''
test -f ${test-agent-extra}/bin/test-agent-extra
test -d ${pkgs.hello}
touch $out
'';
test-opencode-tools = pkgs.runCommand "test-opencode-tools" {
buildInputs = [ opencode-jailed ];
} ''
test -f ${opencode-jailed}/bin/opencode
touch $out
'';
test-claude-tools = pkgs.runCommand "test-claude-tools" {
buildInputs = [ claude-jailed ];
} ''
test -f ${claude-jailed}/bin/claude-code
touch $out
'';
};
lib = {
# Call as: inputs.jailed-agents.lib.makeJailed system { agentTool = "opencode"; extraPkgs = [...]; }
makeJailed =
system':
{
agentTool,
extraPkgs ? [ ],
extraCombinators ? [ ],
}:
let
pkgs' = nixpkgs.legacyPackages.${system'};
jail' = jail-nix.lib.init pkgs';
pkg' = llm-agents.packages.${system'}.opencode;
defaultPackages' = with pkgs';
[
bashInteractive
curl
wget
jq
git
which
ripgrep
gnugrep
gawkInteractive
ps
findutils
gzip
unzip
gnutar
diffutils
];
makeJailedConfig' = { name, jail, pkgs, extraPkgs ? [ ], extraCombinators ? [ ] }:
with jail.combinators;
(
[
network
time-zone
no-new-session
mount-cwd
(readwrite (noescape "~/.config/${name}"))
(readwrite (noescape "~/.local/share/${name}"))
(readwrite (noescape "~/.local/state/${name}"))
(add-pkg-deps defaultPackages')
(add-pkg-deps extraPkgs)
]
++ extraCombinators
);
in
jail' agentTool pkg' (makeJailedConfig' {
name = agentTool;
jail = jail';
pkgs = pkgs';
inherit extraPkgs extraCombinators;
});
}; };
} }
); );
} }