ivorn 0.1.7

A web-based chat interface for ACP-compatible AI coding agents
Documentation

Ivorn

A web-based chat interface for ACP-compatible AI coding agents (like Kiro CLI, Goose, etc.) that enables browser-based interaction with multiple parallel project sessions.

Named after Ivor the Engine, the beloved Welsh stop-motion steam train.

Tech Stack

Component Technology Purpose
Backend Rust + Axum HTTP server, process management, async runtime
Frontend Alpine.js Lightweight reactive UI (~15KB, no build step)
Communication SSE + POST Server-to-browser streaming, client requests
Protocol ACP JSON-RPC 2.0 over stdio for agent communication
Build cargo only Single binary, no separate frontend build

Architecture

flowchart LR
    Browser["Browser<br/>(Alpine.js)"]
    Axum["Axum Server"]
    ACP["ACP Module"]
    Agent["ACP Agent<br/>(e.g., Kiro CLI)"]
    
    Browser -->|"POST /api/..."| Axum
    Axum -->|"SSE stream"| Browser
    Axum --- ACP
    ACP -->|"stdin (JSON-RPC)"| Agent
    Agent -->|"stdout (JSON-RPC)"| ACP

The browser connects to the Axum server via HTTP. User messages are sent as POST requests. Agent responses stream back via Server-Sent Events (SSE). The ACP module (src/acp/) handles JSON-RPC 2.0 communication with agents over stdin/stdout.

Quick Start

Prerequisites

  • Rust (stable toolchain)

Build and Run

# Build
cargo build --release

# Run (provide path to projects directory)
./target/release/ivorn /path/to/projects

# Or use environment variable
IVORN_PROJECTS_DIR=/path/to/projects ./target/release/ivorn

Open http://localhost:8181 in your browser.

Configuration

Configuration is loaded from ~/.config/ivorn/config.toml (XDG spec). See config.example.toml for all available settings.

[server]
port = 8181
bind_address = "0.0.0.0"

[paths]
projects_dir = "/path/to/projects"

[logging]
conversation_log = true  # Enable conversation logging to markdown files

[upload]
max_size_mb = 25    # Maximum upload file size in MB

Environment Variables

All settings support environment variable overrides with IVORN_ prefix:

Variable Description Default
IVORN_SERVER_PORT Server port 8181
IVORN_SERVER_BIND_ADDRESS Bind address 0.0.0.0
IVORN_PROJECTS_DIR Projects directory Current directory
IVORN_LOGGING_CONVERSATION_LOG Enable conversation logging true
IVORN_UPLOAD_MAX_SIZE_MB Maximum upload file size in MB 25

Precedence (highest to lowest): CLI arguments > Environment variables > Config file > Defaults

ACP agents are external executables that implement the Agent Client Protocol over stdio. Ivorn discovers agents from configuration files.

