Ivorn
A web-based chat interface for Kiro CLI that enables browser-based interaction with multiple parallel project sessions.
Quick Start
# Build
# Run (provide path to projects directory)
# Or use environment variable
IVORN_PROJECTS_DIR=/path/to/projects
Open http://localhost:8181 in your browser.
Configuration
Config File
Configuration is loaded from ~/.config/ivorn/config.toml (XDG spec).
See config.example.toml for all available settings.
[]
= 8181
= "0.0.0.0"
[]
= "/path/to/projects"
[]
= true # Enable conversation logging to markdown files
[]
= 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
Configuration values are resolved in this order (highest to lowest):
- CLI arguments
- Environment variables
- Config file
- Defaults
ACP Agents
ACP (Agent Client Protocol) agents are external executables that implement the ACP protocol over stdio. Ivorn discovers agents from JSON configuration files.
Configuration Locations
- Global:
~/.config/ivorn/acp/*.json - Project-local:
{project}/.ivorn/acp/*.json(takes precedence over global)
Config File Format
| 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 |
Example: Kiro CLI
Create ~/.config/ivorn/acp/kiro-cli.json:
API Endpoint
GET /api/acp-agents returns the list of discovered agents (global only, project-local requires session context).
Projects Directory Structure
The projects directory should contain project subdirectories directly:
/path/to/projects/
├── project-a/
├── project-b/
└── project-c/
Features
- Multi-tab interface for parallel project sessions
- Standalone session pages:
/projectsfor ACP-only project list,/session/{id}for single-session view (opens in new browser tab) - Running sessions visible on project list: each project shows its active sessions with status indicators and clickable links to open in new tab
- Rich activity indicators on session tabs: 5 distinct states (idle, streaming, permission_pending, turn_complete, timeout) with visual icons and colors; timeout triggers after 30s of no SSE events (pauses during tool execution); standalone pages update browser title with state prefix (⟳, ⚠, ✓, ⏳)
- Terminated session auto-resume: "Show history" toggle on projects page reveals terminated sessions; clicking resurrects the session by spawning a new ACP subprocess and calling
session/loadto replay kiro-cli's history - Real-time project page updates: project list subscribes to global SSE stream (
/api/stream) for instant session lifecycle events (created, closed, status changes) without page refresh - Session termination control: ⏻ button explicitly terminates session (kills subprocess); closing browser tab leaves session alive for later reconnection;
beforeunloadprompt reminds user session is still running - Per-tab input state (draft messages preserved when switching tabs)
- Project list sorted by most recently used, then alphabetically
- ACP (Agent Client Protocol) sessions using JSON-RPC 2.0 for structured agent communication
- File upload for ACP sessions: drag-and-drop, clipboard paste, or button; stored in project uploads/ directory with text reference
Attached file: ./uploads/{filename} (mime/type, size) - All file types supported; images (detected via magic bytes) sent as base64 Image content blocks + text reference; non-images sent as text reference only
- File attachment display: images render as clickable thumbnails with modal; non-images render as card-style badges with MIME-based icons (📄 PDF, 📦 archives, 📝 text, 📋 JSON, 📎 other)
- MIME type detection using
infercrate (magic bytes) with extension fallback for text files (txt, md, json, xml, csv, log) - Session-type-aware SSE handlers with shared event infrastructure
- ACP spec-aligned tool call visibility (kind, status, title, raw I/O)
- Tool call cards with purpose display, collapsible Parameters and Output sections, and status badges
- Tool call Parameters and Output sections: both shown when expanded, each independently collapsible with state persisted across page reloads
- Tool call output parsing: extracts text from MCP response wrappers (
items[*].Json.content[*].text), falls back to pretty-printed JSON - Tool call kind-based default expansion: impactful tools (edit, delete, move, execute) expanded by default; read-only tools collapsed
- Tool call expansion state persisted across SSE updates and page reloads via toolCallsById
- Tool call diff display: file changes shown as colored inline diff (red/green) instead of raw args when diff data available
- Tool call content display: text (markdown), diff, terminal, and unknown content types rendered in expandable cards
- Tool call error display: failed tool calls show error details in collapsible section with red styling; auto-expands when tool fails
- Interspersed tool cards: tool calls appear inline at their first occurrence position, not grouped at bottom
- Turn completion dividers with elapsed time and timestamps
- Unified inline activity indicator: single indicator appears after the last user message; shows "Waiting for response..." with spinner + timer + cancel during waiting state, transitions to bouncing dots during typing state; timer and cancel button visible in both states
- Bidirectional agent communication (incoming requests, interactive permission UI)
- Interactive permission request UI: inline menu above input area, "View" link to highlight tool card, user approves/rejects tool calls
- Agent info and capability detection (name, version, image prompts, session restore, MCP transports)
- ACP authentication support (auth method parsing and automatic authenticate flow)
- ACP agent selection: projects page shows collapsible sections with agent cards; click agent to start session; auto-expand projects with active sessions; localStorage persistence for expand/collapse state
- Session mode and config option parsing from session/new response
- Session config option setting via ACP session/set_config_option (generic key-value)
- Config change system messages: full-width banner in chat history when config options change (e.g., agent, model), showing old→new values; distinguishes user-initiated vs agent-initiated changes
- Config snapshot in message footer: assistant messages display current config values (agent, model) left of timestamp for context
- Config options UI for ACP sessions (model/agent dropdowns with search, keyboard shortcuts: Ctrl+M model, Ctrl+A agent)
- Unified prompt menu: type
@at start of message OR click the@button to search and select prompts (arrow keys, Enter/Tab to select, Esc to dismiss), sorted alphabetically by name; ACP sessions receive prompts via SSE with full content for local/global prompts, auto-refreshed on agent change - Session load/resume via fjall history database (single source of truth for chat history)
- Session resume auto-message: on page reload, automatically sends a message to the agent with time since last activity, asking it to summarize where things left off and suggest next steps
- Config options restored on page reload via
/messages/latestAPI (ACP sessions only) - Real-time streaming via Server-Sent Events (SSE) including stop reason events
- Agent execution plan display (ACP plan events)
- Graceful handling of unknown/future ACP session update types
- Mobile-friendly responsive design with consolidated gear menu (⚙️) for config options, history, prompts, and file upload
- Text selection popup: desktop centers on mouse position, mobile centers on viewport with smart vertical placement
- Markdown rendering with syntax highlighting for both user and agent messages; diff code blocks display with red/green line coloring
- Clickable lines in ACP sessions: list items and headings submit as prompts on click (cursor pointer, green hover border)
- Session persistence across page reloads
- Session isolation: every message stamped with
_sid(session ID), validated on load/tab-switch/periodic, misplaced messages auto-corrected - Server-side chat history storage (fjall embedded DB) for multi-device sync and server restart resilience
- Build info system: compile-time capture of version, git hash, jj change id, build timestamp via
build.rs - About modal: hamburger menu → "About" displays version, git hash, jj change id, build time
- Version mismatch detection: polls
/api/healthevery 5 min + checks before each message send; non-blocking gold banner with reload button when server version changes - Graceful model unavailability handling (ACP): inline error banner, pulsing model selector highlight, automatic retry on model change
- Session error surfacing (ACP): fatal errors (subprocess death) show red banner with "New Session" button; recoverable errors (JSON-RPC failures) show yellow banner with "Retry" button
- Scroll-to-bottom button: circular floating button (bottom-right) appears when scrolled up, with unread message count badge
- Loading progress indicator: thin indeterminate progress bar at top of input area during history sync, terminated session load, and SSE connection setup
- Prompt navigation buttons: ↑/↓ buttons on user messages navigate between user messages; ↑/↓ buttons on write tool calls (edit/delete/move) navigate between write tools
- Keyboard shortcuts for navigation:
J(next user message) andK(previous user message),j(next write tool) andk(previous write tool) when input not focused - Frontend performance optimizations for long conversations: rendered HTML caching on message objects, debounced scroll and copy button operations via requestAnimationFrame
- Server-side conversation logging (ACP only): real-time markdown logs saved to OS data directory for conversation recovery after server issues
- Shell command execution (ACP only):
!-prefixed messages execute as shell commands in the project directory; output streamed with ANSI color rendering (standard colors, bright colors, bold, 256-color palette) in dark terminal-style container with copy button; stop button kills process group
Conversation Log Files
ACP sessions automatically log conversations to markdown files for recovery after server issues. Logs are stored in the OS data directory:
| Platform | Location |
|---|---|
| Linux | ~/.local/share/ivorn/conversations/{project}/yyyy/mm/dd/{session_id}.md |
| macOS | ~/Library/Application Support/ivorn/conversations/{project}/yyyy/mm/dd/{session_id}.md |
| Windows | C:\Users\<user>\AppData\Roaming\ivorn\conversations\{project}\yyyy\mm\dd\{session_id}.md |
Logs include:
- User messages (as blockquotes)
- Assistant messages (plain text)
- Tool calls with purpose and parameters
- Tool status updates (✅ completed, ❌ failed)
- Tool results: short results (<21 lines) inlined, long results saved to
{session_id}/{tool_call_id}.txt(sibling directory) - Turn separators
Logs are created lazily on first user message and can be used to resume conversations in new sessions by referencing the file.
Migration: On first run, existing flat files from {project}/conversations/ are automatically migrated to the date-based hierarchy using the **Started** header timestamp.
Privacy Note: Conversation logs contain full message content. Set conversation_log = false in the [logging] config section to disable.
Session State File
Session state is persisted to ivorn-sessions.json in the projects directory root. This file tracks:
- Active session IDs and their associated projects
- Session type (ACP)
- Clean termination status (for session resume)
Closing a session (DELETE /api/session/{id}) removes it from both server memory and the JSON file. This works even after a server restart when the session exists only on disk.
Terminated Sessions File
Terminated session metadata is persisted to ivorn-terminated-sessions.json in the projects directory root. This file tracks:
- Session ID, project, and session type
- Termination timestamp
This enables auto-resuming terminated sessions. The "Show history" toggle on the projects page reveals these sessions; clicking one spawns a new ACP subprocess and calls session/load to replay kiro-cli's history, then moves the session back to active state.
Project Usage File
Project usage timestamps are persisted to ivorn-project-usage.json in the projects directory root. This file tracks:
- Last used timestamp per project (for project list sorting)
- Persists even after sessions are closed
Development
# Format code
# Run CI checks
# Run locally
JavaScript Tests
Frontend unit tests use Deno (no Node.js required):
# Run JS tests locally
# Or directly with Deno
Requirements: Deno must be installed locally (mise install deno or see deno.land).
See design-docs/initial-design.md for architecture details.
License
Copyright (C) 2026 Paul Campbell
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.