The Complete Computing Environment

Nearly Stateless Computing Using Syncthing

LifeTechEmacsTopicsArcology

Syncthing is a piece of Free Software designed to … sync … things. It's a peer-to-peer file syncronization tool which I run on nearly every computer I own to sync collections of files between online devices in nearly real-time. This has allowed me to increasingly treat my endpoint machines, my laptop, my GPD Pocket, etc as fungible, nearly stateless machines, at the cost of making Secure Backup Infrastructure slightly more difficult, I off-set this on NixOS with local ZFS rollbacks for data loss scenarios.

I have a lot of data exposed in my Syncthing; this laptop, with my entire Music and Photo libraries on it, as well as all of my Code, this Wiki, and some upload-only directories, I am managing 500GiB of data via Syncthing. My server has yet more folders managed by Syncthing. It makes Bootstrapping A Laptop with the CCE easier, it lets me take more daring steps to harden and grow my environment. Once this whole experiment integrating CCE in to my wiki is done, I'll probably be able to simplify my bootstrap even further.

Syncthing is installed with home-manager, and bootstrapped manually, copying IDs around in some way. it's a bit of a pain and i'd like to have a better configuration generator some day, or at least documentation of this bootstrap process…

This is straightforward:

{ lib, pkgs, ...}:
{
  services.syncthing.enable = true;
  services.syncthing.extraOptions = ["--gui-address=http://0.0.0.0:8384"];
  services.syncthing.tray.enable = true;
  home.packages = [ pkgs.syncthingtray ];
  systemd.user.services.syncthingtray.Service.ExecStart = lib.mkForce "${pkgs.syncthingtray}/bin/syncthingtray --wait";
  systemd.user.services.syncthingtray.Unit.After = lib.mkForce ["graphical-session-pre.target"];
  systemd.user.services.syncthingtray.Unit.Requires = lib.mkForce [];
}

I poke a hole in the firewall in My NixOS configuration:

{ ... }:
{
  networking.firewall.allowedTCPPorts = [ 22000 ];
  networking.firewall.interfaces.tailscale0.allowedTCPPorts = [ 8384 ];
}

I have some functions which help deal with conflicts:

(defun cce/syncthing-deconflict (&optional dir)
  (interactive "D")
  (let* ((dir (or dir org-roam-directory))
         (possible-pairs
          (->> (f-glob "*sync-conflict*" org-roam-directory) 
               (--map (list it (replace-regexp-in-string (rx (seq (literal ".sync-conflict") (one-or-more (not ".")))) "" it)))))
         (stale-conflict-files          ; source doesn't exist or was moved elsewhere.
          (--remove (file-exists-p (second it)) possible-pairs))
         (ediff-candidates              ; source does exist and should be ediff'd
          (--filter (file-exists-p (second it)) possible-pairs)))
    (dolist (f stale-conflict-files)
      (when (yes-or-no-p (format "%s does not exist. Delete %s? " (second f) (first f)))
        (delete-file (first f))))
    (dolist (f ediff-candidates)
      (apply #'ediff-files f)))))