README.md

# Legatus

> **JSON-RPC STDIO ↔ HTTP Proxy**  
> _A messenger agent speaking between worlds_

**Legatus** is a minimal, stateless bridge that translates dialogue over STDIN into HTTP actions. It embodies the Umwelt principle of perception–action unity: every request passes through a complete functional circle — perception, cognition, action — before returning as a meaningful response.

## Why Legatus Exists

Legatus solves a fundamental problem: **how to let any process speak JSON-RPC over STDIO while delegating execution to an HTTP realm.**

Use cases:
- **Editor integrations**: Connect your editor to language servers over HTTP
- **CLI tools**: Build command-line tools that proxy to HTTP APIs
- **Agent communication**: Enable processes to communicate via standard streams
- **Testing**: Interact with HTTP JSON-RPC servers from shell scripts

Legatus is not merely a tool; it is a **ritual of translation** — a disciplined pattern for agent-to-world communication.

## Quick Start

```bash
# Build the escript
mix escript.install hex legatus

# or
git clone git@github.com:sovetnik/legatus.git
mix escript.build

# Start your JSON-RPC HTTP server (example on port 4000)
# Then send a request:
echo '{"jsonrpc":"2.0","method":"add","params":[2,3],"id":1}' | \
  ./legatus http://localhost:4000/rpc
```

**Expected output:**
```json
{"jsonrpc":"2.0","result":5,"id":1}
```

That's it. One line in, one line out. STDIO becomes HTTP, HTTP becomes STDIO.

## Philosophy: The Umwelt Architecture

The architecture follows Jakob von Uexküll's **Umwelt** concept — organisms perceive and act within their own "subjective universe":

- **Aussenwelt** — The external world (STDIO streams)
- **Merkwelt** — Perception layer (request validation)
- **Wirkwelt** — Action layer (HTTP transport)
- **Umwelt** — The complete functional circle (pipeline coordinator)
- **Geist** — The animating principle (main loop)

Legatus acts within the **Clausura Operationalis** — it knows only the world it perceives and acts upon it coherently. Its simplicity is not limitation but discipline.

## Usage Modes

### As Escript (recommended for production)

```bash
mix escript.build
./legatus http://localhost:4000/rpc
```

### As Mix Task (for development)

```bash
mix legatus http://localhost:4000/rpc
```

### With Bearer Token Authentication

When your upstream server requires authentication, pass the token via environment variable:

```bash
# Using escript
token=your_secret_token ./legatus http://localhost:4000/rpc

# Using mix task
token=your_secret_token mix legatus http://localhost:4000/rpc
```

Legatus will automatically add the `Authorization: Bearer <token>` header to all HTTP requests.

**Editor integration example (Zed, Claude Code, etc.):**

```json
{
  "context_servers": {
    "my_server": {
      "source": "custom",
      "enabled": true,
      "command": "legatus",
      "args": ["http://localhost:4000/rpc"],
      "env": {"token": "your_secret_token"}
    }
  }
}
```

### Pipeline Flow

Every request flows through the complete Umwelt cycle:

```
STDIN → Geist.loop → Aussenwelt.receptio → Merkwelt.percipere → 
Wirkwelt.portare → Aussenwelt.profanatio → STDOUT
```

Each arrow represents a transformation of meaning, not just data.

## Architecture

### Data Flow

The pipeline uses tagged tuples to track data state:

1. **Receptio** (Aussenwelt): Parse JSON  
   `{:phaenomenon, map}` | `{:fiasco, json_error}`

2. **Percipere** (Merkwelt): Validate request  
   `{:actio, map}` | `{:fiasco, error_map}`

3. **Portare** (Wirkwelt): HTTP transport  
   `{:gloria, map}` | `{:fiasco, error_map}` | `{:silentium, map}`

4. **Profanatio** (Aussenwelt): Format output  
   `{:gloria, json}` | `{:fiasco, json}` | `{:silentium, "Nullius in verba"}`

5. **Emit** (Geist): Write to STDOUT or skip

### Module Responsibilities

- **Legatus** — Entry point and configuration
- **Geist** — Main loop (STDIN → STDOUT cycle)
- **Aussenwelt** — I/O boundaries (parse/format JSON)
- **Merkwelt** — Request validation (JSON-RPC compliance)
- **Wirkwelt** — HTTP coordinator (interprets transport responses into Umwelt concepts)
- **Wirkwelt.Httpc** — HTTP transport (pure POST with status/body)
- **Chronica** — Logging (stderr diagnostics)

### Transport Layer Design

Wirkwelt follows a clean separation of concerns:

**Wirkwelt behaviour contract:**
```elixir
@callback post(request :: map()) :: 
  {status :: pos_integer(), body :: map() | String.t()} | {:error, reason :: term()}
```

- **Transport (Httpc)**: Returns raw HTTP responses `{200, body}` or `{:error, reason}`
- **Coordinator (Wirkwelt)**: Interprets responses into `{:gloria, map()}`, `{:silentium, map()}`, or `{:fiasco, map()}`

This design makes it easy to add new HTTP adapters (Finch, Hackney) without changing business logic.

### Error Handling

All errors are JSON-RPC compliant:

- `-32700` Parse error (invalid JSON)
- `-32600` Invalid Request (missing method)
- `-32000` HTTP errors (4xx/5xx)
- `-32001` Transport errors (connection refused)

## Configuration

Legatus is configured via **command-line argument** — no static config required:

```bash
mix legatus http://localhost:4000/rpc
```

The upstream URL is set dynamically at runtime via `Application.put_env/3`.

## Design Principles

1. **Tagged tuples** for explicit data flow
2. **Pure functions** where possible (I/O at boundaries)
3. **Pattern matching** over conditionals
4. **Separation of concerns** (parse, validate, transport, format)
5. **Testability** through dependency injection

## JSON-RPC Support

### Requests
- ✅ Standard requests with `id`
- ✅ Notifications (no `id`)
- ✅ Batches (array of requests)

### Responses
- ✅ Success responses (`result`)
- ✅ Error responses (`error`)
- ✅ HTTP 204 handling (notifications)
- ✅ Batch responses

### Limitations
- STDIO-based (one request per line)
- HTTP POST only (no WebSocket/SSE)
- No built-in retry logic
- Single upstream server

## License

See LICENSE file.

## Etymology

- **Legatus** (Latin) — envoy, ambassador, messenger
- **Umwelt** (German) — environment, "self-centered world"
- **Geist** (German) — spirit, mind, animating principle
- **Aussenwelt** (German) — outer world
- **Merkwelt** (German) — perceptual world
- **Wirkwelt** (German) — world of action

## Next Steps

**Explore further:**
- Read [umwelt.dev/docs](https://umwelt.dev/docs) for philosophical foundations
- Join the discussion in [Issues](https://github.com/sovetnik/legatus/issues)
- Contribute via [Pull Requests](https://github.com/sovetnik/legatus/pulls)

**Related projects:**
- **Skull** — The cognitive substrate for agent systems
- **Reticulum Universalis** — Communication patterns for distributed agents

Legatus is an experiment in **operational closure** — every component speaks the same language of perception and action. If this resonates with you, welcome to the conversation.