Configuration Locations

  • Global: ~/.config/ivorn/acp/*.json or *.toml
  • Project-local: {project}/.ivorn/acp/*.json or *.toml (takes precedence)

When both JSON and TOML files define the same agent name, TOML takes precedence.

TOML format (kiro-cli.toml):

name = "kiro-cli"
title = "Kiro CLI"
command = "/usr/local/bin/kiro-cli"
args = ["acp"]
description = "Default Kiro CLI agent"

[[env]]
name = "KEY"
value = "val"

JSON format (kiro-cli.json):

{
  "name": "kiro-cli",
  "title": "Kiro CLI",
  "command": "/usr/local/bin/kiro-cli",
  "args": ["acp"],
  "description": "Default Kiro CLI agent",
  "env": [{"name": "KEY", "value": "val"}]
}
Field Required Description
name Yes Unique identifier for the agent
title No Display name (falls back to name)
command Yes Path to the executable
args No Command-line arguments
description No Description shown in UI
env No Environment variables to set

Projects can provide custom context injected into every message sent to the ACP agent.

Context is loaded from three sources, merged in order (later overrides earlier):

  1. Global static~/.config/ivorn/context.toml or context.json
  2. Project static.ivorn/context.toml or .ivorn/context.json
  3. Dynamic hook.ivorn/context-hook script (highest priority)

Static Context (.ivorn/context.toml):

team = "platform"
environment = "development"

Dynamic Hook (.ivorn/context-hook):

#!/bin/sh
echo "git_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown)"

All values are prefixed with project_hook_ in the ivorn-context block.

Projects can define custom slash commands that execute shell commands or send prompts to the LLM.

Configuration Locations

  • Global: ~/.config/ivorn/commands/*.json or *.toml
  • Project-local: {project}/.ivorn/commands/*.json or *.toml (takes precedence)

Example (.ivorn/commands/dev.toml):

[[commands]]
name = "test"
description = "Run tests"
command = "cargo test {args}"
output = "display"

[[commands]]
name = "clippy"
description = "Run clippy and fix issues"
command = "cargo clippy {args} 2>&1"
output = "prompt"
prompt = "Fix these clippy warnings:\n\n{}"
input = { hint = "clippy args (e.g., --fix)" }
Field Required Description
name Yes Command name (without leading /)
description Yes Description shown in command palette
command No Shell command to execute ({args} for user arguments)
output No display (default), file, or prompt
prompt No Prompt template ({} for output content)
input No Input hint object with hint field

Project Structure

ivorn/
├── src/
│   ├── main.rs              # Entry point, CLI parsing
│   ├── config.rs            # User configuration loading
│   ├── state.rs             # Application state management
│   ├── acp_agents.rs        # ACP agent discovery
│   ├── acp/                 # ACP protocol implementation
│   │   ├── jsonrpc.rs       # JSON-RPC types and builders
│   │   └── subprocess/      # Agent subprocess management
│   │       ├── lifecycle.rs # Spawn, kill, stdin writer
│   │       ├── protocol.rs  # Initialize, session methods
│   │       ├── reader.rs    # Background stdout reader
│   │       └── ...          # Notifications, permissions, etc.
│   ├── api/                 # HTTP API endpoints
│   │   ├── session/         # Session management
│   │   ├── projects.rs      # Project listing
│   │   ├── upload.rs        # File upload handling
│   │   └── ...
│   ├── history.rs           # Chat history (fjall DB)
│   ├── events.rs            # SSE event types
│   └── ...
├── static/                  # HTML pages (session.html, projects.html)
├── tests/                   # JavaScript tests (Deno)
└── Cargo.toml

Features

  • Session naming with click-to-edit in session header
  • Light/dark theme toggle with system theme follow
  • Running sessions visible on project list with status indicators
  • Session termination control (explicit terminate vs. leave running)
  • File upload (drag-and-drop, clipboard paste, or button)
  • Tool call cards with purpose, parameters, output, and diff display
  • Tool call status-based card tinting (blue=in_progress, green=completed, red=failed)
  • Tool call selection for follow-up messages (pin button)
  • Interactive permission request UI
  • Config options UI (model/agent dropdowns with search, keyboard shortcuts)
  • Unified prompt menu (type @ or click button)
  • Command palette (type / to discover slash commands)
  • Shell command execution (!-prefixed messages)
  • Real-time streaming via Server-Sent Events
  • Mobile-friendly responsive design
  • Markdown rendering with syntax highlighting
  • Clickable items (list items and headings submit as prompts)
  • Server-side chat history storage for multi-device sync
  • Version mismatch detection with reload banner
  • Server-side conversation logging to markdown files
  • Ivorn context injection (project metadata in every message)
  • Project context hooks (static files and dynamic scripts)

Data Storage

All application data is stored in the OS data directory:

Platform Location
Linux ~/.local/share/ivorn/
macOS ~/Library/Application Support/ivorn/
Windows C:\Users\<user>\AppData\Roaming\ivorn\
~/.local/share/ivorn/
├── state/                              # Global state files
│   ├── sessions.json                   # Active session metadata
│   ├── terminated-sessions.json        # Terminated session records
│   └── project-usage.json              # Project usage timestamps
└── projects/{project}/                 # Per-project data
    ├── conversations/yyyy/mm/dd/       # Conversation logs
    └── uploads/                        # Uploaded files

Development

# Format code
just fmt

# Run CI checks
just ci

# Run locally
just run /path/to/projects

# Run JavaScript tests (requires Deno)
just test-js

License

AGPL-3.0-or-later

Copyright (C) 2026 Paul Campbell