clauderelay

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 13, 2025 License: MIT Imports: 22 Imported by: 0

README

Claude Relay - WebSocket Server

A Go-based WebSocket server that provides an isolated Claude Code CLI environment with portable Bun installation.

Features

  • Isolated Claude Installation: Uses a portable Bun installation and isolated Claude instance in the project directory
  • Auto-Setup: Automatically downloads and installs Bun and Claude on first run
  • Authentication Handling: Guides you through Claude authentication when needed
  • Conversation Context: Maintains conversation history using Claude's --print mode
  • Undo/Restore: Per-message undo functionality with restore capability
  • Progress Indicators: Shows thinking indicators while Claude processes requests
  • Web UI: Clean, modern web interface with 120-character width support
  • Session Isolation: Each connection gets its own temporary directory
  • Automatic Cleanup: Processes and directories cleaned up on disconnect

Installation

go mod download
go build -o claude-relay .

First Time Setup

On first run, the application will:

  1. Download and install portable Bun in .bun/
  2. Install Claude Code CLI locally
  3. Guide you through Claude authentication

Run the setup:

./claude-relay -port 8081

When Claude starts and shows the interactive prompt, type /login and follow the authentication instructions.

Usage

After initial setup, simply run:

./claude-relay -port 8081

Then open client.html in your browser or navigate to http://localhost:8081

Directory Structure

After setup, the following directories will be created:

  • .bun/ - Portable Bun installation
  • .claude-home/ - Isolated Claude configuration and authentication
  • claude-relay-*/ - Temporary directories for Claude workspace (created at runtime)

Architecture

  • main.go: Entry point, setup initialization, and HTTP server
  • setup.go: Handles Bun and Claude installation, authentication
  • websocket.go: WebSocket connection and message handling
  • process.go: Claude process management and conversation state
  • config.go: Configuration management
  • client.html: Modern web UI with undo/restore functionality

Features in Detail

Isolated Environment
  • No system-wide installation required
  • All dependencies contained in project directory
  • Separate Claude home directory for configuration
  • Uses custom HOME environment for Claude
Conversation Management
  • Maintains conversation history across messages
  • Uses Claude's --print mode with context injection
  • Automatic history pruning (keeps last 10 exchanges)
Undo/Restore System
  • Click the ↶ button on any message to undo from that point
  • Removes the selected message and all subsequent ones
  • Restore button appears after undo operations
  • Can restore the last undone conversation
Progress Indicators
  • "Claude is thinking..." initial indicator
  • Progressive status updates during processing
  • Visual pulse animation in the UI

Configuration

Default port is 8080, customize with:

./claude-relay -port 3000

WebSocket Protocol

Client to Server
  • Plain text messages sent to Claude
  • /undo-from:N - Undo from message index N
  • /restore - Restore last undone conversation
Server to Client
  • Claude responses as plain text
  • Progress indicators (with emoji prefixes)
  • UNDO_SUCCESS:N - Undo confirmation
  • RESTORE_SUCCESS:[...] - Restore with message array
  • Error messages prefixed with ❌

Troubleshooting

Authentication Issues
rm -rf .claude-home
./claude-relay -port 8081
# Complete login when prompted
Port Already in Use
lsof -ti :8081 | xargs kill -9
Reset Everything
rm -rf .bun .claude-home
./claude-relay -port 8081
Check Installation
ls -la .bun/bin/       # Should show bun and claude
ls -la .claude-home/   # Should show .config/claude/

Development

Building
go build -o claude-relay .
Testing
# Run server
./claude-relay -port 8081

# In another terminal
python test_ws.py
Helper Scripts
  • setup.sh - Interactive setup script
  • test.sh - Run tests
  • Makefile - Build and run commands

Security Notes

  • Each session isolated in temporary directory
  • Processes killed and cleaned up on disconnect
  • Authentication stored in isolated .claude-home
  • CORS configured to accept any origin (customize as needed)
  • Uses --dangerously-skip-permissions flag for Claude

Requirements

  • Go 1.19 or higher
  • Internet connection for initial setup
  • macOS or Linux (Windows not tested)

License

[Your License Here]

Documentation

Overview

Package clauderelay provides a WebSocket relay server for Claude Code CLI with isolated, portable installation support.

This package enables you to programmatically create and manage multiple isolated Claude instances, each with its own configuration and authentication, without requiring system-wide installation.

Features

• Isolated Installation: Each instance has its own Bun and Claude installation • Multiple Instances: Run multiple Claude relays on different ports • Auto-Setup: Automatically downloads and installs dependencies • Authentication Management: Handle Claude authentication programmatically • WebSocket Interface: Connect to Claude via WebSocket protocol • Conversation State: Maintains context across messages • Undo/Restore: Support for undoing and restoring conversations

