Skip to main content
Rampart’s LD_PRELOAD library intercepts exec-family syscalls at the OS level. This is the universal fallback method — it works with any dynamically-linked process, regardless of whether it reads $SHELL or has a hook system.

Quick Setup

1

Start Rampart service

Launch the policy server:
rampart serve install
Runs on port 9090 with token at ~/.rampart/token.
2

Build preload library (if needed)

Check if library exists:
ls ~/.rampart/lib/librampart.so      # Linux
ls ~/.rampart/lib/librampart.dylib   # macOS
If missing, build from source:
cd /path/to/rampart/preload
make
make install  # Installs to ~/.rampart/lib/
3

Run with preload

Launch any process with Rampart protection:
rampart preload -- python my_agent.py
rampart preload -- node agent.js
rampart preload -- /usr/bin/codex
The library intercepts every exec call from the process and all children.

How It Works

The preload library sits between the process and libc, intercepting all exec-family functions before they reach the kernel.

Intercepted Functions

execve("/bin/rm", ["/bin/rm", "-rf", "/"], envp)
→ Rampart checks → allowed/denied
Coverage: Every way a process can spawn a command — exec, system, popen, posix_spawn, and Linux-specific execvpe.

Environment Variables

The preload library reads configuration from environment:
Default: http://127.0.0.1:9090Policy server endpoint:
export RAMPART_URL="http://localhost:19090"
rampart preload -- python agent.py

Command-Line Usage

rampart preload -- python my_agent.py
Uses defaults:
  • Port 9090
  • Token from ~/.rampart/token
  • Enforce mode
  • Fail-open enabled

Platform Support

Coverage: ~95% of dynamically-linked binariesMechanism: LD_PRELOADWorks with:
  • Python interpreters (python, python3)
  • Node.js (node, nodejs)
  • Go binaries (if dynamically linked)
  • Rust binaries (if dynamically linked)
  • Any dynamically-linked executable
Check if binary is compatible:
file $(which python3)
# Should show: dynamically linked
Limitations:
  • Static binaries cannot be intercepted
  • Requires libcurl installed

Policy Configuration

~/.rampart/policies/custom.yaml
version: "1"
default_action: allow

policies:
  - name: preload-safe-dev
    match:
      agent: ["preload"]  # Default agent name
      tool: ["exec"]
    rules:
      - action: allow
        when:
          command_matches:
            - "git *"
            - "npm *"
            - "python *"
            - "ls *"
        message: "Safe development commands"

  - name: preload-block-destructive
    match:
      agent: ["preload"]
      tool: ["exec"]
    rules:
      - action: deny
        when:
          command_matches:
            - "rm -rf /*"
            - "dd if=*"
            - "mkfs.*"
            - ":(){ :|:& };:"
        message: "Destructive command blocked"

  - name: preload-ask-network
    match:
      agent: ["preload"]
      tool: ["exec"]
    rules:
      - action: ask
        when:
          command_contains: ["curl", "wget", "nc"]
        message: "Network command requires approval"
Reload:
rampart serve --reload

Building from Source

Linux

cd /path/to/rampart/preload

# Install dependencies (Ubuntu/Debian)
sudo apt-get install build-essential libcurl4-openssl-dev

# Build
make

# Install to ~/.rampart/lib/
make install

# Test
./test_preload.sh

macOS

cd /path/to/rampart/preload

# Xcode command line tools (includes libcurl)
xcode-select --install

# Build
make

# Install to ~/.rampart/lib/
make install

# Test
./test_preload.sh

Debug Build

make debug
# Includes symbols for gdb/lldb debugging

AddressSanitizer Build

make asan
# Detects memory errors during development

Example Session

Terminal output with preload active:
$ rampart preload --debug -- python my_agent.py

rampart: [DEBUG] Library loaded
rampart: [DEBUG] URL: http://127.0.0.1:9090
rampart: [DEBUG] Mode: enforce
rampart: [DEBUG] Fail-open: true

Agent starting...

rampart: [DEBUG] Intercepted system: ls -la
rampart: [DEBUG] Policy check: POST /v1/preflight/exec
rampart: [DEBUG] Decision: allow
total 48
drwxr-xr-x  6 user user  4096 Mar  3 14:23 .
drwxr-xr-x 24 user user  4096 Mar  3 14:20 ..
...

