A web interface for the Claude Code CLI with PAM authentication and real-time streaming.
Forked from sugyan/claude-code-webui - Thanks to the original author for the foundation.
This fork adds:
- PAM Authentication - Linux system authentication for multi-user environments
- Session Management - SQLite-backed user sessions with secure tokens
- Proper SDK Permission Handling - Uses
canUseToolcallback instead of message-based flow
- Real-time streaming responses via Claude Agent SDK
- PAM-based login (uses system credentials)
- Project directory selection for context-aware sessions
- Conversation history with session persistence
- Tool permission management with granular control
- Dark/light theme with system preference detection
- Mobile-responsive interface
- Permission mode switching (normal, plan, auto-accept)
- Node.js >= 20.0.0
- Claude CLI installed and authenticated
- Linux system with PAM (for authentication)
libpam0g-devpackage (Debian/Ubuntu) or equivalent
git clone https://github.com/thassiov/relay.git
cd relay
# Install PAM development headers (Debian/Ubuntu)
sudo apt install libpam0g-dev
# Backend
cd backend
npm install
npm run dev
# Frontend (new terminal)
cd frontend
npm install
npm run devAccess at http://localhost:3000 (frontend) or http://localhost:8080 (backend only).
cd backend
npm run build
npm start| Option | Description | Default |
|---|---|---|
-p, --port <port> |
Port to listen on | 8080 |
--host <host> |
Host address (0.0.0.0 for all interfaces) | 127.0.0.1 |
--claude-path <path> |
Path to claude executable | Auto-detect |
-d, --debug |
Enable debug logging | false |
--no-auth |
Disable PAM authentication | false |
# Default (localhost:8080 with auth)
relay
# Without authentication (development)
relay --no-auth
# Bind to all interfaces
relay --host 0.0.0.0 --port 9000
# Custom Claude CLI path
relay --claude-path /path/to/clauderelay/
├── backend/ # Hono server + Claude Agent SDK
│ ├── handlers/ # API endpoints (chat, auth, permission, histories)
│ ├── middleware/ # PAM auth middleware
│ ├── utils/ # DB, session, permission state management
│ └── cli/ # Entry points
├── frontend/ # React + Vite + TailwindCSS
│ ├── components/ # UI components
│ ├── hooks/ # Chat state, streaming, auth
│ └── contexts/ # Auth context
└── shared/ # TypeScript types
POST /api/login- PAM authenticationPOST /api/logout- Session terminationGET /api/me- Current user infoPOST /api/chat- Streaming chat (requires auth)POST /api/permission/:id- Permission grant/deny responseGET /api/projects- List project directoriesGET /api/projects/:name/histories- Conversation history
- User submits credentials at
/login - Backend validates against PAM
- Session token created in SQLite database
- Token stored in httpOnly cookie
- Subsequent requests validated via middleware
Sessions expire after 24 hours of inactivity.
When Claude needs tool access:
- SDK fires
canUseToolcallback - Backend emits
permission_requestto stream - Frontend shows permission dialog
- User clicks Allow/Deny
- Frontend POSTs to
/api/permission/:permissionId - Backend resolves waiting Promise
- SDK continues execution
This replaces the previous message-based flow that created multiple sessions.
# Run quality checks
make check
# Format code
make format
# Run tests
make test
# Individual commands
make lint
make typecheck
make test-frontend
make test-backend- PAM authentication means users log in with system credentials
- Sessions stored server-side in SQLite
- Cookies are httpOnly (not accessible via JavaScript)
- Never expose to public internet without additional security (reverse proxy, TLS)
relay --claude-path "$(which claude)"- Ensure user has valid system account
- Check PAM configuration in
/etc/pam.d/ - Run with
--debugfor detailed logs
Run with debug mode to see SDK callback flow:
relay --debugThis project is a fork of sugyan/claude-code-webui. Thanks to @sugyan for creating the original web interface that made this possible.
MIT License - see LICENSE for details.