Basic Usage

Create a simple relay server:

import "github.com/rizrmd/claude-relay"

func main() {
	// Create relay with automatic setup
	relay, err := clauderelay.New(clauderelay.Options{
		Port:          "8081",
		BaseDir:       "./claude-instance",
		AutoSetup:     true,
		EnableLogging: true,
	})
	if err != nil {
		log.Fatal(err)
	}
	defer relay.Close()

	// Check authentication
	if authenticated, _ := relay.IsAuthenticated(); !authenticated {
		if err := relay.Authenticate(); err != nil {
			log.Fatal(err)
		}
	}

	// Start the server
	if err := relay.Start(); err != nil {
		log.Fatal(err)
	}

	// Wait for shutdown
	select {}
}

Multiple Instances

Run multiple isolated Claude instances:

// Create instance 1
relay1, _ := clauderelay.New(clauderelay.Options{
	Port:    "8081",
	BaseDir: "./claude-1",
})
relay1.Start()

// Create instance 2
relay2, _ := clauderelay.New(clauderelay.Options{
	Port:    "8082",
	BaseDir: "./claude-2",
})
relay2.Start()

Embedding in Existing Server

Integrate Claude relay into your existing HTTP server:

relay, _ := clauderelay.New(clauderelay.Options{
	Port: "8081",
})
relay.Start()

// Use relay.GetWebSocketURL() to get the WebSocket endpoint
// Integrate with your existing routes

WebSocket Protocol

The relay server exposes a WebSocket endpoint at /ws that accepts text messages and returns Claude's responses.

Client to Server: • Plain text messages to send to Claude • /undo-from:N - Undo from message index N • /restore - Restore last undone conversation

Server to Client: • Claude's responses as plain text • Progress indicators during processing • Error messages prefixed with ❌

Directory Structure

Each Claude instance creates the following structure in its BaseDir:

BaseDir/
├── .bun/                  // Portable Bun installation
│   └── bin/
│       ├── bun            // Bun executable
│       └── claude         // Claude CLI
├── .claude-home/          // Claude configuration
│   └── .config/
│       └── claude/
│           └── auth.json  // Authentication token
└── claude-relay-*/        // Temporary workspaces (runtime)

Authentication

Claude requires authentication on first use. The library provides multiple authentication methods to suit different deployment scenarios:

## Authentication Methods

Claude Code CLI uses browser-based authentication, NOT API keys.

### 1. Authenticate() - Interactive/Terminal-Based

Standard authentication flow - requires terminal and browser access.

err := relay.Authenticate()
// User will:
// 1. See Claude interface in terminal
// 2. Choose a theme (press 1 for dark)
// 3. Type /login
// 4. Complete browser authentication

### 2. CopyAuthFrom() - Reuse Existing Authentication

Copy authentication from another machine or backup.

// First, authenticate on a machine with browser access
// Then copy .claude-home/.config/claude/ directory
err := relay.CopyAuthFrom("/path/to/claude/config")

### 3. Options.PreAuthDirectory - Pre-authenticated Setup

Provide pre-authenticated config during initialization.

relay, _ := clauderelay.New(clauderelay.Options{
	PreAuthDirectory: "/backup/claude-auth",
})

## When to Use Each Method

Authenticate() - Use when: • Local development with terminal • Initial setup with browser access • One-time authentication

CopyAuthFrom() - Use when: • Deploying to servers without browser • Docker containers • CI/CD pipelines • Reusing auth across instances

## Authentication in Production

For production deployments without browser access:

1. Authenticate once on a development machine:

relay.Authenticate() // Interactive login
authPath := relay.GetAuthConfigPath()
// Save the files in authPath

2. Copy auth files to production:

relay.CopyAuthFrom("/deployed/auth/backup")

## Checking Authentication Status

// Simple check
authenticated, err := relay.IsAuthenticated()

// Detailed status
authenticated, message, err := relay.GetAuthStatus()
// Returns: (true, "Authenticated", nil) or (false, "No authentication file found", nil)

Thread Safety

The Relay type is thread-safe and can be safely accessed from multiple goroutines. Multiple relay instances can run concurrently without interference.

Package clauderelay provides a WebSocket relay server for Claude Code CLI with isolated, portable installation support.

This package allows you to create multiple isolated Claude instances, each with its own configuration and authentication, without requiring system-wide installation.

Basic usage:

import "github.com/yourusername/claude-relay"

