{
  "schema_version": 3,
  "app": "syncthing",
  "display_name": "Syncthing",
  "upstream_repo": "https://github.com/syncthing/syncthing",
  "docs_url": "https://docs.syncthing.net/users/contrib.html#docker",
  "status": "ok",
  "summary": {
    "primary_service": "syncthing",
    "app_version": "v2.1.6",
    "app_version_pinned": true,
    "runs": 3,
    "publishable": true,
    "publishable_note": null,
    "idle_total_mem_mib": 45.44,
    "idle_total_cpu_pct": 1.31,
    "peak_total_mem_mib": 109.36,
    "peak_is_under_load": true,
    "startup_seconds": 5.8,
    "startup_peak_total_mem_mib": 45.81,
    "settled": true,
    "settle_seconds_used": 122.1,
    "peak_max_mib": 111.25,
    "sizing_peak_mib": 111.25,
    "idle_mem_mib": {
      "median": 45.44,
      "min": 45.4,
      "max": 45.58,
      "values": [
        45.4,
        45.58,
        45.44
      ]
    },
    "idle_cpu_pct": {
      "median": 1.31,
      "min": 1.21,
      "max": 1.75,
      "values": [
        1.75,
        1.31,
        1.21
      ]
    },
    "peak_mem_mib": {
      "median": 109.36,
      "min": 108.8,
      "max": 111.25,
      "values": [
        108.8,
        111.25,
        109.36
      ]
    },
    "startup_peak_mem_mib": {
      "median": 45.81,
      "min": 45.58,
      "max": 49.46,
      "values": [
        49.46,
        45.81,
        45.58
      ]
    },
    "startup_s": {
      "median": 5.8,
      "min": 5.8,
      "max": 6.9,
      "values": [
        5.8,
        5.8,
        6.9
      ]
    },
    "settled_after_healthy_s": {
      "median": 124.6,
      "min": 124.6,
      "max": 124.7,
      "values": [
        124.6,
        124.6,
        124.7
      ]
    },
    "settled_runs": 3,
    "per_service": {
      "syncthing": {
        "idle_mem_mib": {
          "median": 21.88,
          "min": 21.74,
          "max": 23.69,
          "values": [
            21.88,
            21.74,
            23.69
          ]
        },
        "peak_mem_mib": {
          "median": 64.64,
          "min": 64.0,
          "max": 65.3,
          "values": [
            65.3,
            64.64,
            64.0
          ]
        }
      },
      "syncthing-b": {
        "idle_mem_mib": {
          "median": 23.52,
          "min": 21.75,
          "max": 23.84,
          "values": [
            23.52,
            23.84,
            21.75
          ]
        },
        "peak_mem_mib": {
          "median": 45.36,
          "min": 43.5,
          "max": 46.61,
          "values": [
            43.5,
            46.61,
            45.36
          ]
        }
      }
    },
    "variance_flag": null,
    "representative_run": 3,
    "cpu_models": [
      "AMD EPYC 7763 64-Core Processor"
    ],
    "cpu_model_mismatch": false,
    "rule": "idle = median of the run idle means; peak = median of the run peaks (highest simultaneous total, under load when the app has a load step); peak_max_mib = highest run peak; sizing_peak_mib = highest over runs of max(run peak, run startup peak); variance_flag when max/min over runs is above 1.5 for idle or peak."
  },
  "run": {
    "started_at": "2026-10-16T04:56:47Z",
    "date": "2026-10-16",
    "harness_version": "3.0.0",
    "runs_requested": 3,
    "docker_host": null,
    "remote": false,
    "runs_completed": 3
  },
  "host": {
    "label": "github-actions ubuntu-latest (2 vCPU, 8 GB)",
    "ci": {
      "runs_on": "ubuntu-latest",
      "runner_environment": "github-hosted",
      "runner_os": "Linux",
      "runner_arch": "X64",
      "image_os": "ubuntu24",
      "image_version": "20261012.334.1"
    },
    "cpu_model": "AMD EPYC 7763 64-Core Processor",
    "cpu_model_note": null,
    "cores": 2,
    "mem_total_mib": 7938,
    "kernel": "6.17.0-1022-azure",
    "os": "Ubuntu 24.04.5 LTS",
    "arch": "x86_64",
    "oci_arch": "amd64",
    "docker_server_version": "28.0.4",
    "docker_client_version": "28.0.4",
    "compose_client_version": "2.38.2",
    "storage_driver": "overlay2",
    "containerd_image_store": false,
    "source": "docker info + throwaway container reading /proc/cpuinfo on the daemon host",
    "compose_note": "Compose runs on the client machine; Docker server fields are from the daemon host"
  },
  "helper_images": {
    "curlimages/curl:latest": {
      "pulled_as": "curlimages/curl:latest",
      "digest": "curlimages/curl@sha256:a134550be58ab783810e613ae96fd253c38f7bdd16d9bf9be350700c7a29ac06"
    },
    "python:3.12-slim": {
      "pulled_as": "python:3.12-slim",
      "digest": "python@sha256:a6e34c598f2467ed0e9a8d349809fcd8b5c603269512df273a0bb1784edc11b1"
    }
  },
  "pull_seconds": 1.2,
  "finished_at": "2026-10-16T05:05:28Z",
  "elapsed_seconds": 521.7,
  "services": {
    "syncthing": {
      "image_ref": "syncthing/syncthing:2.1.6",
      "image_repo": "syncthing/syncthing",
      "image_tag": "2.1.6",
      "digest": "sha256:4d0281bdcb4ed9baf503d251d0544ddd856f49fce3f114056257ae172b7fb670",
      "digest_note": null,
      "version": "v2.1.6",
      "version_source": "label:org.opencontainers.image.version",
      "version_pinned": true,
      "version_note": null,
      "resolved_tag": "2.1.6",
      "resolved_tag_note": null,
      "image_size": {
        "unpacked_mb": 41.5,
        "unpacked_source": "docker image ls Size: unpacked image on the daemon host, rounded by docker to 3 significant figures",
        "compressed_mb": 18.7,
        "compressed_source": "sum of compressed layer sizes in the registry manifest for linux/amd64",
        "compressed_null_reason": null,
        "unit_note": "MB = 10^6 bytes. unpacked = disk used on the host; compressed = what is downloaded"
      },
      "compose_image": "syncthing/syncthing:2.1.6",
      "compose_comment": "sha256:4d0281bdcb4ed9baf503d251d0544ddd856f49fce3f114056257ae172b7fb670  (version v2.1.6, from label:org.opencontainers.image.version)",
      "restart_count": 0,
      "oom_killed": false
    },
    "syncthing-b": {
      "image_ref": "syncthing/syncthing:2.1.6",
      "image_repo": "syncthing/syncthing",
      "image_tag": "2.1.6",
      "digest": "sha256:4d0281bdcb4ed9baf503d251d0544ddd856f49fce3f114056257ae172b7fb670",
      "digest_note": null,
      "version": "v2.1.6",
      "version_source": "label:org.opencontainers.image.version",
      "version_pinned": true,
      "version_note": null,
      "resolved_tag": "2.1.6",
      "resolved_tag_note": null,
      "image_size": {
        "unpacked_mb": 41.5,
        "unpacked_source": "docker image ls Size: unpacked image on the daemon host, rounded by docker to 3 significant figures",
        "compressed_mb": 18.7,
        "compressed_source": "sum of compressed layer sizes in the registry manifest for linux/amd64",
        "compressed_null_reason": null,
        "unit_note": "MB = 10^6 bytes. unpacked = disk used on the host; compressed = what is downloaded"
      },
      "compose_image": "syncthing/syncthing:2.1.6",
      "compose_comment": "sha256:4d0281bdcb4ed9baf503d251d0544ddd856f49fce3f114056257ae172b7fb670  (version v2.1.6, from label:org.opencontainers.image.version)",
      "restart_count": 0,
      "oom_killed": false
    }
  },
  "runs": [
    {
      "run": 1,
      "started_at": "2026-10-16T04:56:53Z",
      "host": {
        "label": "github-actions ubuntu-latest (2 vCPU, 8 GB)",
        "ci": {
          "runs_on": "ubuntu-latest",
          "runner_environment": "github-hosted",
          "runner_os": "Linux",
          "runner_arch": "X64",
          "image_os": "ubuntu24",
          "image_version": "20261012.334.1"
        },
        "cpu_model": "AMD EPYC 7763 64-Core Processor",
        "cpu_model_note": null,
        "cores": 2,
        "mem_total_mib": 7938,
        "kernel": "6.17.0-1022-azure",
        "os": "Ubuntu 24.04.5 LTS",
        "arch": "x86_64",
        "oci_arch": "amd64",
        "docker_server_version": "28.0.4",
        "docker_client_version": "28.0.4",
        "compose_client_version": "2.38.2",
        "storage_driver": "overlay2",
        "containerd_image_store": false,
        "source": "docker info + throwaway container reading /proc/cpuinfo on the daemon host",
        "compose_note": "Compose runs on the client machine; Docker server fields are from the daemon host"
      },
      "date": "2026-10-16",
      "startup": {
        "seconds": 5.8,
        "ok": true,
        "checks": [
          {
            "type": "container",
            "ok": true,
            "ready_after_s": 5.8,
            "detail": "shb-syncthing-syncthing-b-1=running/healthy, shb-syncthing-syncthing-1=running/healthy"
          }
        ],
        "up_command_seconds": 0.4,
        "definition": "wall clock from starting `docker compose up -d` until every health check passed; checks poll every 0.5 s from a throwaway container on the compose network, so resolution is about +/- 1 s"
      },
      "versions": {
        "syncthing": "v2.1.6",
        "syncthing-b": "v2.1.6"
      },
      "setup": [
        {
          "exit_code": 0,
          "duration_s": 1.6,
          "reported": {
            "syncthing_version": "v2.1.6",
            "syncthing_version_b": "v2.1.6",
            "device_id_a": "QDKDN4E-6TTPOKE-SB7SU4G-QATJWWB-BLXODUB-PTVC2NJ-K77N7TQ-3VMCWAV",
            "device_id_b": "H4UOP7N-FHJWFHL-N7DC76T-6AHGWAA-G3CALIU-R7YIAZ6-7WRDQUJ-TISWFQX",
            "paired_after_s": 1.0,
            "folder_id": "lab",
            "settings_off": [
              "autoUpgradeIntervalH",
              "crashReportingEnabled",
              "globalAnnounceEnabled",
              "localAnnounceEnabled",
              "natEnabled",
              "relaysEnabled",
              "startBrowser",
              "stunKeepaliveMinS",
              "stunKeepaliveStartS",
              "urAccepted"
            ],
            "restart_required": [],
            "node_reported_alloc_bytes": {
              "a": 5242880,
              "b": 5526344
            }
          },
          "output_tail": [
            "node A v2.1.6 and node B v2.1.6 paired in 1.0s; folder 'lab': A /data/a, B /data/b; settings off on both: ['autoUpgradeIntervalH', 'crashReportingEnabled', 'globalAnnounceEnabled', 'localAnnounceEnabled', 'natEnabled', 'relaysEnabled', 'startBrowser', 'stunKeepaliveMinS', 'stunKeepaliveStartS', 'urAccepted']; restart required: no"
          ],
          "description": "Pair the two nodes with Syncthing's REST API. Make the folders /data/a, /data/b and /data/stage on the shared lab volume (this step shares node A's volumes), wait for both APIs, switch off global discovery, local discovery, relays, NAT traversal (UPnP / NAT-PMP), STUN, automatic upgrades, usage reporting and crash reporting on both nodes and read the settings back, remove each node's default folder, add each node to the other's device list with a fixed address on the Compose network, add one folder `lab` on both (A: /data/a, B: /data/b, send and receive, Syncthing's defaults otherwise: 10 s watcher delay, hourly rescan, no versioning), and wait until the nodes are connected and report the folder idle."
        }
      ],
      "idle": {
        "per_service": {
          "syncthing": {
            "samples": 12,
            "mem_mean_mib": 21.88,
            "mem_max_mib": 23.53,
            "cpu_mean_pct": 0.86,
            "cpu_max_pct": 3.42
          },
          "syncthing-b": {
            "samples": 12,
            "mem_mean_mib": 23.52,
            "mem_max_mib": 23.65,
            "cpu_mean_pct": 0.89,
            "cpu_max_pct": 3.5
          }
        },
        "total": {
          "samples": 12,
          "dropped_for_total": 0,
          "mem_mean_mib": 45.4,
          "mem_max_mib": 47.07,
          "cpu_mean_pct": 1.75,
          "cpu_max_pct": 6.58
        },
        "window_seconds": 32.3,
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 47.07,
            "total_cpu_pct": 0.19
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 45.26,
            "total_cpu_pct": 6.58
          },
          {
            "t_offset_s": 5.1,
            "total_mem_mib": 45.51,
            "total_cpu_pct": 0.11
          },
          {
            "t_offset_s": 8.1,
            "total_mem_mib": 45.42,
            "total_cpu_pct": 3.44
          },
          {
            "t_offset_s": 11.1,
            "total_mem_mib": 45.5,
            "total_cpu_pct": 0.08
          },
          {
            "t_offset_s": 14.1,
            "total_mem_mib": 44.96,
            "total_cpu_pct": 0.09
          },
          {
            "t_offset_s": 17.2,
            "total_mem_mib": 45.62,
            "total_cpu_pct": 3.52
          },
          {
            "t_offset_s": 20.2,
            "total_mem_mib": 45.24,
            "total_cpu_pct": 0.11
          },
          {
            "t_offset_s": 23.2,
            "total_mem_mib": 44.95,
            "total_cpu_pct": 3.3
          },
          {
            "t_offset_s": 26.2,
            "total_mem_mib": 45.07,
            "total_cpu_pct": 0.06
          },
          {
            "t_offset_s": 29.3,
            "total_mem_mib": 44.92,
            "total_cpu_pct": 0.09
          },
          {
            "t_offset_s": 32.3,
            "total_mem_mib": 45.26,
            "total_cpu_pct": 3.45
          }
        ]
      },
      "settle": {
        "settled": true,
        "mode": "detected",
        "settle_seconds_used": 121.8,
        "settle_started_after_healthy_s": 2.8,
        "params": {
          "window_seconds": 60,
          "threshold_pct": 2.0,
          "threshold_min_mib": 10.0,
          "min_seconds": 120,
          "max_seconds": 600
        },
        "last_window": {
          "n": 21,
          "mean_mib": 48.99,
          "min_mib": 47.07,
          "max_mib": 49.46,
          "spread_mib": 2.39,
          "threshold_mib": 10.0
        },
        "rechecks": 0,
        "rule": "settled = over the last window_seconds the stack total varied (max - min) by less than max(threshold_pct of the window mean, threshold_min_mib); not before min_seconds, at the latest max_seconds, both counted from the end of the setup steps; the idle window must also be flat and agree with it. If max_seconds passes without settling, settled=false and the idle window is measured anyway."
      },
      "startup_peak": {
        "per_service_mem_mib": {
          "syncthing": 23.91,
          "syncthing-b": 25.6
        },
        "total_mem_mib": 49.46,
        "samples": 43,
        "note": "highest value across docker stats samples (about one every 2-3 s) from healthy until the stack settled; shorter spikes between samples are not seen"
      },
      "timeline": {
        "step_s": 10,
        "t0": "healthy",
        "offsets_s": [
          10,
          20,
          30,
          40,
          50,
          60,
          70,
          80,
          90,
          100,
          110,
          120,
          130,
          140,
          150,
          160
        ],
        "total_mem_mib": [
          47.8,
          49.0,
          48.8,
          49.0,
          49.3,
          49.2,
          49.2,
          49.2,
          49.1,
          49.1,
          49.0,
          49.0,
          46.7,
          45.3,
          45.3,
          45.1
        ],
        "per_service_mem_mib": {
          "syncthing": [
            22.5,
            23.7,
            23.6,
            23.5,
            23.8,
            23.7,
            23.7,
            23.7,
            23.7,
            23.7,
            23.5,
            23.6,
            22.6,
            21.8,
            21.8,
            21.6
          ],
          "syncthing-b": [
            25.3,
            25.3,
            25.3,
            25.5,
            25.5,
            25.5,
            25.5,
            25.5,
            25.4,
            25.4,
            25.4,
            25.4,
            24.1,
            23.5,
            23.5,
            23.5
          ]
        }
      },
      "load": {
        "per_service": {
          "syncthing": {
            "samples": 4,
            "mem_mean_mib": 53.55,
            "mem_max_mib": 65.3,
            "cpu_mean_pct": 20.63,
            "cpu_max_pct": 53.88
          },
          "syncthing-b": {
            "samples": 4,
            "mem_mean_mib": 36.79,
            "mem_max_mib": 43.5,
            "cpu_mean_pct": 23.04,
            "cpu_max_pct": 91.8
          }
        },
        "total": {
          "samples": 4,
          "dropped_for_total": 0,
          "mem_mean_mib": 90.34,
          "mem_max_mib": 108.8,
          "cpu_mean_pct": 43.66,
          "cpu_max_pct": 145.68
        },
        "description": "The same 103 files as the Nextcloud, Seafile, ownCloud and File Browser tests (100 small ones of 1 KB to 1 MB, about 50 MB together, and three of 50 MB; same generator, seed, names and order; about 194.5 MB), delivered the way Syncthing works: there is no upload API, so a Python script writes the files into node A's synchronised folder (one atomic rename per file from a staging directory on the same volume, so Syncthing never sees a half-written file), asks node A to scan (POST /rest/db/scan), and polls node B's REST API every 0.2 s until B knows all 103 files, has them and needs nothing. The reported sync time is from the first rename to that moment. The script then reads the files from node B's folder and checks the SHA-256 of all 103 files, lists the folder from node B's index, and asks node A whether it sees B as 100 % complete. Both nodes are containers on the same host and the same network (no WAN, no relay). Generating the files and writing them to a staging directory on the same volume happen before the clock starts; the renames into node A's folder and the whole transfer are inside the measured window. No desktop or mobile client, no versioning, no ignore patterns, no encryption-at-rest folders, no large library.",
        "duration_s": 4.1,
        "window": "from LAB_MARK load_start (or step start) until 2 s after the step ended",
        "reported_by_script": {
          "api": "syncthing rest api (db/scan, db/status, db/completion, db/browse); files written into node A's folder, read back from node B's",
          "files_uploaded": 103,
          "small_files": 100,
          "large_files": 3,
          "bytes_uploaded": 194456599,
          "upload_seconds": 0.01,
          "sync_seconds": 2.73,
          "sync_mb_per_s": 71.2,
          "scan_requested_after_s": 0.25,
          "b_index_complete_seconds": 0.66,
          "a_sees_b_complete_seconds": 3.14,
          "a_sees_b_complete": true,
          "listing_entries": 103,
          "listing_seconds": 0.0,
          "files_verified_on_b": 103,
          "bytes_verified_on_b": 194456599,
          "verify_seconds": 0.2,
          "sha256_match_all_files": true,
          "problems": [],
          "syncthing_memory_reported": {
            "a": {
              "alloc_bytes": 42556240,
              "sys_bytes": 52627736
            },
            "b": {
              "alloc_bytes": 3926728,
              "sys_bytes": 17479944
            }
          },
          "requests": {
            "scan": {
              "sent": 1,
              "ok": 1,
              "statuses": {
                "200": 1
              }
            },
            "status_b": {
              "sent": 13,
              "ok": 13,
              "statuses": {
                "200": 13
              }
            },
            "completion_a": {
              "sent": 3,
              "ok": 3,
              "statuses": {
                "200": 3
              }
            },
            "browse_b": {
              "sent": 1,
              "ok": 1,
              "statuses": {
                "200": 1
              }
            }
          },
          "requests_total": 18,
          "requests_failed": 0
        },
        "output_tail": [
          "generated 103 files (100 small, 3 large), 194.5 MB",
          "103 files, 194.5 MB placed in node A's folder in 0.01s; scan requested (HTTP 200) after 0.25s",
          "node B has all 103 files after 2.73s, B's index was complete after 0.66s",
          "B's files on disk: 0 problem(s), 194.5 MB checked in 0.2s; B's index lists 103 files (names and sizes match: True)"
        ],
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 48.77,
            "total_cpu_pct": 28.78
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 108.8,
            "total_cpu_pct": 145.68
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 101.77,
            "total_cpu_pct": 0.15
          },
          {
            "t_offset_s": 6.0,
            "total_mem_mib": 102.02,
            "total_cpu_pct": 0.05
          }
        ],
        "series_start_offset_s": 0.8,
        "peak_note": "peak = highest value seen across docker stats samples (about one every 2-3 s); shorter spikes between samples are not seen"
      },
      "container_end_state": {
        "syncthing": {
          "restart_count": 0,
          "oom_killed": false
        },
        "syncthing-b": {
          "restart_count": 0,
          "oom_killed": false
        }
      },
      "teardown": {
        "done": true,
        "volumes_removed": true,
        "images_removed": false,
        "error": null
      },
      "warnings": [],
      "finished_at": "2026-10-16T04:59:45Z",
      "elapsed_seconds": 171.8
    },
    {
      "run": 2,
      "started_at": "2026-10-16T04:59:45Z",
      "host": {
        "label": "github-actions ubuntu-latest (2 vCPU, 8 GB)",
        "ci": {
          "runs_on": "ubuntu-latest",
          "runner_environment": "github-hosted",
          "runner_os": "Linux",
          "runner_arch": "X64",
          "image_os": "ubuntu24",
          "image_version": "20261012.334.1"
        },
        "cpu_model": "AMD EPYC 7763 64-Core Processor",
        "cpu_model_note": null,
        "cores": 2,
        "mem_total_mib": 7938,
        "kernel": "6.17.0-1022-azure",
        "os": "Ubuntu 24.04.5 LTS",
        "arch": "x86_64",
        "oci_arch": "amd64",
        "docker_server_version": "28.0.4",
        "docker_client_version": "28.0.4",
        "compose_client_version": "2.38.2",
        "storage_driver": "overlay2",
        "containerd_image_store": false,
        "source": "docker info + throwaway container reading /proc/cpuinfo on the daemon host",
        "compose_note": "Compose runs on the client machine; Docker server fields are from the daemon host"
      },
      "date": "2026-10-16",
      "startup": {
        "seconds": 5.8,
        "ok": true,
        "checks": [
          {
            "type": "container",
            "ok": true,
            "ready_after_s": 5.8,
            "detail": "shb-syncthing-syncthing-1=running/healthy, shb-syncthing-syncthing-b-1=running/healthy"
          }
        ],
        "up_command_seconds": 0.4,
        "definition": "wall clock from starting `docker compose up -d` until every health check passed; checks poll every 0.5 s from a throwaway container on the compose network, so resolution is about +/- 1 s"
      },
      "versions": {
        "syncthing": "v2.1.6",
        "syncthing-b": "v2.1.6"
      },
      "setup": [
        {
          "exit_code": 0,
          "duration_s": 1.5,
          "reported": {
            "syncthing_version": "v2.1.6",
            "syncthing_version_b": "v2.1.6",
            "device_id_a": "AA3N42S-A3RP4D7-7H5FH6I-QP3WUTH-CZW5V35-6DPFC7L-OKWM4EX-WV7JMQD",
            "device_id_b": "E7G274R-NTRWPX6-AFDMXSJ-UTR6GVY-7Q4Y5SP-AXD3L3Z-VZTVQCP-NUMCPAR",
            "paired_after_s": 1.0,
            "folder_id": "lab",
            "settings_off": [
              "autoUpgradeIntervalH",
              "crashReportingEnabled",
              "globalAnnounceEnabled",
              "localAnnounceEnabled",
              "natEnabled",
              "relaysEnabled",
              "startBrowser",
              "stunKeepaliveMinS",
              "stunKeepaliveStartS",
              "urAccepted"
            ],
            "restart_required": [],
            "node_reported_alloc_bytes": {
              "a": 5462040,
              "b": 5502144
            }
          },
          "output_tail": [
            "node A v2.1.6 and node B v2.1.6 paired in 1.0s; folder 'lab': A /data/a, B /data/b; settings off on both: ['autoUpgradeIntervalH', 'crashReportingEnabled', 'globalAnnounceEnabled', 'localAnnounceEnabled', 'natEnabled', 'relaysEnabled', 'startBrowser', 'stunKeepaliveMinS', 'stunKeepaliveStartS', 'urAccepted']; restart required: no"
          ],
          "description": "Pair the two nodes with Syncthing's REST API. Make the folders /data/a, /data/b and /data/stage on the shared lab volume (this step shares node A's volumes), wait for both APIs, switch off global discovery, local discovery, relays, NAT traversal (UPnP / NAT-PMP), STUN, automatic upgrades, usage reporting and crash reporting on both nodes and read the settings back, remove each node's default folder, add each node to the other's device list with a fixed address on the Compose network, add one folder `lab` on both (A: /data/a, B: /data/b, send and receive, Syncthing's defaults otherwise: 10 s watcher delay, hourly rescan, no versioning), and wait until the nodes are connected and report the folder idle."
        }
      ],
      "idle": {
        "per_service": {
          "syncthing": {
            "samples": 11,
            "mem_mean_mib": 21.74,
            "mem_max_mib": 21.98,
            "cpu_mean_pct": 0.35,
            "cpu_max_pct": 3.34
          },
          "syncthing-b": {
            "samples": 11,
            "mem_mean_mib": 23.84,
            "mem_max_mib": 24.02,
            "cpu_mean_pct": 0.96,
            "cpu_max_pct": 3.4
          }
        },
        "total": {
          "samples": 11,
          "dropped_for_total": 0,
          "mem_mean_mib": 45.58,
          "mem_max_mib": 46.0,
          "cpu_mean_pct": 1.31,
          "cpu_max_pct": 3.43
        },
        "window_seconds": 30.3,
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 45.57,
            "total_cpu_pct": 0.09
          },
          {
            "t_offset_s": 3.0,
            "total_mem_mib": 45.43,
            "total_cpu_pct": 3.36
          },
          {
            "t_offset_s": 6.1,
            "total_mem_mib": 46.0,
            "total_cpu_pct": 0.13
          },
          {
            "t_offset_s": 9.1,
            "total_mem_mib": 45.65,
            "total_cpu_pct": 0.13
          },
          {
            "t_offset_s": 12.1,
            "total_mem_mib": 45.75,
            "total_cpu_pct": 3.43
          },
          {
            "t_offset_s": 15.1,
            "total_mem_mib": 45.48,
            "total_cpu_pct": 0.13
          },
          {
            "t_offset_s": 18.2,
            "total_mem_mib": 45.42,
            "total_cpu_pct": 3.41
          },
          {
            "t_offset_s": 21.2,
            "total_mem_mib": 45.42,
            "total_cpu_pct": 0.11
          },
          {
            "t_offset_s": 24.2,
            "total_mem_mib": 45.45,
            "total_cpu_pct": 0.11
          },
          {
            "t_offset_s": 27.2,
            "total_mem_mib": 45.79,
            "total_cpu_pct": 3.42
          },
          {
            "t_offset_s": 30.3,
            "total_mem_mib": 45.41,
            "total_cpu_pct": 0.08
          }
        ]
      },
      "settle": {
        "settled": true,
        "mode": "detected",
        "settle_seconds_used": 122.1,
        "settle_started_after_healthy_s": 2.5,
        "params": {
          "window_seconds": 60,
          "threshold_pct": 2.0,
          "threshold_min_mib": 10.0,
          "min_seconds": 120,
          "max_seconds": 600
        },
        "last_window": {
          "n": 21,
          "mean_mib": 45.36,
          "min_mib": 45.04,
          "max_mib": 45.81,
          "spread_mib": 0.77,
          "threshold_mib": 10.0
        },
        "rechecks": 0,
        "rule": "settled = over the last window_seconds the stack total varied (max - min) by less than max(threshold_pct of the window mean, threshold_min_mib); not before min_seconds, at the latest max_seconds, both counted from the end of the setup steps; the idle window must also be flat and agree with it. If max_seconds passes without settling, settled=false and the idle window is measured anyway."
      },
      "startup_peak": {
        "per_service_mem_mib": {
          "syncthing": 21.89,
          "syncthing-b": 23.93
        },
        "total_mem_mib": 45.81,
        "samples": 43,
        "note": "highest value across docker stats samples (about one every 2-3 s) from healthy until the stack settled; shorter spikes between samples are not seen"
      },
      "timeline": {
        "step_s": 10,
        "t0": "healthy",
        "offsets_s": [
          10,
          20,
          30,
          40,
          50,
          60,
          70,
          80,
          90,
          100,
          110,
          120,
          130,
          140,
          150,
          160
        ],
        "total_mem_mib": [
          45.0,
          45.3,
          45.2,
          45.3,
          45.4,
          45.4,
          45.4,
          45.4,
          45.3,
          45.3,
          45.5,
          45.3,
          45.4,
          45.7,
          45.4,
          45.6
        ],
        "per_service_mem_mib": {
          "syncthing": [
            21.6,
            21.6,
            21.6,
            21.6,
            21.6,
            21.6,
            21.7,
            21.7,
            21.6,
            21.6,
            21.7,
            21.6,
            21.8,
            21.8,
            21.6,
            21.7
          ],
          "syncthing-b": [
            23.4,
            23.7,
            23.6,
            23.7,
            23.7,
            23.8,
            23.7,
            23.7,
            23.7,
            23.7,
            23.8,
            23.6,
            23.6,
            23.9,
            23.8,
            23.9
          ]
        }
      },
      "load": {
        "per_service": {
          "syncthing": {
            "samples": 4,
            "mem_mean_mib": 53.41,
            "mem_max_mib": 64.64,
            "cpu_mean_pct": 22.58,
            "cpu_max_pct": 56.31
          },
          "syncthing-b": {
            "samples": 4,
            "mem_mean_mib": 36.99,
            "mem_max_mib": 46.61,
            "cpu_mean_pct": 23.23,
            "cpu_max_pct": 88.5
          }
        },
        "total": {
          "samples": 4,
          "dropped_for_total": 0,
          "mem_mean_mib": 90.4,
          "mem_max_mib": 111.25,
          "cpu_mean_pct": 45.81,
          "cpu_max_pct": 144.81
        },
        "description": "The same 103 files as the Nextcloud, Seafile, ownCloud and File Browser tests (100 small ones of 1 KB to 1 MB, about 50 MB together, and three of 50 MB; same generator, seed, names and order; about 194.5 MB), delivered the way Syncthing works: there is no upload API, so a Python script writes the files into node A's synchronised folder (one atomic rename per file from a staging directory on the same volume, so Syncthing never sees a half-written file), asks node A to scan (POST /rest/db/scan), and polls node B's REST API every 0.2 s until B knows all 103 files, has them and needs nothing. The reported sync time is from the first rename to that moment. The script then reads the files from node B's folder and checks the SHA-256 of all 103 files, lists the folder from node B's index, and asks node A whether it sees B as 100 % complete. Both nodes are containers on the same host and the same network (no WAN, no relay). Generating the files and writing them to a staging directory on the same volume happen before the clock starts; the renames into node A's folder and the whole transfer are inside the measured window. No desktop or mobile client, no versioning, no ignore patterns, no encryption-at-rest folders, no large library.",
        "duration_s": 4.1,
        "window": "from LAB_MARK load_start (or step start) until 2 s after the step ended",
        "reported_by_script": {
          "api": "syncthing rest api (db/scan, db/status, db/completion, db/browse); files written into node A's folder, read back from node B's",
          "files_uploaded": 103,
          "small_files": 100,
          "large_files": 3,
          "bytes_uploaded": 194456599,
          "upload_seconds": 0.01,
          "sync_seconds": 2.91,
          "sync_mb_per_s": 66.7,
          "scan_requested_after_s": 0.3,
          "b_index_complete_seconds": 0.7,
          "a_sees_b_complete_seconds": 3.12,
          "a_sees_b_complete": true,
          "listing_entries": 103,
          "listing_seconds": 0.0,
          "files_verified_on_b": 103,
          "bytes_verified_on_b": 194456599,
          "verify_seconds": 0.2,
          "sha256_match_all_files": true,
          "problems": [],
          "syncthing_memory_reported": {
            "a": {
              "alloc_bytes": 42596368,
              "sys_bytes": 52709656
            },
            "b": {
              "alloc_bytes": 4000128,
              "sys_bytes": 16771352
            }
          },
          "requests": {
            "scan": {
              "sent": 1,
              "ok": 1,
              "statuses": {
                "200": 1
              }
            },
            "status_b": {
              "sent": 13,
              "ok": 13,
              "statuses": {
                "200": 13
              }
            },
            "completion_a": {
              "sent": 2,
              "ok": 2,
              "statuses": {
                "200": 2
              }
            },
            "browse_b": {
              "sent": 1,
              "ok": 1,
              "statuses": {
                "200": 1
              }
            }
          },
          "requests_total": 17,
          "requests_failed": 0
        },
        "output_tail": [
          "generated 103 files (100 small, 3 large), 194.5 MB",
          "103 files, 194.5 MB placed in node A's folder in 0.01s; scan requested (HTTP 200) after 0.30s",
          "node B has all 103 files after 2.91s, B's index was complete after 0.7s",
          "B's files on disk: 0 problem(s), 194.5 MB checked in 0.2s; B's index lists 103 files (names and sizes match: True)"
        ],
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 48.18,
            "total_cpu_pct": 38.22
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 111.25,
            "total_cpu_pct": 144.81
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 101.14,
            "total_cpu_pct": 0.15
          },
          {
            "t_offset_s": 6.0,
            "total_mem_mib": 101.04,
            "total_cpu_pct": 0.07
          }
        ],
        "series_start_offset_s": 0.9,
        "peak_note": "peak = highest value seen across docker stats samples (about one every 2-3 s); shorter spikes between samples are not seen"
      },
      "container_end_state": {
        "syncthing": {
          "restart_count": 0,
          "oom_killed": false
        },
        "syncthing-b": {
          "restart_count": 0,
          "oom_killed": false
        }
      },
      "teardown": {
        "done": true,
        "volumes_removed": true,
        "images_removed": false,
        "error": null
      },
      "warnings": [],
      "finished_at": "2026-10-16T05:02:35Z",
      "elapsed_seconds": 170.2
    },
    {
      "run": 3,
      "started_at": "2026-10-16T05:02:35Z",
      "host": {
        "label": "github-actions ubuntu-latest (2 vCPU, 8 GB)",
        "ci": {
          "runs_on": "ubuntu-latest",
          "runner_environment": "github-hosted",
          "runner_os": "Linux",
          "runner_arch": "X64",
          "image_os": "ubuntu24",
          "image_version": "20261012.334.1"
        },
        "cpu_model": "AMD EPYC 7763 64-Core Processor",
        "cpu_model_note": null,
        "cores": 2,
        "mem_total_mib": 7938,
        "kernel": "6.17.0-1022-azure",
        "os": "Ubuntu 24.04.5 LTS",
        "arch": "x86_64",
        "oci_arch": "amd64",
        "docker_server_version": "28.0.4",
        "docker_client_version": "28.0.4",
        "compose_client_version": "2.38.2",
        "storage_driver": "overlay2",
        "containerd_image_store": false,
        "source": "docker info + throwaway container reading /proc/cpuinfo on the daemon host",
        "compose_note": "Compose runs on the client machine; Docker server fields are from the daemon host"
      },
      "date": "2026-10-16",
      "startup": {
        "seconds": 6.9,
        "ok": true,
        "checks": [
          {
            "type": "container",
            "ok": true,
            "ready_after_s": 6.9,
            "detail": "shb-syncthing-syncthing-1=running/healthy, shb-syncthing-syncthing-b-1=running/healthy"
          }
        ],
        "up_command_seconds": 1.4,
        "definition": "wall clock from starting `docker compose up -d` until every health check passed; checks poll every 0.5 s from a throwaway container on the compose network, so resolution is about +/- 1 s"
      },
      "versions": {
        "syncthing": "v2.1.6",
        "syncthing-b": "v2.1.6"
      },
      "setup": [
        {
          "exit_code": 0,
          "duration_s": 1.6,
          "reported": {
            "syncthing_version": "v2.1.6",
            "syncthing_version_b": "v2.1.6",
            "device_id_a": "SGMQYDZ-JZHK4ZJ-MM5HVLC-NO3PTMD-DS6DGHJ-26ORYEC-SRVSVMJ-CHOK7AL",
            "device_id_b": "Z2D7BQQ-MSMPPDM-YFBO5XV-LL5LTZZ-YADU35I-SCDCUNZ-2FUUM4C-QJXGSQE",
            "paired_after_s": 1.0,
            "folder_id": "lab",
            "settings_off": [
              "autoUpgradeIntervalH",
              "crashReportingEnabled",
              "globalAnnounceEnabled",
              "localAnnounceEnabled",
              "natEnabled",
              "relaysEnabled",
              "startBrowser",
              "stunKeepaliveMinS",
              "stunKeepaliveStartS",
              "urAccepted"
            ],
            "restart_required": [],
            "node_reported_alloc_bytes": {
              "a": 5307704,
              "b": 5269576
            }
          },
          "output_tail": [
            "node A v2.1.6 and node B v2.1.6 paired in 1.0s; folder 'lab': A /data/a, B /data/b; settings off on both: ['autoUpgradeIntervalH', 'crashReportingEnabled', 'globalAnnounceEnabled', 'localAnnounceEnabled', 'natEnabled', 'relaysEnabled', 'startBrowser', 'stunKeepaliveMinS', 'stunKeepaliveStartS', 'urAccepted']; restart required: no"
          ],
          "description": "Pair the two nodes with Syncthing's REST API. Make the folders /data/a, /data/b and /data/stage on the shared lab volume (this step shares node A's volumes), wait for both APIs, switch off global discovery, local discovery, relays, NAT traversal (UPnP / NAT-PMP), STUN, automatic upgrades, usage reporting and crash reporting on both nodes and read the settings back, remove each node's default folder, add each node to the other's device list with a fixed address on the Compose network, add one folder `lab` on both (A: /data/a, B: /data/b, send and receive, Syncthing's defaults otherwise: 10 s watcher delay, hourly rescan, no versioning), and wait until the nodes are connected and report the folder idle."
        }
      ],
      "idle": {
        "per_service": {
          "syncthing": {
            "samples": 12,
            "mem_mean_mib": 23.69,
            "mem_max_mib": 23.93,
            "cpu_mean_pct": 0.61,
            "cpu_max_pct": 3.49
          },
          "syncthing-b": {
            "samples": 12,
            "mem_mean_mib": 21.75,
            "mem_max_mib": 21.92,
            "cpu_mean_pct": 0.59,
            "cpu_max_pct": 3.4
          }
        },
        "total": {
          "samples": 12,
          "dropped_for_total": 0,
          "mem_mean_mib": 45.44,
          "mem_max_mib": 45.85,
          "cpu_mean_pct": 1.21,
          "cpu_max_pct": 3.55
        },
        "window_seconds": 32.3,
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 45.22,
            "total_cpu_pct": 0.11
          },
          {
            "t_offset_s": 3.0,
            "total_mem_mib": 45.14,
            "total_cpu_pct": 3.39
          },
          {
            "t_offset_s": 6.1,
            "total_mem_mib": 45.39,
            "total_cpu_pct": 0.07
          },
          {
            "t_offset_s": 9.1,
            "total_mem_mib": 45.54,
            "total_cpu_pct": 0.06
          },
          {
            "t_offset_s": 12.1,
            "total_mem_mib": 45.54,
            "total_cpu_pct": 3.45
          },
          {
            "t_offset_s": 15.1,
            "total_mem_mib": 45.7,
            "total_cpu_pct": 0.15
          },
          {
            "t_offset_s": 18.2,
            "total_mem_mib": 45.67,
            "total_cpu_pct": 3.38
          },
          {
            "t_offset_s": 21.2,
            "total_mem_mib": 45.55,
            "total_cpu_pct": 0.07
          },
          {
            "t_offset_s": 24.2,
            "total_mem_mib": 45.1,
            "total_cpu_pct": 0.09
          },
          {
            "t_offset_s": 26.2,
            "total_mem_mib": 45.85,
            "total_cpu_pct": 0.05
          },
          {
            "t_offset_s": 29.3,
            "total_mem_mib": 45.34,
            "total_cpu_pct": 0.09
          },
          {
            "t_offset_s": 32.3,
            "total_mem_mib": 45.2,
            "total_cpu_pct": 3.55
          }
        ]
      },
      "settle": {
        "settled": true,
        "mode": "detected",
        "settle_seconds_used": 122.1,
        "settle_started_after_healthy_s": 2.6,
        "params": {
          "window_seconds": 60,
          "threshold_pct": 2.0,
          "threshold_min_mib": 10.0,
          "min_seconds": 120,
          "max_seconds": 600
        },
        "last_window": {
          "n": 21,
          "mean_mib": 45.16,
          "min_mib": 44.85,
          "max_mib": 45.54,
          "spread_mib": 0.69,
          "threshold_mib": 10.0
        },
        "rechecks": 0,
        "rule": "settled = over the last window_seconds the stack total varied (max - min) by less than max(threshold_pct of the window mean, threshold_min_mib); not before min_seconds, at the latest max_seconds, both counted from the end of the setup steps; the idle window must also be flat and agree with it. If max_seconds passes without settling, settled=false and the idle window is measured anyway."
      },
      "startup_peak": {
        "per_service_mem_mib": {
          "syncthing": 23.81,
          "syncthing-b": 21.84
        },
        "total_mem_mib": 45.58,
        "samples": 43,
        "note": "highest value across docker stats samples (about one every 2-3 s) from healthy until the stack settled; shorter spikes between samples are not seen"
      },
      "timeline": {
        "step_s": 10,
        "t0": "healthy",
        "offsets_s": [
          10,
          20,
          30,
          40,
          50,
          60,
          70,
          80,
          90,
          100,
          110,
          120,
          130,
          140,
          150,
          160
        ],
        "total_mem_mib": [
          44.8,
          45.1,
          45.0,
          45.3,
          45.2,
          45.1,
          45.2,
          45.2,
          45.2,
          45.1,
          45.1,
          45.0,
          45.1,
          45.5,
          45.4,
          45.5
        ],
        "per_service_mem_mib": {
          "syncthing": [
            23.4,
            23.5,
            23.4,
            23.7,
            23.6,
            23.5,
            23.7,
            23.6,
            23.6,
            23.6,
            23.6,
            23.4,
            23.6,
            23.8,
            23.7,
            23.7
          ],
          "syncthing-b": [
            21.4,
            21.6,
            21.6,
            21.6,
            21.6,
            21.6,
            21.6,
            21.6,
            21.6,
            21.5,
            21.6,
            21.6,
            21.6,
            21.8,
            21.8,
            21.8
          ]
        }
      },
      "load": {
        "per_service": {
          "syncthing": {
            "samples": 4,
            "mem_mean_mib": 51.82,
            "mem_max_mib": 64.0,
            "cpu_mean_pct": 20.75,
            "cpu_max_pct": 55.09
          },
          "syncthing-b": {
            "samples": 4,
            "mem_mean_mib": 36.8,
            "mem_max_mib": 45.36,
            "cpu_mean_pct": 23.16,
            "cpu_max_pct": 92.45
          }
        },
        "total": {
          "samples": 4,
          "dropped_for_total": 0,
          "mem_mean_mib": 88.61,
          "mem_max_mib": 109.36,
          "cpu_mean_pct": 43.91,
          "cpu_max_pct": 147.54
        },
        "description": "The same 103 files as the Nextcloud, Seafile, ownCloud and File Browser tests (100 small ones of 1 KB to 1 MB, about 50 MB together, and three of 50 MB; same generator, seed, names and order; about 194.5 MB), delivered the way Syncthing works: there is no upload API, so a Python script writes the files into node A's synchronised folder (one atomic rename per file from a staging directory on the same volume, so Syncthing never sees a half-written file), asks node A to scan (POST /rest/db/scan), and polls node B's REST API every 0.2 s until B knows all 103 files, has them and needs nothing. The reported sync time is from the first rename to that moment. The script then reads the files from node B's folder and checks the SHA-256 of all 103 files, lists the folder from node B's index, and asks node A whether it sees B as 100 % complete. Both nodes are containers on the same host and the same network (no WAN, no relay). Generating the files and writing them to a staging directory on the same volume happen before the clock starts; the renames into node A's folder and the whole transfer are inside the measured window. No desktop or mobile client, no versioning, no ignore patterns, no encryption-at-rest folders, no large library.",
        "duration_s": 3.9,
        "window": "from LAB_MARK load_start (or step start) until 2 s after the step ended",
        "reported_by_script": {
          "api": "syncthing rest api (db/scan, db/status, db/completion, db/browse); files written into node A's folder, read back from node B's",
          "files_uploaded": 103,
          "small_files": 100,
          "large_files": 3,
          "bytes_uploaded": 194456599,
          "upload_seconds": 0.0,
          "sync_seconds": 2.77,
          "sync_mb_per_s": 70.3,
          "scan_requested_after_s": 0.25,
          "b_index_complete_seconds": 0.66,
          "a_sees_b_complete_seconds": 2.97,
          "a_sees_b_complete": true,
          "listing_entries": 103,
          "listing_seconds": 0.0,
          "files_verified_on_b": 103,
          "bytes_verified_on_b": 194456599,
          "verify_seconds": 0.2,
          "sha256_match_all_files": true,
          "problems": [],
          "syncthing_memory_reported": {
            "a": {
              "alloc_bytes": 43041896,
              "sys_bytes": 52836616
            },
            "b": {
              "alloc_bytes": 4495504,
              "sys_bytes": 17836312
            }
          },
          "requests": {
            "scan": {
              "sent": 1,
              "ok": 1,
              "statuses": {
                "200": 1
              }
            },
            "status_b": {
              "sent": 13,
              "ok": 13,
              "statuses": {
                "200": 13
              }
            },
            "completion_a": {
              "sent": 2,
              "ok": 2,
              "statuses": {
                "200": 2
              }
            },
            "browse_b": {
              "sent": 1,
              "ok": 1,
              "statuses": {
                "200": 1
              }
            }
          },
          "requests_total": 17,
          "requests_failed": 0
        },
        "output_tail": [
          "generated 103 files (100 small, 3 large), 194.5 MB",
          "103 files, 194.5 MB placed in node A's folder in 0.00s; scan requested (HTTP 200) after 0.25s",
          "node B has all 103 files after 2.77s, B's index was complete after 0.66s",
          "B's files on disk: 0 problem(s), 194.5 MB checked in 0.2s; B's index lists 103 files (names and sizes match: True)"
        ],
        "series": [
          {
            "t_offset_s": 0.0,
            "total_mem_mib": 46.24,
            "total_cpu_pct": 27.83
          },
          {
            "t_offset_s": 2.0,
            "total_mem_mib": 109.36,
            "total_cpu_pct": 147.54
          },
          {
            "t_offset_s": 4.0,
            "total_mem_mib": 99.56,
            "total_cpu_pct": 0.17
          },
          {
            "t_offset_s": 6.0,
            "total_mem_mib": 99.29,
            "total_cpu_pct": 0.08
          }
        ],
        "series_start_offset_s": 0.9,
        "peak_note": "peak = highest value seen across docker stats samples (about one every 2-3 s); shorter spikes between samples are not seen"
      },
      "container_end_state": {
        "syncthing": {
          "restart_count": 0,
          "oom_killed": false
        },
        "syncthing-b": {
          "restart_count": 0,
          "oom_killed": false
        }
      },
      "teardown": {
        "done": true,
        "volumes_removed": true,
        "images_removed": false,
        "error": null
      },
      "warnings": [],
      "finished_at": "2026-10-16T05:05:28Z",
      "elapsed_seconds": 173.2
    }
  ],
  "load_defined": true,
  "notes": [
    "Host: a GitHub Actions runner (ubuntu-latest, x86_64, 2 vCPU, 8 GB RAM; the CPU model varies between runners and each run records its own). Numbers are valid for that host only.",
    "Two nodes: the headline `summary` numbers are for the whole stack (node A + node B); `per_service` has `syncthing` (node A) and `syncthing-b` (node B). A reader's server runs ONE node, so a single-node figure is about the `syncthing` row, but node A in the lab has a paired peer and (after the load) a folder of 103 files, which a fresh single node does not.",
    "Image: syncthing/syncthing:2.1.6 (stable release 2.1.6, 6 Oct 2026; Docker Hub `latest`, `2` and `2.1` have the same digest). 2.1.7 exists only as release candidates (`2.1.7-rc.2`, 13 Oct 2026) and is not tested.",
    "Nothing leaves the lab: the lab Compose network is `internal: true` (no route out), and the setup step switches off global discovery, local discovery, relays, NAT traversal (UPnP / NAT-PMP), STUN, automatic upgrades, usage reporting and crash reporting in both nodes' configuration through the REST API and reads the settings back. Nodes find each other by a fixed address (tcp://syncthing:22000, tcp://syncthing-b:22000). The first seconds after start-up, before the setup step, run with Syncthing's defaults and failed network attempts to the internet.",
    "Not the default configuration: a reader's node has global discovery and relays ON. With them off the node does less work (no announcements, no relay connections); the idle figure here is for the settings in the setup step. Local discovery is also off. The published compose runs with Syncthing's defaults.",
    "Measured: idle RAM and CPU of two paired nodes with one empty shared folder; RAM and CPU of both nodes while 103 files (about 194.5 MB) are synchronised from A to B; time until B has them all (`sync_seconds`, polled every 0.2 s) with SHA-256 of every file checked on B's volume; image size; start-up time. Not measured: desktop or mobile clients (the Android app, SyncTrayzor, Syncthing-Fork), transfers over the internet or a relay, global discovery, NAT traversal, file versioning, ignore patterns, conflict handling, many folders or devices, large libraries (hundreds of thousands of files; the index database grows and so does memory), initial scan of existing data, background rescans (hourly).",
    "How the transfer is triggered: the script asks node A to scan right after placing the files (POST /rest/db/scan). Without that request Syncthing's file watcher waits fsWatcherDelayS = 10 s after the last change before it scans, which would add about 10 s to `sync_seconds`; a user whose files appear on disk meets that delay.",
    "Equivalence with the other file-sync tests: same 103 files (same generator and seed), same order, one writer, 5 downloads are replaced by a SHA-256 check of all 103 files on node B. Differences that come from the product: there is no request per file (the work is done by the two nodes talking to each other), so `requests` counts the load script's REST calls (one scan, the polls, one browse), `upload_seconds` is the time to move the files into node A's folder (not a network upload), and there are no `files_downloaded` / `download_seconds` keys.",
    "The script reports every REST call kind with the HTTP statuses it got (`requests` in the load result); any failed call, a missing, extra or different file on node B, or a timeout (600 s) fails the run.",
    "Lab-only compose override compose.lab.yaml (NOT in the published compose): Syncthing is peer to peer, so the lab runs TWO nodes: the published service (node A) and a second, identical service (node B, `syncthing-b`), which is not in the published file. The lab publishes no host ports, puts both on a Compose network with no route out (`internal: true`), gives each node a fixed GUI API key, and mounts one shared volume at /data in both nodes so the load step can write into node A's folder and read the files node B received. A real deployment has one node per device, on different machines. Image, user, volume at /var/syncthing and healthcheck are the published file's. The published single-node file has not been run with its ports mapped in the lab.",
    "Service(s) syncthing-b exist only in the lab-only compose override and are NOT in the published compose; their figures are in the result, their image line is not in the verified compose."
  ],
  "warnings": [],
  "files": {
    "compose": "syncthing.compose.yaml",
    "env": null
  },
  "definitions": {
    "memory": "cgroup working set as reported by `docker stats` (usage minus inactive file cache), MiB = 2^20 bytes",
    "cpu": "percent of ONE core as reported by `docker stats` (4 busy cores = 400)",
    "total": "stats over the sum of all containers at the same instant",
    "sample_interval": "docker stats --no-stream takes about 1-2 s per call, so samples are 2-3 s apart",
    "runs": "each run is a full fresh cycle (up, health, setup, settle, idle, load, down -v); the published figure is the median over the runs, shown with the min-max range; see summary.rule"
  }
}
