refactor: v3 config options take precedence over v2 deprecated.

This commit is contained in:
Miroslav Šedivý
2026-10-16 20:34:40 +02:00
parent 3191dbb058
commit 21ad64423f
8 changed files with 171 additions and 78 deletions
+65 -27
View File
@@ -472,24 +472,28 @@ func (s *Capture) Set() {
func (s *Capture) SetV2() {
deprecatedV2ConfigUsed := false
var ok bool
//
// video
//
if display := viper.GetString("display"); display != "" {
s.Display = display
if useV2Fallback("capture.video.display") {
s.Display = display
}
log.Warn().Msg("you are using v2 configuration 'NEKO_DISPLAY' which is deprecated, please use 'NEKO_CAPTURE_VIDEO_DISPLAY' and/or 'NEKO_DESKTOP_DISPLAY' instead, also consider using 'DISPLAY' env variable if both should be the same")
deprecatedV2ConfigUsed = true
}
modifiedVideoCodec := false
if videoCodec := viper.GetString("video_codec"); videoCodec != "" {
s.VideoCodec, ok = codec.ParseStr(videoCodec)
if !ok || s.VideoCodec.Type != webrtc.RTPCodecTypeVideo {
videoCodecValue, valid := codec.ParseStr(videoCodec)
if !valid || videoCodecValue.Type != webrtc.RTPCodecTypeVideo {
log.Warn().Str("codec", videoCodec).Msgf("unknown video codec, using Vp8")
s.VideoCodec = codec.VP8()
if useV2Fallback("capture.video.codec") {
s.VideoCodec = codec.VP8()
}
} else if useV2Fallback("capture.video.codec") {
s.VideoCodec = videoCodecValue
}
log.Warn().Msg("you are using v2 configuration 'NEKO_VIDEO_CODEC' which is deprecated, please use 'NEKO_CAPTURE_VIDEO_CODEC' instead")
deprecatedV2ConfigUsed = true
@@ -497,22 +501,30 @@ func (s *Capture) SetV2() {
}
if viper.GetBool("vp8") {
s.VideoCodec = codec.VP8()
if useV2Fallback("capture.video.codec") {
s.VideoCodec = codec.VP8()
}
log.Warn().Msg("you are using deprecated config setting 'NEKO_VP8=true', use 'NEKO_CAPTURE_VIDEO_CODEC=vp8' instead")
deprecatedV2ConfigUsed = true
modifiedVideoCodec = true
} else if viper.GetBool("vp9") {
s.VideoCodec = codec.VP9()
if useV2Fallback("capture.video.codec") {
s.VideoCodec = codec.VP9()
}
log.Warn().Msg("you are using deprecated config setting 'NEKO_VP9=true', use 'NEKO_CAPTURE_VIDEO_CODEC=vp9' instead")
deprecatedV2ConfigUsed = true
modifiedVideoCodec = true
} else if viper.GetBool("h264") {
s.VideoCodec = codec.H264()
if useV2Fallback("capture.video.codec") {
s.VideoCodec = codec.H264()
}
log.Warn().Msg("you are using deprecated config setting 'NEKO_H264=true', use 'NEKO_CAPTURE_VIDEO_CODEC=h264' instead")
deprecatedV2ConfigUsed = true
modifiedVideoCodec = true
} else if viper.GetBool("av1") {
s.VideoCodec = codec.AV1()
if useV2Fallback("capture.video.codec") {
s.VideoCodec = codec.AV1()
}
log.Warn().Msg("you are using deprecated config setting 'NEKO_AV1=true', use 'NEKO_CAPTURE_VIDEO_CODEC=av1' instead")
deprecatedV2ConfigUsed = true
modifiedVideoCodec = true
@@ -552,7 +564,9 @@ func (s *Capture) SetV2() {
GstPipeline: strings.Replace(pipeline, "show-pointer=true", "show-pointer=false", 1),
},
}
s.VideoIDs = []string{"main"}
if useV2Fallback("capture.video.ids") {
s.VideoIDs = []string{"main"}
}
}
}
@@ -569,17 +583,23 @@ func (s *Capture) SetV2() {
//
if audioDevice := viper.GetString("device"); audioDevice != "" {
s.AudioDevice = audioDevice
if useV2Fallback("capture.audio.device") {
s.AudioDevice = audioDevice
}
log.Warn().Msg("you are using v2 configuration 'NEKO_DEVICE' which is deprecated, please use 'NEKO_CAPTURE_AUDIO_DEVICE' instead")
deprecatedV2ConfigUsed = true
}
modifiedAudioCodec := false
if audioCodec := viper.GetString("audio_codec"); audioCodec != "" {
s.AudioCodec, ok = codec.ParseStr(audioCodec)
if !ok || s.AudioCodec.Type != webrtc.RTPCodecTypeAudio {
audioCodecValue, valid := codec.ParseStr(audioCodec)
if !valid || audioCodecValue.Type != webrtc.RTPCodecTypeAudio {
log.Warn().Str("codec", audioCodec).Msgf("unknown audio codec, using Opus")
s.AudioCodec = codec.Opus()
if useV2Fallback("capture.audio.codec") {
s.AudioCodec = codec.Opus()
}
} else if useV2Fallback("capture.audio.codec") {
s.AudioCodec = audioCodecValue
}
log.Warn().Msg("you are using v2 configuration 'NEKO_AUDIO_CODEC' which is deprecated, please use 'NEKO_CAPTURE_AUDIO_CODEC' instead")
deprecatedV2ConfigUsed = true
@@ -587,22 +607,30 @@ func (s *Capture) SetV2() {
}
if viper.GetBool("opus") {
s.AudioCodec = codec.Opus()
if useV2Fallback("capture.audio.codec") {
s.AudioCodec = codec.Opus()
}
log.Warn().Msg("you are using deprecated config setting 'NEKO_OPUS=true', use 'NEKO_CAPTURE_AUDIO_CODEC=opus' instead")
deprecatedV2ConfigUsed = true
modifiedAudioCodec = true
} else if viper.GetBool("g722") {
s.AudioCodec = codec.G722()
if useV2Fallback("capture.audio.codec") {
s.AudioCodec = codec.G722()
}
log.Warn().Msg("you are using deprecated config setting 'NEKO_G722=true', use 'NEKO_CAPTURE_AUDIO_CODEC=g722' instead")
deprecatedV2ConfigUsed = true
modifiedAudioCodec = true
} else if viper.GetBool("pcmu") {
s.AudioCodec = codec.PCMU()
if useV2Fallback("capture.audio.codec") {
s.AudioCodec = codec.PCMU()
}
log.Warn().Msg("you are using deprecated config setting 'NEKO_PCMU=true', use 'NEKO_CAPTURE_AUDIO_CODEC=pcmu' instead")
deprecatedV2ConfigUsed = true
modifiedAudioCodec = true
} else if viper.GetBool("pcma") {
s.AudioCodec = codec.PCMA()
if useV2Fallback("capture.audio.codec") {
s.AudioCodec = codec.PCMA()
}
log.Warn().Msg("you are using deprecated config setting 'NEKO_PCMA=true', use 'NEKO_CAPTURE_AUDIO_CODEC=pcma' instead")
deprecatedV2ConfigUsed = true
modifiedAudioCodec = true
@@ -613,11 +641,15 @@ func (s *Capture) SetV2() {
// audio pipeline
if modifiedAudioCodec || audioBitrate != 0 || audioPipeline != "" {
pipeline, err := NewAudioPipeline(s.AudioCodec, s.AudioDevice, audioPipeline, audioBitrate)
if err != nil {
log.Warn().Err(err).Msg("unable to create audio pipeline, using default")
if useV2Fallback("capture.audio.pipeline") {
pipeline, err := NewAudioPipeline(s.AudioCodec, s.AudioDevice, audioPipeline, audioBitrate)
if err != nil {
log.Warn().Err(err).Msg("unable to create audio pipeline, using default")
} else {
s.AudioPipeline = pipeline
}
} else {
s.AudioPipeline = pipeline
log.Warn().Msg("ignoring legacy audio pipeline settings because NEKO_CAPTURE_AUDIO_PIPELINE is already set")
}
if audioPipeline != "" {
@@ -633,22 +665,28 @@ func (s *Capture) SetV2() {
//
if viper.IsSet("broadcast_pipeline") {
s.BroadcastPipeline = viper.GetString("broadcast_pipeline")
if useV2Fallback("capture.broadcast.pipeline") {
s.BroadcastPipeline = viper.GetString("broadcast_pipeline")
}
log.Warn().Msg("you are using v2 configuration 'NEKO_BROADCAST_PIPELINE' which is deprecated, please use 'NEKO_CAPTURE_BROADCAST_PIPELINE' instead")
deprecatedV2ConfigUsed = true
}
if viper.IsSet("broadcast_url") {
s.BroadcastUrl = viper.GetString("broadcast_url")
if useV2Fallback("capture.broadcast.url") {
s.BroadcastUrl = viper.GetString("broadcast_url")
}
log.Warn().Msg("you are using v2 configuration 'NEKO_BROADCAST_URL' which is deprecated, please use 'NEKO_CAPTURE_BROADCAST_URL' instead")
deprecatedV2ConfigUsed = true
}
if viper.IsSet("broadcast_autostart") {
s.BroadcastAutostart = viper.GetBool("broadcast_autostart")
if useV2Fallback("capture.broadcast.autostart") {
s.BroadcastAutostart = viper.GetBool("broadcast_autostart")
}
log.Warn().Msg("you are using v2 configuration 'NEKO_BROADCAST_AUTOSTART' which is deprecated, please use 'NEKO_CAPTURE_BROADCAST_AUTOSTART' instead")
deprecatedV2ConfigUsed = true
}
if deprecatedV2ConfigUsed {
log.Warn().Msg("deprecated V2 configuration is in use; migrate to V3 configuration, see https://neko.m1k1o.net/docs/v3/migration-from-v2")
log.Error().Msg("deprecated V2 configuration is in use; migrate to V3 configuration, see https://neko.m1k1o.net/docs/v3/migration-from-v2")
}
}
+2 -2
View File
@@ -121,7 +121,7 @@ func (s *Desktop) SetV2() {
height, err2 := strconv.ParseInt(res[2], 10, 64)
rate, err3 := strconv.ParseInt(res[3], 10, 64)
if err1 == nil && err2 == nil && err3 == nil {
if err1 == nil && err2 == nil && err3 == nil && useV2Fallback("desktop.screen") {
s.ScreenSize.Width = int(width)
s.ScreenSize.Height = int(height)
s.ScreenSize.Rate = int16(rate)
@@ -132,6 +132,6 @@ func (s *Desktop) SetV2() {
}
if deprecatedV2ConfigUsed {
log.Warn().Msg("deprecated V2 configuration is in use; migrate to V3 configuration, see https://neko.m1k1o.net/docs/v3/migration-from-v2")
log.Error().Msg("deprecated V2 configuration is in use; migrate to V3 configuration, see https://neko.m1k1o.net/docs/v3/migration-from-v2")
}
}
+16 -10
View File
@@ -269,22 +269,28 @@ func (s *Member) SetV2() {
deprecatedV2ConfigUsed := false
if viper.IsSet("password") || viper.IsSet("password_admin") {
s.Provider = "multiuser"
if userPassword := viper.GetString("password"); userPassword != "" {
s.Multiuser.UserPassword = userPassword
} else {
s.Multiuser.UserPassword = "neko"
if useV2Fallback("member.provider") {
s.Provider = "multiuser"
}
if adminPassword := viper.GetString("password_admin"); adminPassword != "" {
s.Multiuser.AdminPassword = adminPassword
} else {
s.Multiuser.AdminPassword = "admin"
if viper.IsSet("password") && useV2Fallback("member.multiuser.user_password") {
if userPassword := viper.GetString("password"); userPassword != "" {
s.Multiuser.UserPassword = userPassword
} else {
s.Multiuser.UserPassword = "neko"
}
}
if viper.IsSet("password_admin") && useV2Fallback("member.multiuser.admin_password") {
if adminPassword := viper.GetString("password_admin"); adminPassword != "" {
s.Multiuser.AdminPassword = adminPassword
} else {
s.Multiuser.AdminPassword = "admin"
}
}
log.Warn().Msg("you are using v2 configuration 'NEKO_PASSWORD' and 'NEKO_PASSWORD_ADMIN' which are deprecated, please use 'NEKO_MEMBER_MULTIUSER_USER_PASSWORD' and 'NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD' with 'NEKO_MEMBER_PROVIDER=multiuser' instead")
deprecatedV2ConfigUsed = true
}
if deprecatedV2ConfigUsed {
log.Warn().Msg("deprecated V2 configuration is in use; migrate to V3 configuration, see https://neko.m1k1o.net/docs/v3/migration-from-v2")
log.Error().Msg("deprecated V2 configuration is in use; migrate to V3 configuration, see https://neko.m1k1o.net/docs/v3/migration-from-v2")
}
}
+14 -7
View File
@@ -4,6 +4,7 @@ import (
"os"
"path/filepath"
"runtime"
"slices"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
@@ -11,6 +12,10 @@ import (
"github.com/spf13/viper"
)
func useV2Fallback(v3Keys ...string) bool {
return !slices.ContainsFunc(v3Keys, viper.IsSet)
}
type Root struct {
Config string
@@ -109,14 +114,16 @@ func (s *Root) Set() {
func (s *Root) SetV2() {
if viper.IsSet("logs") {
if viper.GetBool("logs") {
logs := filepath.Join(".", "logs")
if runtime.GOOS == "linux" {
logs = "/var/log/neko"
if useV2Fallback("log.dir") {
if viper.GetBool("logs") {
logs := filepath.Join(".", "logs")
if runtime.GOOS == "linux" {
logs = "/var/log/neko"
}
s.LogDir = logs
} else {
s.LogDir = ""
}
s.LogDir = logs
} else {
s.LogDir = ""
}
log.Warn().Msg("you are using v2 configuration 'NEKO_LOGS' which is deprecated, please use 'NEKO_LOG_DIR=/path/to/logs' instead")
}
+22 -8
View File
@@ -130,37 +130,51 @@ func (s *Server) SetV2() {
deprecatedV2ConfigUsed := false
if viper.IsSet("cert") {
s.Cert = viper.GetString("cert")
if useV2Fallback("server.cert") {
s.Cert = viper.GetString("cert")
}
log.Warn().Msg("you are using v2 configuration 'NEKO_CERT' which is deprecated, please use 'NEKO_SERVER_CERT' instead")
deprecatedV2ConfigUsed = true
}
if viper.IsSet("key") {
s.Key = viper.GetString("key")
if useV2Fallback("server.key") {
s.Key = viper.GetString("key")
}
log.Warn().Msg("you are using v2 configuration 'NEKO_KEY' which is deprecated, please use 'NEKO_SERVER_KEY' instead")
deprecatedV2ConfigUsed = true
}
if viper.IsSet("bind") {
s.Bind = viper.GetString("bind")
if useV2Fallback("server.bind") {
s.Bind = viper.GetString("bind")
}
log.Warn().Msg("you are using v2 configuration 'NEKO_BIND' which is deprecated, please use 'NEKO_SERVER_BIND' instead")
deprecatedV2ConfigUsed = true
}
if viper.IsSet("proxy") {
s.Proxy = viper.GetBool("proxy")
if useV2Fallback("server.proxy") {
s.Proxy = viper.GetBool("proxy")
}
log.Warn().Msg("you are using v2 configuration 'NEKO_PROXY' which is deprecated, please use 'NEKO_SERVER_PROXY' instead")
deprecatedV2ConfigUsed = true
}
if viper.IsSet("static") {
s.Static = viper.GetString("static")
if useV2Fallback("server.static") {
s.Static = viper.GetString("static")
}
log.Warn().Msg("you are using v2 configuration 'NEKO_STATIC' which is deprecated, please use 'NEKO_SERVER_STATIC' instead")
deprecatedV2ConfigUsed = true
}
if viper.IsSet("path_prefix") {
s.PathPrefix = path.Join("/", path.Clean(viper.GetString("path_prefix")))
if useV2Fallback("server.path_prefix") {
s.PathPrefix = path.Join("/", path.Clean(viper.GetString("path_prefix")))
}
log.Warn().Msg("you are using v2 configuration 'NEKO_PATH_PREFIX' which is deprecated, please use 'NEKO_SERVER_PATH_PREFIX' instead")
deprecatedV2ConfigUsed = true
}
if viper.IsSet("cors") {
s.CORS = viper.GetStringSlice("cors")
if useV2Fallback("server.cors") {
s.CORS = viper.GetStringSlice("cors")
}
if slices.Contains(s.CORS, "*") {
s.CORS = []string{"*"}
log.Warn().Msg("CORS is enabled for all origins, this is not recommended for production environments")
@@ -170,7 +184,7 @@ func (s *Server) SetV2() {
}
if deprecatedV2ConfigUsed {
log.Warn().Msg("deprecated V2 configuration is in use; migrate to V3 configuration, see https://neko.m1k1o.net/docs/v3/migration-from-v2")
log.Error().Msg("deprecated V2 configuration is in use; migrate to V3 configuration, see https://neko.m1k1o.net/docs/v3/migration-from-v2")
}
}
+16 -6
View File
@@ -179,9 +179,13 @@ func (s *Session) SetV2() {
switch lock {
// TODO: file_transfer
case "control":
s.LockedControls = true
if useV2Fallback("session.locked_controls") {
s.LockedControls = true
}
case "login":
s.LockedLogins = true
if useV2Fallback("session.locked_logins") {
s.LockedLogins = true
}
}
}
log.Warn().Msg("you are using v2 configuration 'NEKO_LOCKS' which is deprecated, please use 'NEKO_SESSION_LOCKED_CONTROLS' and 'NEKO_SESSION_LOCKED_LOGINS' instead")
@@ -189,22 +193,28 @@ func (s *Session) SetV2() {
}
if viper.IsSet("implicit_control") {
s.ImplicitHosting = viper.GetBool("implicit_control")
if useV2Fallback("session.implicit_hosting") {
s.ImplicitHosting = viper.GetBool("implicit_control")
}
log.Warn().Msg("you are using v2 configuration 'NEKO_IMPLICIT_CONTROL' which is deprecated, please use 'NEKO_SESSION_IMPLICIT_HOSTING' instead")
deprecatedV2ConfigUsed = true
}
if viper.IsSet("control_protection") {
s.ControlProtection = viper.GetBool("control_protection")
if useV2Fallback("session.control_protection") {
s.ControlProtection = viper.GetBool("control_protection")
}
log.Warn().Msg("you are using v2 configuration 'NEKO_CONTROL_PROTECTION' which is deprecated, please use 'NEKO_SESSION_CONTROL_PROTECTION' instead")
deprecatedV2ConfigUsed = true
}
if viper.IsSet("heartbeat_interval") {
s.HeartbeatInterval = viper.GetInt("heartbeat_interval")
if useV2Fallback("session.heartbeat_interval") {
s.HeartbeatInterval = viper.GetInt("heartbeat_interval")
}
log.Warn().Msg("you are using v2 configuration 'NEKO_HEARTBEAT_INTERVAL' which is deprecated, please use 'NEKO_SESSION_HEARTBEAT_INTERVAL' instead")
deprecatedV2ConfigUsed = true
}
if deprecatedV2ConfigUsed {
log.Warn().Msg("deprecated V2 configuration is in use; migrate to V3 configuration, see https://neko.m1k1o.net/docs/v3/migration-from-v2")
log.Error().Msg("deprecated V2 configuration is in use; migrate to V3 configuration, see https://neko.m1k1o.net/docs/v3/migration-from-v2")
}
}
+35 -17
View File
@@ -321,22 +321,30 @@ func (s *WebRTC) SetV2() {
deprecatedV2ConfigUsed := false
if viper.IsSet("nat1to1") {
s.NAT1To1IPs = viper.GetStringSlice("nat1to1")
if useV2Fallback("webrtc.nat1to1") {
s.NAT1To1IPs = viper.GetStringSlice("nat1to1")
}
log.Warn().Msg("you are using v2 configuration 'NEKO_NAT1TO1' which is deprecated, please use 'NEKO_WEBRTC_NAT1TO1' instead")
deprecatedV2ConfigUsed = true
}
if viper.IsSet("tcpmux") {
s.TCPMux = viper.GetInt("tcpmux")
if useV2Fallback("webrtc.tcpmux") {
s.TCPMux = viper.GetInt("tcpmux")
}
log.Warn().Msg("you are using v2 configuration 'NEKO_TCPMUX' which is deprecated, please use 'NEKO_WEBRTC_TCPMUX' instead")
deprecatedV2ConfigUsed = true
}
if viper.IsSet("udpmux") {
s.UDPMux = viper.GetInt("udpmux")
if useV2Fallback("webrtc.udpmux") {
s.UDPMux = viper.GetInt("udpmux")
}
log.Warn().Msg("you are using v2 configuration 'NEKO_UDPMUX' which is deprecated, please use 'NEKO_WEBRTC_UDPMUX' instead")
deprecatedV2ConfigUsed = true
}
if viper.IsSet("icelite") {
s.ICELite = viper.GetBool("icelite")
if useV2Fallback("webrtc.icelite") {
s.ICELite = viper.GetBool("icelite")
}
log.Warn().Msg("you are using v2 configuration 'NEKO_ICELITE' which is deprecated, please use 'NEKO_WEBRTC_ICELITE' instead")
deprecatedV2ConfigUsed = true
}
@@ -350,24 +358,32 @@ func (s *WebRTC) SetV2() {
log.Panic().Err(err).Msg("failed to process iceservers")
}
}
s.ICEServersFrontend = iceServers
s.ICEServersBackend = iceServers
if useV2Fallback("webrtc.iceservers.frontend", "webrtc.iceservers") {
s.ICEServersFrontend = iceServers
}
if useV2Fallback("webrtc.iceservers.backend", "webrtc.iceservers") {
s.ICEServersBackend = iceServers
}
log.Warn().Msg("you are using v2 configuration 'NEKO_ICESERVERS' which is deprecated, please use 'NEKO_WEBRTC_ICESERVERS_FRONTEND' and/or 'NEKO_WEBRTC_ICESERVERS_BACKEND' instead")
deprecatedV2ConfigUsed = true
}
if viper.IsSet("iceserver") {
iceServerSlice := viper.GetStringSlice("iceserver")
if len(iceServerSlice) > 0 {
s.ICEServersFrontend = append(s.ICEServersFrontend, types.ICEServer{URLs: iceServerSlice})
s.ICEServersBackend = append(s.ICEServersBackend, types.ICEServer{URLs: iceServerSlice})
if len(iceServerSlice) > 0 && (useV2Fallback("webrtc.iceservers.frontend", "webrtc.iceservers") || useV2Fallback("webrtc.iceservers.backend", "webrtc.iceservers")) {
if useV2Fallback("webrtc.iceservers.frontend", "webrtc.iceservers") {
s.ICEServersFrontend = append(s.ICEServersFrontend, types.ICEServer{URLs: iceServerSlice})
}
if useV2Fallback("webrtc.iceservers.backend", "webrtc.iceservers") {
s.ICEServersBackend = append(s.ICEServersBackend, types.ICEServer{URLs: iceServerSlice})
}
}
log.Warn().Msg("you are using v2 configuration 'NEKO_ICESERVER' which is deprecated, please use 'NEKO_WEBRTC_ICESERVERS_FRONTEND' and/or 'NEKO_WEBRTC_ICESERVERS_BACKEND' instead")
deprecatedV2ConfigUsed = true
}
if viper.IsSet("ipfetch") {
if len(s.NAT1To1IPs) == 0 {
if len(s.NAT1To1IPs) == 0 && useV2Fallback("webrtc.nat1to1", "webrtc.ip_retrieval_url") {
ipfetch := viper.GetString("ipfetch")
ip, err := utils.HttpRequestGET(ipfetch)
if err != nil {
@@ -396,18 +412,20 @@ func (s *WebRTC) SetV2() {
}
}
if min > max {
s.EphemeralMin = max
s.EphemeralMax = min
} else {
s.EphemeralMin = min
s.EphemeralMax = max
if useV2Fallback("webrtc.epr") {
if min > max {
s.EphemeralMin = max
s.EphemeralMax = min
} else {
s.EphemeralMin = min
s.EphemeralMax = max
}
}
log.Warn().Msg("you are using v2 configuration 'NEKO_EPR' which is deprecated, please use 'NEKO_WEBRTC_EPR' instead")
deprecatedV2ConfigUsed = true
}
if deprecatedV2ConfigUsed {
log.Warn().Msg("deprecated V2 configuration is in use; migrate to V3 configuration, see https://neko.m1k1o.net/docs/v3/migration-from-v2")
log.Error().Msg("deprecated V2 configuration is in use; migrate to V3 configuration, see https://neko.m1k1o.net/docs/v3/migration-from-v2")
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
# Migration from V2
Neko v3 accepts many V2 configuration options as deprecated aliases, which are mapped to their V3 equivalents. When both forms are set, the V2 option takes precedence. The V2 API and client compatibility layer have been removed; clients must use the V3 API.
Neko v3 accepts many V2 configuration options as deprecated aliases, which are mapped to their V3 equivalents. When both forms are set, the V3 option takes precedence. The V2 API and client compatibility layer have been removed; clients must use the V3 API.
## Docker Images {#docker}