Documentation Index
Fetch the complete documentation index at: https://mintlify.com/coollabsio/jean/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Jean includes a built-in HTTP server with WebSocket support, enabling remote access to your projects and sessions from web browsers or other clients. All features available in the native app work in the browser.
Key Capabilities
HTTP Server
Architecture:
// From src-tauri/src/http_server/
mod auth; // Token-based authentication
mod dispatch; // Request routing
mod server; // HTTP server setup
mod websocket; // WebSocket connections
Server features:
- HTTP/1.1 with WebSocket upgrade
- Token-based authentication
- CORS support for web clients
- Static file serving (future)
- RESTful API (future)
Configuration:
interface AppPreferences {
http_server_enabled: boolean // Enable server
http_server_port: number // Port (default: 3456)
http_server_token: string | null // Auth token
http_server_auto_start: boolean // Start on launch
http_server_localhost_only: boolean // Bind to localhost
http_server_token_required: boolean // Require token (default: true)
}
WebSocket Protocol
Event broadcasting:
// WsBroadcaster sends events to all connected clients
pub struct WsBroadcaster {
tx: broadcast::Sender<WsEvent>,
}
pub struct WsEvent {
pub event: String,
pub payload: Value,
}
Dual emission:
// EmitExt trait sends to both Tauri IPC and WebSocket
impl EmitExt for AppHandle {
fn emit_all<S>(&self, event: &str, payload: &S) -> Result<(), String> {
// Send to native app (Tauri IPC)
self.emit(event, payload.clone())?;
// Broadcast to WebSocket clients
if let Some(ws) = self.try_state::<WsBroadcaster>() {
ws.broadcast(event, &serde_json::to_value(payload)?);
}
Ok(())
}
}
Event types:
- Project events (created, updated, deleted)
- Worktree events (creating, created, archived)
- Session events (messages, status changes)
- Git status updates
- PR status changes
- Terminal I/O
Authentication
Token generation:
// Automatically generated on first enable
const token = generateSecureToken() // Cryptographically random
// Format: 32-character alphanumeric string
// Example: "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
Authentication flow:
// 1. Client connects to WebSocket
const ws = new WebSocket('ws://localhost:3456/ws')
// 2. Send auth message
ws.send(JSON.stringify({
type: 'auth',
token: 'your-token-here'
}))
// 3. Server validates token
if (token_valid) {
// Connection established
// Start receiving events
} else {
// Connection closed with error
}
Security:
- Token stored securely in app preferences
- Required by default (configurable)
- Can regenerate token at any time
- Localhost-only binding by default
Localhost vs Network Binding
Localhost-only (default):
// Binds to 127.0.0.1
let addr = SocketAddr::from(([127, 0, 0, 1], port));
// Only accessible from same machine
Network binding:
// Binds to 0.0.0.0
let addr = SocketAddr::from(([0, 0, 0, 0], port));
// Accessible from any device on network
Security implications:
- Localhost: Safe, no network exposure
- Network: Exposed to LAN, require token
- Public network: DO NOT USE without VPN/SSH tunnel
Web Client Access
Connecting from browser:
<!-- Open in browser -->
http://localhost:3456/
<!-- With custom port -->
http://localhost:8080/
<!-- From other device (if network binding enabled) -->
http://192.168.1.100:3456/
Web client features:
- Full Jean UI in browser
- All projects and worktrees
- Chat sessions
- File preview and diffs
- Terminal access
- Real-time updates via WebSocket
Limitations:
- No file system access (browser sandbox)
- No native integrations (editor, terminal apps)
- Slightly higher latency
- Requires active HTTP server
How to Use
Enabling HTTP Server
Initial setup:
- Open Settings (Cmd/Ctrl + ,)
- Navigate to “Remote Access” section
- Toggle “Enable HTTP Server”
- Token generates automatically
- Server starts immediately
Configure options:
// Auto-start on app launch
http_server_auto_start: true
// Port selection
http_server_port: 3456 // Change if port conflict
// Network access
http_server_localhost_only: false // Enable LAN access
// Security
http_server_token_required: true // Always require token
Managing Authentication
View token:
- Settings → Remote Access
- Token displayed in “Authentication Token” field
- Click “Copy” to copy to clipboard
Regenerate token:
- Click “Regenerate Token” button
- Confirm action
- New token generated
- All existing connections terminated
- Re-authenticate clients with new token
Disable token requirement:
- Uncheck “Require Authentication Token”
- WARNING: Anyone on network can access
- Only use on trusted networks
- Not recommended
Connecting from Browser
Same machine:
- Server must be running
- Open browser
- Navigate to
http://localhost:3456
- Enter token when prompted
- Jean loads in browser
Different machine (LAN):
- Enable network binding in settings
- Find host machine’s IP address:
# macOS/Linux
ifconfig | grep "inet "
# Windows
ipconfig
- On client machine, open browser
- Navigate to
http://<host-ip>:3456
- Enter authentication token
- Jean loads remotely
Using WebSocket API
Connect to WebSocket:
const token = 'your-token-here'
const ws = new WebSocket('ws://localhost:3456/ws')
ws.onopen = () => {
// Authenticate
ws.send(JSON.stringify({
type: 'auth',
token: token
}))
}
ws.onmessage = (event) => {
const message = JSON.parse(event.data)
console.log('Event:', message.event)
console.log('Payload:', message.payload)
}
ws.onerror = (error) => {
console.error('WebSocket error:', error)
}
ws.onclose = () => {
console.log('Connection closed')
}
Receive events:
ws.onmessage = (event) => {
const { event: eventName, payload } = JSON.parse(event.data)
switch (eventName) {
case 'worktree-created':
console.log('New worktree:', payload.worktree)
break
case 'session-message':
console.log('New message:', payload.message)
break
case 'pr-status-updated':
console.log('PR status:', payload.status)
break
// ... handle other events
}
}
Server Lifecycle
Manual start:
- Settings → Remote Access
- Click “Start Server”
- Status indicator shows “Running”
Manual stop:
- Click “Stop Server”
- All connections closed
- Status shows “Stopped”
Auto-start:
- Enable in settings
- Server starts when Jean launches
- Survives app restarts
- Stops when app quits
Configuration Options
Settings → Remote Access
Server Control:
http_server_enabled: boolean
// Current server state
// Toggle to start/stop immediately
http_server_auto_start: boolean
// Default: false
// Start server when Jean launches
Network Configuration:
http_server_port: number
// Default: 3456
// Range: 1024-65535
// Restart server after changing
http_server_localhost_only: boolean
// Default: true (secure)
// false = bind to 0.0.0.0 (LAN access)
Security:
http_server_token_required: boolean
// Default: true (recommended)
// false = allow unauthenticated access
http_server_token: string | null
// Auto-generated secure token
// Regenerate to invalidate old tokens
Best Practices
Security
Always use authentication:
- Keep
http_server_token_required: true
- Only disable on isolated networks
- Never disable on public networks
Token management:
- Store token securely (password manager)
- Don’t share tokens publicly
- Regenerate if compromised
- Rotate tokens periodically
Network exposure:
- Keep
localhost_only: true by default
- Only enable network binding when needed
- Use VPN for internet access
- Consider SSH tunneling:
ssh -L 3456:localhost:3456 user@remote-host
Browser vs Native:
- Native app is faster
- Browser adds latency
- Terminal may lag remotely
- Use native for primary work
Network optimization:
- Use LAN, not WiFi for best performance
- Close unused connections
- Limit concurrent clients
- Monitor bandwidth usage
Use Cases
When to use remote access:
- Access from iPad/tablet
- Quick checks from phone
- Secondary display/machine
- Team collaboration (view-only)
- Remote pair programming
When to use native app:
- Primary development
- Performance-critical work
- File system operations
- Editor integration needed
- Terminal-heavy workflows
Port Selection
Default port (3456):
- Usually available
- Easy to remember
- Non-privileged port
Alternative ports:
3000 - Common conflict (dev servers)
8080 - Common conflict (HTTP proxies)
5000 - Common conflict (Python servers)
3456 - Jean default (good choice)
9000 - Alternative option
Check port availability:
# macOS/Linux
lsof -i :3456
# Windows
netstat -ano | findstr :3456
Network Security
Firewall configuration:
# macOS - allow port
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add /Applications/Jean.app
# Linux - allow port
sudo ufw allow 3456/tcp
# Windows - allow port
netsh advfirewall firewall add rule name="Jean" dir=in action=allow protocol=TCP localport=3456
VPN setup for remote access:
- Set up VPN server (WireGuard, Tailscale, etc.)
- Connect remote device to VPN
- Access via VPN IP address
- No port forwarding needed
- Encrypted tunnel
SSH tunnel for secure access:
# On client machine
ssh -L 3456:localhost:3456 user@server-ip
# Then access via
http://localhost:3456
Monitoring
Check server status:
- Settings → Remote Access → Status indicator
- Green = Running
- Red = Stopped
- Yellow = Error
View connected clients:
- Settings → Remote Access → Active Connections
- Shows IP addresses
- Connection timestamps
- Kick option (future)
Server logs:
- Check app logs for errors
- WebSocket connection events
- Authentication failures
- Port binding issues
Troubleshooting
Server won’t start:
# Check if port is in use
lsof -i :3456
# Kill process using port
kill <PID>
# Or change port in settings
Can’t connect from browser:
- Verify server is running
- Check firewall settings
- Confirm correct IP address
- Test with curl:
curl http://localhost:3456
Authentication fails:
- Verify token correct
- Check for extra spaces
- Try regenerating token
- Ensure token required is enabled
WebSocket disconnects:
- Check network stability
- Verify token still valid
- Look for proxy interference
- Test with direct connection
Advanced Usage
Reverse proxy setup:
# nginx configuration
server {
listen 80;
server_name jean.example.com;
location / {
proxy_pass http://localhost:3456;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Custom WebSocket client:
class JeanWebSocket {
private ws: WebSocket
private token: string
constructor(url: string, token: string) {
this.token = token
this.ws = new WebSocket(url)
this.setup()
}
private setup() {
this.ws.onopen = () => {
this.authenticate()
}
this.ws.onmessage = (event) => {
const { event: name, payload } = JSON.parse(event.data)
this.handleEvent(name, payload)
}
}
private authenticate() {
this.send('auth', { token: this.token })
}
private handleEvent(name: string, payload: any) {
// Custom event handling
this.emit(name, payload)
}
public send(type: string, data: any) {
this.ws.send(JSON.stringify({ type, ...data }))
}
}
Docker deployment (future):
FROM rust:latest as builder
WORKDIR /app
COPY . .
RUN cargo build --release
FROM debian:bullseye-slim
COPY --from=builder /app/target/release/jean /usr/local/bin/
EXPOSE 3456
CMD ["jean", "--http-server", "--port", "3456"]