// Create a new relay server
relay, err := clauderelay.New(clauderelay.Options{
	Port:           "8081",
	BaseDir:        "./claude-instance-1",
	AutoSetup:      true,
})
if err != nil {
	log.Fatal(err)
}
defer relay.Close()

// Start the server
if err := relay.Start(); err != nil {
	log.Fatal(err)
}

Package clauderelay provides isolated Claude Code CLI instances.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func PromptUser

func PromptUser(prompt string) string

Types

type ClaudeProcess

type ClaudeProcess struct {
	// contains filtered or unexported fields
}

func NewClaudeProcess

func NewClaudeProcess(setup *ClaudeSetup) (*ClaudeProcess, error)

func (*ClaudeProcess) CanRestore

func (p *ClaudeProcess) CanRestore() bool

func (*ClaudeProcess) CanUndo

func (p *ClaudeProcess) CanUndo() bool

func (*ClaudeProcess) Cleanup

func (p *ClaudeProcess) Cleanup() error

func (*ClaudeProcess) GetLastExchange

func (p *ClaudeProcess) GetLastExchange() (string, string, error)

func (*ClaudeProcess) GetRestoredMessagesForClient

func (p *ClaudeProcess) GetRestoredMessagesForClient() []map[string]string

func (*ClaudeProcess) GetWorkingDirectory

func (p *ClaudeProcess) GetWorkingDirectory() string

func (*ClaudeProcess) IsRunning

func (p *ClaudeProcess) IsRunning() bool

func (*ClaudeProcess) Kill

func (p *ClaudeProcess) Kill() error

func (*ClaudeProcess) ReadFile

func (p *ClaudeProcess) ReadFile(filename string) ([]byte, error)

func (*ClaudeProcess) RestoreLastUndo

func (p *ClaudeProcess) RestoreLastUndo() ([]string, error)

func (*ClaudeProcess) SaveFile

func (p *ClaudeProcess) SaveFile(filename string, content []byte) error

func (*ClaudeProcess) SaveState

func (p *ClaudeProcess) SaveState()

func (*ClaudeProcess) SendMessage

func (p *ClaudeProcess) SendMessage(message string) (string, error)

func (*ClaudeProcess) SendMessageWithProgress

func (p *ClaudeProcess) SendMessageWithProgress(message string, progressChan chan<- []byte, doneChan <-chan bool) (string, error)

func (*ClaudeProcess) UndoLastExchange

func (p *ClaudeProcess) UndoLastExchange() error

func (*ClaudeProcess) UndoToIndex

func (p *ClaudeProcess) UndoToIndex(messageIndex int) error

type ClaudeProcessV2

type ClaudeProcessV2 struct {
	// contains filtered or unexported fields
}

func NewClaudeProcessV2

func NewClaudeProcessV2() (*ClaudeProcessV2, error)

func (*ClaudeProcessV2) Cleanup

func (p *ClaudeProcessV2) Cleanup() error

func (*ClaudeProcessV2) GetWorkingDirectory

func (p *ClaudeProcessV2) GetWorkingDirectory() string

func (*ClaudeProcessV2) ReadFile

func (p *ClaudeProcessV2) ReadFile(filename string) ([]byte, error)

func (*ClaudeProcessV2) SaveFile

func (p *ClaudeProcessV2) SaveFile(filename string, content []byte) error

func (*ClaudeProcessV2) SendMessage

func (p *ClaudeProcessV2) SendMessage(message string) (string, error)

type ClaudeSetup

type ClaudeSetup struct {
	// contains filtered or unexported fields
}

func NewClaudeSetup

func NewClaudeSetup() (*ClaudeSetup, error)

func NewClaudeSetupWithBaseDir

func NewClaudeSetupWithBaseDir(baseDir string) (*ClaudeSetup, error)

NewClaudeSetupWithBaseDir creates a ClaudeSetup with a custom base directory. This allows multiple isolated Claude instances.

func (*ClaudeSetup) CheckAuthentication

func (cs *ClaudeSetup) CheckAuthentication() (bool, error)

func (*ClaudeSetup) CopyAuthFrom added in v1.0.1

func (cs *ClaudeSetup) CopyAuthFrom(sourceDir string) error

CopyAuthFrom copies authentication config from another directory

func (*ClaudeSetup) GetAuthStatus

func (cs *ClaudeSetup) GetAuthStatus() (bool, string, error)

GetAuthStatus returns detailed authentication status

func (*ClaudeSetup) GetClaudeEnv

func (cs *ClaudeSetup) GetClaudeEnv() []string

func (*ClaudeSetup) GetClaudeHome

func (cs *ClaudeSetup) GetClaudeHome() string

