A secure PTY (pseudo-terminal) bridge for running AI agents under isolated system users on Linux. This implements the "Agent-as-User" architecture where each agent runs as a dedicated system user, providing kernel-level isolation through standard Unix permissions.
# Download the latest release
wget https://github.com/AgentaaU/AaaU/releases/latest/download/aaau-linux.tar.gz
# Extract binaries
tar -xzf aaau-linux.tar.gz
# Install to system
sudo install server /usr/local/bin/aaau-server
sudo install client /usr/local/bin/aaau# Create agent user and required directories
sudo aaau-server init# Run the server (requires sudo for PTY and user switching)
sudo aaau-server run# Connect client and run claude in agent user environment
aaau -p 'claude --dangerously-skip-permissions'
# Shortcut for codex with the standard bypass flag
aaau codex
# Shortcut alias for claude with the standard skip-permissions flag
aaau claude
# Run with custom program
aaau -p 'your-command arg1 arg2'┌─────────────┐ ┌─────────────┐
│ Human │ │ Human │
│ (Operator) │ │ (Observer) │
└──────┬──────┘ └──────┬──────┘
│ │
└──────────┬───────────────┘
│
┌──────────▼──────────┐
│ aaau-server │
│ (Unix Socket) │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ agent-session │
│ (PTY bridge) │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ agent process │
│ (runs as separate │
│ system user) │
└─────────────────────┘
- Process Isolation: Each agent runs as a dedicated system user
- Process Group Termination: Agent process groups are killed on shutdown, preventing zombie processes
- Resource Limits: Leverages cgroups via systemd for resource control
- File Isolation: Each agent has its own
$HOMEdirectory - Audit Logging: Complete session recording in JSON format
- Multi-Client Support: Multiple humans can connect to observe/interact
- Permission Levels: Read-only, Interactive, and Admin access levels
- Unix Domain Sockets: Fast, secure local communication
- Robust Handshake: Handles fragmented network reads during connection establishment
This system implements the Agent-as-User (AaaU) security model:
| Feature | Implementation |
|---|---|
| User Creation | useradd / systemd-sysusers |
| Process Isolation | setuid() / dedicated users |
| Resource Limits | cgroups v2 (systemd) |
| File Isolation | Per-user $HOME directories |
| IPC Control | Unix socket permissions |
| Audit | JSONL format logs |
- OCaml >= 4.14
- opam
- Linux (for PTY and user isolation features)
opam install -y dune lwt lwt_ppx logs fmt cmdliner yojson uuidm mtime cstructdune buildsudo cp _build/install/default/bin/aaau-server /usr/local/bin/
sudo cp _build/install/default/bin/aaau /usr/local/bin/# Initialize everything with defaults
sudo aaau-server init
# The init command creates:
# - User: agent
# - Group: agent
# - Directories: /var/run/aaau, /var/log/aaauIf you prefer to set up manually:
# Create dedicated user for running agents
sudo useradd -r -s /bin/false -d /home/agent agent# Create group for authorized human users
sudo groupadd agent-shared
sudo usermod -aG agent-shared $USERsudo mkdir -p /var/run/aaau
sudo mkdir -p /var/log/aaau
sudo chown root:agent-shared /var/run/aaau
sudo chmod 775 /var/run/aaauBefore running the server for the first time, initialize the environment:
# Initialize with defaults
sudo aaau-server init
# Or customize all options
sudo aaau-server init \
-u agent \
-g agent-shared \
-s /var/run/aaau/server.sock \
-l /var/log/aaau \
-h /home/agentThe init command will:
- Create the shared group (e.g.,
agent) - Create the agent user (e.g.,
agent) - Create socket directory with proper permissions
- Create log directory with proper permissions
- Display sudo configuration suggestions
# Run in foreground
sudo aaau-server run -s /var/run/aaau.sock -g agent
# Or as daemon
sudo aaau-server run -d -s /var/run/aaau.sock -g agentOptions:
-s, --socket: Unix socket path (default:/var/run/aaau.sock)-g, --group: Authorized group name (default:agent)-u, --user: Agent system user (default:agent)-l, --log-dir: Audit log directory (default:/var/log/aaau)-d, --daemon: Run as daemon
# Create new session
aaau -s /var/run/aaau/server.sock
# Join existing session
aaau -s /var/run/aaau/server.sock -n <session-id>
# Read-only mode (observe only)
aaau -s /var/run/aaau/server.sock -n <session-id> -rThe client-server protocol uses a simple text-based format:
| Message | Description |
|---|---|
<text> |
Regular terminal input |
\x01RESIZE:<rows>,<cols> |
Terminal resize |
\x01PING |
Keepalive ping |
\x01GET_STATUS |
Query session status |
\x01FORCE_KILL |
Admin: terminate session |
| Message | Description |
|---|---|
<text> |
Terminal output |
\x01PONG |
Ping response |
\x01STATUS:<json> |
Session status |
\x01ERROR:<msg> |
Error message |
\x01CONTROL:<msg> |
Control notification |
open Lwt.Syntax
let () =
let audit = AaaU.Audit.create ~log_dir:"/var/log/aaau" in
let* result = AaaU.Session.create
~session_id:"sess-123"
~creator:"operator"
~audit
in
match result with
| Ok session ->
Printf.printf "Session created: %s\n" (AaaU.Session.get_id session)
| Error e ->
Printf.eprintf "Error: %s\n" elet server = AaaU.Bridge.create
~socket_path:"/var/run/aaau/server.sock"
~shared_group:"agent-shared"
~agent_user:"agent"
~log_dir:"/var/log/aaau"
in
Lwt_main.run (AaaU.Bridge.start server)| Level | Permissions |
|---|---|
ReadOnly |
View output only |
Interactive |
View + send input |
Admin |
Full control + force kill |
Authentication is based on Unix socket credentials:
- Client's UID/GID verified via
SO_PEERCRED - User must be member of configured shared group
- Root users (UID < 1000) get Admin permissions
All actions are logged in JSON Lines format:
{"timestamp": 1711523456.789, "source": "human", "user": "alice", "session_id": "sess-123", "command_type": "input", "content": "ls -la", "metadata": {}}
{"timestamp": 1711523457.012, "source": "system", "user": "system", "session_id": "sess-123", "command_type": "session_start", "content": "Agent PID 12345", "metadata": {"pty": "/dev/pts/5"}}lib/
├── pty.mli/ml # PTY operations
├── protocol.mli/ml # Communication protocol
├── audit.mli/ml # Audit logging
├── auth.mli/ml # Authentication/authorization
├── session.mli/ml # Session management
└── bridge.mli/ml # Main server
bin/
├── server.ml # aaau-server executable
└── client.ml # aaau executable
| Approach | Isolation | Overhead | Startup |
|---|---|---|---|
| Docker/Podman | Namespace | High | 50-500ms |
| Firecracker VM | Hardware | Very High | ~150ms |
| gVisor | Syscall intercept | Medium | 10-20ms |
| AaaU | User/UID | Minimal | ~1ms |
- Linux only (relies on Unix sockets, PTYs, and user isolation)
- Requires root/sudo for user switching
- Some ioctl operations require C bindings (currently simplified)
- GPU/graphics access requires additional setup
MIT License - See LICENSE file
Contributions welcome! Please ensure:
- Code follows OCaml conventions
- Tests pass (
dune test) - Documentation is updated
See CHANGELOG.md for version history.