# Pidgin — A Compact Agent Handoff Protocol & Runtime
[](https://crates.io/crates/pidgin-lang)
[](https://github.com/anomalyco/pidgin-lang/actions/workflows/ci.yml)
[](LICENSE)
**Pidgin** is a small, fast, local-first protocol and runtime for compact, structured handoffs between AI agents, between an agent and a human operator, and between an orchestrator and the tools/executors it drives.
```text
Take a compact Pidgin packet (9 lines of key=value text)
→ parse it
→ validate it against a schema and registries
→ run it through a safety gate
→ resolve short references into real paths/IDs
→ expand it into a fully-specified, executable packet
→ estimate token cost
→ recommend a route
→ log every step
```
Pidgin is **not** a model, not an agent framework, not a replacement for MCP/A2A/ACP. It is a narrow waist — a deliberately minimal layer that sits between whatever produces a task and whatever executes it.
---
## Quick Start
```bash
# Install
cargo install pidgin-lang
# Create project config
mkdir -p .pidgin
# Copy registries from your project or start from defaults
# Parse a packet
pgn parse examples/basic/generic_task.pgn
# Full pipeline: parse → validate → safety → resolve → expand
pgn run examples/basic/generic_task.pgn
# Check (validate + safety + resolve, with detailed output)
pgn check examples/basic/generic_task.pgn
# Token-cost estimation
pgn measure examples/basic/generic_task.pgn
# Validate host configuration
pgn doctor
```
---
## Packet Grammar
A Pidgin packet is plain text — a header line followed by `key=value` fields:
```text
@run task.example
wf=generic_review
mode=draft
in=[primary_subject,source_refs]
out=[review_notes]
do=[draft,review]
deny=[publish,send,delete,secrets]
risk=med
human=yes
```
| Header | `@directive run_id` | `@run task.example` |
| Scalar | `key=value` | `risk=med` |
| List | `key=[val1,val2]` | `in=[a,b,c]` |
| Quoted string | `key="value"` | `note="Draft only"` |
| Comment | `# text` | `# This is a comment` |
**Directives:** `@run`, `@result`, `@approval`, `@context`
**Reference syntax** (inside lists and the `route` field):
- Namespace ref: `namespace:id` — e.g. `file:src/main.rs`, `ep:UNIT012`, `workflow:generic_review`
- Bare alias: resolved through `REFERENCE_ALIASES.yaml`
### Why this grammar?
- Every packet fits in under 15 lines
- Every field is unambiguous to a machine without needing an LLM
- Looks like `.env`/TOML — any developer recognizes it instantly
- One canonical way to write each syntax — no alternatives
---
## CLI Commands
| `pgn parse <file>` | Parse a packet and print the AST |
| `pgn validate <files> --host .` | Validate syntax + schema against registries |
| `pgn check <file> --host .` | Parse → validate → safety → resolve (end-to-end check) |
| `pgn resolve <file> --host .` | Resolve all short references |
| `pgn expand <file> --host . [--out file]` | Expand into executable YAML |
| `pgn context-plan <file> --host .` | Build a context retrieval plan |
| `pgn measure <file>` | Estimate token cost |
| `pgn compare --pgn <file> --verbose <file>` | Compare Pidgin vs verbose token cost |
| `pgn run <file> --host . [--out file]` | Full pipeline: parse → validate → safety → resolve → expand |
| `pgn doctor --host .` | Check host configuration |
---
## Host Configuration
Pidgin reads its configuration from `.pidgin/` in the host directory:
| `WORKFLOW_REGISTRY.yaml` | Workflow definitions with modes, inputs, executors |
| `ACTION_REGISTRY.yaml` | Action tiers: `safe`, `controlled`, `human_gated` |
| `SAFETY_RULES.yaml` | Default denies, private paths, human approval rules |
| `REFERENCE_ALIASES.yaml` | Short-name aliases for frequently-used references |
| `PIDGIN_RUNTIME_CONFIG.yaml` | Runtime settings (paths, modes, defaults) |
Create these files in `.pidgin/` to define your host's capabilities.
---
## Safety Gate
The safety gate enforces 9 numbered rules (SG-1 through SG-9):
- **SG-1**: Action in both `do` and `deny` → blocked
- **SG-2**: Human-gated action without `human=yes` → blocked
- **SG-3**: High/critical risk forces `human=yes`, cannot override
- **SG-4**: References resolving to private paths → blocked
- **SG-5**: Unknown workflow → blocked
- **SG-6**: Invalid mode → blocked
- **SG-7**: Free-text `note` field is never parsed for instructions
- **SG-8**: Unresolved required inputs → expansion blocked
- **SG-9**: Critical risk requires an `@approval` packet
**Safety-first principle:** If the runtime is ever uncertain, it fails closed (blocks, asks for human approval, or refuses to expand) rather than fail open.
---
## Build & Test
### Prerequisites
- Rust stable toolchain (`stable-x86_64-pc-windows-gnu` on Windows)
- On Windows: MSYS2 tools (`ucrt64`) in PATH
### Build
```bash
cargo build --release
```
### Test
```bash
cargo test
```
77 tests (74 unit + 3 property-based fuzz tests), zero warnings.
### Clippy
```bash
cargo clippy --all-targets -- -D warnings
```
### Security audit
```bash
cargo audit
```
The CI pipeline runs build → test → clippy → audit on every push.
---
## Using as a Library
Add `pidgin-lang` to your `Cargo.toml`:
```toml
[dependencies]
pidgin-lang = "0.1"
```
```rust
use pidgin_lang::parser::parse_packet;
use pidgin_lang::safety::check_safety;
use pidgin_lang::expander::expand_to_run_packet;
let packet = parse_packet("@run my.task\nwf=generic_review\nmode=draft")?;
```
All public modules:
- `parser` — parse `.pgn` text into `PgnPacket`
- `lexer` — tokenizer (winnow-based)
- `ast` — typed AST types (`PgnPacket`, `FieldValue`, etc.)
- `validator::syntax` — structural validation
- `validator::schema` — registry-checked value validation
- `safety` — the safety gate (SG-1 through SG-9)
- `resolver` — reference resolution (file, folder, alias, namespace)
- `expander` — packet expansion to executable YAML
- `context` — context retrieval planning
- `metrics` — token cost estimation
- `router` — executor recommendation
- `logging` — structured CSV logging
- `registry` — YAML config deserialization
- `errors` — typed error enum
---
## Project Structure
```
pidgin/
├── Cargo.toml # workspace root
├── crates/
│ └── pidgin-lang/ # library + CLI binary
│ ├── src/
│ │ ├── lib.rs # public module exports
│ │ ├── main.rs # CLI entrypoint
│ │ ├── ast.rs # typed packet AST
│ │ ├── parser.rs # grammar parser
│ │ ├── lexer.rs # tokenizer
│ │ ├── safety.rs # safety gate
│ │ ├── resolver.rs # reference resolver
│ │ ├── expander.rs # packet expander
│ │ ├── context.rs # context planner
│ │ ├── metrics.rs # token estimation
│ │ ├── router.rs # route planner
│ │ ├── registry.rs # YAML registry loader
│ │ ├── logging.rs # structured logging
│ │ └── errors.rs # error types
│ └── tests/
│ └── proptest_parser.rs
├── .pidgin/ # host configuration
├── examples/ # example .pgn packets
├── docs/ # specification & documentation
├── python/ # Python SDK (planned)
├── schemas/ # JSON Schema definitions
└── deny.toml # cargo-deny configuration
```
---
## License
Licensed under either of [MIT](LICENSE-MIT) or [Apache 2.0](LICENSE-Apache-2.0) at your option.
---
## Links
- [crates.io: pidgin-lang](https://crates.io/crates/pidgin-lang)
- [Specification](docs/SPEC.md)
- [Security Review](docs/SECURITY_REVIEW.md)
- [GitHub](https://github.com/anomalyco/pidgin-lang)