Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 32 additions & 7 deletions cli/cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ var (
bandwidthMbps float64
psiphonConfigPath string
statsFilePath string
numInstances int
)

var startCmd = &cobra.Command{
Expand All @@ -61,8 +62,10 @@ func init() {

startCmd.Flags().IntVarP(&maxClients, "max-clients", "m", config.DefaultMaxClients, "maximum number of proxy clients (1-1000)")
startCmd.Flags().Float64VarP(&bandwidthMbps, "bandwidth", "b", config.DefaultBandwidthMbps, "total bandwidth limit in Mbps (-1 for unlimited)")
startCmd.Flags().StringVarP(&statsFilePath, "stats-file", "s", "", "persist stats to JSON file (default: stats.json in data dir if flag used without value)")
startCmd.Flags().StringVarP(&statsFilePath, "stats-file", "s", "", "persist stats to JSON file (-s for default, -s=path or --stats-file=path for custom)")
startCmd.Flags().Lookup("stats-file").NoOptDefVal = "stats.json"
startCmd.Flags().IntVar(&numInstances, "multi-instance", 0, "run multiple instances: 0=single (default), N=N instances, or no value=auto (1 per 50 clients, max 32)")
startCmd.Flags().Lookup("multi-instance").NoOptDefVal = "-1" // -1 means auto-calculate

// Only show --psiphon-config flag if no config is embedded
if !config.HasEmbeddedConfig() {
Expand Down Expand Up @@ -108,12 +111,6 @@ func runStart(cmd *cobra.Command, args []string) error {
return fmt.Errorf("failed to load configuration: %w", err)
}

// Create conduit service
service, err := conduit.New(cfg)
if err != nil {
return fmt.Errorf("failed to create conduit service: %w", err)
}

// Setup context with cancellation
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
Expand All @@ -128,6 +125,34 @@ func runStart(cmd *cobra.Command, args []string) error {
cancel()
}()

// Multi-instance mode: spawn subprocesses
if numInstances != 0 {
instances := numInstances
if instances < 0 {
instances = conduit.CalculateInstances(maxClients)
}
if instances > conduit.MaxInstances {
return fmt.Errorf("too many instances: %d (maximum: %d)", instances, conduit.MaxInstances)
}

multiService, err := conduit.NewMultiService(cfg, instances)
if err != nil {
return fmt.Errorf("failed to create multi-instance service: %w", err)
}

if err := multiService.Run(ctx); err != nil && ctx.Err() == nil {
return fmt.Errorf("multi-instance service error: %w", err)
}

return nil
}

// Single instance mode (default)
service, err := conduit.New(cfg)
if err != nil {
return fmt.Errorf("failed to create conduit service: %w", err)
}

// Print startup message
bandwidthStr := "unlimited"
if bandwidthMbps != config.UnlimitedBandwidth {
Expand Down
Loading