func (*ClaudeSetup) GetClaudePath

func (cs *ClaudeSetup) GetClaudePath() string

func (*ClaudeSetup) HandleAuthenticationPrompt

func (cs *ClaudeSetup) HandleAuthenticationPrompt(process *ClaudeProcess) error

func (*ClaudeSetup) InstallBun

func (cs *ClaudeSetup) InstallBun() error

func (*ClaudeSetup) InstallClaude

func (cs *ClaudeSetup) InstallClaude() error

func (*ClaudeSetup) IsAuthenticationNeeded

func (cs *ClaudeSetup) IsAuthenticationNeeded(output string) bool

func (*ClaudeSetup) IsInstalled

func (cs *ClaudeSetup) IsInstalled() bool

func (*ClaudeSetup) RunClaudeLogin

func (cs *ClaudeSetup) RunClaudeLogin() error

func (*ClaudeSetup) SetAuthToken

func (cs *ClaudeSetup) SetAuthToken(authToken string) error

SetAuthToken sets the Claude Code CLI authentication token programmatically Note: This is NOT a Claude API key, but rather the auth token from Claude Code CLI login

func (*ClaudeSetup) Setup

func (cs *ClaudeSetup) Setup() error

func (*ClaudeSetup) SetupClaudeHome

func (cs *ClaudeSetup) SetupClaudeHome() error

func (*ClaudeSetup) WaitForAuth

func (cs *ClaudeSetup) WaitForAuth(timeout time.Duration) error

WaitForAuth waits for authentication to be completed This can be used after SetAuthToken to verify authentication worked

type Config

type Config struct {
	Port         string            `json:"port"`
	ClaudePath   string            `json:"claude_path"`
	MaxProcesses int               `json:"max_processes"`
	Endpoints    map[string]string `json:"endpoints,omitempty"`
	AllowOrigins []string          `json:"allow_origins,omitempty"`
	TempDirBase  string            `json:"temp_dir_base,omitempty"`
}

func DefaultConfig

func DefaultConfig() *Config

func LoadConfig

func LoadConfig(path string) (*Config, error)

func (*Config) Save

func (c *Config) Save(path string) error

type Connection

type Connection struct {
	// contains filtered or unexported fields
}

type ConnectionV2

type ConnectionV2 struct {
	// contains filtered or unexported fields
}

type ConversationState

type ConversationState struct {
	History   []string
	Timestamp time.Time
}

type Options

type Options struct {
	// Port is the port number for the WebSocket server.
	// Default: "8080"
	Port string

	// BaseDir is the directory where Claude and Bun will be installed.
	// Each instance should have a unique BaseDir for isolation.
	// Default: current directory
	BaseDir string

	// AutoSetup automatically installs Claude and Bun if not present.
	// Default: true
	AutoSetup bool

	// MaxProcesses limits the number of concurrent Claude processes.
	// Default: 100
	MaxProcesses int

	// EnableLogging enables detailed logging output.
	// Default: true
	EnableLogging bool

	// CustomClaudePath allows specifying a custom Claude executable path.
	// If empty, uses the isolated installation.
	CustomClaudePath string

	// AuthToken provides a pre-existing Claude Code CLI auth token for reuse.
	// This is obtained from a previous Claude Code CLI login session.
	// Note: This is NOT a Claude API key.
	AuthToken string

	// PreAuthDirectory specifies a directory containing pre-authenticated Claude config.
	// Copy from another machine's .claude-home/.config/claude/ directory.
	PreAuthDirectory string
}

Options configures the Claude relay server.

type Relay

type Relay struct {
	// contains filtered or unexported fields
}

Relay represents a Claude WebSocket relay server instance.

func New

func New(opts Options) (*Relay, error)

New creates a new Claude relay instance with the given options.

Example:

relay, err := clauderelay.New(clauderelay.Options{
	Port:    "8081",
	BaseDir: "./my-claude",
})

func (*Relay) Authenticate

func (r *Relay) Authenticate() error

Authenticate runs the Claude authentication process interactively. This should be called when IsAuthenticated returns false.

Example:

authenticated, _ := relay.IsAuthenticated()
if !authenticated {
	fmt.Println("Please complete Claude authentication:")
	if err := relay.Authenticate(); err != nil {
		log.Fatal(err)
	}
}

func (*Relay) Close

func (r *Relay) Close() error

Close stops the server and cleans up resources. It's safe to call Close multiple times.

Example:

defer relay.Close()

func (*Relay) CopyAuthFrom added in v1.0.1

func (r *Relay) CopyAuthFrom(sourceDir string) error