rampart: [DEBUG] Intercepted execve: /bin/rm -rf /tmp/*
rampart: [DEBUG] Policy check: POST /v1/preflight/exec
rampart: [DEBUG] Decision: deny
rampart: [ERROR] Policy denied: Destructive command blocked

Traceback (most recent call last):
  File "my_agent.py", line 42, in <module>
    subprocess.run(['rm', '-rf', '/tmp/*'])
  File "/usr/lib/python3.10/subprocess.py", line 524, in run
    raise CalledProcessError(retcode, process.args)
PermissionError: [Errno 1] Operation not permitted

Monitoring

Audit Trail

# Tail logs
rampart audit tail --follow

# Search for preload activity
rampart audit search --agent preload --decision deny

# Stats
rampart audit stats
Output:
Agent: preload
  Total: 1,247
  Allowed: 1,201
  Denied: 12
  Logged: 34

Live Dashboard

open http://localhost:9090/dashboard/
Shows all intercepted commands in real time.

Troubleshooting

Library not found

  1. Check library exists:
    ls -la ~/.rampart/lib/librampart.so      # Linux
    ls -la ~/.rampart/lib/librampart.dylib   # macOS
    
  2. Build if missing:
    cd /path/to/rampart/preload
    make && make install
    
  3. Check dependencies:
    ldd ~/.rampart/lib/librampart.so         # Linux
    otool -L ~/.rampart/lib/librampart.dylib # macOS
    # Should show libcurl and pthread
    

Library won’t load

  1. Test basic loading:
    LD_PRELOAD=~/.rampart/lib/librampart.so echo test
    # Should print "test" without errors
    
  2. Check architecture:
    file ~/.rampart/lib/librampart.so
    # Should match system (x86_64, arm64, etc.)
    
  3. Enable debug output:
    RAMPART_DEBUG=1 LD_PRELOAD=~/.rampart/lib/librampart.so echo test
    # Should show "rampart: [DEBUG] Library loaded"
    

Commands not being intercepted

  1. Check if binary is dynamically linked:
    file $(which your-binary)
    # Should show "dynamically linked"
    
  2. Check if SIP is blocking (macOS):
    codesign -dv $(which your-binary) 2>&1
    # If shows restrictions, use Homebrew version
    
  3. Test with debug:
    RAMPART_DEBUG=1 rampart preload -- your-command
    # Should show "Intercepted execve" messages
    

Policy server connection fails

  1. Check service is running:
    curl http://localhost:9090/healthz
    # Should return "ok"
    
  2. Check token:
    cat ~/.rampart/token
    # Should output token starting with "rampart_"
    
  3. Test with fail-closed:
    rampart preload --fail-open=false -- echo test
    # If service is down, should fail
    

Security Considerations

Threat Model

The preload library protects against: Hallucinating AI agents executing dangerous commands
Malicious plugins/skills running credential theft
Accidental destructive commands from autonomous agents
Subprocess cascades — all children inherit the library
Does NOT protect against:
  • Deliberate unsetenv("LD_PRELOAD") before exec (bypass)
  • Direct syscalls bypassing libc (requires assembly)
  • Static binaries (no dynamic linking)
  • Direct file operations (open(), read(), write())
  • Network operations (socket(), connect())

Bypass Resistance

Against AI agents: High — they don’t know to unset LD_PRELOAD
Against humans: Low — easy to bypass if you know how
Against accidents: Perfect — catches all standard exec paths

Performance

Overhead per command:
OperationWithout RampartWith RampartOverhead
echo hello2ms3.5ms+1.5ms
ls /tmp3ms5ms+2ms
git status45ms47ms+2ms
npm test3.2s3.202s+0.002s
Policy checks add 1-3ms per command — invisible to users.

Advanced: Custom Agent Names

Tag events with custom identifiers:
rampart preload --agent my-agent --session prod-deploy -- ./tool
Write agent-specific policies:
policies:
  - name: my-agent-rules
    match:
      agent: ["my-agent"]
      session_matches: ["prod-*"]
    rules:
      - action: deny
        when:
          command_contains: ["rm", "delete"]
        message: "Production agent: destructive commands blocked"

Build docs developers (and LLMs) love