tear-ws-bridge 0.1.3

Tear WebSocket bridge — exposes the typed tear-daemon CBOR wire over ws://, so any browser / wasm renderer can attach without speaking UDS or TCP directly.
# Home-manager module for the tear-ws-bridge sidecar.
#
# The bridge exposes the tear-daemon CBOR wire over ws:// so any
# browser / wasm renderer can attach without speaking UDS or TCP
# directly. Lifecycle is managed by a per-user launchd agent on
# Darwin or a systemd user unit on Linux — independent of the
# tear-daemon's own unit, so operators can enable browser access
# without touching the daemon's posture.
#
# Usage from a consumer flake:
#
#   imports = [ inputs.tear.homeManagerModules.ws-bridge ];
#   programs.tear-ws-bridge = {
#     enable = true;
#     listen = "127.0.0.1:8181";          # default
#     tear   = "/Users/me/.local/share/tear/tear.sock";  # default
#   };
#
# Both the bridge AND the daemon must be running for browser
# attach to work. The daemon's HM module is at
# `inputs.tear.homeManagerModules.default` (the substrate
# module-trio output for `programs.tear.daemon`).

{ tearPackages, hmHelpers }:
{ config, lib, pkgs, ... }:

let
  cfg = config.programs.tear-ws-bridge;
  system = pkgs.stdenv.hostPlatform.system;
  bridgePackage =
    if cfg.package != null then cfg.package
    else tearPackages.${system}.tear-ws-bridge;
  homeDir = config.home.homeDirectory;
  args = [
    "--listen" cfg.listen
    "--tear" cfg.tear
  ];
in
{
  options.programs.tear-ws-bridge = {
    enable = lib.mkEnableOption "tear-ws-bridge — WebSocket → tear-daemon CBOR bridge for browser / wasm renderers";

    package = lib.mkOption {
      type = lib.types.nullOr lib.types.package;
      default = null;
      description = ''
        Override the bridge package. `null` (default) pulls the
        bridge from `inputs.tear.packages.<system>.tear-ws-bridge`
        — the canonical workspace build.
      '';
    };

    listen = lib.mkOption {
      type = lib.types.str;
      default = "127.0.0.1:8181";
      description = ''
        TCP listen address for the bridge. Use `0.0.0.0:8181` to
        accept browser connections from the LAN; tunnel through
        SSH / WireGuard / a TLS proxy for untrusted networks (the
        wire is unencrypted at this layer).
      '';
    };

    tear = lib.mkOption {
      type = lib.types.str;
      default = "${homeDir}/.local/share/tear/tear.sock";
      description = ''
        Tear-daemon target. Filesystem path → UDS, or
        `tcp://host:port` → TCP (the bridge can front a REMOTE
        daemon, so a browser on one machine can attach to a
        tear-daemon on another).
      '';
    };
  };

  config = lib.mkIf cfg.enable (lib.mkMerge [
    { home.packages = [ bridgePackage ]; }

    (lib.mkIf pkgs.stdenv.isDarwin (hmHelpers.mkLaunchdService {
      name = "tear-ws-bridge";
      label = "io.pleme.tear-ws-bridge";
      command = "${bridgePackage}/bin/tear-ws-bridge";
      inherit args;
      logDir = "${homeDir}/Library/Logs";
    }))

    (lib.mkIf (!pkgs.stdenv.isDarwin) (hmHelpers.mkSystemdService {
      name = "tear-ws-bridge";
      description = "tear-ws-bridge — WebSocket → tear-daemon CBOR bridge";
      command = "${bridgePackage}/bin/tear-ws-bridge";
      inherit args;
    }))
  ]);
}