PUBKEY-SSH.pub | 1 +
PUBKEY-SSH.pub.asc | 7 +++++++
build | 27 +++++++++++++++++++++++++++
cmd/client/audio.go | 6 +++---
cmd/client/dec.go | 9 ---------
cmd/client/dec_complex.go | 9 ---------
cmd/client/gui.go | 4 ++--
cmd/client/main.go | 35 ++++++++++++++++++++++++-----------
cmd/client/stats.go | 4 ++--
cmd/keygen/main.go | 17 ++++++++++++++---
cmd/server/gui.go | 6 +++---
cmd/server/main.go | 24 +++++++++++++++++++-----
cmd/vad/main.go | 4 ++--
contrib/dl-and-build | 33 ---------------------------------
doc/download.texi | 4 ++++
doc/index.texi | 1 -
doc/install.texi | 36 ++++++++++++++++++++++++------------
doc/integrity.texi | 11 +++++++++++
doc/libopus.texi | 26 --------------------------
doc/mk-info | 3 ++-
doc/usage.texi | 21 ++++++++++++++++++++-
go.mod | 2 +-
go.sum | 4 ++--
internal/version.go | 24 ++++++++++++++++++++++++
makedist | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++
mk-bin | 11 -----------
diff --git a/PUBKEY-SSH.pub b/PUBKEY-SSH.pub
new file mode 100644
index 0000000000000000000000000000000000000000..476427a0b9f025af44d93ed6ad7fcf7f971965e51174636ab5db54d2e113065e
--- /dev/null
+++ b/PUBKEY-SSH.pub
@@ -0,0 +1 @@
+vors@stargrave.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICasc3InBH3nN+b8/iKj7uOkr4Ue/teGfnWusXlepNxB
diff --git a/PUBKEY-SSH.pub.asc b/PUBKEY-SSH.pub.asc
new file mode 100644
index 0000000000000000000000000000000000000000..254a2c3b12a9affad1d2f2db82b323c38d64f213240fcb4a215e59018a67a061
--- /dev/null
+++ b/PUBKEY-SSH.pub.asc
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+
+iHUEABYKAB0WIQTbL/jtRAp+lJhvt3bSI36ECQhstwUCZhzfDQAKCRDSI36ECQhs
+t8/OAQC2pXGJIhaZha1MRWgSxg60ioThenTlmwF4MFhs1WWb+gEApDvItkW4TF6M
+D/esXPWelNqqhU3ccZIrMNFMgkfjVgQ=
+=Noac
+-----END PGP SIGNATURE-----
diff --git a/build b/build
new file mode 100755
index 0000000000000000000000000000000000000000..4372d1a259ad1c1ee9e6021792c39acc4fb1a0056f933bcd74106571490086b3
--- /dev/null
+++ b/build
@@ -0,0 +1,27 @@
+#!/bin/sh -e
+
+opus=opus-1.5.2
+if ! [ -d local/lib ] ; then
+ rm -fr local $opus
+ tar xf $opus.tar
+ cd $opus
+ ./configure --prefix=$(realpath ..)/local \
+ --enable-deep-plc \
+ --enable-osce \
+ --disable-doc \
+ --disable-extra-programs
+ make -j4
+ make install
+ cd ..
+ rm -r $opus
+fi
+export PKG_CONFIG_PATH="$(realpath local)/lib/pkgconfig:$PKG_CONFIG_PATH"
+
+mkdir -p bin
+[ -d vendor ] && vendor="-mod=vendor"
+strip=-ldflags=-s
+tags="nolibopusfile"
+go build -C cmd/vad $strip $vendor -o ../../bin/vors-vad
+go build -C cmd/keygen $strip $vendor -o ../../bin/vors-keygen
+go build -C cmd/server $strip $vendor -o ../../bin/vors-server
+go build -C cmd/client -tags $tags $strip $vendor -o ../../bin/vors-client
diff --git a/cmd/client/audio.go b/cmd/client/audio.go
index 0b41d029eb18932a8270dd6040e0cab2ec8d33d3d3f7715577bd67754c989c4d..446162ffd13ad06408698891ee1ed5b6c073bc9165f384c36da5f900ce0ec303 100644
--- a/cmd/client/audio.go
+++ b/cmd/client/audio.go
@@ -8,9 +8,9 @@ //
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
+// GNU Affero General Public License for more details.
//
-// You should have received a copy of the GNU General Public License
+// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
package main
@@ -18,8 +18,8 @@
import (
"log"
+ "go.stargrave.org/opus/v2"
vors "go.stargrave.org/vors/internal"
- "gopkg.in/hraban/opus.v2"
)
func newOpusEnc() *opus.Encoder {
diff --git a/cmd/client/dec.go b/cmd/client/dec.go
deleted file mode 100644
index f94b000a1df99ab6efc59f6ba734c0724e93034526cab0eb89dbe057be7863f3..0000000000000000000000000000000000000000
--- a/cmd/client/dec.go
+++ /dev/null
@@ -1,9 +0,0 @@
-//go:build !decodersetcomplexity
-
-package main
-
-import "gopkg.in/hraban/opus.v2"
-
-func DecoderSetComplexity(dec *opus.Decoder, complexity int) error {
- return nil
-}
diff --git a/cmd/client/dec_complex.go b/cmd/client/dec_complex.go
deleted file mode 100644
index 49aad0d4a2582e050192a204af232da3056f0f2827e30846e767c74145a16ac9..0000000000000000000000000000000000000000
--- a/cmd/client/dec_complex.go
+++ /dev/null
@@ -1,9 +0,0 @@
-//go:build decodersetcomplexity
-
-package main
-
-import "gopkg.in/hraban/opus.v2"
-
-func DecoderSetComplexity(dec *opus.Decoder, complexity int) error {
- return dec.SetComplexity(complexity)
-}
diff --git a/cmd/client/gui.go b/cmd/client/gui.go
index cb79c76e04685667fe360bc201d0d8670d3da2943401352dbe0df98ad19376a1..aed177b4870144459ec9c1d2f0e12bf34c5729165519463b91241436ffa0af6f 100644
--- a/cmd/client/gui.go
+++ b/cmd/client/gui.go
@@ -8,9 +8,9 @@ //
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
+// GNU Affero General Public License for more details.
//
-// You should have received a copy of the GNU General Public License
+// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
package main
diff --git a/cmd/client/main.go b/cmd/client/main.go
index 0174268ddfcc397620fde3360084c1164d3f85e27e321694c38d22b7ca70c7a3..7ff841c8e9d553f2440ae58f3616f47d490084655a0679bb6ec238cdbe2ab3ab 100644
--- a/cmd/client/main.go
+++ b/cmd/client/main.go
@@ -8,9 +8,9 @@ //
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
+// GNU Affero General Public License for more details.
//
-// You should have received a copy of the GNU General Public License
+// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
package main
@@ -18,9 +18,11 @@
import (
"bytes"
"crypto/subtle"
+ "encoding/base64"
"encoding/binary"
"encoding/hex"
"flag"
+ "fmt"
"io"
"log"
"net"
@@ -32,11 +34,11 @@ "time"
"github.com/flynn/noise"
"github.com/jroimartin/gocui"
+ "go.stargrave.org/opus/v2"
vors "go.stargrave.org/vors/internal"
"golang.org/x/crypto/blake2s"
"golang.org/x/crypto/chacha20"
"golang.org/x/crypto/poly1305"
- "gopkg.in/hraban/opus.v2"
)
type Stream struct {
@@ -50,8 +52,8 @@ var (
Streams = map[byte]*Stream{}
Finish = make(chan struct{})
OurStats = &Stats{dead: make(chan struct{})}
- Name = flag.String("name", "test", "Username")
- Room = flag.String("room", "/", "Room name")
+ Name = flag.String("name", "test", "username")
+ Room = flag.String("room", "/", "room name")
Muted bool
)
@@ -88,17 +90,28 @@ const soxParams = "--no-show-progress --buffer 1920 --channels 1 --endian little --encoding signed --rate 48000 --bits 16 --type raw -"
func main() {
srvAddr := flag.String("srv", "vors.home.arpa:"+strconv.Itoa(vors.DefaultPort),
- "Host:TCP/UDP port to connect to")
- srvPubHex := flag.String("pub", "", "Server's public key, hex")
+ "host:TCP/UDP port to connect to")
+ srvPubB64 := flag.String("pub", "", "server's public key, Base64")
recCmd := flag.String("rec", "rec "+soxParams, "rec command")
playCmd := flag.String("play", "play "+soxParams, "play command")
vadRaw := flag.Uint("vad", 0, "VAD threshold")
- passwd := flag.String("passwd", "", "Protected room's password")
- muteToggle := flag.String("mute-toggle", "", "Path to FIFO to toggle mute")
+ passwd := flag.String("passwd", "", "protected room's password")
+ muteToggle := flag.String("mute-toggle", "", "path to FIFO to toggle mute")
+ version := flag.Bool("version", false, "print version")
+ warranty := flag.Bool("warranty", false, "print warranty information")
flag.Parse()
log.SetFlags(log.Lmicroseconds | log.Lshortfile)
- srvPub, err := hex.DecodeString(*srvPubHex)
+ if *warranty {
+ fmt.Println(vors.Warranty)
+ return
+ }
+ if *version {
+ fmt.Println(vors.GetVersion())
+ return
+ }
+
+ srvPub, err := base64.RawURLEncoding.DecodeString(*srvPubB64)
if err != nil {
log.Fatal(err)
}
@@ -344,7 +357,7 @@ dec, err := opus.NewDecoder(vors.Rate, 1)
if err != nil {
log.Fatal(err)
}
- if err = DecoderSetComplexity(dec, 10); err != nil {
+ if err = dec.SetComplexity(10); err != nil {
log.Fatal(err)
}
diff --git a/cmd/client/stats.go b/cmd/client/stats.go
index 795dfbf2b7fd73b556d31ebc13ab126930ddeeda6ea581b928b947cfd049f48a..1d44e854094c12e896b08eb3bc8508b78ecd8261cb64fb27994ff3bd924ac2bb 100644
--- a/cmd/client/stats.go
+++ b/cmd/client/stats.go
@@ -8,9 +8,9 @@ //
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
+// GNU Affero General Public License for more details.
//
-// You should have received a copy of the GNU General Public License
+// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
package main
diff --git a/cmd/keygen/main.go b/cmd/keygen/main.go
index 03c3b6368e06a1cb723fe19d13f599201d24708b5360339902bc5de16c65cfa0..d14f1f821fb91632ada403bc044835b220f6cf8ff2c17fe4d48a6ad7c96e0e3c 100644
--- a/cmd/keygen/main.go
+++ b/cmd/keygen/main.go
@@ -2,7 +2,7 @@ package main
import (
"crypto/rand"
- "encoding/hex"
+ "encoding/base64"
"flag"
"fmt"
"io"
@@ -10,11 +10,22 @@ "log"
"os"
"github.com/flynn/noise"
+ vors "go.stargrave.org/vors/internal"
)
func main() {
- pub := flag.Bool("pub", false, "Print hexadecimal public key")
+ pub := flag.Bool("pub", false, "print Base64 public key")
+ version := flag.Bool("version", false, "print version")
+ warranty := flag.Bool("warranty", false, "print warranty information")
flag.Parse()
+ if *warranty {
+ fmt.Println(vors.Warranty)
+ return
+ }
+ if *version {
+ fmt.Println(vors.GetVersion())
+ return
+ }
if *pub {
data, err := io.ReadAll(os.Stdin)
if err != nil {
@@ -23,7 +34,7 @@ }
if len(data) != 2*32 {
log.Fatal("wrong length")
}
- fmt.Printf("%s\n", hex.EncodeToString(data[32:]))
+ fmt.Printf("%s\n", base64.RawURLEncoding.EncodeToString(data[32:]))
return
}
kp, err := noise.DH25519.GenerateKeypair(rand.Reader)
diff --git a/cmd/server/gui.go b/cmd/server/gui.go
index 6e16d02a8a911a373c67bdf139088ebda1b717a46978f53b51a16ea2b08f860e..5d2e1212537a48d674d583a1a585abc69ee491272d7ac00cb2f4c52fcd64dea6 100644
--- a/cmd/server/gui.go
+++ b/cmd/server/gui.go
@@ -8,9 +8,9 @@ //
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
+// GNU Affero General Public License for more details.
//
-// You should have received a copy of the GNU General Public License
+// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
package main
@@ -26,7 +26,7 @@ "github.com/jroimartin/gocui"
)
var (
- NoGUI = flag.Bool("tuiless", false, "No fancy TUI, only logs")
+ NoGUI = flag.Bool("tuiless", false, "no fancy TUI, only logs")
GUI *gocui.Gui
GUIReady bool
GUIReadyC = make(chan struct{})
diff --git a/cmd/server/main.go b/cmd/server/main.go
index 354de6c88e68634b77459312a2dccff594a3b6cef1a64b2e949c2929f38e5aa2..514e8feb04915b12bccb0d1e893ab4783a30c0c0821cf4c1f97f1dd93880f31f 100644
--- a/cmd/server/main.go
+++ b/cmd/server/main.go
@@ -8,9 +8,9 @@ //
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
+// GNU Affero General Public License for more details.
//
-// You should have received a copy of the GNU General Public License
+// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
package main
@@ -19,6 +19,7 @@ import (
"crypto/rand"
"crypto/subtle"
"crypto/tls"
+ "encoding/base64"
"encoding/hex"
"flag"
"fmt"
@@ -302,10 +303,21 @@ }
func main() {
bind := flag.String("bind", "[::1]:"+strconv.Itoa(vors.DefaultPort),
- "Host:TCP/UDP port to listen on")
- kpFile := flag.String("key", "key", "Path to keypair file")
+ "host:TCP/UDP port to listen on")
+ kpFile := flag.String("key", "key", "path to keypair file")
+ version := flag.Bool("version", false, "print version")
+ warranty := flag.Bool("warranty", false, "print warranty information")
flag.Parse()
log.SetFlags(log.Lmicroseconds | log.Lshortfile)
+
+ if *warranty {
+ fmt.Println(vors.Warranty)
+ return
+ }
+ if *version {
+ fmt.Println(vors.GetVersion())
+ return
+ }
{
data, err := os.ReadFile(*kpFile)
@@ -452,7 +464,9 @@ }()
go func() {
<-LoggerReady
- slog.Info("listening", "bind", *bind, "pub", hex.EncodeToString(Pub))
+ slog.Info("listening",
+ "bind", *bind,
+ "pub", base64.RawURLEncoding.EncodeToString(Pub))
for {
conn, err := lnTCP.AcceptTCP()
if err != nil {
diff --git a/cmd/vad/main.go b/cmd/vad/main.go
index 0b4fb9490c5d4f2771b6d30f798cb04568e3a3a390e1f3c4a4d9aff8c6c0afa0..03e62cfcb71ce395251df0608f3df596a3a60184348226a03dabe6a2923d8381 100644
--- a/cmd/vad/main.go
+++ b/cmd/vad/main.go
@@ -8,9 +8,9 @@ //
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
+// GNU Affero General Public License for more details.
//
-// You should have received a copy of the GNU General Public License
+// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
package main
diff --git a/contrib/dl-and-build b/contrib/dl-and-build
deleted file mode 100755
index c25f6abc425ca8dedc0c7d422c7401407cfaef6b8f3309bcd5ab3c733a52ab86..0000000000000000000000000000000000000000
--- a/contrib/dl-and-build
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/sh -e
-
-opus=opus-1.5.2
-if ! [ -s $opus.tar.gz ] ; then
- wget --output-document=$opus.tar.gz.tmp \
- https://downloads.xiph.org/releases/opus/$opus.tar.gz
- command -v sha512 >/dev/null && SHA512="sha512" || SHA512="sha512sum --binary"
- hsh=$($SHA512 < $opus.tar.gz.tmp | { read hsh rem ; echo $hsh ; })
- [ "$hsh" = "78d963cd56d5504611f111e2b3606e236189a3585d65fae1ecdbec9bf4545632b1956f11824328279a2d1ea2ecf441ebc11e455fb598d20a458df15185e95da4" ]
- mv $opus.tar.gz.tmp $opus.tar.gz
-fi
-rm -fr $opus
-tar xf $opus.tar.gz
-cd $opus
-./configure --prefix=$(realpath ..)/libopus \
- --enable-deep-plc --enable-osce
-make -j8
-rm -fr ../libopus
-make install
-cd ..
-go_opus_commit=8dfbda309d77a0b97277d754b93fef44239abff9
-if ! [ -d go-opus ] ; then
- git clone --depth 1 git://git.stargrave.org/go-opus.git
- cd go-opus
- git fetch origin $go_opus_commit
- git checkout $go_opus_commit
- cd ..
-fi
-echo "replace gopkg.in/hraban/opus.v2 => $(realpath go-opus)" >> go.mod
-export PKG_CONFIG_PATH=$(realpath libopus/lib/pkgconfig)
-./mk-bin decodersetcomplexity
-echo Here you go:
-ls bin
diff --git a/doc/download.texi b/doc/download.texi
new file mode 100644
index 0000000000000000000000000000000000000000..7db47235f2a2a944571e30bdb7796f759e1487831760a588a5cbdd74f82e391e
--- /dev/null
+++ b/doc/download.texi
@@ -0,0 +1,4 @@
+@multitable {XXXXX} {XXXX-XX-XX} {XXXX KiB} {meta4 tar ssh}
+@headitem Version @tab Date @tab Size @tab Tarball
+
+@end multitable
diff --git a/doc/index.texi b/doc/index.texi
index 06185d37a9c9814375c74796ff3656e94f867fec4570d43e70e6b3a55c93268a..b1b19ee60221ae3161dcfbf206e7223fcd9c774b3bc61c7bf103cf2ca58cbaf3 100644
--- a/doc/index.texi
+++ b/doc/index.texi
@@ -57,7 +57,6 @@ @include features.texi
@include install.texi
@include usage.texi
@include vad.texi
-@include libopus.texi
@include proto.texi
@bye
diff --git a/doc/install.texi b/doc/install.texi
index b01529f9bb1729cd3596bde595b4c53cb48054ff3ee6a64effa15a4f02aac6da..69db64c15d57841560295ff73a50cb9fca7327edf30228025f41db31284c1c92 100644
--- a/doc/install.texi
+++ b/doc/install.texi
@@ -1,18 +1,14 @@
@node Install
@unnumbered Install
-VoRS is written on @url{https://go.dev/, Go}, but depends on
-@url{https://github.com/hraban/opus, gopkg.in/hraban/opus.v2}
-library, that links it with C-written
-@url{https://opus-codec.org/, libopus} library.
-So you will need its development headers.
-
-@example
-$ git clone git://git.stargrave.org/vors.git
-$ cd vors
-$ ./mk-bin
-$ cp bin/vors-* $PATH/bin
-@end example
+VoRS is written on @url{https://go.dev/, Go}, but also uses wrapper on
+@url{https://opus-codec.org/, libopus} library. Provided tarballs
+include its source code
+(@url{https://wiki.hydrogenaud.io/index.php?title=Opus, look} how many
+improvements Opus got over the years) and a fork of the
+@url{https://github.com/hraban/opus, gopkg.in/hraban/opus.v2} with the
+added ability to use @code{Decoder.SetComplexity} call, that is required
+for @url{https://opus-codec.org/demo/opus-1.5/, ML-related} optimisations.
Audio recording and playback is done through external utilities, capable
of reading/writing raw audio samples from stdin/stdout.
@@ -20,3 +16,19 @@ @url{https://sourceforge.net/projects/sox/, SoX}'es @command{rec} and
@command{play} are used by default. You can use anything you want by
overriding them with @option{-rec} and @option{-play} options to
@command{vors-client}.
+
+@example
+$ [fetch|wget] http://www.vors.stargrave.org/download/vors-@value{VERSION}.tar.zst
+$ [fetch|wget] http://www.vors.stargrave.org/download/vors-@value{VERSION}.tar.zst.sig
+[verify signature]
+$ tar xf vors-@value{VERSION}.tar.zst
+$ cd vors-@value{VERSION}
+$ ./build
+$ mv bin/vors-* $PATH/bin
+@end example
+
+@include download.texi
+@include integrity.texi
+
+Also there is @url{https://yggdrasil-network.github.io/, Yggdrasil}
+accessible address: @url{http://y.www.vors.stargrave.org}.
diff --git a/doc/integrity.texi b/doc/integrity.texi
new file mode 100644
index 0000000000000000000000000000000000000000..e26a34d49c048b52789d14ac36d81f97088edb991c26606dca0ebfe2ac87105a
--- /dev/null
+++ b/doc/integrity.texi
@@ -0,0 +1,11 @@
+You @strong{have to} verify downloaded tarballs authenticity to be sure
+that you retrieved trusted and untampered software.
+@url{https://www.openssh.com/, OpenSSH} @file{.sig} signature
+@url{PUBKEY-SSH.pub, Public key} and its OpenPGP
+@url{PUBKEY-SSH.pub.asc, signature} made with the key above.
+Its fingerprint: @code{SHA256:qmlbyzvDRNXGJNxteapAWOmJRrBrZ7afLsEqr36M6kA}.
+
+@example
+$ ssh-keygen -Y verify -f PUBKEY-SSH.pub -I vors@@cypherpunks.ru -n file \
+ -s vors-@value{VERSION}.tar.zst.sig < vors-@value{VERSION}.tar.zst
+@end example
diff --git a/doc/libopus.texi b/doc/libopus.texi
deleted file mode 100644
index 7a3c589c6cbc6374a1ce941e13df5ce8267afba2600d875544d6b871c7dc0530..0000000000000000000000000000000000000000
--- a/doc/libopus.texi
+++ /dev/null
@@ -1,26 +0,0 @@
-@node libopus
-@unnumbered libopus
-
-It is @strong{strongly} advisable to use the latest and much more
-advanced version of @code{libopus}.
-@url{https://wiki.hydrogenaud.io/index.php?title=Opus, Look} how many
-features and optimisations it gains from version to version.
-
-Current latest version @url{https://opus-codec.org/demo/opus-1.5/, brings}
-various ML-related enhancements for the decoder. Unfortunately Opus
-wrapper library does not provide @code{Decoder.SetComplexity} call.
-There is @url{git://git.stargrave.org/go-opus.git, fork} fork including
-it. Clone it somewhere, add Go module replacement in your current's
-@file{go.mod} file and build client with @code{decodersetcomplexity} tag
-specified:
-
-@example
-$ git clone git://git.stargrave.org/go-opus.git
-$ realpath go-opus | read pth
-$ echo "replace gopkg.in/hraban/opus.v2 => $pth" >> go.mod
-$ ./mk-bin decodersetcomplexity
-@end example
-
-Do not forget, that @code{libopus} must be built with
-@code{--enable-deep-plc} and @code{--enable-osce} to enable use of those
-advanced techniques.
diff --git a/doc/mk-info b/doc/mk-info
index c0dd121289639e3d729131cf617698a23a308225d69c494bb50633f397cd5048..a4858ab6738a9175795230020c2aac3141850701a6401239454be1233a81e8a4 100755
--- a/doc/mk-info
+++ b/doc/mk-info
@@ -1,6 +1,7 @@
#!/bin/sh -e
-makeinfo \
+version=$(perl -ne 'print "$1\n" if /Version.*"(.*)"$/' <../internal/version.go)
+makeinfo -D "VERSION $version" \
--set-customization-variable SECTION_NAME_IN_TITLE=1 \
--set-customization-variable TREE_TRANSFORMATIONS=complete_tree_nodes_menus \
--set-customization-variable ASCII_PUNCTUATION=1 \
diff --git a/doc/usage.texi b/doc/usage.texi
index 84e566096e2626bf47a51dcf83fdbdac2dacca8d713f9e67ab6dd8caee9db3bc..c4c12c141f5818b6f1c3b5bb8e5ddb988997ccaf395859ac9557cf79195628b8 100644
--- a/doc/usage.texi
+++ b/doc/usage.texi
@@ -37,6 +37,25 @@ client means "mute" toggling.
@item
@option{-room} allows you to join non-root room.
- @option{-passwd} allows you to protect with provided password.
+ @option{-passwd} allows you protecting it with provided password.
+
+@item
+ @option{-mute-toggle} allows you to toggle mute by external
+ utilities. For example you can use suckless minimalistic
+ @url{https://github.com/baskerville/sxhkd, sxhkd} daemon to capture
+ X11 key events and execute commands:
+
+@example
+$ mkfifo /tmp/vors-mute-toggle.fifo
+$ cat >sxhkd.cfg <.`
+)
+
+func GetVersion() string {
+ return "VoRS version " + Version + " built with " + runtime.Version()
+}
diff --git a/makedist b/makedist
new file mode 100755
index 0000000000000000000000000000000000000000..d4fcdb44ad724c58355a17040451e991108551e23eec9a97241014b5b9064364
--- /dev/null
+++ b/makedist
@@ -0,0 +1,79 @@
+#!/bin/sh -ex
+
+cur=$(pwd)
+tmp=$(mktemp -d)
+release=$1
+[ -n "$release" ]
+
+git clone . $tmp/vors-$release
+cd $tmp/vors-$release
+git checkout v$release
+perl -ne 'print "$1\n" if /Version.*"(.*)"$/' VERSION
+
+########################################################################
+
+cd doc
+cat >download.texi <$texi <opus-1.5.2.tar
+rm -rf .git makedist
+find . -type d -exec chmod 755 {} +
+find . -type f -exec chmod 644 {} +
+chmod +x build
+
+cd ..
+tar cvf vors-"$release".tar --uid=0 --gid=0 --numeric-owner vors-"$release"
+zstd --ultra -22 -v vors-"$release".tar
+tarball=vors-"$release".tar.zst
+ssh-keygen -Y sign -f ~/.ssh/sign/vors@stargrave.org -n file $tarball
+meta4ra-create -fn "$tarball" -mtime "$tarball" \
+ -sig-ssh "$tarball".sig \
+ http://www.vors.stargrave.org/download/"$tarball" \
+ http://y.www.vors.stargrave.org/download/"$tarball" <"$tarball" >"$tarball".meta4
+
+size=$(( $(stat -f %z $tarball) / 1024 ))
+release_date=$(date "+%Y-%m-%d")
+
+release_underscored=`echo $release | tr . _`
+cat <