diff --git a/cmd/olcrtc/main.go b/cmd/olcrtc/main.go index 2be27b5..b8c2bdf 100644 --- a/cmd/olcrtc/main.go +++ b/cmd/olcrtc/main.go @@ -1,11 +1,14 @@ // Package main provides the olcrtc CLI entrypoint. +// +// Usage: olcrtc +// +// All runtime settings come from the YAML file. There are no other CLI flags. package main import ( "bytes" "context" "errors" - "flag" "fmt" "io" "log" @@ -26,8 +29,11 @@ import ( const modeGen = "gen" -// ErrDataDirRequired is returned when no data directory is specified. -var ErrDataDirRequired = errors.New("data directory required (use -data data)") +// ErrConfigPathRequired is returned when no config file is provided. +var ErrConfigPathRequired = errors.New("usage: olcrtc ") + +// ErrDataDirRequired is returned when the YAML config does not specify a data directory. +var ErrDataDirRequired = errors.New("data directory required (set 'data:' in YAML)") //nolint:gochecknoglobals // Tests replace the long-running session runner with a bounded function. var runSession = session.Run @@ -35,45 +41,12 @@ var runSession = session.Run //nolint:gochecknoglobals // Tests replace gen runner with a stub. var runGen = execGen -type config struct { - configPath string - mode string - link string - transport string - auth string - engine string - url string - token string - roomID string - clientID string - socksPort int - socksHost string - socksUser string - socksPass string - keyHex string - debug bool - dataDir string - dnsServer string - socksProxyAddr string - socksProxyPort int - videoWidth int - videoHeight int - videoFPS int - videoBitrate string - videoHW string - videoQRSize int - videoQRRecovery string - videoCodec string - videoTileModule int - videoTileRS int - vp8FPS int - vp8BatchSize int - seiFPS int - seiBatchSize int - seiFragmentSize int - seiAckTimeoutMS int - amount int - ffmpegPath string +// loadedConfig bundles the parsed YAML file and the derived session config. +type loadedConfig struct { + scfg session.Config + dataDir string + debug bool + ffmpegPath string } func main() { @@ -90,76 +63,54 @@ func run() error { func runWithArgs(args []string) error { session.RegisterDefaults() - cfg, err := parseFlagsFrom(args, flag.ExitOnError) + if len(args) != 1 || args[0] == "-h" || args[0] == "--help" || args[0] == "-help" { + return ErrConfigPathRequired + } + + cfg, err := loadConfig(args[0]) if err != nil { return err } return runWithConfig(cfg) } -// applyConfigFile loads cfg.configPath (if set) and merges its values into scfg. -// CLI flags (already populated) take precedence over YAML. -func applyConfigFile(cfg config, scfg session.Config) (session.Config, error) { - if cfg.configPath == "" { - return scfg, nil - } - f, err := configpkg.Load(cfg.configPath) +func loadConfig(path string) (loadedConfig, error) { + f, err := configpkg.Load(path) if err != nil { - return scfg, fmt.Errorf("load config: %w", err) + return loadedConfig{}, fmt.Errorf("load config: %w", err) } - return configpkg.Apply(scfg, f), nil + return loadedConfig{ + scfg: configpkg.Apply(session.Config{}, f), + dataDir: f.Data, + debug: f.Debug, + ffmpegPath: f.FFmpeg, + }, nil } -// mergeFileMeta fills cmd-level fields (data dir, debug, ffmpeg) that aren't -// part of session.Config but still need to come from the YAML file. -func mergeFileMeta(cfg *config, f configpkg.File) { - if cfg.dataDir == "" { - cfg.dataDir = f.Data - } - if !cfg.debug { - cfg.debug = f.Debug - } - if (cfg.ffmpegPath == "" || cfg.ffmpegPath == "ffmpeg") && f.FFmpeg != "" { - cfg.ffmpegPath = f.FFmpeg - } -} - -func runWithConfig(cfg config) error { - if cfg.configPath != "" { - f, err := configpkg.Load(cfg.configPath) - if err != nil { - return fmt.Errorf("load config: %w", err) - } - mergeFileMeta(&cfg, f) - } - +func runWithConfig(cfg loadedConfig) error { configureLogging(cfg.debug) if cfg.ffmpegPath != "ffmpeg" && cfg.ffmpegPath != "" { videochannel.FFmpegPath = cfg.ffmpegPath } - if cfg.mode == modeGen { - return runGen(cfg) - } - - return runSessionMode(cfg) -} - -func runSessionMode(cfg config) error { - scfg, err := applyConfigFile(cfg, toSessionConfig(cfg)) - if err != nil { - return err - } - scfg, err = session.ApplyAuthDefaults(scfg) + scfg, err := session.ApplyAuthDefaults(cfg.scfg) if err != nil { return fmt.Errorf("validate config: %w", err) } + + if scfg.Mode == modeGen { + return runGen(scfg) + } + + return runSessionMode(cfg.dataDir, scfg) +} + +func runSessionMode(dataDir string, scfg session.Config) error { if err := session.Validate(scfg); err != nil { return fmt.Errorf("validate config: %w", err) } - dataDir := cfg.dataDir if dataDir == "" { return ErrDataDirRequired } @@ -194,15 +145,7 @@ func runSessionMode(cfg config) error { } } -func execGen(cfg config) error { - scfg, err := applyConfigFile(cfg, toSessionConfig(cfg)) - if err != nil { - return err - } - scfg, err = session.ApplyAuthDefaults(scfg) - if err != nil { - return fmt.Errorf("validate gen config: %w", err) - } +func execGen(scfg session.Config) error { if err := session.ValidateGen(scfg); err != nil { return fmt.Errorf("validate gen config: %w", err) } @@ -227,62 +170,6 @@ func execGen(cfg config) error { } } -func parseFlagsFrom(args []string, errorHandling flag.ErrorHandling) (config, error) { - cfg := config{} - fs := flag.NewFlagSet("olcrtc", errorHandling) - if errorHandling == flag.ContinueOnError { - fs.SetOutput(io.Discard) - } - - fs.StringVar(&cfg.configPath, "config", "", "Path to YAML config file (CLI flags override file values)") - fs.StringVar(&cfg.mode, "mode", "", "Mode: srv or cnc") - fs.StringVar(&cfg.link, "link", "", "Link: direct (p2p connection type)") - fs.StringVar(&cfg.transport, "transport", "", "Transport: datachannel, videochannel, seichannel") - fs.StringVar(&cfg.auth, "auth", "", "Auth provider: telemost, jazz, wbstream, none") - fs.StringVar(&cfg.engine, "engine", "", "Engine (required when -auth none): livekit, goolom, salutejazz") - fs.StringVar(&cfg.url, "url", "", "SFU WebSocket URL (required when -auth none)") - fs.StringVar(&cfg.token, "token", "", "Access token (required when -auth none)") - fs.StringVar(&cfg.roomID, "id", "", "Room ID") - fs.StringVar(&cfg.clientID, "client-id", "", "Client ID: binds one srv to one cnc (required)") - fs.IntVar(&cfg.socksPort, "socks-port", 0, "SOCKS5 port (client only)") - fs.StringVar(&cfg.socksHost, "socks-host", "", "SOCKS5 listen host (client only)") - fs.StringVar(&cfg.socksUser, "socks-user", "", "SOCKS5 username for incoming connections (client only, optional)") - fs.StringVar(&cfg.socksPass, "socks-pass", "", "SOCKS5 password for incoming connections (client only, optional)") - fs.StringVar(&cfg.keyHex, "key", "", "Shared encryption key (hex)") - fs.BoolVar(&cfg.debug, "debug", false, "Enable verbose logging") - fs.StringVar(&cfg.dataDir, "data", "", "Path to data directory") - fs.StringVar(&cfg.dnsServer, "dns", "", "DNS server (e.g. 1.1.1.1:53)") - fs.StringVar(&cfg.socksProxyAddr, "socks-proxy", "", "SOCKS5 proxy address (server only)") - fs.IntVar(&cfg.socksProxyPort, "socks-proxy-port", 0, "SOCKS5 proxy port (server only)") - fs.IntVar(&cfg.videoWidth, "video-w", 0, "Video logical width (videochannel only)") - fs.IntVar(&cfg.videoHeight, "video-h", 0, "Video logical height (videochannel only)") - fs.IntVar(&cfg.videoFPS, "video-fps", 0, "Video frames per second (videochannel only)") - fs.StringVar(&cfg.videoBitrate, "video-bitrate", "", "Video bitrate (videochannel only)") - fs.StringVar(&cfg.videoHW, "video-hw", "", "Hardware acceleration (none, nvenc)") - fs.IntVar(&cfg.videoQRSize, "video-qr-size", 0, "Video QR code fragment size (videochannel only)") - fs.StringVar(&cfg.videoQRRecovery, "video-qr-recovery", "low", - "QR error correction: low (7%), medium (15%), high (25%), highest (30%)") - fs.StringVar(&cfg.videoCodec, "video-codec", "qrcode", "Visual codec: qrcode or tile") - fs.IntVar(&cfg.videoTileModule, "video-tile-module", 0, - "Tile module size in pixels 1..270 (videochannel tile only, default 4)") - fs.IntVar(&cfg.videoTileRS, "video-tile-rs", 0, - "Tile Reed-Solomon parity percent 0..200 (videochannel tile only, default 20)") - fs.IntVar(&cfg.vp8FPS, "vp8-fps", 0, "VP8 frames per second (vp8channel only, default 25)") - fs.IntVar(&cfg.vp8BatchSize, "vp8-batch", 0, "VP8 frames per tick (vp8channel only, default 1)") - fs.IntVar(&cfg.seiFPS, "fps", 0, "Frames per second for transports that use video timing (seichannel)") - fs.IntVar(&cfg.seiBatchSize, "batch", 0, "Transport frames per tick for batched transports (seichannel)") - fs.IntVar(&cfg.seiFragmentSize, "frag", 0, "Fragment size in bytes for fragmented transports (seichannel)") - fs.IntVar(&cfg.seiAckTimeoutMS, "ack-ms", 0, "ACK timeout in milliseconds for reliable visual transports (seichannel)") - fs.IntVar(&cfg.amount, "amount", 0, "Number of rooms to generate (gen mode only)") - fs.StringVar(&cfg.ffmpegPath, "ffmpeg", "ffmpeg", "Path to ffmpeg executable") - - if err := fs.Parse(args); err != nil { - return cfg, fmt.Errorf("parse flags: %w", err) - } - - return cfg, nil -} - // noisyPrefixes lists log prefixes from third-party libs that spam via std log. var noisyPrefixes = [][]byte{ //nolint:gochecknoglobals // package-level filter list []byte("turnc"), []byte("[turn]"), []byte("Fail to refresh permissions"), @@ -309,10 +196,8 @@ func configureLogging(debug bool) { logger.SetVerbose(true) return } - // Suppress noisy LiveKit/pion logs unless debug is enabled. _ = os.Setenv("PION_LOG_DISABLE", "all") lksdk.SetLogger(protoLogger.GetDiscardLogger()) - // turnc logs via std log directly — filter it out. log.SetOutput(filteredWriter{w: os.Stderr}) } @@ -339,45 +224,6 @@ func loadNames(dataDir string) error { return nil } -func toSessionConfig(cfg config) session.Config { - return session.Config{ - Mode: cfg.mode, - Link: cfg.link, - Transport: cfg.transport, - Auth: cfg.auth, - Engine: cfg.engine, - URL: cfg.url, - Token: cfg.token, - RoomID: cfg.roomID, - ClientID: cfg.clientID, - KeyHex: cfg.keyHex, - SOCKSHost: cfg.socksHost, - SOCKSPort: cfg.socksPort, - SOCKSUser: cfg.socksUser, - SOCKSPass: cfg.socksPass, - DNSServer: cfg.dnsServer, - SOCKSProxyAddr: cfg.socksProxyAddr, - SOCKSProxyPort: cfg.socksProxyPort, - VideoWidth: cfg.videoWidth, - VideoHeight: cfg.videoHeight, - VideoFPS: cfg.videoFPS, - VideoBitrate: cfg.videoBitrate, - VideoHW: cfg.videoHW, - VideoQRSize: cfg.videoQRSize, - VideoQRRecovery: cfg.videoQRRecovery, - VideoCodec: cfg.videoCodec, - VideoTileModule: cfg.videoTileModule, - VideoTileRS: cfg.videoTileRS, - VP8FPS: cfg.vp8FPS, - VP8BatchSize: cfg.vp8BatchSize, - SEIFPS: cfg.seiFPS, - SEIBatchSize: cfg.seiBatchSize, - SEIFragmentSize: cfg.seiFragmentSize, - SEIAckTimeoutMS: cfg.seiAckTimeoutMS, - Amount: cfg.amount, - } -} - func waitForShutdown(errCh <-chan error) error { done := make(chan error, 1) go func() { diff --git a/cmd/olcrtc/main_test.go b/cmd/olcrtc/main_test.go index 26526c9..44f93ef 100644 --- a/cmd/olcrtc/main_test.go +++ b/cmd/olcrtc/main_test.go @@ -3,7 +3,6 @@ package main import ( "context" "errors" - "flag" "os" "path/filepath" "testing" @@ -14,110 +13,40 @@ import ( var errBoom = errors.New("boom") -//nolint:cyclop // table-driven test naturally has many branches -func TestToSessionConfig(t *testing.T) { - cfg := config{ - mode: "cnc", - link: "direct", //nolint:goconst // test literal, repetition is intentional - transport: "vp8channel", - auth: "jazz", //nolint:goconst // test literal, repetition is intentional - roomID: "room", //nolint:goconst // test literal, repetition is intentional - clientID: "client", //nolint:goconst // test literal, repetition is intentional - keyHex: "key", //nolint:goconst // test literal, repetition is intentional - socksHost: "127.0.0.1", - socksPort: 1080, - dnsServer: "1.1.1.1:53", //nolint:goconst // test literal, repetition is intentional - socksProxyAddr: "proxy", - socksProxyPort: 1081, - videoWidth: 640, - videoHeight: 480, - videoFPS: 30, - videoBitrate: "1M", - videoHW: "none", - videoQRSize: 4, - videoQRRecovery: "low", - videoCodec: "qrcode", - videoTileModule: 4, - videoTileRS: 20, - vp8FPS: 25, - vp8BatchSize: 8, - seiFPS: 40, - seiBatchSize: 3, - seiFragmentSize: 512, - seiAckTimeoutMS: 1500, - amount: 5, - } - - got := toSessionConfig(cfg) - if got.Mode != cfg.mode || got.Auth != "jazz" || got.SOCKSPort != cfg.socksPort || - got.VideoTileRS != cfg.videoTileRS || got.VP8BatchSize != cfg.vp8BatchSize || - got.SEIFPS != cfg.seiFPS || got.SEIBatchSize != cfg.seiBatchSize || - got.SEIFragmentSize != cfg.seiFragmentSize || got.SEIAckTimeoutMS != cfg.seiAckTimeoutMS || - got.Amount != cfg.amount { - t.Fatalf("toSessionConfig() = %+v", got) +func writeYAML(t *testing.T, body string) string { + t.Helper() + dir := t.TempDir() + path := filepath.Join(dir, "olcrtc.yaml") + if err := os.WriteFile(path, []byte(body), 0o600); err != nil { + t.Fatalf("write yaml: %v", err) } + return path } -//nolint:cyclop // table-driven test naturally has many branches -func TestParseFlagsFrom(t *testing.T) { - cfg, err := parseFlagsFrom([]string{ - "-mode", "srv", //nolint:goconst // test literal, repetition is intentional - "-link", "direct", - "-transport", "vp8channel", - "-auth", "telemost", - "-id", "room", - "-client-id", "client", - "-socks-port", "1080", - "-socks-host", "127.0.0.1", - "-key", "key", - "-debug", - "-data", "data", - "-dns", "9.9.9.9:53", - "-socks-proxy", "proxy", - "-socks-proxy-port", "1081", - "-video-w", "640", - "-video-h", "480", - "-video-fps", "30", - "-video-bitrate", "1M", - "-video-hw", "none", - "-video-qr-size", "128", - "-video-qr-recovery", "high", - "-video-codec", "tile", - "-video-tile-module", "6", - "-video-tile-rs", "40", - "-vp8-fps", "24", - "-vp8-batch", "3", - "-fps", "40", - "-batch", "4", - "-frag", "512", - "-ack-ms", "1500", - "-amount", "7", - }, flag.ContinueOnError) - if err != nil { - t.Fatalf("parseFlagsFrom() error = %v", err) +func TestRunWithArgsRequiresConfig(t *testing.T) { + session.RegisterDefaults() + if err := runWithArgs(nil); !errors.Is(err, ErrConfigPathRequired) { + t.Fatalf("runWithArgs(nil) = %v, want %v", err, ErrConfigPathRequired) } - if cfg.mode != "srv" || cfg.auth != "telemost" || cfg.roomID != "room" || - cfg.debug != true || cfg.videoCodec != "tile" || cfg.videoTileRS != 40 || - cfg.vp8FPS != 24 || cfg.vp8BatchSize != 3 || cfg.seiFPS != 40 || - cfg.seiBatchSize != 4 || cfg.seiFragmentSize != 512 || cfg.seiAckTimeoutMS != 1500 || - cfg.amount != 7 { - t.Fatalf("parseFlagsFrom() = %+v", cfg) + if err := runWithArgs([]string{"-h"}); !errors.Is(err, ErrConfigPathRequired) { + t.Fatalf("runWithArgs(-h) = %v, want %v", err, ErrConfigPathRequired) } - - _, err = parseFlagsFrom([]string{"-bad"}, flag.ContinueOnError) - if err == nil { - t.Fatal("parseFlagsFrom(bad flag) error = nil") + if err := runWithArgs([]string{"a.yaml", "b.yaml"}); !errors.Is(err, ErrConfigPathRequired) { + t.Fatalf("runWithArgs(two args) = %v, want %v", err, ErrConfigPathRequired) } } func TestRunGenModeValidationErrors(t *testing.T) { session.RegisterDefaults() - if err := runWithConfig(config{mode: "gen"}); err == nil { //nolint:goconst // test literal, repetition is intentional + if err := runWithConfig(loadedConfig{scfg: session.Config{Mode: "gen"}}); err == nil { t.Fatal("runWithConfig(gen, no carrier) error = nil") } - if err := runWithConfig(config{mode: "gen", auth: "wbstream", dnsServer: "1.1.1.1:53"}); err == nil { //nolint:goconst,lll // test literal, repetition is intentional + cfg := loadedConfig{scfg: session.Config{ + Mode: "gen", Auth: "wbstream", DNSServer: "1.1.1.1:53", + }} + if err := runWithConfig(cfg); err == nil { t.Fatal("runWithConfig(gen, amount=0) error = nil") } } @@ -128,16 +57,18 @@ func TestRunGenModeCallsGen(t *testing.T) { var collected []string oldRunGen := runGen t.Cleanup(func() { runGen = oldRunGen }) - runGen = func(cfg config) error { - if cfg.auth != "wbstream" || cfg.dnsServer != "1.1.1.1:53" || cfg.amount != 3 { - t.Fatalf("runGen cfg = %+v", cfg) + runGen = func(scfg session.Config) error { + if scfg.Auth != "wbstream" || scfg.DNSServer != "1.1.1.1:53" || scfg.Amount != 3 { + t.Fatalf("runGen scfg = %+v", scfg) } collected = append(collected, "ok") return nil } - err := runWithConfig(config{mode: "gen", auth: "wbstream", dnsServer: "1.1.1.1:53", amount: 3}) - if err != nil { + cfg := loadedConfig{scfg: session.Config{ + Mode: "gen", Auth: "wbstream", DNSServer: "1.1.1.1:53", Amount: 3, + }} + if err := runWithConfig(cfg); err != nil { t.Fatalf("runWithConfig(gen) error = %v", err) } if len(collected) != 1 { @@ -147,22 +78,21 @@ func TestRunGenModeCallsGen(t *testing.T) { func TestRunWithConfigValidationAndDataDirErrors(t *testing.T) { session.RegisterDefaults() - cfg := config{ - mode: "srv", - link: "direct", - transport: "datachannel", - auth: "jazz", - clientID: "client", - keyHex: "key", - dnsServer: "1.1.1.1:53", - videoCodec: "qrcode", + scfg := session.Config{ + Mode: "srv", + Link: "direct", + Transport: "datachannel", + Auth: "jazz", + ClientID: "client", + KeyHex: "key", + DNSServer: "1.1.1.1:53", } - if err := runWithConfig(cfg); !errors.Is(err, ErrDataDirRequired) { + if err := runWithConfig(loadedConfig{scfg: scfg}); !errors.Is(err, ErrDataDirRequired) { t.Fatalf("runWithConfig(no data dir) = %v, want %v", err, ErrDataDirRequired) } - cfg.mode = "" - if err := runWithConfig(cfg); err == nil { + scfg.Mode = "" + if err := runWithConfig(loadedConfig{scfg: scfg}); err == nil { t.Fatal("runWithConfig(invalid config) error = nil") } } @@ -194,17 +124,22 @@ func TestRunWithArgsSuccessfulSessionReturn(t *testing.T) { return nil } - err := runWithArgs([]string{ - "-mode", "srv", - "-link", "direct", - "-transport", "datachannel", - "-auth", "jazz", - "-client-id", "client", - "-key", "key", - "-dns", "1.1.1.1:53", - "-data", dir, - }) - if err != nil { + yamlPath := writeYAML(t, ` +mode: srv +link: direct +auth: + provider: jazz +room: + client_id: client +crypto: + key: key +net: + transport: datachannel + dns: 1.1.1.1:53 +data: `+dir+` +`) + + if err := runWithArgs([]string{yamlPath}); err != nil { t.Fatalf("runWithArgs() error = %v", err) } if !called { diff --git a/docs/client.example.yaml b/docs/client.example.yaml index c5cf611..009d830 100644 --- a/docs/client.example.yaml +++ b/docs/client.example.yaml @@ -5,7 +5,6 @@ mode: cnc link: direct -carrier: "" auth: provider: wbstream # must match the server diff --git a/docs/configuration.md b/docs/configuration.md index f933b00..b8ebc7f 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,17 +1,10 @@ # Configuration -olcrtc accepts the same settings via CLI flags or a YAML file. Use whichever -fits your deployment: +olcrtc reads its entire runtime configuration from a single YAML file. +There are no other CLI flags. ```bash -# CLI flags (existing behaviour) -olcrtc -mode srv -auth wbstream -id room123 -key $(openssl rand -hex 32) ... - -# YAML file -olcrtc -config /etc/olcrtc/server.yaml - -# YAML file plus CLI overrides — any flag wins over the corresponding YAML field -olcrtc -config /etc/olcrtc/server.yaml -id room999 +olcrtc /etc/olcrtc/server.yaml ``` Examples: @@ -21,31 +14,24 @@ Examples: ## Schema -| YAML path | CLI flag | Notes | -|----------------------------|----------------------|-----------------------------------------------| -| `mode` | `-mode` | `srv`, `cnc`, or `gen` | -| `link` | `-link` | `direct` | -| `auth.provider` | `-auth` | `telemost`, `jazz`, `wbstream`, `none` | -| `room.id` | `-id` | conference room id | -| `room.client_id` | `-client-id` | deprecated, will be removed | -| `crypto.key` | `-key` | 64-char hex (32 bytes) | -| `net.transport` | `-transport` | `datachannel`, `videochannel`, `seichannel`, `vp8channel` | -| `net.dns` | `-dns` | resolver `host:port` | -| `socks.host` / `.port` | `-socks-host` / `-socks-port` | client-side listener | -| `socks.user` / `.pass` | `-socks-user` / `-socks-pass` | optional client-side auth | -| `socks.proxy_addr` / `.proxy_port` | `-socks-proxy` / `-socks-proxy-port` | server-side egress proxy | -| `engine.name` / `.url` / `.token` | `-engine` / `-url` / `-token` | only when `auth.provider: none` | -| `video.*` | `-video-*` | videochannel tuning | -| `vp8.*` | `-vp8-*` | vp8channel tuning | -| `sei.fps` / `.batch_size` / `.fragment_size` / `.ack_timeout_ms` | `-fps` / `-batch` / `-frag` / `-ack-ms` | seichannel tuning | -| `gen.amount` | `-amount` | gen mode: number of rooms to create | -| `data` | `-data` | path to data directory | -| `debug` | `-debug` | verbose logging | -| `ffmpeg` | `-ffmpeg` | path to ffmpeg binary | - -## Precedence - -`CLI flag (non-zero) > YAML value > zero value`. - -A CLI flag with its zero value (e.g. `-socks-port 0`) does NOT override a YAML -value — pass an explicit non-zero value to override. +| YAML path | Notes | +|------------------------------------------------------------------|-----------------------------------------------------------| +| `mode` | `srv`, `cnc`, or `gen` | +| `link` | `direct` | +| `auth.provider` | `telemost`, `jazz`, `wbstream`, `none` | +| `room.id` | conference room id | +| `room.client_id` | deprecated, will be removed | +| `crypto.key` | 64-char hex (32 bytes) | +| `net.transport` | `datachannel`, `videochannel`, `seichannel`, `vp8channel` | +| `net.dns` | resolver `host:port` | +| `socks.host` / `.port` | client-side listener | +| `socks.user` / `.pass` | optional client-side auth | +| `socks.proxy_addr` / `.proxy_port` | server-side egress proxy | +| `engine.name` / `.url` / `.token` | only when `auth.provider: none` | +| `video.*` | videochannel tuning | +| `vp8.*` | vp8channel tuning | +| `sei.fps` / `.batch_size` / `.fragment_size` / `.ack_timeout_ms` | seichannel tuning | +| `gen.amount` | gen mode: number of rooms to create | +| `data` | path to data directory | +| `debug` | verbose logging | +| `ffmpeg` | path to ffmpeg binary | diff --git a/docs/server.example.yaml b/docs/server.example.yaml index af44256..dfe1982 100644 --- a/docs/server.example.yaml +++ b/docs/server.example.yaml @@ -6,7 +6,6 @@ mode: srv # Connection topology link: direct # p2p link type -carrier: "" # leave empty for default selection from auth provider auth: provider: wbstream # telemost | jazz | wbstream | none diff --git a/internal/config/config.go b/internal/config/config.go index 3a061f9..9b60e71 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -23,7 +23,6 @@ var ErrConfigNotFound = errors.New("config file not found") type File struct { Mode string `yaml:"mode"` Link string `yaml:"link"` - Carrier string `yaml:"carrier"` Auth Auth `yaml:"auth"` Room Room `yaml:"room"` Crypto Crypto `yaml:"crypto"`