Compare commits

...

27 Commits

Author SHA1 Message Date
Syncthing Release Automation
ddafc5f6e5 chore(gui, man, authors): update docs, translations, and contributors 2026-02-16 04:33:52 +00:00
Marcus B Spencer
75dd940128 chore(config, connections): use same reconnection interval for QUIC and TCP (fixes #10507) (#10573)
Signed-off-by: Marcus B Spencer <marcus@marcusspencer.us>
2026-02-12 10:41:30 +01:00
Jakob Borg
dc2a77ab8e chore: build with Go 1.26; use Go 1.25 features (#10570)
WaitGroup.Go and built-in gomaxprocs handling.

Signed-off-by: Jakob Borg <jakob@kastelo.net>
2026-02-11 10:41:38 +00:00
Val Markovic
478d8a007d chore(etc): add more comprehensive systemd sandboxing (#10421)
Update the existing minimal service hardening with a comprehensive
sandbox to minimize blast damage from service compromise.

Please see the detailed code comments for an explanation of what is
sandboxed.

Roughly, we limit: /dev, /proc, /tmp, AF_UNIX, AF_PACKET, execution of
_any_ binary other than "/usr/bin/syncthing" and "/usr/lib",
uncommon syscalls plus io_uring, tons of kernel internals and more. We
also enable a bunch of kernel namespaces for isolation.

In short, pretty much everything is sandboxed and specifically tuned for
syncthing's behavior.

Sadly, we cannot use ProtectSystem=strict by default because we don't
know the directories that the user will be sharing. There's a big
comment block explaining how users can enable it for "extra credit". :)

If the user did add the following options as the unit file recommends:

- ProtectSystem=strict
- ReadWritePaths=/my/shared/dir1 /my/shared/dir2
- ProtectHome=true

Then the user would end up with a *far* more comprehensive sandbox than
anything a container runtime (like Docker/Podman/whatever) would
provide.

Much (but not all) of these options could be ported to the
user/syncthing.service file, BUT it would require work. Systemd does not
allow all of these options to be used with the user service manager,
although using PrivateUsers=true would help with most of it.

I cannot justify the time investment to develop, audit and test the
port to user/syncthing.service so I leave that for interested
contributors.

Tested on Debian Trixie (13) with the following versions:
- v1.29.5, Linux (64-bit Intel/AMD)
- latest HEAD (d3d3fc2d0 committed on Mon Oct 6 01:42:58 2025)

Signed-off-by: Val Markovic <val@markovic.io>
2026-02-11 11:05:49 +01:00
Michael Wang 汪東陽
2ecdc1a593 fix(gui): remove width limit for language select items (#10531)
Signed-off-by: Michael Wang <michael19920327@gmail.com>
2026-02-11 10:54:08 +01:00
Tao
f86c1d83db fix(gui): show restarting modal during upgrade restart (fixes #1248) (#10566)
Signed-off-by: steadytao <mail@steadytao.com>
Signed-off-by: Jakob Borg <jakob@kastelo.net>
Co-authored-by: Jakob Borg <jakob@kastelo.net>
2026-02-11 09:46:37 +00:00
Tommy van der Vorst
5cf9168dc2 chore(db): add ability to wait for programmatically started database maintenance, query last maintenance time (#10565)
Also adds a method to query the last database maintenance time.

Signed-off-by: Tommy van der Vorst <tommy@pixelspark.nl>
Co-authored-by: Jakob Borg <jakob@kastelo.net>
2026-02-11 09:28:11 +00:00
Syncthing Release Automation
0b5a08c99a chore(gui, man, authors): update docs, translations, and contributors 2026-02-09 04:34:38 +00:00
Epifeny
1bba07a152 chore(gui): add id and name to Stay logged in checkbox for password managers (#10558)
gui: Add id and name to Stay logged in checkbox for password manager support

Signed-off-by: epifeny <epifeny@users.noreply.github.com>
Co-authored-by: epifeny <epifeny@users.noreply.github.com>
2026-02-05 08:17:06 +01:00
Jakob Borg
2cf7197bfb Merge branch 'infrastructure'
* infrastructure:
  refactor: remove unused support for Azure blob stores
  fix(stdiscosrv): must not modify database entries in-place
2026-02-04 11:06:48 +01:00
Jakob Borg
f8a711af4e refactor: remove unused support for Azure blob stores
Signed-off-by: Jakob Borg <jakob@kastelo.net>
2026-02-04 10:24:17 +01:00
Jakob Borg
f731cfa746 fix(stdiscosrv): must not modify database entries in-place
Signed-off-by: Jakob Borg <jakob@kastelo.net>
2026-02-04 10:24:17 +01:00
Syncthing Release Automation
b40f2acdad chore(gui, man, authors): update docs, translations, and contributors 2026-02-02 04:32:24 +00:00
Jakob Borg
8adcffbb59 build: increase allowed permissions for nightly build
Signed-off-by: Jakob Borg <jakob@kastelo.net>
2026-01-30 13:17:20 +01:00
Syncthing Release Automation
c9f0c1c79b chore(gui, man, authors): update docs, translations, and contributors 2026-01-26 04:12:14 +00:00
Shivam Kumar
6a3a28fee7 fix(stdiscosrv): log full device ID on startup (#10541)
Signed-off-by: maishivamhoo123 <maishivamhoo@gmail.com>
2026-01-25 09:05:12 +01:00
Shivam Kumar
a5de148d79 fix(stdiscosrv): use fmt.Println for version output (fixes #10523) (#10527)
Signed-off-by: Shivam <maishivamhoo@gmail.com>
2026-01-24 12:06:54 +01:00
Jakob Borg
0735c25c92 build: add build attestation step at release (#10540)
Signed-off-by: Jakob Borg <jakob@kastelo.net>
2026-01-24 08:39:01 +00:00
Maxwell G
a4783ad5cd chore(gui): include license files for fork-awesome assets (#10539)
The css and svg files have license headers, but there were no separate
license files like the other vendored assets in `gui/default/vendor/*`.
This issue came up while we were working on updating and modernizing the
syncthing package in Fedora Linux.

This commit copies the existing license headers into separate files
to make things easier for license scanning and SCA tools,
such as [Go Vendor Tools](https://fedora.gitlab.io/sigs/go/go-vendor-tools/).

* [...]/css/LICENSE.txt is copied from the license header in
  gui/default/vendor/fork-awesome/css/fork-awesome.css.
* [...]/fonts/LICENSE.txt is copied from the license text in the
  <metadata> tag of
  gui/default/vendor/fork-awesome/fonts/forkawesome-webfont.svg.

Relates: https://src.fedoraproject.org/rpms/syncthing/pull-request/4

Signed-off-by: Maxwell G <maxwell@gtmx.me>
2026-01-23 21:21:00 +01:00
Syncthing Release Automation
cb391d25b8 chore(gui, man, authors): update docs, translations, and contributors 2026-01-19 04:08:47 +00:00
Tommy van der Vorst
5bf27a432c chore(sqlite): allow periodic database maintenance to be disabled (#10441)
This change allows the periodic database maintenance to be disabled, while providing a way to programmatically start maintenance at a convenient moment.

Signed-off-by: Tommy van der Vorst <tommy@pixelspark.nl>
2026-01-14 21:10:54 +00:00
Syncthing Release Automation
ed0baec2ca chore(gui, man, authors): update docs, translations, and contributors 2026-01-12 04:08:17 +00:00
Syncthing Release Automation
38e95441ee chore(gui, man, authors): update docs, translations, and contributors 2026-01-05 04:11:46 +00:00
Prathik P Kulkarni
50fe0053e8 chore(api): remove charset declaration from JSON content-type (fixes #10500) (#10508)
updated content-type  to application/json (fixes #10500)

Signed-off-by: prathik8794 <me20b030@iittp.ac.in>
2025-12-29 09:49:21 +00:00
Syncthing Release Automation
8f9ffd3c79 chore(gui, man, authors): update docs, translations, and contributors 2025-12-29 04:07:59 +00:00
Jakob Borg
1843cac6d9 fix(db): remove incorrect comment 2025-12-28 19:33:35 +01:00
bt90
b7b494b7cf fix(beacon): skip point-to-point interfaces on Android (#10504) 2025-12-23 18:56:20 +00:00
91 changed files with 761 additions and 521 deletions

View File

@@ -7,7 +7,7 @@ on:
- infra-*
env:
GO_VERSION: "~1.25.0"
GO_VERSION: "~1.26.0"
CGO_ENABLED: "0"
BUILD_USER: docker
BUILD_HOST: github.syncthing.net

View File

@@ -9,6 +9,9 @@ on:
permissions:
contents: write
packages: write
artifact-metadata: write
attestations: write
id-token: write
jobs:
build-syncthing:

View File

@@ -13,7 +13,7 @@ env:
# The go version to use for builds. We set check-latest to true when
# installing, so we get the latest patch version that matches the
# expression.
GO_VERSION: "~1.25.0"
GO_VERSION: "~1.26.0"
# Optimize compatibility on the slow architectures.
GOMIPS: softfloat
@@ -103,7 +103,7 @@ jobs:
runner: ["windows-latest", "ubuntu-latest", "macos-latest"]
# The oldest version in this list should match what we have in our go.mod.
# Variables don't seem to be supported here, or we could have done something nice.
go: ["~1.24.0", "~1.25.0"]
go: ["~1.25.0", "~1.26.0"]
runs-on: ${{ matrix.runner }}
steps:
- name: Set git to use LF
@@ -187,7 +187,7 @@ jobs:
- uses: actions/setup-go@v6
with:
go-version: ${{ needs.facts.outputs.go-version }}
go-version: "~1.25.7" # temporarily stay on Go 1.25 due to linker error on Go 1.26
cache: false
- uses: mlugg/setup-zig@v2
@@ -818,6 +818,9 @@ jobs:
environment: release
permissions:
contents: write
id-token: write
attestations: write
artifact-metadata: write
needs:
- sign-for-upgrade
- package-debian
@@ -844,10 +847,10 @@ jobs:
name: debian-packages
path: packages
- uses: actions/setup-go@v6
- name: Create GitHub build provenance attestations
uses: actions/attest-build-provenance@v3
with:
go-version: ${{ needs.facts.outputs.go-version }}
cache: false
subject-path: packages/*
- name: Push to object store (${{ env.VERSION }})
uses: docker://docker.io/rclone/rclone:latest

View File

@@ -119,6 +119,8 @@ Elliot Huffman <thelich2@gmail.com>
Emil Hessman (ceh) <emil@hessman.se>
Eng Zer Jun <engzerjun@gmail.com>
entity0xfe <109791748+entity0xfe@users.noreply.github.com> <entity0xfe@my.domain>
Epifeny <batterystarter@gmail.com>
epifeny <epifeny@users.noreply.github.com>
Eric Lesiuta <elesiuta@gmail.com>
Erik Meitner (WSGCSysadmin) <e.meitner@willystreet.coop>
Evan Spensley <94762716+0evan@users.noreply.github.com>
@@ -215,8 +217,10 @@ Max <github@germancoding.com>
Max Schulze (kralo) <max.schulze@online.de> <kralo@users.noreply.github.com>
MaximAL <almaximal@ya.ru>
Maximilian <maxi.rostock@outlook.de> <public@complexvector.space>
Maxwell G <maxwell@gtmx.me>
Michael Jephcote (Rewt0r) <rewt0r@gmx.com> <Rewt0r@users.noreply.github.com>
Michael Rienstra <mrienstra@gmail.com>
Michael Wang 汪東陽 <michael19920327@gmail.com>
MichaIng <micha@dietpi.com>
Migelo <miha@filetki.si>
Mike Boone <mike@boonedocks.net>
@@ -257,6 +261,7 @@ Philippe Schommers (filoozoom) <philippe@schommers.be>
Phill Luby (pluby) <phill.luby@newredo.com>
Piotr Bejda (piobpl) <piotrb10@gmail.com>
polyfloyd <polyfloyd@users.noreply.github.com>
Prathik P Kulkarni <83969842+prathik8794@users.noreply.github.com>
pullmerge <166967364+pullmerge@users.noreply.github.com>
Quentin Hibon <qh.public@yahoo.com>
Rahmi Pruitt <rjpruitt16@gmail.com>
@@ -276,6 +281,7 @@ Sergey Mishin (ralder) <ralder@yandex.ru>
Sertonix <83883937+Sertonix@users.noreply.github.com>
Severin von Wnuck-Lipinski <ss7@live.de>
Shaarad Dalvi <60266155+shaaraddalvi@users.noreply.github.com> <shdalv@microsoft.com>
Shivam Kumar <155747305+maishivamhoo123@users.noreply.github.com>
Simon Mwepu <simonmwepu@gmail.com>
Simon Pickup <simon@pickupinfinity.com>
Sly_tom_cat <slytomcat@mail.ru>
@@ -285,6 +291,7 @@ Steven Eckhoff <steven.eckhoff.opensource@gmail.com>
Suhas Gundimeda (snugghash) <suhas.gundimeda@gmail.com> <snugghash@gmail.com>
Sven Bachmann <dev@mcbachmann.de>
Sébastien WENSKE <sebastien@wenske.fr>
Tao <mail@steadytao.com>
Taylor Khan (nelsonkhan) <nelsonkhan@gmail.com>
Terrance <git@terrance.allofti.me>
TheCreeper <TheCreeper@users.noreply.github.com>
@@ -301,6 +308,7 @@ Tully Robinson (tojrobinson) <tully@tojr.org>
Tyler Brazier (tylerbrazier) <tyler@tylerbrazier.com>
Tyler Kropp <kropptyler@gmail.com>
Unrud (Unrud) <unrud@openaliasbox.org> <Unrud@users.noreply.github.com>
Val Markovic <val@markovic.io>
vapatel2 <149737089+vapatel2@users.noreply.github.com>
Veeti Paananen (veeti) <veeti.paananen@rojekti.fi>
Victor Buinsky (buinsky) <vix_booja@tut.by>

View File

@@ -15,8 +15,6 @@ import (
"log"
"os"
"path/filepath"
_ "github.com/syncthing/syncthing/lib/automaxprocs"
)
func main() {

View File

@@ -18,7 +18,6 @@ import (
"google.golang.org/protobuf/proto"
"github.com/syncthing/syncthing/internal/gen/discoproto"
_ "github.com/syncthing/syncthing/lib/automaxprocs"
"github.com/syncthing/syncthing/lib/beacon"
"github.com/syncthing/syncthing/lib/discover"
"github.com/syncthing/syncthing/lib/protocol"

View File

@@ -14,8 +14,6 @@ import (
"net/http"
"os"
"time"
_ "github.com/syncthing/syncthing/lib/automaxprocs"
)
type event struct {

View File

@@ -13,7 +13,6 @@ import (
"os"
"path/filepath"
_ "github.com/syncthing/syncthing/lib/automaxprocs"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/scanner"
)

View File

@@ -16,7 +16,6 @@ import (
"os"
"time"
_ "github.com/syncthing/syncthing/lib/automaxprocs"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/discover"
"github.com/syncthing/syncthing/lib/events"

View File

@@ -12,7 +12,6 @@ import (
"fmt"
"os"
_ "github.com/syncthing/syncthing/lib/automaxprocs"
"github.com/syncthing/syncthing/lib/fs"
"github.com/syncthing/syncthing/lib/ignore"
)

View File

@@ -15,8 +15,6 @@ import (
"os"
"path/filepath"
"time"
_ "github.com/syncthing/syncthing/lib/automaxprocs"
)
func main() {

View File

@@ -12,7 +12,6 @@ import (
"log"
"os"
_ "github.com/syncthing/syncthing/lib/automaxprocs"
"github.com/syncthing/syncthing/lib/signature"
"github.com/syncthing/syncthing/lib/upgrade"
)

View File

@@ -26,7 +26,6 @@ import (
"sync/atomic"
"time"
_ "github.com/syncthing/syncthing/lib/automaxprocs"
"github.com/syncthing/syncthing/lib/protocol"
)
@@ -53,11 +52,9 @@ func main() {
// Run one certificate generator per CPU core.
var wg sync.WaitGroup
for i := 0; i < runtime.GOMAXPROCS(-1); i++ {
wg.Add(1)
go func() {
wg.Go(func() {
generatePrefixed(prefix, &count, found, stop)
wg.Done()
}()
})
}
// Save the result, when one has been found.

View File

@@ -13,8 +13,6 @@ import (
"io"
"os"
"time"
_ "github.com/syncthing/syncthing/lib/automaxprocs"
)
func main() {

View File

@@ -28,7 +28,6 @@ import (
"github.com/alecthomas/kong"
raven "github.com/getsentry/raven-go"
"github.com/prometheus/client_golang/prometheus/promhttp"
_ "github.com/syncthing/syncthing/lib/automaxprocs"
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/ur"
)

View File

@@ -27,7 +27,6 @@ import (
"github.com/syncthing/syncthing/cmd/infra/strelaypoolsrv/auto"
"github.com/syncthing/syncthing/lib/assets"
_ "github.com/syncthing/syncthing/lib/automaxprocs"
"github.com/syncthing/syncthing/lib/geoip"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/rand"

View File

@@ -108,8 +108,7 @@ func refreshStats() {
results := make(chan statsFetchResult, len(relays))
for _, rel := range relays {
wg.Add(1)
go func(rel *relay) {
wg.Go(func() {
t0 := time.Now()
stats := fetchStats(rel)
duration := time.Since(t0).Seconds()
@@ -123,8 +122,7 @@ func refreshStats() {
relay: rel,
stats: fetchStats(rel),
}
wg.Done()
}(rel)
})
}
wg.Wait()

View File

@@ -25,7 +25,6 @@ import (
"github.com/alecthomas/kong"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/syncthing/syncthing/internal/slogutil"
_ "github.com/syncthing/syncthing/lib/automaxprocs"
"github.com/syncthing/syncthing/lib/httpcache"
"github.com/syncthing/syncthing/lib/upgrade"
)

View File

@@ -13,7 +13,6 @@ import (
"github.com/alecthomas/kong"
"github.com/syncthing/syncthing/cmd/infra/ursrv/serve"
_ "github.com/syncthing/syncthing/lib/automaxprocs"
)
type CLI struct {

View File

@@ -27,7 +27,6 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/puzpuzpuz/xsync/v3"
"github.com/syncthing/syncthing/internal/blob"
"github.com/syncthing/syncthing/internal/blob/azureblob"
"github.com/syncthing/syncthing/internal/blob/s3"
"github.com/syncthing/syncthing/internal/slogutil"
"github.com/syncthing/syncthing/lib/build"
@@ -49,10 +48,6 @@ type CLI struct {
S3Bucket string `name:"s3-bucket" env:"UR_S3_BUCKET"`
S3AccessKeyID string `name:"s3-access-key-id" env:"UR_S3_ACCESS_KEY_ID"`
S3SecretKey string `name:"s3-secret-key" env:"UR_S3_SECRET_KEY"`
AzureBlobAccount string `name:"azure-blob-account" env:"UR_AZUREBLOB_ACCOUNT"`
AzureBlobKey string `name:"azure-blob-key" env:"UR_AZUREBLOB_KEY"`
AzureBlobContainer string `name:"azure-blob-container" env:"UR_AZUREBLOB_CONTAINER"`
}
var (
@@ -138,12 +133,6 @@ func (cli *CLI) Run() error {
slog.Error("Failed to create S3 session", slogutil.Error(err))
return err
}
} else if cli.AzureBlobAccount != "" {
blobs, err = azureblob.NewBlobStore(cli.AzureBlobAccount, cli.AzureBlobKey, cli.AzureBlobContainer)
if err != nil {
slog.Error("Failed to create Azure blob store", slogutil.Error(err))
return err
}
}
if _, err := os.Stat(cli.DumpFile); err != nil && blobs != nil {

View File

@@ -113,7 +113,7 @@ func (s *inMemoryStore) merge(key *protocol.DeviceID, addrs []*discosrv.Database
}
if oldRec, ok := s.m.Load(*key); ok {
newRec = merge(oldRec, newRec)
newRec = merge(newRec, oldRec)
}
s.m.Store(*key, newRec)
@@ -135,7 +135,13 @@ func (s *inMemoryStore) get(key *protocol.DeviceID) (*discosrv.DatabaseRecord, e
return &discosrv.DatabaseRecord{}, nil
}
rec.Addresses = expire(rec.Addresses, s.clock.Now())
naddresses, changed := expire(rec.Addresses, s.clock.Now())
if changed {
rec = &discosrv.DatabaseRecord{
Addresses: naddresses,
Seen: rec.Seen,
}
}
databaseOperations.WithLabelValues(dbOpGet, dbResSuccess).Inc()
return rec, nil
}
@@ -184,12 +190,12 @@ func (s *inMemoryStore) expireAndCalculateStatistics() {
}
n++
addresses := expire(rec.Addresses, now)
if len(addresses) == 0 {
rec.Addresses = nil
s.m.Store(key, rec)
} else if len(addresses) != len(rec.Addresses) {
rec.Addresses = addresses
addresses, changed := expire(rec.Addresses, now)
if changed {
rec = &discosrv.DatabaseRecord{
Addresses: addresses,
Seen: rec.Seen,
}
s.m.Store(key, rec)
}
@@ -371,9 +377,9 @@ func (s *inMemoryStore) read() (int, error) {
}
slices.SortFunc(rec.Addresses, Cmp)
rec.Addresses = slices.CompactFunc(rec.Addresses, Equal)
rec.Addresses, _ = expire(slices.CompactFunc(rec.Addresses, Equal), s.clock.Now())
s.m.Store(key, &discosrv.DatabaseRecord{
Addresses: expire(rec.Addresses, s.clock.Now()),
Addresses: rec.Addresses,
Seen: rec.Seen,
})
nr++
@@ -384,7 +390,7 @@ func (s *inMemoryStore) read() (int, error) {
// merge returns the merged result of the two database records a and b. The
// result is the union of the two address sets, with the newer expiry time
// chosen for any duplicates. The address list in a is overwritten and
// reused for the result.
// reused for the result; b is not modified.
func merge(a, b *discosrv.DatabaseRecord) *discosrv.DatabaseRecord {
// Both lists must be sorted for this to work.
@@ -415,25 +421,33 @@ func merge(a, b *discosrv.DatabaseRecord) *discosrv.DatabaseRecord {
return a
}
// expire returns the list of addresses after removing expired entries.
// Expiration happen in place, so the slice given as the parameter is
// destroyed. Internal order is preserved.
func expire(addrs []*discosrv.DatabaseAddress, now time.Time) []*discosrv.DatabaseAddress {
// expire returns the list of addresses after removing expired entries. A
// new slice is allocated if any changes are required, and the changed
// boolean indicates whether that happened or not.
func expire(addrs []*discosrv.DatabaseAddress, now time.Time) (result []*discosrv.DatabaseAddress, changed bool) {
cutoff := now.UnixNano()
naddrs := addrs[:0]
for i := range addrs {
if i > 0 && addrs[i].Address == addrs[i-1].Address {
// Skip duplicates
continue
}
if addrs[i].Expires >= cutoff {
naddrs = append(naddrs, addrs[i])
remains := 0
for _, a := range addrs {
if a.Expires < cutoff {
changed = true
} else {
remains++
}
}
if len(naddrs) == 0 {
return nil
if !changed {
return addrs, false
}
return naddrs
if remains == 0 {
return nil, true
}
naddrs := make([]*discosrv.DatabaseAddress, 0, remains)
for _, a := range addrs {
if a.Expires >= cutoff {
naddrs = append(naddrs, a)
}
}
return naddrs, true
}
func Cmp(d, other *discosrv.DatabaseAddress) (n int) {

View File

@@ -161,7 +161,7 @@ func TestFilter(t *testing.T) {
}
for _, tc := range cases {
res := expire(tc.a, time.Unix(0, 10))
res, _ := expire(tc.a, time.Unix(0, 10))
if fmt.Sprint(res) != fmt.Sprint(tc.b) {
t.Errorf("Incorrect result %v, expected %v", res, tc.b)
}

View File

@@ -9,6 +9,7 @@ package main
import (
"context"
"crypto/tls"
"fmt"
"log/slog"
"net/http"
"os"
@@ -21,10 +22,8 @@ import (
"github.com/thejerf/suture/v4"
"github.com/syncthing/syncthing/internal/blob"
"github.com/syncthing/syncthing/internal/blob/azureblob"
"github.com/syncthing/syncthing/internal/blob/s3"
"github.com/syncthing/syncthing/internal/slogutil"
_ "github.com/syncthing/syncthing/lib/automaxprocs"
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/rand"
@@ -76,10 +75,6 @@ type CLI struct {
DBS3AccessKeyID string `name:"db-s3-access-key-id" group:"Database (S3 backup)" hidden:"true" help:"S3 access key ID for database" env:"DISCOVERY_DB_S3_ACCESS_KEY_ID"`
DBS3SecretKey string `name:"db-s3-secret-key" group:"Database (S3 backup)" hidden:"true" help:"S3 secret key for database" env:"DISCOVERY_DB_S3_SECRET_KEY"`
DBAzureBlobAccount string `name:"db-azure-blob-account" env:"DISCOVERY_DB_AZUREBLOB_ACCOUNT"`
DBAzureBlobKey string `name:"db-azure-blob-key" env:"DISCOVERY_DB_AZUREBLOB_KEY"`
DBAzureBlobContainer string `name:"db-azure-blob-container" env:"DISCOVERY_DB_AZUREBLOB_CONTAINER"`
AMQPAddress string `group:"AMQP replication" hidden:"true" help:"Address to AMQP broker" env:"DISCOVERY_AMQP_ADDRESS"`
Debug bool `short:"d" help:"Print debug output" env:"DISCOVERY_DEBUG"`
@@ -95,11 +90,11 @@ func main() {
level = slog.LevelDebug
}
slogutil.SetDefaultLevel(level)
slog.Info(build.LongVersionFor("stdiscosrv"))
if cli.Version {
fmt.Println(build.LongVersionFor("stdiscosrv"))
return
}
slog.Info(build.LongVersionFor("stdiscosrv"))
buildInfo.WithLabelValues(build.Version, runtime.Version(), build.User, build.Date.UTC().Format("2006-01-02T15:04:05Z")).Set(1)
@@ -119,7 +114,7 @@ func main() {
os.Exit(1)
}
devID := protocol.NewDeviceID(cert.Certificate[0])
slog.Info("Loaded certificate keypair", "deviceID", devID)
slog.Info("Loaded certificate keypair", "deviceId", devID.String())
}
// Root of the service tree.
@@ -133,8 +128,6 @@ func main() {
var err error
if cli.DBS3Endpoint != "" {
blobs, err = s3.NewSession(cli.DBS3Endpoint, cli.DBS3Region, cli.DBS3Bucket, cli.DBS3AccessKeyID, cli.DBS3SecretKey)
} else if cli.DBAzureBlobAccount != "" {
blobs, err = azureblob.NewBlobStore(cli.DBAzureBlobAccount, cli.DBAzureBlobKey, cli.DBAzureBlobContainer)
}
if err != nil {
slog.Error("Failed to create blob store", "error", err)

View File

@@ -22,7 +22,6 @@ import (
"golang.org/x/time/rate"
_ "github.com/syncthing/syncthing/lib/automaxprocs"
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/events"

View File

@@ -158,19 +158,12 @@ func (s *session) Serve() {
}
wg := sync.WaitGroup{}
wg.Add(2)
var err0 error
go func() {
err0 = s.proxy(s.conns[0], s.conns[1])
wg.Done()
}()
wg.Go(func() { err0 = s.proxy(s.conns[0], s.conns[1]) })
var err1 error
go func() {
err1 = s.proxy(s.conns[1], s.conns[0])
wg.Done()
}()
wg.Go(func() { err1 = s.proxy(s.conns[1], s.conns[0]) })
sessionMut.Lock()
activeSessions = append(activeSessions, s)

View File

@@ -15,7 +15,6 @@ import (
"path/filepath"
"time"
_ "github.com/syncthing/syncthing/lib/automaxprocs"
syncthingprotocol "github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/relay/client"
"github.com/syncthing/syncthing/lib/relay/protocol"

View File

@@ -42,7 +42,6 @@ import (
"github.com/syncthing/syncthing/internal/db"
"github.com/syncthing/syncthing/internal/db/sqlite"
"github.com/syncthing/syncthing/internal/slogutil"
_ "github.com/syncthing/syncthing/lib/automaxprocs"
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/dialer"
@@ -155,7 +154,7 @@ type serveCmd struct {
AllowNewerConfig bool `help:"Allow loading newer than current config version" env:"STALLOWNEWERCONFIG"`
Audit bool `help:"Write events to audit file" env:"STAUDIT"`
AuditFile string `name:"auditfile" help:"Specify audit file (use \"-\" for stdout, \"--\" for stderr)" placeholder:"PATH" env:"STAUDITFILE"`
DBMaintenanceInterval time.Duration `help:"Database maintenance interval" default:"8h" env:"STDBMAINTENANCEINTERVAL"`
DBMaintenanceInterval time.Duration `help:"Database maintenance interval; set to zero to disable periodic maintenance" default:"8h" env:"STDBMAINTENANCEINTERVAL"`
DBDeleteRetentionInterval time.Duration `help:"Database deleted item retention interval" default:"10920h" env:"STDBDELETERETENTIONINTERVAL"`
GUIAddress string `name:"gui-address" help:"Override GUI address (e.g. \"http://192.0.2.42:8443\")" placeholder:"URL" env:"STGUIADDRESS"`
GUIAPIKey string `name:"gui-apikey" help:"Override GUI API key" placeholder:"API-KEY" env:"STGUIAPIKEY"`
@@ -748,8 +747,13 @@ func autoUpgrade(cfg config.Wrapper, app *syncthing.App, evLogger events.Logger)
continue
}
sub.Unsubscribe()
restartDelay := time.Minute
evLogger.Log(events.UpgradeRestartScheduled, map[string]any{
"delayS": int(restartDelay / time.Second),
"newVersion": rel.Tag,
})
slog.Error("Automatically upgraded, restarting in 1 minute", slog.String("newVersion", rel.Tag))
time.Sleep(time.Minute)
time.Sleep(restartDelay)
app.Stop(svcutil.ExitUpgrade)
return
}

View File

@@ -137,17 +137,8 @@ func (c *serveCmd) monitorMain() {
var wg sync.WaitGroup
wg.Add(1)
go func() {
copyStderr(stderr, dst)
wg.Done()
}()
wg.Add(1)
go func() {
copyStdout(stdout, dst)
wg.Done()
}()
wg.Go(func() { copyStderr(stderr, dst) })
wg.Go(func() { copyStdout(stdout, dst) })
exit := make(chan error)

View File

@@ -39,3 +39,10 @@
darwin: "21"
linux: "3.2"
windows: "10.0"
- runtime: go1.26 # no changes from 1.25
requirements:
darwin: "21"
linux: "3.2"
windows: "10.0"

View File

@@ -16,16 +16,200 @@ RestartSec=1
SuccessExitStatus=3 4
RestartForceExitStatus=3 4
# Hardening
ProtectSystem=full
PrivateTmp=true
SystemCallArchitectures=native
MemoryDenyWriteExecute=true
NoNewPrivileges=true
#############
# SANDBOXING
#############
#
# This section contains best-effort sandboxing of syncthing. Such sandboxing is
# useful to reduce the blast damage of a syncthing exploit.
#
# The sandboxing is "best-effort" only because some of these options are ignored
# if your systemd or kernel are too old or configured in unusual ways. Systemd
# should (but may not) tell you in the journal logs if that's the case. See the
# logs (after starting the service) with:
#
# journalctl --boot --pager-end --unit syncthing@<user-you-used>.service
#
# See systemd's analysis of syncthing's sandbox with:
#
# systemd-analyze security syncthing@<user-you-used>.service
#
# Most of these sandboxing options are documented in `man systemd.exec`.
#
# NOTE: Some of these options _appear_ redundant with each other... but
# depending on the version and configs of systemd and the kernel, some of the
# "redundant" options may be non-functional while others still work.
# We recommend leaving the "redundant" options in place.
# Elevated permissions to sync ownership (disabled by default),
# see https://docs.syncthing.net/advanced/folder-sync-ownership
# Makes /usr, /boot, /efi and /etc read-only.
ProtectSystem=full
# Protect several system areas syncthing should not be touching.
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectControlGroups=true
ProtectHostname=true
ProtectClock=true
# No new privileges through SUID/SGID binaries
NoNewPrivileges=true
# Prevents *setting* SUID/SGID bits on files/dirs
RestrictSUIDSGID=true
# Prevent memory pages that are both writable and executable. This kills JIT
# compilers, but syncthing is precompiled.
MemoryDenyWriteExecute=true
# Prevents creation of unprivileged user namespaces which are a significant
# source of privilege escalation exploits.
#
# (In 2023, Google saw 44% of kernel exploits using unpriv. user namespaces.
# Source: https://ubuntu.com/blog/ubuntu-23-10-restricted-unprivileged-user-namespaces)
#
# The service can still be placed *inside* such user namespaces (and is, through
# other sandboxing options), it just can't create any itself.
RestrictNamespaces=true
# RT task scheduling can be abused for denial-of-service
RestrictRealtime=true
# NOTE: This option is poorly named. It doesn't _restrict_ the listed families,
# it _allows_ the listed families. Unlisted ones are restricted.
#
# Specifically, notice the absence of AF_PACKET (raw packets).
# AF_UNIX is needed to support binding to UNIX sockets.
# AF_NETLINK is needed to support hotplugging of network devices and because
# otherwise we see the following (non-fatal) error on startup:
#
# Failed to list network interfaces (error="route ip+net: netlinkrib:
# address family not supported by protocol" log.pkg=upnp)
#
# This option does NOT affect systemd socket passing using .socket units.
RestrictAddressFamilies=AF_INET AF_INET6 AF_NETLINK AF_UNIX
# The lifetime limit of (superuser) capabilities that syncthing can acquire.
# This option _restricts_ capabilities.
CapabilityBoundingSet=
# Start with empty (superuser) capabilities.
# This option _expands_ capabilities.
# AmbientCapabilities should equal CapabilityBoundingSet.
AmbientCapabilities=
# Disables `personality` system call; it can be used for privilege escalation.
LockPersonality=true
# Prevents circumvention of restrictions through the use of x86 syscalls on
# x86-64 systems.
SystemCallArchitectures=native
# Clean up IPC objects after service stops.
RemoveIPC=true
# Create private namespace for System V IPC.
# NOTE: This does not apply to AF_UNIX sockets which are more commonly used.
PrivateIPC=true
# Completely isolated /tmp and /var/tmp
PrivateTmp=disconnected
# New /dev with safe virtual devices like /dev/null
PrivateDevices=true
# Allow access to devices explicitly listed with DeviceAllow and pseudo devices
# like /dev/null.
DevicePolicy=closed
# Creates a new PID namespace. /proc now contains only entries for processes
# in this PID namespace.
PrivatePIDs=true
# Make processes owned by other users hidden in /proc/
ProtectProc=invisible
# Prevent access to non-pid interfaces in /proc.
ProcSubset=pid
# System call allow-list. `@system-service` is a systemd-provided category that
# allows common syscalls needed for system services.
SystemCallFilter=@system-service
# Explicitly disallow @privileged syscalls. Syncthing fails to start if we also
# disallow @resources (which `systemd-analyze` is unhappy about).
# Also disallow io_uring syscalls which are as of 2025 a significant source of
# kernel exploits.
# We do not include io_uring_enter2 because it's just a wrapper for
# io_uring_enter and systemd issues a warning.
SystemCallFilter=~@privileged io_uring_enter io_uring_register io_uring_setup
# Return EPERM when a disallowed syscall is made instead of killing the process.
SystemCallErrorNumber=EPERM
# Digits from left to right; disallow creation of files with:
# - special security-related bits like setuid/setgid
# - (no restrictions on file owner permissions)
# - group-writable access
# - world-readable access
# NOTE: The default value is 0022. We are only restricting special security bits
# and world-readable access.
# NOTE: Syncthing can still _explicitly_ change file permissions using `chmod`.
UMask=7027
# The default HOME folder for system users on Debian-like systems is
# /nonexistent, which should never exist.
# We prevent syncthing from accessing that folder it if was previously created
# through misconfiguration, or from creating it if it's (correctly) missing.
InaccessiblePaths=-/nonexistent
##################
# OPTIONAL CONFIG
##################
#
# Users that want to tweak this service file should add a systemd drop-in
# file to avoid changing the original file.
#
# Documentation describing drop-in files:
# https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html
#
# Example drop-in file location (assuming user "syncthing"):
# /etc/systemd/system/syncthing@syncthing.service.d/override.conf
#
## Elevated permissions to sync ownership (disabled by default),
## see https://docs.syncthing.net/advanced/folder-sync-ownership
##
## NOTE:
## - Use the same value for *both* of these options.
## - PrivateUsers=false must be set (false is the default, but you might have
## changed it to true in the "extra credit" section below).
#AmbientCapabilities=CAP_CHOWN CAP_FOWNER
#CapabilityBoundingSet=CAP_CHOWN CAP_FOWNER
#########################
# EXTRA CREDIT FOR USERS
#########################
#
# Users that want to harden their systems further should set the following
# properties. (Also through a systemd drop-in file; see comments above.)
#
## Makes all of / read-only *except*:
## - /dev/, /proc/ and /sys/ (see other Protect* options)
## - ReadWritePaths=
## - StateDirectory=, LogsDirectory= and similar
##
## This cannot be enabled by default because we don't know which folders you wish to
## share. If enabling this option, enable it along with ReadWritePaths=, e.g.:
## ReadWritePaths=/my/shared/dir1 /my/shared/dir2
#ProtectSystem=strict
#
## When enabled, sets up a new user namespace. Maps the "root" user and group as
## well as the unit's own user and group to themselves and everything else to
## the "nobody" user and group.
## This is useful to securely detach the user and group databases used by the
## unit from the rest of the system, and thus to create an effective sandbox
## environment.
#PrivateUsers=true
#
## Makes /home, /root and /run/user *invisible* while allowing BindPaths= and
## BindReadOnlyPaths= to "carve out" access to parts of those dirs.
## (Use 'true' instead of 'tmpfs' if you don't need to carve out anything.)
##
## "Invisible" is superior to read-only provided by ProtectSystem=strict because
## it prevents information disclosure of private user data in case of service
## compromise.
#ProtectHome=tmpfs
#
## Disallow execution of all binaries. ExecPaths= below carves out exceptions.
## Can't be enabled by default due to the External File Versioning feature:
## https://docs.syncthing.net/users/versioning.html#external-file-versioning
##
## If you do not use that feature, you can enable both NoExecPaths and
## ExecPaths.
## If you do use that featuer, you can still use these options; just add
## the paths to the binaries you invoke to ExecPaths so they can be executed.
#NoExecPaths=/
## Allow execution of syncthing and system shared libraries.
## NOTE: If you are seeing an error like
## "Failed to execute /some/path/to/syncthing: Permission denied", this is the
## option you need to update to use your non-standard install location.
#ExecPaths=/usr/bin/syncthing /usr/lib
[Install]
WantedBy=multi-user.target

63
go.mod
View File

@@ -1,11 +1,10 @@
module github.com/syncthing/syncthing
go 1.24.0
go 1.25.0
require (
github.com/AudriusButkevicius/recli v0.0.7
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2
github.com/alecthomas/kong v1.12.1
github.com/alecthomas/kong v1.14.0
github.com/aws/aws-sdk-go v1.55.8
github.com/calmh/incontainer v1.0.0
github.com/calmh/xdr v1.2.0
@@ -13,27 +12,27 @@ require (
github.com/coreos/go-semver v0.3.1
github.com/d4l3k/messagediff v1.2.1
github.com/getsentry/raven-go v0.2.0
github.com/go-ldap/ldap/v3 v3.4.11
github.com/go-ldap/ldap/v3 v3.4.12
github.com/gobwas/glob v0.2.3
github.com/gofrs/flock v0.12.1
github.com/gofrs/flock v0.13.0
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/jackpal/gateway v1.0.16
github.com/jackpal/gateway v1.1.1
github.com/jackpal/go-nat-pmp v1.0.2
github.com/jmoiron/sqlx v1.4.0
github.com/julienschmidt/httprouter v1.3.0
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/maruel/panicparse/v2 v2.5.0
github.com/mattn/go-sqlite3 v1.14.31
github.com/mattn/go-sqlite3 v1.14.34
github.com/maxmind/geoipupdate/v6 v6.1.0
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75
github.com/oschwald/geoip2-golang v1.13.0
github.com/pierrec/lz4/v4 v4.1.22
github.com/prometheus/client_golang v1.23.0
github.com/pierrec/lz4/v4 v4.1.25
github.com/prometheus/client_golang v1.23.2
github.com/puzpuzpuz/xsync/v3 v3.5.1
github.com/quic-go/quic-go v0.56.0
github.com/quic-go/quic-go v0.59.0
github.com/rabbitmq/amqp091-go v1.10.0
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9
github.com/shirou/gopsutil/v4 v4.25.6 // https://github.com/shirou/gopsutil/issues/1898
github.com/shirou/gopsutil/v4 v4.26.1 // https://github.com/shirou/gopsutil/issues/1898
github.com/syncthing/notify v0.0.0-20250528144937-c7027d4f7465
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d
github.com/thejerf/suture/v4 v4.0.6
@@ -41,21 +40,18 @@ require (
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0
github.com/willabides/kongplete v0.4.0
github.com/wlynxg/anet v0.0.5
go.uber.org/automaxprocs v1.6.0
golang.org/x/crypto v0.44.0
golang.org/x/exp v0.0.0-20250811191247-51f88131bc50
golang.org/x/net v0.47.0
golang.org/x/sys v0.38.0
golang.org/x/text v0.31.0
golang.org/x/time v0.12.0
google.golang.org/protobuf v1.36.7
modernc.org/sqlite v1.38.2
golang.org/x/crypto v0.48.0
golang.org/x/exp v0.0.0-20260209203927-2842357ff358
golang.org/x/net v0.50.0
golang.org/x/sys v0.41.0
golang.org/x/text v0.34.0
golang.org/x/time v0.14.0
google.golang.org/protobuf v1.36.11
modernc.org/sqlite v1.45.0
sigs.k8s.io/yaml v1.6.0
)
require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
@@ -64,7 +60,7 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/ebitengine/purego v0.8.4 // indirect
github.com/ebitengine/purego v0.9.1 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
@@ -78,7 +74,7 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/maxbrunsfeld/counterfeiter/v6 v6.12.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/nxadm/tail v1.4.11 // indirect
github.com/oschwald/maxminddb-golang v1.13.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
@@ -86,23 +82,24 @@ require (
github.com/posener/complete v1.2.3 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.65.0 // indirect
github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.10.0 // indirect
github.com/stretchr/testify v1.11.1 // indirect
github.com/tklauser/go-sysconf v0.3.16 // indirect
github.com/tklauser/numcpus v0.11.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
golang.org/x/mod v0.30.0 // indirect
golang.org/x/sync v0.18.0 // indirect
golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54 // indirect
golang.org/x/tools v0.39.0 // indirect
golang.org/x/mod v0.33.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 // indirect
golang.org/x/tools v0.42.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/libc v1.66.3 // indirect
modernc.org/libc v1.67.6 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
)

153
go.sum
View File

@@ -2,37 +2,23 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/AudriusButkevicius/recli v0.0.7 h1:9zjbYlTupi+W5SJXm2cR2sV2mJAIg1sIfDcsW7hrkPM=
github.com/AudriusButkevicius/recli v0.0.7/go.mod h1:Nhfib1j/VFnLrXL9cHgA+/n2O6P5THuWelOnbfPNd78=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 h1:Wc1ml6QlJs2BHQ/9Bqu1jiyggbsSjramq2oUmp5WeIo=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 h1:/Zt+cDPnpC3OVDm/JKLOs7M2DKmLRIIp3XIx9pHHiig=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1/go.mod h1:Ng3urmn6dYe8gnbCMoHHVl5APYz2txho3koEkV2o2HA=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2 h1:FwladfywkNirM+FZYLBR2kBz5C8Tg0fw5w5Y7meRXWI=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2/go.mod h1:vv5Ad0RrIoT1lJFdWBZwt4mB1+j+V8DUroixmKDTCdk=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/kong v1.12.1 h1:iq6aMJDcFYP9uFrLdsiZQ2ZMmcshduyGv4Pek0MQPW0=
github.com/alecthomas/kong v1.12.1/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/alecthomas/kong v1.14.0 h1:gFgEUZWu2ZmZ+UhyZ1bDhuutbKN1nTtJTwh19Wsn21s=
github.com/alecthomas/kong v1.14.0/go.mod h1:wrlbXem1CWqUV5Vbmss5ISYhsVPkBb1Yo7YKJghju2I=
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktpoUAgOJK3OTFc/xug0PCXYCqU0FgDKI=
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ=
github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/calmh/glob v0.0.0-20220615080505-1d823af5017b h1:Fjm4GuJ+TGMgqfGHN42IQArJb77CfD/mAwLbDUoJe6g=
github.com/calmh/glob v0.0.0-20220615080505-1d823af5017b/go.mod h1:91K7jfEsgJSyfSrX+gmrRfZMtntx6JsHolWubGXDopg=
github.com/calmh/go-sqlite3 v1.14.32-0.20250812195006-80712c77b76a h1:lTe5qJApKNO+zZCa3/P/7UxM4c58CXWOegv9eODPWvs=
github.com/calmh/go-sqlite3 v1.14.32-0.20250812195006-80712c77b76a/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/calmh/incontainer v1.0.0 h1:g2cTUtZuFGmMGX8GoykPkN1Judj2uw8/3/aEtq4Z/rg=
github.com/calmh/incontainer v1.0.0/go.mod h1:eOhqnw15c9X+4RNBe0W3HlUZFfX16O0EDsCOInTndHY=
github.com/calmh/xdr v1.2.0 h1:GaGSNH4ZDw9kNdYqle6+RcAENiaQ8/611Ok+jQbBEeU=
@@ -59,8 +45,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
@@ -71,18 +57,16 @@ github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JY
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU=
github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM=
github.com/go-ldap/ldap/v3 v3.4.12 h1:1b81mv7MagXZ7+1r7cLTWmyuTqVqdwbtJSjC0DAp9s4=
github.com/go-ldap/ldap/v3 v3.4.12/go.mod h1:+SPAGcTtOfmGsCb3h1RFiq4xpp4N636G75OEace8lNo=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0=
github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@@ -120,8 +104,8 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jackpal/gateway v1.0.16 h1:mTBRuHSW8qviVqX7kXnxKevqlfS/OA01ys6k6fxSX7w=
github.com/jackpal/gateway v1.0.16/go.mod h1:IOn1OUbso/cGYmnCBZbCEqhNCLSz0xxdtIpUpri5/nA=
github.com/jackpal/gateway v1.1.1 h1:UXXXkJGIHFsStms9ZBgGpoaFEJP7oJtFn5vplIT68E8=
github.com/jackpal/gateway v1.1.1/go.mod h1:Tl1vZVtUaXx5j6P5HFmv45alhEi4yHHLfT4PRbB7eyw=
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
@@ -163,6 +147,8 @@ github.com/maruel/panicparse/v2 v2.5.0/go.mod h1:DA2fDiBk63bKfBf4CVZP9gb4fuvzdPb
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.34 h1:3NtcvcUnFBPsuRcno8pUtupspG/GM+9nZ88zgJcp6Zk=
github.com/mattn/go-sqlite3 v1.14.34/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/maxbrunsfeld/counterfeiter/v6 v6.12.0 h1:aOeI7xAOVdK+R6xbVsZuU9HmCZYmQVmZgPf9xJUd2Sg=
github.com/maxbrunsfeld/counterfeiter/v6 v6.12.0/go.mod h1:0hZWbtfeCYUQeAQdPLUzETiBhUSns7O6LDj9vH88xKA=
github.com/maxmind/geoipupdate/v6 v6.1.0 h1:sdtTHzzQNJlXF5+fd/EoPTucRHyMonYt/Cok8xzzfqA=
@@ -171,8 +157,8 @@ github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75 h1:cUVxyR+U
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75/go.mod h1:pBbZyGwC5i16IBkjVKoy/sznA8jPD/K9iedwe1ESE6w=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
@@ -193,10 +179,8 @@ github.com/oschwald/geoip2-golang v1.13.0 h1:Q44/Ldc703pasJeP5V9+aFSZFmBN7DKHbNs
github.com/oschwald/geoip2-golang v1.13.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo=
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pierrec/lz4/v4 v4.1.25 h1:kocOqRffaIbU5djlIBr7Wh+cx82C0vtFb0fOurZHqD0=
github.com/pierrec/lz4/v4 v4.1.25/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -206,20 +190,18 @@ github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXq
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/quic-go/quic-go v0.56.0 h1:q/TW+OLismmXAehgFLczhCDTYB3bFmua4D9lsNBWxvY=
github.com/quic-go/quic-go v0.56.0/go.mod h1:9gx5KsFQtw2oZ6GZTyh+7YEvOxWCL9WZAepnHxgAo6c=
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg=
@@ -234,8 +216,8 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
github.com/shirou/gopsutil/v4 v4.26.1 h1:TOkEyriIXk2HX9d4isZJtbjXbEjf5qyKPAzbzY0JWSo=
github.com/shirou/gopsutil/v4 v4.26.1/go.mod h1:medLI9/UNAb0dOI9Q3/7yWSqKkj00u+1tgY8nvv41pc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -247,18 +229,19 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/syncthing/notify v0.0.0-20250528144937-c7027d4f7465 h1:yhxdTGmFkAM2TFA65c3NgGwpnIkUM8oVqPX2e9S7IVg=
github.com/syncthing/notify v0.0.0-20250528144937-c7027d4f7465/go.mod h1:J0q59IWjLtpRIJulohwqEZvjzwOfTEPp8SVhDJl+y0Y=
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs=
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
github.com/thejerf/suture/v4 v4.0.6 h1:QsuCEsCqb03xF9tPAsWAj8QOAJBgQI1c0VqJNaingg8=
github.com/thejerf/suture/v4 v4.0.6/go.mod h1:gu9Y4dXNUWFrByqRt30Rm9/UZ0wzRSt9AJS6xu/ZGxU=
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ=
github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo=
@@ -271,8 +254,6 @@ github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguH
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
@@ -284,13 +265,13 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
golang.org/x/exp v0.0.0-20250811191247-51f88131bc50 h1:3yiSh9fhy5/RhCSntf4Sy0Tnx50DmMpQ4MQdKKk4yg4=
golang.org/x/exp v0.0.0-20250811191247-51f88131bc50/go.mod h1:rT6SFzZ7oxADUDx58pcaKFTcZ+inxAa9fTrYx/uVYwg=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/exp v0.0.0-20260209203927-2842357ff358 h1:kpfSV7uLwKJbFSEgNhWzGSL47NDSF/5pYYQw1V0ub6c=
golang.org/x/exp v0.0.0-20260209203927-2842357ff358/go.mod h1:R3t0oliuryB5eenPWl3rrQxwnNM3WTwnsRZZiXLAAW8=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -299,13 +280,13 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -328,25 +309,25 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54 h1:E2/AqCUMZGgd73TQkxUMcMla25GB9i/5HOdLr+uH7Vo=
golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 h1:bTLqdHv7xrGlFbvf5/TXNxy/iUwwdkjhqQTJDjW7aj0=
golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4/go.mod h1:g5NllXBEermZrmR51cJDQxmJUHUOfRAaNyWBM+R+548=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -360,8 +341,8 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -377,18 +358,20 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM=
modernc.org/cc/v4 v4.26.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
modernc.org/fileutil v1.3.8 h1:qtzNm7ED75pd1C7WgAGcK4edm4fvhtBsEiI/0NQ54YM=
modernc.org/fileutil v1.3.8/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc=
modernc.org/ccgo/v4 v4.30.1/go.mod h1:bIOeI1JL54Utlxn+LwrFyjCx2n2RDiYEaJVSrgdrRfM=
modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE=
modernc.org/gc/v3 v3.1.1/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ=
modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8=
modernc.org/libc v1.67.6 h1:eVOQvpModVLKOdT+LvBPjdQqfrZq+pC39BygcT+E7OI=
modernc.org/libc v1.67.6/go.mod h1:JAhxUVlolfYDErnwiqaLvUqc8nfb2r6S6slAgZOnaiE=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
@@ -397,8 +380,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.38.2 h1:Aclu7+tgjgcQVShZqim41Bbw9Cho0y/7WzYptXqkEek=
modernc.org/sqlite v1.38.2/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E=
modernc.org/sqlite v1.45.0 h1:r51cSGzKpbptxnby+EIIz5fop4VuE4qFoVEjNvWoObs=
modernc.org/sqlite v1.45.0/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=

View File

@@ -221,8 +221,8 @@ li[language-select] > .dropdown-menu {
}
li[language-select] > .dropdown-menu > li > a {
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
overflow-wrap: break-word;
}
.nav>li{

View File

@@ -11,6 +11,7 @@
"Add Device": "Lisa seade",
"Add Folder": "Lisa kaust",
"Add Remote Device": "Lisa kaugseade",
"Add devices from the introducer to our device list, for mutually shared folders.": "Ühiselt jagatud kaustade halduseks lisa kokkuviija poolt teadaantud seadmed meie seadmeloendisse.",
"Add filter entry": "Lisa filtrikirje",
"Add ignore patterns": "Lisa eiramismustreid",
"Add new folder?": "Kas lisad uue kausta?",
@@ -24,6 +25,7 @@
"Allow Anonymous Usage Reporting?": "Kas lubad anonüümset statistikakogumist rakenduse kasutamise kohta?",
"Allowed Networks": "Lubatud võrgud",
"Alphabetic": "Tähestikuline",
"Altered by ignoring deletes.": "Muudetud eirates kustutamisi.",
"Always turned on when the folder type is \"{%foldertype%}\".": "Kui kausta tüüp on „{{foldertype}}“, siis on alati lülitatud sisse.",
"Anonymous Usage Reporting": "Anonüümne aruandlus kasutuse kohta",
"Applied to LAN": "Kehtib kohtvõrgu puhul",
@@ -39,8 +41,10 @@
"Authors": "Autorid",
"Auto Accept": "Nõustu automaatselt",
"Automatic Crash Reporting": "Automaatne teavitus rakenduste kokkujooksmise kohta",
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Automaatsed uuendused võimaldavad nüüd valida stabiilsete versioonide ja kandidaatversioonide vahel.",
"Automatic upgrades": "Automaatsed uuendused",
"Automatic upgrades are always enabled for candidate releases.": "Automaatsed uuendused on kandidaatversioonide puhul alati lubatud.",
"Available debug logging facilities:": "Saadavaolevad logimisklassid veaotsinguteadate logimiseks:",
"Be careful!": "Ettevaatust!",
"Body:": "Kirja sisu:",
"Bugs": "Vead",
@@ -53,6 +57,7 @@
"Click to see full identification string and QR code.": "Täispikka tunnust ja QR-koodi näed, kui klõpsad siin.",
"Close": "Sulge",
"Command": "Käsk",
"Comment, when used at the start of a line": "Kommentaar, kasutades rea alguses",
"Compression": "Pakkimine",
"Configuration Directory": "Seadistuste kaust",
"Configuration File": "Seadistuste fail",
@@ -62,6 +67,7 @@
"Connection Management": "Ühenduste haldus",
"Connection Type": "Ühenduse tüüp",
"Connections": "Ühendused",
"Connections via relays might be rate limited by the relay": "Edastussõlmede kasutamisel võivad kohalduda ühenduse kiiruspiirangud",
"Copied from elsewhere": "Kopeeritud mujalt",
"Copied from original": "Kopeeritud algallikast",
"Copied!": "Kopeeritud!",
@@ -80,6 +86,7 @@
"Default Ignore Patterns": "Vaikimisi eiramismustrid",
"Defaults": "Vaikimisi väärtused",
"Delete": "Kustuta",
"Delete Unexpected Items": "Kustuta mittesoovitud objektid",
"Deleted {%file%}": "Kustutasin {{file}} faili",
"Deselect All": "Eemalda kogu valik",
"Deselect devices to stop sharing this folder with.": "Eemalda seadmed, millega sa enam ei taha seda kausta jagada.",
@@ -97,6 +104,9 @@
"Devices": "Seadmed",
"Disable Crash Reporting": "Lülita rakenduse kokkujooksmisest teavitamine välja",
"Disabled": "Pole kasutusel",
"Disabled periodic scanning and disabled watching for changes": "Regulaarne skaneerimine ja muudatuste jälgimine on lülitatud välja",
"Disabled periodic scanning and enabled watching for changes": "Regulaarne skaneerimine on lülitatud välja ja muudatuste jälgimine on lülitatud sisse",
"Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:": "Regulaarne skaneerimine on lülitatud välja ja muudatuste jälgimise sisselülitamine ei õnnestunud, proovin iga minuti järel uuesti:",
"Discard": "Loobu",
"Disconnected": "Ühendus puudub",
"Disconnected (Inactive)": "Ühendus on katkestatud (pole aktiivne)",
@@ -123,16 +133,22 @@
"Enable NAT traversal": "Luba NAT traversal",
"Enable Relaying": "Luba edastussõlmede kasutamine",
"Enabled": "Kasutusel",
"Enter a non-privileged port number (1024 - 65535).": "Sisesta pordi number vabast segmendist (1024 - 65535).",
"Enter ignore patterns, one per line.": "Sisesta eiramiste mustrid üks muster ühele reale.",
"Enter up to three octal digits.": "Sisesta kuni kolm kaheksandsüsteemis arvu.",
"Error": "Viga",
"Extended Attributes": "Täiendavad atribuudid",
"Extended Attributes": "Täiendavad atribuudid/andmed",
"Extended Attributes Filter": "Täiendavate atribuutide/andmete filter",
"External": "Väline",
"External File Versioning": "Väline failide versioonihaldus",
"Failed Items": "Ebaõnnestunud objektid",
"Failed to load file versions.": "Failiversioonide laadimine ei õnnestunud.",
"Failed to load ignore patterns.": "Eiramismustrite laadimine ei õnnestunud.",
"Failed to set up, retrying": "Seadistamine ei õnnestunud, proovin uuesti",
"File Pull Order": "Failide Tirimise Järjekord",
"File Versioning": "Failide versioonihaldus",
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Failid liigutatakse asendamisel või kustutamisel .stversions kataloogi.",
"Filesystem Watcher Errors": "Failisüsteemi jälgija vead",
"Filter by date": "Filtreeri kuupäeva alusel",
"Filter by name": "Filtreeri nime alusel",
"Folder": "Kaust",
@@ -145,14 +161,17 @@
"Forever": "Igavesti",
"Full Rescan Interval (s)": "Täiemahulise kordusskaneerimise välp (sek)",
"GUI": "Kasutajaliides",
"GUI / API HTTPS Certificate": "GUI / API HTTPS sertifikaat",
"GUI Authentication Password": "Salasõna kasutajaliidese autentimiseks",
"GUI Authentication User": "GUI Autentimise Kasutajatunnus",
"GUI Authentication: Set User and Password": "Kasutajaliidese autentimine: sisesta kasutajanimi ja salasõna",
"GUI Listen Address": "Aadress, mida kasutajaliides kuulab",
"GUI Override Directory": "Kasutajaliidese sürjutamise kaust",
"GUI Theme": "GUI Teema",
"General": "Üldist",
"Generate": "Genereeri",
"Global Discovery": "Üldine avastamine",
"Global Discovery Servers": "Üldised tuvastusserverid",
"Global State": "Üldine olek",
"Help": "Abiteave",
"Home page": "Avaleht",
@@ -168,7 +187,11 @@
"Incoming Rate Limit (KiB/s)": "Siseneva liikluse kiiruspiirang (KiB/s)",
"Incorrect user name or password.": "Vigane kasutajanimi või salasõna.",
"Info": "Teave",
"Internally used paths:": "Sisemiselt kasutatud asukohad:",
"Introduced By": "Seda on kokku viinud",
"Introducer": "Kokkuviija",
"Introduction": "Sissejuhatus",
"Inversion of the given condition (i.e. do not exclude)": "Antud tingimuse pöördtingimus (ehk ära välista)",
"Keep Versions": "Säilita Versioone",
"LDAP": "LDAP",
"Largest First": "Esmalt suuremad",
@@ -182,6 +205,9 @@
"Learn more at {%url%}": "Lisateavet leiad siit: {{url}}",
"Limit": "Piirang",
"Limit Bandwidth in LAN": "Piira ribalaiust kohtvõrgus",
"Listener Failures": "Kuulaja vead",
"Listener Status": "Kuulaja olek",
"Listeners": "Kuulajad",
"Loading data...": "Laadin andmeid…",
"Loading...": "Laadin...",
"Local Additions": "Kohalikud lisandused",
@@ -252,12 +278,15 @@
"QR code": "QR-kood",
"QUIC LAN": "QUIC LAN",
"QUIC WAN": "QUIC WAN",
"Quick guide to supported patterns": "Toetatud mustrite kiirjuhend",
"Random": "Juhuslik",
"Receive Encrypted": "Vastuvõtmine krüptituna",
"Receive Only": "Ainult vastuvõtmine",
"Received data is already encrypted": "Vastuvõetud andmed on juba krüptitud",
"Recent Changes": "Hiljutised muudatused",
"Reduced by ignore patterns": "Hiljutised eiramismustrid",
"Relay LAN": "Edasta kohtvõrgus",
"Relay WAN": "Edasta laivõrgus",
"Release Notes": "Muudatuste logi",
"Remote Devices": "Kaugseadmed",
"Remote GUI": "Kaugseadme graafiline kasutajaliides",
@@ -289,11 +318,13 @@
"Select latest version": "Vali viimane versioon",
"Select oldest version": "Vali vanim versioon",
"Send & Receive": "Saatmine ja vastuvõtmine",
"Send Extended Attributes": "Saada täiendavaid andmeid",
"Send Only": "Ainult saatmine",
"Send Ownership": "Edasta omand",
"Set Ignores on Added Folder": "Märgista lisatud kausta eiramised",
"Settings": "Seadistused",
"Share": "Jaga",
"Share Folder": "Jaga Kausta",
"Share Folder": "Jaga kausta",
"Share by Email": "Jaga e-kirjaga",
"Share by SMS": "Jaga SMS-iga",
"Share this folder?": "Kas tahad jagada seda kausta?",
@@ -302,16 +333,24 @@
"Sharing": "Jagamine",
"Show ID": "Kuva ID",
"Show QR": "Kuva QR",
"Show detailed discovery status": "Näita avastamise üksikasjalikku olekut",
"Show detailed listener status": "Näita kuulaja üksikasjalikku olekut",
"Show diff with previous version": "Näita erinevust eelmise versiooniga",
"Shut Down": "Lülita välja",
"Shutdown Complete": "Väljalülitamine on lõppenud",
"Simple": "Lihtne",
"Simple File Versioning": "Lihtne Faili Versioonindus",
"Simple File Versioning": "Failide lihtne versioonihaldus",
"Single level wildcard (matches within a directory only)": "Ühetasandiline metamärk (vastavust otsitakse vaid kausta sees)",
"Size": "Suurus",
"Smallest First": "Esmalt väiksemad",
"Some items could not be restored:": "Mõne objekti taastamine polnud võimalik:",
"Some listening addresses could not be enabled to accept connections:": "Ühenduse loomiseks polnud võimalik mõnda kuulamisaadressi kinnitada:",
"Source Code": "Lähtekood",
"Stable releases only": "Ainult stabiilsed väljalasked",
"Stable releases and release candidates": "Stabiilsed versioonid ja kandidaatversioonid",
"Stable releases are delayed by about two weeks. During this time they go through testing as release candidates.": "Stabiilsete versioonide viiteaeg on kaks nädalat. Selle aja kestel neid testitakse kui kandidaatversioone.",
"Stable releases only": "Ainult stabiilsed versioonid",
"Staggered": "Järkjärguline",
"Staggered File Versioning": "Järkjärguline failide versioonihaldus",
"Start Browser": "Käivita Brauser",
"Statistics": "Statistika",
"Stay logged in": "Jää sisselogituks",
@@ -320,18 +359,27 @@
"Subject:": "Teema:",
"Support": "Abi",
"Support Bundle": "Veaotsingu tugipakett",
"Sync Extended Attributes": "Sünkrooni täiendavaid atribuute/andmeid",
"Sync Ownership": "Sünkrooni omand",
"Sync Protocol Listen Addresses": "Sünkroonimisprotokolli kuulamisaadress",
"Sync Status": "Sünkroonimise olek",
"Syncing": "Sünkroniseerimine",
"Syncthing device ID for \"{%devicename%}\"": "Syncthingi seadmetunnus „{{devicename}}“ seadme jaoks",
"Syncthing has been shut down.": "Syncthing on seisatud.",
"Syncthing includes the following software or portions thereof:": "Syncthingis leidub järgnevat tarkvara või nende alamkomponenete:",
"Syncthing is restarting.": "Syncthing taaskäivitub.",
"Syncthing is saving changes.": "Syncthing salvestab muudatusi.",
"Syncthing is upgrading.": "Syncthing uueneb.",
"Syncthing now supports automatically reporting crashes to the developers. This feature is enabled by default.": "Syncthing oskab nüüd kokkujooksmistest arendajatele automaatselt teada anda. See funktsionaalsus on vaikimisi lülitatud sisse.",
"Syncthing seems to be down, or there is a problem with your Internet connection. Retrying…": "Syncthing ei tundu toimima, aga võib-olla on lihtsalt probleem internetiühendusega. Proovin uuesti…",
"Syncthing seems to be experiencing a problem processing your request. Please refresh the page or restart Syncthing if the problem persists.": "Syncthing ei suutnud sinu päringut töödelda. Probleemi püsimisel värskenda lehte või taaskäivita Syncthing.",
"TCP LAN": "Kohtvõrgu TCP",
"TCP WAN": "Laivõrgu TCP",
"Take me back": "Vii mind tagasi",
"The Syncthing Authors": "Syncthingi autorid",
"The Syncthing admin interface is configured to allow remote access without a password.": "Syncthingi haldusliides on seadistatud lubamaks ligipääsu ilma salasõnata.",
"The aggregated statistics are publicly available at the URL below.": "Koondstatistika on avalikult saadaval järgneval lehel.",
"The cleanup interval cannot be blank.": "Puhastamise välp ei saa jääda tühjaks.",
"The device ID cannot be blank.": "Seadme tunnus ei tohi olla tühi.",
"The encrypted usage report is sent daily. It is used to track common platforms, folder sizes, and app versions. If the reported data set is changed you will be prompted with this dialog again.": "Krüptitud kasutusaruanne saadetakse kord päevas. Sellega peetakse arvet kasutatavate platvormide, rakenduste versioonide ja kaustade suuruste üle. Kui varemteatatud andmekogu muutub, siis sa näed seda vaadet uuesti.",
"The entered device ID does not look valid. It should be a 52 or 56 character string consisting of letters and numbers, with spaces and dashes being optional.": "Seadme tunnus ei tundu olema korrektne. Ta peaks olema 52 või 56 märki pikk, kus leidub vaid numbreid ja tähemärke ning võib olla ka tühikuid ja kriipse.",
@@ -339,6 +387,9 @@
"The folder ID must be unique.": "Kausta ID peab olema unikaalne.",
"The folder path cannot be blank.": "Kausta asukoht ei tohi olla tühi.",
"The following items could not be synchronized.": "Järgnevaid üksusi ei õnnestunud sünkroniseerida.",
"The following items were changed locally.": "Järgnevad objektid on muudetud kohalikus seadmes.",
"The following unexpected items were found.": "Leidus järgmisi ootamatuid objekte.",
"The interval must be a positive number of seconds.": "Välp peab olema positiivne täisarv sekundites.",
"The maximum age must be a number and cannot be blank.": "Maksimaalne vanus peab olema arv ning ei tohi olla tühi.",
"The number of connections must be a non-negative number.": "Ühenduste arv peab olema nullist suurem number.",
"The number of days must be a number and cannot be blank.": "Päevade arv peab olema number ega tohi jääda tühjaks.",
@@ -346,6 +397,9 @@
"The number of old versions to keep, per file.": "Alleshoitavate faili vanade versioonide arv.",
"The number of versions must be a number and cannot be blank.": "Versioonide arv peab olema number ega tohi jääda tühjaks.",
"The path cannot be blank.": "Asukoht ei saa jääda tühjaks.",
"The rate limit must be a non-negative number (0: no limit)": "Kiiruspiirang peab olema positiivne täisarv (0 tähendab piirangu puudumist)",
"The remote device has not accepted sharing this folder.": "Kaugseade pole selle kausta sünkroonimisega nõustunud.",
"The remote device has paused this folder.": "Kaugseade on peatanud selle kausta sünkroonimise.",
"There are no devices to share this folder with.": "Pole ühtegi seadet, millega saaks seda kausta jagada.",
"There are no file versions to restore.": "Pole ühtegi taastatavat failiversiooni.",
"There are no folders to share with this device.": "Pole ühtegi kausta, mida selle seadmega jagada.",
@@ -356,10 +410,14 @@
"Time the item was last modified": "Aeg, millal objekti viimati muudeti",
"Today": "Täna",
"Trash Can": "Prügikast",
"Trash Can File Versioning": "Prügikastifailide versioonihaldus",
"Type": "Tüüp",
"UNIX Permissions": "UNIX-i õigused",
"Unavailable": "Pole saadaval",
"Unavailable/Disabled by administrator or maintainer": "Pole saadaval või haldaja/peakasutaja poolt keelatud",
"Undecided (will prompt)": "Määratlemata (kuulub üleküsimisele)",
"Unexpected Items": "Ootamatud ja plaanivälised objektid",
"Unexpected items have been found in this folder.": "Selles kaustas leidus ootamatuid objekte.",
"Unignore": "Lõpeta eiramine",
"Unknown": "Teadmata",
"Unshared": "Pole jagatud",
@@ -373,22 +431,45 @@
"Upgrading": "Uuendan",
"Upload Rate": "Üleslaadimise kiirus",
"Uptime": "Kasulik tööaeg",
"Usage reporting is always enabled for candidate releases.": "Kandidaatversioonide puhul on kasutusaruannete saatmine alati sisse lülitatud.",
"Use HTTPS for GUI": "Kasuta HTTPS'i GUI jaoks",
"Use notifications from the filesystem to detect changed items.": "Muudetud objektide tuvastamiseks kasuta operatsioonisüsteemi teavitusi.",
"User": "Kasutaja",
"User Home": "Kasutaja kodukaust",
"Username/Password has not been set for the GUI authentication. Please consider setting it up.": "Kasutajaliidese autentimiseks pole lisatud kasutajanime ja salasõna. Me soovitame, et kindlasti teed seda.",
"Using a QUIC connection over LAN": "Kasutades QUIC-ühendust kohtvõrgus",
"Using a QUIC connection over WAN": "Kasutades QUIC-ühendust laivõrgus",
"Using a direct TCP connection over LAN": "Kasutades TCP otseühendust kohtvõrgus",
"Using a direct TCP connection over WAN": "Kasutades TCP otseühendust kaugvõrgus",
"Version": "Versioon",
"Versions": "Versioonid",
"Versions Path": "Versioonide asukoht",
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Varasemad versioonid kustutatakse automaatselt, kui nende vanus ületab suurima lubatud vanuse või nende arv ületab antud ajavahemikule lubatud arvu.",
"Waiting to Clean": "Ootan tühjendamist",
"Waiting to Scan": "Ootan skaneerimist",
"Waiting to Sync": "Ootan sünkroonimist",
"Warning": "Hoiatus",
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Hoiatus: see asukoht on olemasoleva „{{otherFolder}}“ kausta ülemkaust.",
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Hoiatus: see asukoht on olemasoleva „{{otherFolderLabel}}“ ({{otherFolder}}) kausta ülemkaust.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Hoiatus: see asukoht on olemasoleva „{{otherFolder}}“ kausta alamkaust.",
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Hoiatus: see asukoht on olemasoleva „{{otherFolderLabel}}“ ({{otherFolder}}) kausta alamkaust.",
"Warning: If you are using an external watcher like {%syncthingInotify%}, you should make sure it is deactivated.": "Hoiatus: kui kasutad välist muudatuste jälgijat, nagu {{syncthingInotify}}, siis palun kontrolli, et ta on lülitatud välja.",
"Watch for Changes": "Jälgi muudatusi",
"Watching for Changes": "Jälgin muudatusi",
"Watching for changes discovers most changes without periodic scanning.": "Muudatuste jälgimisega on võimalik tuvastada enamus muudatusi ilma perioodilise skaneerimiseta.",
"Yes": "Jah",
"Yesterday": "Eile",
"You can also copy and paste the text into a new message manually.": "Sa võid ka kopeerida ja asetada teksti uude sõnumisse käsitsi.",
"You can also select one of these nearby devices:": "Sa võid ka valida mõne läheduses asuvatest seadmetest:",
"You can change your choice at any time in the Settings dialog.": "Sa võid oma valikut alati muuta vaatest „Seadistused“.",
"You can read more about the two release channels at the link below.": "Mainitud versioonitüübi kohta saad lähemalt lugeda klikates järgnevat linki.",
"You have no ignored devices.": "Sul pole ühtegi eiratud seadet.",
"You have no ignored folders.": "Sul pole ühtegi eiratud kausta.",
"You have unsaved changes. Do you really want to discard them?": "Sul on salvestamata muudatusi. Kas sa kindlasti tahad neist loobuda?",
"You must keep at least one version.": "Sa pead alles hoidma vähemalt ühe versiooni.",
"You should never add or change anything locally in a \"{%receiveEncrypted%}\" folder.": "Sa ei peaks kunagi kohalikult lisama kausta „{{receiveEncrypted}}“ ega seal midagi muutma.",
"Your SMS app should open to let you choose the recipient and send it from your own number.": "Nüüd peaks avanema sinu SMS-rakendus, saad valida adressaadi ja talle saata info oma nime alt.",
"Your email app should open to let you choose the recipient and send it from your own address.": "Nüüd peaks avanema sinu e-posti rakendus, saad valida adressaadi ja talle saata info oma nime alt.",
"days": "päeva",
"deleted": "kustutatud",
"deny": "keela",
@@ -411,5 +492,6 @@
},
"unknown device": "tundmatu seade",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} soovib jagada kausta \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} soovib jagada kausta \"{{folderlabel}}\" ({{folder}})."
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} soovib jagada kausta \"{{folderlabel}}\" ({{folder}}).",
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} võib selle seadme uuesti kokku viia."
}

View File

@@ -122,7 +122,7 @@
"Discovery Failures": "Échecs de découverte",
"Discovery Status": "État de la découverte",
"Dismiss": "Écarter",
"Do not add it to the ignore list, so this notification may recur.": "Attendre la disparition de cette demande : évite l'ajout immédiat à la liste noire persistante.",
"Do not add it to the ignore list, so this notification may recur.": "Attendre l'expiration de cette demande : évite l'ajout immédiat à la liste noire persistante.",
"Do not restore": "Ne pas restaurer",
"Do not restore all": "Ne pas tout restaurer",
"Do you want to enable watching for changes for all your folders?": "Voulez-vous activer la surveillance des changements sur tous vos partages ?",
@@ -278,7 +278,7 @@
"OK": "OK",
"Off": "Désactivée",
"Oldest First": "Les plus anciens en premier",
"Optional descriptive label for the folder. Can be different on each device.": "Nom local, descriptif et optionnel du partage. Il peut être différent sur chaque appareil.",
"Optional descriptive label for the folder. Can be different on each device.": "Nom local, convivial et optionnel du partage, à votre guise. Il peut être différent sur chaque appareil. Par notification initiale, il sera proposé tel quel aux nouveaux participants.\nAstuce : comme il est modifiable ultérieurement, soyez prévenant en indiquant un nom éventuellement plus parlant pour les invités, puis renommez-le pour votre propre usage quand ils l'auront accepté (exemple d'un partage à deux membres où l'initiateur commence par donner son propre nom au partage pour l'invitation, puis le renomme plus tard au nom du partenaire quand celui-ci l'a accepté - Pensez, pour vous, au chemin ~définitif~ du répertoire, modifiable dans \"Chemin racine...\" ci-dessous à la création). Évitez les erreurs d'orthographe dans le nom car il servira aussi suffixe au chemin proposé en création (local et distant) et ce chemin, côté local, n'est modifiable à posteriori que dans la configuration avancée.",
"Options": "Options",
"Out of Sync": "Désynchronisé",
"Out of Sync Items": "Éléments non synchronisés",
@@ -288,7 +288,7 @@
"Ownership": "Propriétaire",
"Password": "Mot de passe",
"Path": "Chemin",
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Chemin local du partage. Est créé s'il n'existe pas. Le caractère tilde (~) peut être utilisé comme un raccourci pour",
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "En création/acceptation manuelle d'un nouveau partage, c'est le chemin complet du répertoire à partager dans l'appareil local. Il peut être différent du \"Nom du partage\" (voir l'astuce dans l'aide correspondante ci-dessus). Il sera créé s'il n'existe pas. Vous pouvez entrer un chemin absolu (p.ex \"/home/moi/Sync/Exemple\") ou relatif à celui du programme (p.ex \"..\\Partages\\Exemple\" - utile pour installation portable).\n\nDans l'écran de définition des valeurs par défaut \"Préférences pour les créations\"/\"Pour les nouveaux partages\" (menu Actions/Configuration), ce champ indique le chemin de base dans lequel seront créés les répertoires racine des partages que vous créez ou auxquels vous acceptez de participer. Le caractère tilde (~) est un raccourci pour votre répertoire personnel",
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Chemin où les versions seront conservées (laisser vide pour utiliser le dossier par défaut .stversions dans le partage).",
"Paths": "Chemins",
"Pause": "Pause",
@@ -365,14 +365,14 @@
"Share this folder?": "Acceptez-vous ce partage ?",
"Shared Folders": "Partages",
"Shared With": "Participant(s)",
"Sharing": "Partages",
"Sharing": "Liaisons",
"Show ID": "Afficher mon ID",
"Show QR": "Afficher le QR",
"Show detailed discovery status": "Afficher l'état détaillé de découverte",
"Show detailed listener status": "Afficher l'état détaillé de l'écouteur",
"Show diff with previous version": "Afficher les différences avec la version précédente",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Affiché à la place de l'ID de l'appareil dans l'état du groupe. Sera diffusé aux autres appareils comme nom convivial optionnel par défaut.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Nom convivial local affiché à la place de l'ID de l'appareil dans la plupart des écrans. Si laissé vide, c'est le nom convivial local de l'appareil distant qui sera utilisé. (Modifiable ultérieurement).",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Nom convivial local affiché à la place de l'ID de l'appareil dans la plupart des écrans. Si laissé vide, c'est le nom convivial local de l'appareil distant qui sera utilisé dès que celui-ci sera en ligne. (Modifiable ultérieurement).",
"Shut Down": "Arrêter",
"Shutdown Complete": "Arrêt complet",
"Simple": "Suivi simplifié",

View File

@@ -99,6 +99,7 @@
"Device ID": "ID Perangkat",
"Device Identification": "Identifikasi Perangkat",
"Device Name": "Nama Perangkat",
"Device Status": "Status Perangkat",
"Device is untrusted, enter encryption password": "Perangkat tidak dipercaya, masukkan sandi enkripsi",
"Device rate limits": "Batas kecepatan perangkat",
"Device that last modified the item": "Perangkat yang terakhir mengubah berkas",
@@ -136,6 +137,8 @@
"Enable NAT traversal": "Aktifkan Traversal NAT",
"Enable Relaying": "Aktifkan Relay",
"Enabled": "Aktif",
"Enables sending extended attributes to other devices, and applying incoming extended attributes. May require running with elevated privileges.": "Mengaktifkan pengiriman atribut tambahan ke perangkat lain, serta penerapan atribut tambahan yang diterima. Mungkin memerlukan eksekusi dengan hak akses tingkat tinggi.",
"Enables sending extended attributes to other devices, but not applying incoming extended attributes. This can have a significant performance impact. Always enabled when \"Sync Extended Attributes\" is enabled.": "Mengaktifkan pengiriman atribut tambahan ke perangkat lain, tetapi tidak menerapkan atribut tambahan yang diterima. Hal ini dapat berdampak signifikan terhadap kinerja. Selalu aktif ketika “Sync Extended Attributes” diaktifkan.",
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Masukkan nomor yang bukan negatif (contoh \"2.35\") dan pilih sebuah unit. Persentase adalah bagian dari total ukuran penyimpanan.",
"Enter a non-privileged port number (1024 - 65535).": "Masukkan nomor port yang tidak \"istimewa\" (1024 - 65535).",
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Masukkan alamat yang dipisah oleh koma (\"tcp://ip:port\", \"tcp://host:port\") atau \"dynamic\" untuk melakukan penemuan alamat secara otomatis.",

View File

@@ -26,6 +26,7 @@
"Allow Anonymous Usage Reporting?": "Siųsti anoniminę naudojimo ataskaitą?",
"Allowed Networks": "Leidžiami tinklai",
"Alphabetic": "Abėcėlės tvarka",
"Always turned on when the folder type is \"{%foldertype%}\".": "Visada įjungta, kai aplanko tipas yra „{{foldertype}}“.",
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Išorinė komanda apdoroja versijų valdymą. Ji turi pašalinti failą iš bendrinamo aplanko. Jei kelyje į programą yra tarpų, jie turėtų būti imami į kabutes.",
"Anonymous Usage Reporting": "Anoniminė naudojimo ataskaita",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Anoniminės naudojimo ataskaitos formatas pasikeitė. Ar norėtumėte pereiti prie naujojo formato?",
@@ -51,8 +52,10 @@
"Body:": "Turinys:",
"Bugs": "Klaidos",
"Cancel": "Atsisakyti",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Negali būti įjungta, kai aplanko tipas yra „{{foldertype}}“.",
"Changelog": "Keitinių žurnalas",
"Clean out after": "Išvalyti po",
"Cleaning Versions": "Versijų išvalymas",
"Cleanup Interval": "Išvalymo intervalas",
"Click to see full identification string and QR code.": "Spustelėkite, kad pamatytumėte visą identifikavimo eilutę ir QR kodą.",
"Close": "Užverti",
@@ -78,10 +81,13 @@
"Custom Range": "Tinkintas rėžis",
"Danger!": "Pavojus!",
"Database Location": "Duomenų bazės vieta",
"Debug": "Derinimas",
"Debugging Facilities": "Derinimo priemonės",
"Default": "Numatytasis",
"Default Configuration": "Numatytoji konfigūracija",
"Default Device": "Numatytasis įrenginys",
"Default Folder": "Numatytasis aplankas",
"Default Ignore Patterns": "Numatytieji nepaisymo šablonai",
"Defaults": "Numatytosios reikšmės",
"Delete": "Ištrinti",
"Delete Unexpected Items": "Ištrinti netikėtus elementus",
@@ -195,6 +201,8 @@
"Incoming Rate Limit (KiB/s)": "Atsiunčiamo srauto maksimalus greitis (KiB/s)",
"Incorrect configuration may damage your folder contents and render Syncthing inoperable.": "Neteisinga konfigūracija gali pažeisti jūsų aplankų turinį ir padaryti Syncthing neoperuotina.",
"Incorrect user name or password.": "Neteisingas naudotojo vardas ar slaptažodis.",
"Info": "Informacija",
"Internally used paths:": "Viduje naudojami keliai:",
"Introduced By": "Supažindė",
"Introducer": "Supažindintojas",
"Inversion of the given condition (i.e. do not exclude)": "Apversti sąlygas (pvz.: nenustoti naudoti)",
@@ -207,6 +215,8 @@
"Learn more": "Sužinoti daugiau",
"Learn more at {%url%}": "Sužinokite daugiau, adresu {{url}}",
"Limit": "Apribojimas",
"Listener Failures": "Klausytojo nesėkmės",
"Listener Status": "Klausytojo būsena",
"Listeners": "Klausytojai",
"Loading data...": "Įkeliami duomenys...",
"Loading...": "Įkeliama...",
@@ -334,6 +344,7 @@
"Show ID": "Rodyti ID",
"Show QR": "Rodyti QR",
"Show detailed discovery status": "Rodyti išsamią atradimo būseną",
"Show detailed listener status": "Rodyti išsamią klausytojo būseną",
"Show diff with previous version": "Rodyti skirtumus su ankstesne versija",
"Shown instead of Device ID in the cluster status. Will be advertised to other devices as an optional default name.": "Grupės būsenoje rodomas vietoje įrenginio vardo. Kiti įrenginiai matys kaip pasirinktinį vardą.",
"Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.": "Grupės būsenoje rodomas vietoje įrenginio vardo. Bus atnaujintas į įrenginio vardą jei nieko neįrašysite.",

View File

@@ -354,7 +354,7 @@
<div class="form-group">
<label>
<input type="checkbox" ng-model="login.stayLoggedIn" >&nbsp;<span translate>Stay logged in</span>
<input type="checkbox" id="stayLoggedIn" name="stayLoggedIn" ng-model="login.stayLoggedIn" >&nbsp;<span translate>Stay logged in</span>
</label>
</div>

View File

@@ -30,7 +30,7 @@
<h4 class="text-center" translate>The Syncthing Authors</h4>
<div class="row">
<div class="col-md-12" id="contributor-list">
Jakob Borg, Audrius Butkevicius, Simon Frei, Tomasz Wilczyński, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, bt90, Caleb Callaway, Daniel Harte, Emil Lundberg, Eric P, Evgeny Kuznetsov, greatroar, Lars K.W. Gohlke, Lode Hoste, Marcus B Spencer, Michael Ploujnikov, Ross Smith II, Stefan Tatschner, Tommy van der Vorst, Wulf Weich, Adam Piggott, Adel Qalieh, Aleksey Vasenev, Alessandro G., Alex Ionescu, Alex Lindeman, Alex Xu, Alexander Seiler, Alexandre Alves, Aman Gupta, Andreas Sommer, andresvia, Andrew Rabert, Andrey D, andyleap, Anjan Momi, Anthony Goeckner, Antoine Lamielle, Anur, Aranjedeath, ardevd, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Ashish Bhate, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Beat Reichenbach, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benno Fünfstück, Benny Ng, boomsquared, Boqin Qin, Boris Rybalkin, Brendan Long, Catfriend1, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Christian Kujau, Christian Prescott, chucic, cjc7373, Colin Kennedy, Cromefire_, Cyprien Devillez, d-volution, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Daniel Padrta, Daniil Gentili, Darshil Chanpura, dashangcun, David Rimmer, DeflateAwning, Denis A., Dennis Wilson, derekriemer, DerRockWolf, desbma, Devon G. Redekopp, digital, Dimitri Papadopoulos Orfanos, Dmitry Saveliev, domain, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eng Zer Jun, entity0xfe, Eric Lesiuta, Erik Meitner, Evan Spensley, Federico Castagnini, Felix, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, georgespatton, ghjklw, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, guangwu, gudvinr, Gusted, Han Boetes, HansK-p, Harrison Jones, Hazem Krimi, Heiko Zuerker, Hireworks, Hugo Locurcio, Iain Barnett, Ian Johnson, ignacy123, Iskander Sharipov, Jaakko Hannikainen, Jack Croft, Jacob, Jake Peterson, James O'Beirne, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaspitta, Jaya Chithra, Jaya Kumar, Jeffery To, jelle van der Waa, Jens Diemer, Jochen Voss, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jose Manuel Delicado, jtagcat, Julian Lehrhuber, Jörg Thalheim, Jędrzej Kula, Kapil Sareen, Karol Różycki, Kebin Liu, Keith Harrison, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., klemens, Kurt Fitzner, kylosus, Lars Lehtonen, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, LSmithx2, Lukas Lihotzki, Luke Hamburg, luzpaz, Majed Abdulaziz, Marc Laporte, Marcel Meyer, Marcin Dziadus, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Mateusz Naściszewski, Mateusz Ż, mathias4833, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maximilian, Michael Jephcote, Michael Rienstra, MichaIng, Migelo, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, mv1005, Nate Morrison, nf, Nicholas Rishel, Nick Busey, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, orangekame3, otbutz, overkill, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Paul Donald, Pawel Palenica, perewa, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Philippe Schommers, Phill Luby, Piotr Bejda, polyfloyd, pullmerge, Quentin Hibon, Rahmi Pruitt, red_led, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, rubenbe, Ruslan Yevdokymov, Ryan Qian, Ryan Sullivan, Sacheendra Talluri, Scott Klupfel, sec65, Sergey Mishin, Sertonix, Severin von Wnuck-Lipinski, Shaarad Dalvi, Simon Mwepu, Simon Pickup, Sly_tom_cat, Sonu Kumar Saw, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Sven Bachmann, Sébastien WENSKE, Taylor Khan, Terrance, TheCreeper, Thomas, Thomas Hipp, Tim Abell, Tim Howes, Tobias Frölich, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, vapatel2, Veeti Paananen, Victor Buinsky, Vik, Vil Brekin, villekalliomaki, Vladimir Rusinov, wangguoliang, WangXi, Will Rouesnel, William A. Kennington III, wouter bolsterlee, xarx00, Xavier O., xjtdy888, Yannic A., yparitcher, 佛跳墙, 落心
Jakob Borg, Audrius Butkevicius, Simon Frei, Tomasz Wilczyński, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, bt90, Caleb Callaway, Daniel Harte, Emil Lundberg, Eric P, Evgeny Kuznetsov, greatroar, Lars K.W. Gohlke, Lode Hoste, Marcus B Spencer, Michael Ploujnikov, Ross Smith II, Stefan Tatschner, Tommy van der Vorst, Wulf Weich, Adam Piggott, Adel Qalieh, Aleksey Vasenev, Alessandro G., Alex Ionescu, Alex Lindeman, Alex Xu, Alexander Seiler, Alexandre Alves, Aman Gupta, Andreas Sommer, andresvia, Andrew Rabert, Andrey D, andyleap, Anjan Momi, Anthony Goeckner, Antoine Lamielle, Anur, Aranjedeath, ardevd, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Ashish Bhate, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Beat Reichenbach, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benno Fünfstück, Benny Ng, boomsquared, Boqin Qin, Boris Rybalkin, Brendan Long, Catfriend1, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Christian Kujau, Christian Prescott, chucic, cjc7373, Colin Kennedy, Cromefire_, Cyprien Devillez, d-volution, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Daniel Padrta, Daniil Gentili, Darshil Chanpura, dashangcun, David Rimmer, DeflateAwning, Denis A., Dennis Wilson, derekriemer, DerRockWolf, desbma, Devon G. Redekopp, digital, Dimitri Papadopoulos Orfanos, Dmitry Saveliev, domain, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eng Zer Jun, entity0xfe, Epifeny, epifeny, Eric Lesiuta, Erik Meitner, Evan Spensley, Federico Castagnini, Felix, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, georgespatton, ghjklw, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, guangwu, gudvinr, Gusted, Han Boetes, HansK-p, Harrison Jones, Hazem Krimi, Heiko Zuerker, Hireworks, Hugo Locurcio, Iain Barnett, Ian Johnson, ignacy123, Iskander Sharipov, Jaakko Hannikainen, Jack Croft, Jacob, Jake Peterson, James O'Beirne, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaspitta, Jaya Chithra, Jaya Kumar, Jeffery To, jelle van der Waa, Jens Diemer, Jochen Voss, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jose Manuel Delicado, jtagcat, Julian Lehrhuber, Jörg Thalheim, Jędrzej Kula, Kapil Sareen, Karol Różycki, Kebin Liu, Keith Harrison, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., klemens, Kurt Fitzner, kylosus, Lars Lehtonen, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, LSmithx2, Lukas Lihotzki, Luke Hamburg, luzpaz, Majed Abdulaziz, Marc Laporte, Marcel Meyer, Marcin Dziadus, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Mateusz Naściszewski, Mateusz Ż, mathias4833, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maximilian, Maxwell G, Michael Jephcote, Michael Rienstra, Michael Wang 汪東陽, MichaIng, Migelo, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, mv1005, Nate Morrison, nf, Nicholas Rishel, Nick Busey, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, orangekame3, otbutz, overkill, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Paul Donald, Pawel Palenica, perewa, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Philippe Schommers, Phill Luby, Piotr Bejda, polyfloyd, Prathik P Kulkarni, pullmerge, Quentin Hibon, Rahmi Pruitt, red_led, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, rubenbe, Ruslan Yevdokymov, Ryan Qian, Ryan Sullivan, Sacheendra Talluri, Scott Klupfel, sec65, Sergey Mishin, Sertonix, Severin von Wnuck-Lipinski, Shaarad Dalvi, Shivam Kumar, Simon Mwepu, Simon Pickup, Sly_tom_cat, Sonu Kumar Saw, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Sven Bachmann, Sébastien WENSKE, Tao, Taylor Khan, Terrance, TheCreeper, Thomas, Thomas Hipp, Tim Abell, Tim Howes, Tobias Frölich, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, Val Markovic, vapatel2, Veeti Paananen, Victor Buinsky, Vik, Vil Brekin, villekalliomaki, Vladimir Rusinov, wangguoliang, WangXi, Will Rouesnel, William A. Kennington III, wouter bolsterlee, xarx00, Xavier O., xjtdy888, Yannic A., yparitcher, 佛跳墙, 落心
</div>
</div>
</div>
@@ -62,7 +62,7 @@ Jakob Borg, Audrius Butkevicius, Simon Frei, Tomasz Wilczyński, Alexander Graf,
<li><a href="https://github.com/davecgh/go-spew">davecgh/go-spew</a>, Copyright &copy; 2012-2016 Dave Collins.</li>
<li><a href="https://github.com/go-asn1-ber/asn1-ber">go-asn1-ber/asn1-ber</a>, Copyright &copy; 2011-2015 Michael Mitton (mmitton@gmail.com).</li>
<li><a href="https://github.com/go-ldap/ldap">go-ldap/ldap</a>, Copyright &copy; 2011-2015 Michael Mitton (mmitton@gmail.com).</li>
<li><a href="https://github.com/uber-go/automaxprocs">go.uber.org/automaxprocs</a>, Copyright &copy; 2017 Uber Technologies, Inc.</li>
<li><a href="https://go.yaml.in/yaml/v2">go.yaml.in/yaml/v2</a>, Copyright &copy; 2026, the go.yaml.in/yaml/v2 authors.</li>
<li><a href="https://github.com/gobwas/glob">gobwas/glob</a>, Copyright &copy; 2016 Sergey Kamardin.</li>
<li><a href="https://github.com/gofrs/flock">gofrs/flock</a>, Copyright &copy; 2018-2025, The Gofrs.</li>
<li><a href="https://github.com/golang/snappy">golang/snappy</a>, Copyright &copy; 2011 The Snappy-Go Authors.</li>

View File

@@ -73,6 +73,7 @@ angular.module('syncthing.core')
CLUSTER_CONFIG_RECEIVED: 'ClusterConfigReceived', // Emitted when receiving a remote device's cluster config
DOWNLOAD_PROGRESS: 'DownloadProgress', // Emitted during file downloads for each folder for each file
FAILURE: 'Failure', // Specific errors sent to the usage reporting server for diagnosis
UPGRADE_RESTART_SCHEDULED: 'UpgradeRestartScheduled', // Auto-upgrade completed, restart scheduled
FOLDER_COMPLETION: 'FolderCompletion', //Emitted when the local or remote contents for a folder changes
FOLDER_REJECTED: 'FolderRejected', // DEPRECATED: Emitted when a device sends index information for a folder we do not have, or have but do not share with the device in question
PENDING_FOLDERS_CHANGED: 'PendingFoldersChanged', // Emitted when pending folders were added / updated (offered by some device, but not shared to them) or removed (folder ignored or added or no longer offered from the remote device)

View File

@@ -11,6 +11,8 @@ angular.module('syncthing.core')
var navigatingAway = false;
var online = false;
var restarting = false;
var restartExpectedFrom = 0;
var restartExpectedUntil = 0;
function initController() {
LocaleService.autoConfigLocale();
@@ -33,6 +35,34 @@ angular.module('syncthing.core')
Events.start();
}
function clearRestartExpectation() {
restartExpectedFrom = 0;
restartExpectedUntil = 0;
}
function setRestartExpectation(delayS) {
var delay = delayS > 0 ? delayS : 60;
var delayMs = delay * 1000;
var earlyMs = 5 * 1000;
var graceMs = 60 * 1000;
var now = Date.now();
restartExpectedFrom = now + Math.max(0, delayMs - earlyMs);
restartExpectedUntil = now + delayMs + graceMs;
}
function restartExpectedNow() {
if (!restartExpectedUntil) {
return false;
}
var now = Date.now();
if (now > restartExpectedUntil) {
clearRestartExpectation();
return false;
}
return now >= restartExpectedFrom;
}
// public/scope definitions
// window.metadata is set in /meta.js which requires authentication
@@ -204,6 +234,7 @@ angular.module('syncthing.core')
online = true;
restarting = false;
clearRestartExpectation();
hideModal('#networkError');
hideModal('#restarting');
hideModal('#shutdown');
@@ -218,10 +249,26 @@ angular.module('syncthing.core')
console.log('UIOffline');
online = false;
if (!restarting) {
showModal('#networkError');
if (restartExpectedNow()) {
restarting = true;
showModal('#restarting');
} else {
showModal('#networkError');
}
}
});
$scope.$on(Events.UPGRADE_RESTART_SCHEDULED, function (_event, arg) {
var delayS = 0;
if (arg && arg.data && arg.data.delayS !== undefined) {
delayS = parseInt(arg.data.delayS, 10);
if (isNaN(delayS) || delayS < 0) {
delayS = 0;
}
}
setRestartExpectation(delayS);
});
$scope.$on('HTTPError', function (event, arg) {
// Emitted when a HTTP call fails. We use the status code to try
// to figure out what's wrong.

View File

@@ -0,0 +1,9 @@
License - https://forkaweso.me/Fork-Awesome/license
Copyright 2018 Dave Gandy & Fork Awesome
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1 @@
The Fork Awesome font is licensed under the SIL OFL 1.1 (http://scripts.sil.org/OFL). Fork Awesome is a fork based of off Font Awesome 4.7.0 by Dave Gandy. More info on licenses at https://forkawesome.github.io

View File

@@ -1,80 +0,0 @@
// Copyright (C) 2025 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package azureblob
import (
"context"
"io"
"time"
stblob "github.com/syncthing/syncthing/internal/blob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blockblob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
)
var _ stblob.Store = (*BlobStore)(nil)
type BlobStore struct {
client *azblob.Client
container string
}
func NewBlobStore(accountName, accountKey, containerName string) (*BlobStore, error) {
credential, err := azblob.NewSharedKeyCredential(accountName, accountKey)
if err != nil {
return nil, err
}
url := "https://" + accountName + ".blob.core.windows.net/"
sc, err := azblob.NewClientWithSharedKeyCredential(url, credential, &azblob.ClientOptions{})
if err != nil {
return nil, err
}
// This errors when the container already exists, which we ignore.
_, _ = sc.CreateContainer(context.Background(), containerName, &container.CreateOptions{})
return &BlobStore{
client: sc,
container: containerName,
}, nil
}
func (a *BlobStore) Upload(ctx context.Context, key string, data io.Reader) error {
_, err := a.client.UploadStream(ctx, a.container, key, data, &blockblob.UploadStreamOptions{})
return err
}
func (a *BlobStore) Download(ctx context.Context, key string, w stblob.Writer) error {
resp, err := a.client.DownloadStream(ctx, a.container, key, &blob.DownloadStreamOptions{})
if err != nil {
return err
}
defer resp.Body.Close()
_, err = io.Copy(w, resp.Body)
return err
}
func (a *BlobStore) LatestKey(ctx context.Context) (string, error) {
opts := &azblob.ListBlobsFlatOptions{}
pager := a.client.NewListBlobsFlatPager(a.container, opts)
var latest string
var lastModified time.Time
for pager.More() {
page, err := pager.NextPage(ctx)
if err != nil {
return "", err
}
for _, blob := range page.Segment.BlobItems {
if latest == "" || blob.Properties.LastModified.After(lastModified) {
latest = *blob.Name
lastModified = *blob.Properties.LastModified
}
}
}
return latest, nil
}

View File

@@ -4,7 +4,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package db // import "github.com/syncthing/syncthing/internal/db/sqlite"
package db
import (
"iter"
@@ -15,8 +15,22 @@ import (
"github.com/thejerf/suture/v4"
)
type DBService interface {
suture.Service
// Starts maintenance asynchronously, if not already running
// Returns a channel that will receive the result when the requested maintenance finishes
StartMaintenance() <-chan error
// Returns the last time database maintenance completed
// This will return time zero when database maintenance has never completed successfully.
LastMaintenanceTime() time.Time
}
type DB interface {
Service(maintenanceInterval time.Duration) suture.Service
// Create a service that performs database maintenance periodically (no
// more often than the requested interval)
Service(maintenanceInterval time.Duration) DBService
// Basics
Update(folder string, device protocol.DeviceID, fs []protocol.FileInfo) error

View File

@@ -19,7 +19,6 @@ import (
"github.com/syncthing/syncthing/internal/db"
"github.com/syncthing/syncthing/internal/slogutil"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/thejerf/suture/v4"
)
const (
@@ -32,7 +31,7 @@ const (
gcMaxRuntime = 5 * time.Minute // max time to spend on gc, per table, per run
)
func (s *DB) Service(maintenanceInterval time.Duration) suture.Service {
func (s *DB) Service(maintenanceInterval time.Duration) db.DBService {
return newService(s, maintenanceInterval)
}
@@ -40,6 +39,7 @@ type Service struct {
sdb *DB
maintenanceInterval time.Duration
internalMeta *db.Typed
start chan chan error
}
func (s *Service) String() string {
@@ -51,12 +51,21 @@ func newService(sdb *DB, maintenanceInterval time.Duration) *Service {
sdb: sdb,
maintenanceInterval: maintenanceInterval,
internalMeta: db.NewTyped(sdb, internalMetaPrefix),
start: make(chan chan error),
}
}
func (s *Service) StartMaintenance() <-chan error {
finishChan := make(chan error, 1)
select {
case s.start <- finishChan:
default:
}
return finishChan
}
func (s *Service) Serve(ctx context.Context) error {
// Run periodic maintenance
// Figure out when we last ran maintenance and schedule accordingly. If
// it was never, do it now.
lastMaint, _, _ := s.internalMeta.Time(lastMaintKey)
@@ -66,25 +75,44 @@ func (s *Service) Serve(ctx context.Context) error {
wait = time.Minute
}
slog.DebugContext(ctx, "Next periodic run due", "after", wait)
timer := time.NewTimer(wait)
if s.maintenanceInterval == 0 {
timer.Stop()
}
for {
var finishChan chan error
select {
case <-ctx.Done():
return ctx.Err()
case <-timer.C:
case finishChan = <-s.start:
}
if err := s.periodic(ctx); err != nil {
err := s.periodic(ctx)
if finishChan != nil {
finishChan <- err
}
if err != nil {
return wrap(err)
}
timer.Reset(s.maintenanceInterval)
slog.DebugContext(ctx, "Next periodic run due", "after", s.maintenanceInterval)
if s.maintenanceInterval != 0 {
timer.Reset(s.maintenanceInterval)
slog.DebugContext(ctx, "Next periodic run due", "after", s.maintenanceInterval)
}
_ = s.internalMeta.PutTime(lastMaintKey, time.Now())
}
}
func (s *Service) LastMaintenanceTime() time.Time {
lastMaint, _, _ := s.internalMeta.Time(lastMaintKey)
return lastMaint
}
func (s *Service) periodic(ctx context.Context) error {
t0 := time.Now()
slog.DebugContext(ctx, "Periodic start")

View File

@@ -863,12 +863,10 @@ func TestConcurrentUpdate(t *testing.T) {
const n = 32
res := make([]error, n)
var wg sync.WaitGroup
wg.Add(n)
for i := range n {
go func() {
wg.Go(func() {
res[i] = db.Update(folderID, protocol.DeviceID{byte(i), byte(i), byte(i)}, files)
wg.Done()
}()
})
}
wg.Wait()
for i, err := range res {

View File

@@ -203,7 +203,7 @@ func (s *service) getListener(guiCfg config.GUIConfiguration) (net.Listener, err
}
func sendJSON(w http.ResponseWriter, jsonObject interface{}) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("Content-Type", "application/json")
// Marshalling might fail, in which case we should return a 500 with the
// actual error.
bs, err := json.MarshalIndent(jsonObject, "", " ")

View File

@@ -1,15 +0,0 @@
// Copyright (C) 2024 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package automaxprocs
import (
"go.uber.org/automaxprocs/maxprocs"
)
func init() {
maxprocs.Set()
}

View File

@@ -14,6 +14,7 @@ import (
"time"
"github.com/syncthing/syncthing/internal/slogutil"
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/netutil"
)
@@ -64,6 +65,11 @@ func writeBroadcasts(ctx context.Context, inbox <-chan []byte, port int) error {
continue
}
if build.IsAndroid && intf.Flags&net.FlagPointToPoint != 0 {
// skip cellular interfaces
continue
}
addrs, err := netutil.InterfaceAddrsByInterface(&intf)
if err != nil {
l.Debugln("Failed to list interface addresses:", err)

View File

@@ -13,6 +13,7 @@ import (
"net"
"time"
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/netutil"
"golang.org/x/net/ipv6"
@@ -74,6 +75,11 @@ func writeMulticasts(ctx context.Context, inbox <-chan []byte, addr string) erro
continue
}
if build.IsAndroid && intf.Flags&net.FlagPointToPoint != 0 {
// skip cellular interfaces
continue
}
wcm.IfIndex = intf.Index
pconn.SetWriteDeadline(time.Now().Add(time.Second))
_, err = pconn.WriteTo(bs, wcm, gaddr)
@@ -129,8 +135,12 @@ func readMulticasts(ctx context.Context, outbox chan<- recv, addr string) error
pconn := ipv6.NewPacketConn(conn)
joined := 0
for _, intf := range intfs {
if intf.Flags&net.FlagMulticast == 0 {
slog.DebugContext(ctx, "Not joining multicast group on non-multicast interface", "name", intf.Name, slog.String("flags", intf.Flags.String()))
if intf.Flags&net.FlagRunning == 0 || intf.Flags&net.FlagMulticast == 0 {
continue
}
if build.IsAndroid && intf.Flags&net.FlagPointToPoint != 0 {
// skip cellular interfaces
continue
}

View File

@@ -33,7 +33,7 @@ import (
const (
OldestHandledVersion = 10
CurrentVersion = 51
CurrentVersion = 52
MaxRescanIntervalS = 365 * 24 * 60 * 60
)

View File

@@ -62,7 +62,7 @@ func TestDefaultValues(t *testing.T) {
LocalAnnMCAddr: "[ff12::8384]:21027",
MaxSendKbps: 0,
MaxRecvKbps: 0,
ReconnectIntervalS: 60,
ReconnectIntervalS: 20,
RelaysEnabled: true,
RelayReconnectIntervalM: 10,
StartBrowser: true,

View File

@@ -30,6 +30,7 @@ import (
// put the newest on top for readability.
var (
migrations = migrationSet{
{52, migrateToConfigV52},
{51, migrateToConfigV51},
{50, migrateToConfigV50},
{37, migrateToConfigV37},
@@ -101,6 +102,11 @@ func (m migration) apply(cfg *Configuration) {
cfg.Version = m.targetVersion
}
func migrateToConfigV52(cfg *Configuration) {
oldQuicInterval := max(cfg.Options.ReconnectIntervalS/3, 10)
cfg.Options.ReconnectIntervalS = min(cfg.Options.ReconnectIntervalS, oldQuicInterval)
}
func migrateToConfigV51(cfg *Configuration) {
oldDefault := 2
for i, fcfg := range cfg.Folders {

View File

@@ -34,3 +34,25 @@ func TestMigrateCrashReporting(t *testing.T) {
}
}
}
func TestMigrateReconnectInterval(t *testing.T) {
cases := []struct {
oldInterval int
expectedNewInterval int
}{
{oldInterval: 60, expectedNewInterval: 20},
{oldInterval: 120, expectedNewInterval: 40},
{oldInterval: 25, expectedNewInterval: 10},
{oldInterval: 5, expectedNewInterval: 5},
}
for i, tc := range cases {
cfg := Configuration{Version: 51, Options: OptionsConfiguration{ReconnectIntervalS: tc.oldInterval}}
migrationsMut.Lock()
migrations.apply(&cfg)
migrationsMut.Unlock()
if cfg.Options.ReconnectIntervalS != tc.expectedNewInterval {
t.Errorf("%d: unexpected result, ReconnectIntervalS: %v != %v", i, cfg.Options.ReconnectIntervalS, tc.expectedNewInterval)
}
}
}

View File

@@ -28,7 +28,7 @@ type OptionsConfiguration struct {
LocalAnnMCAddr string `json:"localAnnounceMCAddr" xml:"localAnnounceMCAddr" default:"[ff12::8384]:21027"`
MaxSendKbps int `json:"maxSendKbps" xml:"maxSendKbps"`
MaxRecvKbps int `json:"maxRecvKbps" xml:"maxRecvKbps"`
ReconnectIntervalS int `json:"reconnectionIntervalS" xml:"reconnectionIntervalS" default:"60"`
ReconnectIntervalS int `json:"reconnectionIntervalS" xml:"reconnectionIntervalS" default:"20"`
RelaysEnabled bool `json:"relaysEnabled" xml:"relaysEnabled" default:"true"`
RelayReconnectIntervalM int `json:"relayReconnectIntervalM" xml:"relayReconnectIntervalM" default:"10"`
StartBrowser bool `json:"startBrowser" xml:"startBrowser" default:"true"`

View File

@@ -1,4 +1,4 @@
<configuration version="34">
<configuration version="52">
<options>
<listenAddress>tcp://:23000</listenAddress>
<allowDelete>false</allowDelete>

View File

@@ -329,12 +329,10 @@ func (w *wrapper) replaceLocked(to Configuration) (Waiter, error) {
func (w *wrapper) notifyListeners(from, to Configuration) Waiter {
wg := new(sync.WaitGroup)
wg.Add(len(w.subs))
for _, sub := range w.subs {
go func(committer Committer) {
w.notifyListener(committer, from, to)
wg.Done()
}(sub)
wg.Go(func() {
w.notifyListener(sub, from, to)
})
}
return wg
}

View File

@@ -337,23 +337,20 @@ func BenchmarkConnections(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
var wg sync.WaitGroup
wg.Add(2)
errC := make(chan error, 2)
go func() {
wg.Go(func() {
if _, err := client.Write(data); err != nil {
errC <- err
return
}
wg.Done()
}()
go func() {
})
wg.Go(func() {
if _, err := io.ReadFull(server, data); err != nil {
errC <- err
return
}
total += sz
wg.Done()
}()
})
wg.Wait()
close(errC)
err := <-errC

View File

@@ -99,16 +99,9 @@ func (d *quicDialer) Dial(ctx context.Context, _ protocol.DeviceID, uri *url.URL
type quicDialerFactory struct{}
func (quicDialerFactory) New(opts config.OptionsConfiguration, tlsCfg *tls.Config, registry *registry.Registry, lanChecker *lanChecker) genericDialer {
// So the idea is that we should probably try dialing every 20 seconds.
// However it would still be nice if this was adjustable/proportional to ReconnectIntervalS
// But prevent something silly like 1/3 = 0 etc.
quicInterval := opts.ReconnectIntervalS / 3
if quicInterval < 10 {
quicInterval = 10
}
return &quicDialer{
commonDialer: commonDialer{
reconnectInterval: time.Duration(quicInterval) * time.Second,
reconnectInterval: time.Duration(opts.ReconnectIntervalS) * time.Second,
tlsCfg: tlsCfg,
lanChecker: lanChecker,
lanPriority: opts.ConnectionPriorityQUICLAN,

View File

@@ -605,15 +605,13 @@ func (s *service) dialDevices(ctx context.Context, now time.Time, cfg config.Con
dialWG.Wait()
dialCancel()
}()
for i := range queue {
for _, entry := range queue {
select {
case <-dialCtx.Done():
return
default:
}
dialWG.Add(1)
go func(entry dialQueueEntry) {
defer dialWG.Done()
dialWG.Go(func() {
conn, ok := s.dialParallel(dialCtx, entry.id, entry.targets, dialSemaphore)
if !ok {
return
@@ -630,7 +628,7 @@ func (s *service) dialDevices(ctx context.Context, now time.Time, cfg config.Con
}
}
numConnsMut.Unlock()
}(queue[i])
})
}
}
@@ -1118,12 +1116,8 @@ func (s *service) dialParallel(ctx context.Context, deviceID protocol.DeviceID,
wg := sync.WaitGroup{}
for _, tgt := range tgts {
sema.Take(1)
wg.Add(1)
go func(tgt dialTarget) {
defer func() {
wg.Done()
sema.Give(1)
}()
wg.Go(func() {
defer sema.Give(1)
conn, err := tgt.Dial(ctx)
if err == nil {
// Closes the connection on error
@@ -1136,7 +1130,7 @@ func (s *service) dialParallel(ctx context.Context, deviceID protocol.DeviceID,
l.Debugln("dialing", deviceID, tgt.uri, "success:", conn)
res <- conn
}
}(tgt)
})
}
// Spawn a routine which will unblock main routine in case we fail

View File

@@ -57,6 +57,7 @@ const (
ListenAddressesChanged
LoginAttempt
Failure
UpgradeRestartScheduled
AllEvents = (1 << iota) - 1
)
@@ -134,6 +135,8 @@ func (t EventType) String() string {
return "FolderWatchStateChanged"
case Failure:
return "Failure"
case UpgradeRestartScheduled:
return "UpgradeRestartScheduled"
default:
return "Unknown"
}
@@ -221,6 +224,8 @@ func UnmarshalEventType(s string) EventType {
return FolderWatchStateChanged
case "Failure":
return Failure
case "UpgradeRestartScheduled":
return UpgradeRestartScheduled
default:
return 0
}

View File

@@ -378,11 +378,8 @@ func TestUnsubscribeContention(t *testing.T) {
stopListeners := make(chan struct{})
var listenerWg sync.WaitGroup
listenerWg.Add(listeners)
for i := 0; i < listeners; i++ {
go func() {
defer listenerWg.Done()
listenerWg.Go(func() {
s := l.Subscribe(AllEvents)
defer s.Unsubscribe()
@@ -394,7 +391,7 @@ func TestUnsubscribeContention(t *testing.T) {
return
}
}
}()
})
}
// Start senders. These send pointless events until the stop channel is
@@ -403,11 +400,8 @@ func TestUnsubscribeContention(t *testing.T) {
stopSenders := make(chan struct{})
defer close(stopSenders)
var senderWg sync.WaitGroup
senderWg.Add(senders)
for i := 0; i < senders; i++ {
go func() {
defer senderWg.Done()
senderWg.Go(func() {
t := time.NewTicker(time.Millisecond)
for {
@@ -419,7 +413,7 @@ func TestUnsubscribeContention(t *testing.T) {
return
}
}
}()
})
}
// Give everything time to start up.

View File

@@ -256,36 +256,28 @@ func (f *sendReceiveFolder) pullerIteration(ctx context.Context, scanChan chan<-
f.sl.DebugContext(ctx, "Starting puller iteration", "copiers", f.Copiers, "pullerPendingKiB", f.PullerMaxPendingKiB)
updateWg.Add(1)
var changed int // only read after updateWg closes
go func() {
updateWg.Go(func() {
// dbUpdaterRoutine finishes when dbUpdateChan is closed
changed = f.dbUpdaterRoutine(dbUpdateChan)
updateWg.Done()
}()
})
for range f.Copiers {
copyWg.Add(1)
go func() {
copyWg.Go(func() {
// copierRoutine finishes when copyChan is closed
f.copierRoutine(ctx, copyChan, pullChan, finisherChan)
copyWg.Done()
}()
})
}
pullWg.Add(1)
go func() {
pullWg.Go(func() {
// pullerRoutine finishes when pullChan is closed
f.pullerRoutine(ctx, pullChan, finisherChan)
pullWg.Done()
}()
})
doneWg.Add(1)
// finisherRoutine finishes when finisherChan is closed
go func() {
doneWg.Go(func() {
f.finisherRoutine(ctx, finisherChan, dbUpdateChan, scanChan)
doneWg.Done()
}()
})
fileDeletions, dirDeletions, err := f.processNeeded(ctx, dbUpdateChan, copyChan, scanChan)
@@ -1534,14 +1526,10 @@ func (f *sendReceiveFolder) pullerRoutine(ctx context.Context, in <-chan pullBlo
continue
}
wg.Add(1)
go func() {
defer wg.Done()
wg.Go(func() {
defer requestLimiter.Give(bytes)
f.pullBlock(ctx, state, out)
}()
})
}
wg.Wait()
}

View File

@@ -463,11 +463,9 @@ func TestDeregisterOnFailInPull(t *testing.T) {
copyChan, copyWg := startCopier(t.Context(), f, pullChan, finisherBufferChan)
var pullWg sync.WaitGroup
pullWg.Add(1)
go func() {
pullWg.Go(func() {
f.pullerRoutine(t.Context(), pullChan, finisherBufferChan)
pullWg.Done()
}()
})
go f.finisherRoutine(t.Context(), finisherChan, dbUpdateChan, make(chan string))
defer func() {
// Unblock copier and puller
@@ -1246,10 +1244,8 @@ func cleanupSharedPullerState(s *sharedPullerState) {
func startCopier(ctx context.Context, f *sendReceiveFolder, pullChan chan<- pullBlockState, finisherChan chan<- *sharedPullerState) (chan copyBlocksState, *sync.WaitGroup) {
copyChan := make(chan copyBlocksState)
wg := new(sync.WaitGroup)
wg.Add(1)
go func() {
wg.Go(func() {
f.copierRoutine(ctx, copyChan, pullChan, finisherChan)
wg.Done()
}()
})
return copyChan, wg
}

View File

@@ -993,15 +993,13 @@ func TestIssue5063(t *testing.T) {
if fcfg, ok := m.cfg.Folder(id); !ok || !fcfg.SharedWith(device1) {
t.Error("expected shared", id)
}
wg.Done()
}
reps := 10
ids := make([]string, reps)
for i := 0; i < reps; i++ {
wg.Add(1)
ids[i] = srand.String(8)
go addAndVerify(ids[i])
wg.Go(func() { addAndVerify(ids[i]) })
}
finished := make(chan struct{})
@@ -2945,16 +2943,14 @@ func TestFolderRestartZombies(t *testing.T) {
// for the commit to complete, but there are many of them.
var wg sync.WaitGroup
for i := 0; i < 25; i++ {
wg.Add(1)
go func() {
defer wg.Done()
wg.Go(func() {
t0 := time.Now()
for time.Since(t0) < time.Second {
fcfg := folderCfg.Copy()
fcfg.MaxConflicts = mrand.Int() // safe change that should cause a folder restart
setFolder(t, wrapper, fcfg)
}
}()
})
}
// Wait for the above to complete and check how many folders we have

View File

@@ -82,9 +82,7 @@ func TestStressBufferPool(t *testing.T) {
var wg sync.WaitGroup
fail := make(chan struct{}, routines)
for i := 0; i < routines; i++ {
wg.Add(1)
go func() {
defer wg.Done()
wg.Go(func() {
for time.Since(t0) < runtime {
blocks := make([][]byte, 10)
for i := range blocks {
@@ -102,7 +100,7 @@ func TestStressBufferPool(t *testing.T) {
bp.Put(blocks[i])
}
}
}()
})
}
wg.Wait()

View File

@@ -110,24 +110,18 @@ func TestCloseOnBlockingSend(t *testing.T) {
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
wg.Go(func() {
c.ClusterConfig(&ClusterConfig{}, nil)
wg.Done()
}()
})
wg.Add(1)
go func() {
wg.Go(func() {
c.Close(errManual)
wg.Done()
}()
})
// This simulates an error from ping timeout
wg.Add(1)
go func() {
wg.Go(func() {
c.internalClose(ErrTimeout)
wg.Done()
}()
})
done := make(chan struct{})
go func() {

View File

@@ -72,6 +72,7 @@ type App struct {
stopOnce sync.Once
mainServiceCancel context.CancelFunc
stopped chan struct{}
dbService db.DBService
// Access to internals for direct users of this package. Note that the interface in Internals is unstable!
Internals *Internals
@@ -114,10 +115,22 @@ func (a *App) Start() error {
return nil
}
// StartMaintenance asynchronously triggers database maintenance to start.
func (a *App) StartMaintenance() <-chan error {
return a.dbService.StartMaintenance()
}
// LastMaintenanceTime returns the last time database maintenance completed successfully
// This will return time zero when database maintenance has never completed successfully.
func (a *App) LastMaintenanceTime() time.Time {
return a.dbService.LastMaintenanceTime()
}
func (a *App) startup() error {
a.mainService.Add(ur.NewFailureHandler(a.cfg, a.evLogger))
a.mainService.Add(a.sdb.Service(a.opts.DBMaintenanceInterval))
a.dbService = a.sdb.Service(a.opts.DBMaintenanceInterval)
a.mainService.Add(a.dbService)
if a.opts.AuditWriter != nil {
a.mainService.Add(newAuditService(a.opts.AuditWriter, a.evLogger))

View File

@@ -198,10 +198,8 @@ func TryMigrateDatabase(ctx context.Context, deleteRetention time.Duration) erro
fis := make(chan protocol.FileInfo, 50)
var writeErr error
var wg sync.WaitGroup
wg.Add(1)
writerDone := make(chan struct{})
go func() {
defer wg.Done()
wg.Go(func() { //nolint:contextcheck
defer close(writerDone)
var batch []protocol.FileInfo
files, blocks := 0, 0
@@ -238,7 +236,7 @@ func TryMigrateDatabase(ctx context.Context, deleteRetention time.Duration) erro
slog.Info("Migrated folder", "folder", folder, "files", files, "blocks", blocks, "duration", d.Truncate(time.Second), "filesrate", float64(files)/d.Seconds())
totFiles += files
totBlocks += blocks
}()
})
// Iterate the existing files
fs, err := olddb.NewFileSet(folder, ll)

View File

@@ -48,16 +48,13 @@ func TestTimeoutCond(t *testing.T) {
var results [routines][2]int
var wg sync.WaitGroup
for i := 0; i < routines; i++ {
i := i
wg.Add(1)
go func() {
wg.Go(func() {
d := time.Duration(i) * timeMult * time.Millisecond
t.Logf("Routine %d waits for %v\n", i, d)
succ, fail := runLocks(t, iterations, c, d)
results[i][0] = succ
results[i][1] = fail
wg.Done()
}()
})
}
wg.Wait()

View File

@@ -123,28 +123,26 @@ func Discover(ctx context.Context, _, timeout time.Duration) []nat.Device {
continue
}
wg.Add(1)
// Discovery is done sequentially per interface because we discovered that
// FritzBox routers return a broken result sometimes if the IPv4 and IPv6
// request arrive at the same time.
go func(iface net.Interface) {
defer wg.Done()
hasGUA, err := interfaceHasGUAIPv6(iface)
wg.Go(func() {
hasGUA, err := interfaceHasGUAIPv6(intf)
if err != nil {
l.Debugf("Couldn't check for IPv6 GUAs on %s: %s", iface.Name, err)
l.Debugf("Couldn't check for IPv6 GUAs on %s: %s", intf.Name, err) //nolint:contextcheck
} else if hasGUA {
// Discover IPv6 gateways on interface. Only discover IGDv2, since IGDv1
// + IPv6 is not standardized and will lead to duplicates on routers.
// Only do this when a non-link-local IPv6 is available. if we can't
// enumerate the interface, the IPv6 code will not work anyway
discover(ctx, &iface, urnIgdV2, timeout, resultChan, true)
discover(ctx, &intf, urnIgdV2, timeout, resultChan, true)
}
// Discover IPv4 gateways on interface.
for _, deviceType := range []string{urnIgdV2, urnIgdV1} {
discover(ctx, &iface, deviceType, timeout, resultChan, false)
discover(ctx, &intf, deviceType, timeout, resultChan, false)
}
}(intf)
})
}
go func() {

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "STDISCOSRV" "1" "Dec 07, 2025" "v2.0.0" "Syncthing"
.TH "STDISCOSRV" "1" "Feb 12, 2026" "v2.0.0" "Syncthing"
.SH NAME
stdiscosrv \- Syncthing Discovery Server
.SH SYNOPSIS
@@ -394,6 +394,6 @@ the discovery server and your particular setup.
.SH AUTHOR
The Syncthing Authors
.SH COPYRIGHT
2014-2025, The Syncthing Authors
2014-2026, The Syncthing Authors
.\" Generated by docutils manpage writer.
.

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "STRELAYSRV" "1" "Dec 07, 2025" "v2.0.0" "Syncthing"
.TH "STRELAYSRV" "1" "Feb 12, 2026" "v2.0.0" "Syncthing"
.SH NAME
strelaysrv \- Syncthing Relay Server
.SH SYNOPSIS
@@ -317,6 +317,6 @@ relay://<host name|IP>[:port]/?id=<relay device ID>&token=mySecretToken
.SH AUTHOR
The Syncthing Authors
.SH COPYRIGHT
2014-2025, The Syncthing Authors
2014-2026, The Syncthing Authors
.\" Generated by docutils manpage writer.
.

View File

@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-BEP" "7" "Dec 07, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-BEP" "7" "Feb 12, 2026" "v2.0.0" "Syncthing"
.SH NAME
syncthing-bep \- Block Exchange Protocol v1
.SH INTRODUCTION AND DEFINITIONS
@@ -1057,6 +1057,6 @@ process occurs for device A at 15.
.SH AUTHOR
The Syncthing Authors
.SH COPYRIGHT
2014-2025, The Syncthing Authors
2014-2026, The Syncthing Authors
.\" Generated by docutils manpage writer.
.

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-CONFIG" "5" "Dec 07, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-CONFIG" "5" "Feb 12, 2026" "v2.0.0" "Syncthing"
.SH NAME
syncthing-config \- Syncthing Configuration
.SH OVERVIEW
@@ -1699,6 +1699,6 @@ send only mode for just the configuration folder.
.SH AUTHOR
The Syncthing Authors
.SH COPYRIGHT
2014-2025, The Syncthing Authors
2014-2026, The Syncthing Authors
.\" Generated by docutils manpage writer.
.

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-DEVICE-IDS" "7" "Dec 07, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-DEVICE-IDS" "7" "Feb 12, 2026" "v2.0.0" "Syncthing"
.SH NAME
syncthing-device-ids \- Understanding Device IDs
.sp
@@ -255,6 +255,6 @@ accepting the connection.
.SH AUTHOR
The Syncthing Authors
.SH COPYRIGHT
2014-2025, The Syncthing Authors
2014-2026, The Syncthing Authors
.\" Generated by docutils manpage writer.
.

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-EVENT-API" "7" "Dec 07, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-EVENT-API" "7" "Feb 12, 2026" "v2.0.0" "Syncthing"
.SH NAME
syncthing-event-api \- Event API
.SH DESCRIPTION
@@ -1116,6 +1116,6 @@ seconds and is now in state \fBidle\fP\&.
.SH AUTHOR
The Syncthing Authors
.SH COPYRIGHT
2014-2025, The Syncthing Authors
2014-2026, The Syncthing Authors
.\" Generated by docutils manpage writer.
.

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-FAQ" "7" "Dec 07, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-FAQ" "7" "Feb 12, 2026" "v2.0.0" "Syncthing"
.SH NAME
syncthing-faq \- Frequently Asked Questions
.INDENT 0.0
@@ -745,6 +745,6 @@ For example, the two emphasized lines below would be removed from the file.
.SH AUTHOR
The Syncthing Authors
.SH COPYRIGHT
2014-2025, The Syncthing Authors
2014-2026, The Syncthing Authors
.\" Generated by docutils manpage writer.
.

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-GLOBALDISCO" "7" "Dec 07, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-GLOBALDISCO" "7" "Feb 12, 2026" "v2.0.0" "Syncthing"
.SH NAME
syncthing-globaldisco \- Global Discovery Protocol v3
.SH ANNOUNCEMENTS
@@ -119,6 +119,6 @@ The discovery server prints its certificate ID in this manner on startup.
.SH AUTHOR
The Syncthing Authors
.SH COPYRIGHT
2014-2025, The Syncthing Authors
2014-2026, The Syncthing Authors
.\" Generated by docutils manpage writer.
.

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-LOCALDISCO" "7" "Dec 07, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-LOCALDISCO" "7" "Feb 12, 2026" "v2.0.0" "Syncthing"
.SH NAME
syncthing-localdisco \- Local Discovery Protocol v4
.SH MODE OF OPERATION
@@ -114,6 +114,6 @@ between two announces and conclude that the announcing device has restarted.
.SH AUTHOR
The Syncthing Authors
.SH COPYRIGHT
2014-2025, The Syncthing Authors
2014-2026, The Syncthing Authors
.\" Generated by docutils manpage writer.
.

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-NETWORKING" "7" "Dec 07, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-NETWORKING" "7" "Feb 12, 2026" "v2.0.0" "Syncthing"
.SH NAME
syncthing-networking \- Firewall Setup
.SH ROUTER SETUP
@@ -163,6 +163,6 @@ Syncthing can use a SOCKS5 proxy for outbound connections. Please see \fI\%Using
.SH AUTHOR
The Syncthing Authors
.SH COPYRIGHT
2014-2025, The Syncthing Authors
2014-2026, The Syncthing Authors
.\" Generated by docutils manpage writer.
.

View File

@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-RELAY" "7" "Dec 07, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-RELAY" "7" "Feb 12, 2026" "v2.0.0" "Syncthing"
.SH NAME
syncthing-relay \- Relay Protocol v1
.SH WHAT IS A RELAY?
@@ -707,6 +707,6 @@ T}
.SH AUTHOR
The Syncthing Authors
.SH COPYRIGHT
2014-2025, The Syncthing Authors
2014-2026, The Syncthing Authors
.\" Generated by docutils manpage writer.
.

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-REST-API" "7" "Dec 07, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-REST-API" "7" "Feb 12, 2026" "v2.0.0" "Syncthing"
.SH NAME
syncthing-rest-api \- REST API
.sp
@@ -1955,6 +1955,6 @@ Returns a \fB{\(dqstatus\(dq: \(dqOK\(dq}\fP object.
.SH AUTHOR
The Syncthing Authors
.SH COPYRIGHT
2014-2025, The Syncthing Authors
2014-2026, The Syncthing Authors
.\" Generated by docutils manpage writer.
.

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-SECURITY" "7" "Dec 07, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-SECURITY" "7" "Feb 12, 2026" "v2.0.0" "Syncthing"
.SH NAME
syncthing-security \- Security Principles
.sp
@@ -167,6 +167,6 @@ avoid leaking keys and metadata. Or, use whole disk encryption.
.SH AUTHOR
The Syncthing Authors
.SH COPYRIGHT
2014-2025, The Syncthing Authors
2014-2026, The Syncthing Authors
.\" Generated by docutils manpage writer.
.

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-STIGNORE" "5" "Dec 07, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-STIGNORE" "5" "Feb 12, 2026" "v2.0.0" "Syncthing"
.SH NAME
syncthing-stignore \- Prevent files from being synchronized to other nodes
.SH SYNOPSIS
@@ -288,6 +288,6 @@ as the escape character for that particular file.
.SH AUTHOR
The Syncthing Authors
.SH COPYRIGHT
2014-2025, The Syncthing Authors
2014-2026, The Syncthing Authors
.\" Generated by docutils manpage writer.
.

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-VERSIONING" "7" "Dec 07, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING-VERSIONING" "7" "Feb 12, 2026" "v2.0.0" "Syncthing"
.SH NAME
syncthing-versioning \- Keep automatic backups of deleted files by other nodes
.sp
@@ -375,6 +375,6 @@ quoted.
.SH AUTHOR
The Syncthing Authors
.SH COPYRIGHT
2014-2025, The Syncthing Authors
2014-2026, The Syncthing Authors
.\" Generated by docutils manpage writer.
.

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING" "1" "Dec 07, 2025" "v2.0.0" "Syncthing"
.TH "SYNCTHING" "1" "Feb 12, 2026" "v2.0.0" "Syncthing"
.SH NAME
syncthing \- Syncthing
.SH SYNOPSIS
@@ -549,6 +549,6 @@ Set to any nonempty value to use it.
.SH AUTHOR
The Syncthing Authors
.SH COPYRIGHT
2014-2025, The Syncthing Authors
2014-2026, The Syncthing Authors
.\" Generated by docutils manpage writer.
.

View File

@@ -59,8 +59,7 @@ var copyrightMap = map[string]string{
}
var urlMap = map[string]string{
"fontawesome.io": "https://github.com/FortAwesome/Font-Awesome",
"go.uber.org/automaxprocs": "https://github.com/uber-go/automaxprocs",
"fontawesome.io": "https://github.com/FortAwesome/Font-Awesome",
// "go.uber.org/mock": "https://github.com/uber-go/mock",
"google.golang.org/protobuf": "https://github.com/protocolbuffers/protobuf-go",
// "gopkg.in/yaml.v2": "", // ignore, as gopkg.in/yaml.v3 supersedes

View File

@@ -43,17 +43,14 @@ func TestRescanWithDelay(t *testing.T) {
var wg sync.WaitGroup
log.Println("Starting scans...")
for j := 0; j < 20; j++ {
j := j
wg.Add(1)
go func() {
defer wg.Done()
wg.Go(func() {
err := st.RescanDelay("default", 1)
log.Println(j)
if err != nil {
log.Println(err)
t.Fatal(err)
}
}()
})
}
wg.Wait()

View File

@@ -42,17 +42,14 @@ func TestRescanInParallel(t *testing.T) {
var wg sync.WaitGroup
log.Println("Starting scans...")
for j := 0; j < 20; j++ {
j := j
wg.Add(1)
go func() {
defer wg.Done()
wg.Go(func() {
err := st.Rescan("default")
log.Println(j)
if err != nil {
log.Println(err)
t.Fatal(err)
}
}()
})
}
wg.Wait()