CopyAuthFrom copies authentication from another Claude installation. Use this to reuse authentication from another machine or instance.

Example:

// Copy auth from another instance
err := relay.CopyAuthFrom("/backup/claude-auth/")

func (*Relay) GetAuthConfigPath added in v1.0.1

func (r *Relay) GetAuthConfigPath() string

GetAuthConfigPath returns the path where Claude Code CLI stores authentication. This can be used to backup or transfer authentication between machines.

Example:

path := relay.GetAuthConfigPath()
// Returns: "/path/to/.claude-home/.config/claude"

func (*Relay) GetAuthStatus

func (r *Relay) GetAuthStatus() (bool, string, error)

GetAuthStatus returns detailed authentication status. Returns: (isAuthenticated, statusMessage, error)

func (*Relay) GetBaseDir

func (r *Relay) GetBaseDir() string

GetBaseDir returns the base directory for this Claude instance.

func (*Relay) GetPort

func (r *Relay) GetPort() string

GetPort returns the port the server is configured to use.

func (*Relay) GetServer

func (r *Relay) GetServer() *Server

GetServer returns the underlying Server instance for advanced usage.

func (*Relay) GetSetup

func (r *Relay) GetSetup() *ClaudeSetup

GetSetup returns the underlying ClaudeSetup instance for advanced usage.

func (*Relay) GetWebSocketURL

func (r *Relay) GetWebSocketURL() string

GetWebSocketURL returns the WebSocket URL for connecting to this relay.

func (*Relay) IsAuthenticated

func (r *Relay) IsAuthenticated() (bool, error)

IsAuthenticated checks if Claude is authenticated.

func (*Relay) IsInstalled

func (r *Relay) IsInstalled() bool

IsInstalled checks if Claude and Bun are installed.

func (*Relay) IsRunning

func (r *Relay) IsRunning() bool

IsRunning returns true if the relay server is currently running.

func (*Relay) SetAuthToken

func (r *Relay) SetAuthToken(authToken string) error

SetAuthToken sets a pre-existing Claude Code CLI auth token. Note: This is NOT a Claude API key, but the token from Claude Code CLI login. This method is mainly for advanced use cases where you have the raw token.

Example:

// If you somehow have the auth token from Claude Code CLI
token := getClaudeAuthToken() // from previous session
if err := relay.SetAuthToken(token); err != nil {
	log.Fatal(err)
}

func (*Relay) Setup

func (r *Relay) Setup() error

Setup installs Claude and Bun if not already installed. This is called automatically if AutoSetup is true.

func (*Relay) Start

func (r *Relay) Start() error

Start starts the WebSocket relay server. The server runs in the background and returns immediately.

Example:

if err := relay.Start(); err != nil {
	log.Fatal(err)
}

func (*Relay) Stop

func (r *Relay) Stop() error

Stop gracefully stops the relay server.

Example:

if err := relay.Stop(); err != nil {
	log.Printf("Error stopping relay: %v", err)
}

func (*Relay) Wait

func (r *Relay) Wait()

Wait blocks until the server is stopped.

Example:

go relay.Start()
relay.Wait() // Block until stopped

type Server

type Server struct {
	// contains filtered or unexported fields
}

func NewServer

func NewServer(config *Config, setup *ClaudeSetup) *Server

func (*Server) HandleWebSocket

func (s *Server) HandleWebSocket(w http.ResponseWriter, r *http.Request)

type ServerV2

type ServerV2 struct {
	// contains filtered or unexported fields
}

func NewServerV2

func NewServerV2(config *Config) *ServerV2

func (*ServerV2) HandleWebSocketV2

func (s *ServerV2) HandleWebSocketV2(w http.ResponseWriter, r *http.Request)

Directories

Path Synopsis
cmd
claude-relay command
Command claude-relay starts a WebSocket relay server for Claude Code CLI.
Command claude-relay starts a WebSocket relay server for Claude Code CLI.
examples
auth-preconfig command
Example of using pre-configured authentication (copying from existing auth).
Example of using pre-configured authentication (copying from existing auth).
basic command
Basic example of using the clauderelay library.
Basic example of using the clauderelay library.
docker-deployment command
Example of deploying Claude relay in Docker/production environments.
Example of deploying Claude relay in Docker/production environments.
embedded command
Example of embedding Claude relay in an existing HTTP server.
Example of embedding Claude relay in an existing HTTP server.
multiple command
Example of running multiple isolated Claude instances.
Example of running multiple isolated Claude instances.
webservice command
Example of using Claude relay in a web service with non-interactive authentication.
Example of using Claude relay in a web service with non-interactive authentication.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL