{ description = "tummycrypt/tcfs - FOSS self-hosted odrive replacement"; # Public Attic read endpoint used by local dev and CI. # CI/release workflows push with `attic login` separately. nixConfig = { extra-substituters = [ "https://nix-cache.tinyland.dev/main" ]; extra-trusted-public-keys = [ "main:eaUydxuDu7xBoy5cCo3MdknYAkVyTIASQ7DGuwxa+XA=" ]; }; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; rust-overlay = { url = "github:oxalica/rust-overlay"; inputs.nixpkgs.follows = "nixpkgs"; }; crane.url = "github:ipetkov/crane"; flake-utils.url = "github:numtide/flake-utils"; }; outputs = { self, nixpkgs, rust-overlay, crane, flake-utils }: flake-utils.lib.eachDefaultSystem (system: let overlays = [ (import rust-overlay) ]; pkgs = import nixpkgs { inherit system overlays; }; rustVersion = "1.93.0"; rustTargets = [ "x86_64-unknown-linux-gnu" "aarch64-unknown-linux-gnu" "x86_64-apple-darwin" "aarch64-apple-darwin" "aarch64-apple-ios" "aarch64-apple-ios-sim" ]; rustToolchain = pkgs.rust-bin.stable.${rustVersion}.default.override { extensions = [ "rust-src" "rust-analyzer" "clippy" "rustfmt" ]; targets = rustTargets; }; craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchain; # Common build inputs for all crates commonBuildInputs = with pkgs; [ protobuf pkg-config openssl ] ++ pkgs.lib.optionals pkgs.stdenv.isLinux [ fuse3 rocksdb ] ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [ apple-sdk ]; # Source filter: include .proto files alongside standard Cargo sources src = let protoFilter = path: _type: builtins.match ".*\\.proto$" path != null; filter = path: type: (protoFilter path type) || (craneLib.filterCargoSources path type); in pkgs.lib.cleanSourceWith { src = craneLib.path ./.; inherit filter; }; # Common args shared by all crate builds commonArgs = { inherit src; buildInputs = commonBuildInputs; nativeBuildInputs = with pkgs; [ pkg-config protobuf perl ]; } // pkgs.lib.optionalAttrs pkgs.stdenv.isLinux { ROCKSDB_INCLUDE_DIR = "${pkgs.rocksdb}/include"; ROCKSDB_LIB_DIR = "${pkgs.rocksdb}/lib"; }; # Pre-build workspace deps (shared across all crate builds for caching) cargoArtifacts = craneLib.buildDepsOnly commonArgs; # The Linux Nix build sandbox cannot faithfully run tests that # exercise tcfs's fail-closed trusted-path/ACL validators, for two # environment reasons verified at syscall level (TIN-2853): # 1. Nix's sandbox seccomp filter fails the entire xattr syscall # family with ENOTSUP (upstream linux-derivation-builder.cc: # "Prevent builders from using EAs or ACLs"), so the POSIX ACL # probe in tcfs-sync's path_acl can never reach the filesystem # and correctly fails closed. # 2. The sandbox user namespace maps a single uid, so every # ancestor above /build (/, /nix/store, /tmp) appears as # overflowuid 65534 and is rejected by the euid-or-root # ancestor-ownership validator. # Rather than disabling whole checkPhases, skip exactly the affected # tests; the checked-in lists under nix/checks/ document each one. # Darwin runs the full suites (empty flags). linuxSandboxSkipFlags = skipFile: pkgs.lib.optionalString pkgs.stdenv.isLinux ( let names = builtins.filter (line: line != "" && !pkgs.lib.hasPrefix "#" line) (pkgs.lib.splitString "\n" (builtins.readFile skipFile)); in "-- " + pkgs.lib.concatMapStringsSep " " (name: "--skip ${name}") names ); # Build individual crates as separate derivations tcfsd = craneLib.buildPackage (commonArgs // { inherit cargoArtifacts; pname = "tcfsd"; # Registered-root safety tests create real fixture repositories. # gitMinimal (not pkgs.git) keeps the docbook/full-git doc chain out # of the check closure, matching tcfs-cli below. nativeCheckInputs = [ pkgs.gitMinimal ]; # Skip only the trusted-path/ACL validator tests that the Linux # sandbox cannot faithfully run (see linuxSandboxSkipFlags above and # the list's header for the full analysis, TIN-2853). cargoTestExtraArgs = linuxSandboxSkipFlags ./nix/checks/linux-sandbox-skip-tcfsd.txt; # Vendor OpenSSL on macOS to avoid dyld Team ID mismatch # when launchd loads the binary (Nix store openssl has different # code signature than the daemon binary). cargoExtraArgs = "-p tcfsd" + pkgs.lib.optionalString pkgs.stdenv.isDarwin " --features openssl-vendored"; meta.mainProgram = "tcfsd"; }); tcfs-cli = craneLib.buildPackage (commonArgs // { inherit cargoArtifacts; pname = "tcfs-cli"; cargoExtraArgs = "-p tcfs-cli"; # keep-both CLI tests (TIN-2658) run real `git` repos in checkPhase; # the build sandbox has no ambient git. nativeBuildInputs = commonArgs.nativeBuildInputs ++ [ pkgs.gitMinimal ]; # Skip only the trusted-path/ACL validator tests that the Linux # sandbox cannot faithfully run (see linuxSandboxSkipFlags above and # the list's header for the full analysis, TIN-2853). cargoTestExtraArgs = linuxSandboxSkipFlags ./nix/checks/linux-sandbox-skip-tcfs-cli.txt; meta.mainProgram = "tcfs"; }); tcfs-tui = craneLib.buildPackage (commonArgs // { inherit cargoArtifacts; pname = "tcfs-tui"; cargoExtraArgs = "-p tcfs-tui"; meta.mainProgram = "tcfs-tui"; }); tcfs-mcp = craneLib.buildPackage (commonArgs // { inherit cargoArtifacts; pname = "tcfs-mcp"; cargoExtraArgs = "-p tcfs-mcp"; meta.mainProgram = "tcfs-mcp"; }); # FileProvider FFI static library (pure Rust build). # Header: $out/include/tcfs_file_provider.h # Library: $out/lib/libtcfs_file_provider.a # Used by: swift/fileprovider/build.sh (impure, needs system swiftc) tcfs-file-provider-staticlib = craneLib.buildPackage (commonArgs // { inherit cargoArtifacts; pname = "tcfs-file-provider-staticlib"; cargoExtraArgs = "-p tcfs-file-provider --no-default-features --features grpc"; postInstall = '' mkdir -p $out/lib $out/include find target -name "libtcfs_file_provider.a" -exec cp {} $out/lib/ \; find target -name "tcfs_file_provider.h" -exec cp {} $out/include/ \; ''; }); # macOS .app bundle for TCC persistence. # TCC grants (Full Disk Access, etc.) are tied to bundle ID + CDHash. # Bare binaries in /nix/store/ lose grants on every rebuild. # This bundle provides a stable identity (io.tinyland.tcfsd). tcfsd-app = pkgs.lib.optionalAttrs pkgs.stdenv.isDarwin ( pkgs.stdenv.mkDerivation { pname = "tcfsd-app"; version = tcfsd.version or "0.12.6"; dontUnpack = true; buildInputs = [ pkgs.darwin.sigtool ]; installPhase = '' mkdir -p $out/Applications/TCFSDaemon.app/Contents/MacOS cp ${tcfsd}/bin/tcfsd $out/Applications/TCFSDaemon.app/Contents/MacOS/tcfsd cp ${./swift/daemon/resources/Info.plist} $out/Applications/TCFSDaemon.app/Contents/Info.plist # Ad-hoc sign for local use; Developer ID signing done out-of-band codesign -f -s - --options runtime $out/Applications/TCFSDaemon.app || true ''; } ); in { packages = { default = tcfsd; inherit tcfsd tcfs-cli tcfs-tui tcfs-mcp tcfs-file-provider-staticlib; } // pkgs.lib.optionalAttrs pkgs.stdenv.isDarwin { inherit tcfsd-app; }; # Eval-only regression check for the programs.tcfs config renderer # (natsTokenFile must not drop syncRoot; OI-1003-Q177). checks.tcfs-user-config-render = assert import ./nix/tests/tcfs-user-config-render.nix { inherit (pkgs) lib; }; pkgs.runCommand "tcfs-user-config-render" { } "touch $out"; devShells.default = pkgs.mkShell { buildInputs = commonBuildInputs ++ (with pkgs; [ rustToolchain # Proto codegen protobuf # Security tooling age sops # Infrastructure opentofu kubectl kubernetes-helm kustomize # Build tooling go-task cargo-watch cargo-deny cargo-audit shellcheck jq # NATS natscli # Lazy hydration demo helpers awscli2 s5cmd minio-client openssh # Docs tooling lychee pandoc tectonic mermaid-cli # Dev tools git just yq-go ]); TCFS_RUST_TOOLCHAIN = rustVersion; shellHook = '' # Home Manager user profiles may carry an older rustc/cargo ahead # of Nix's shell paths. Keep the project-pinned toolchain first. export PATH="$PWD/target/debug:$PWD/target/release:${pkgs.lib.makeBinPath [ rustToolchain pkgs.go-task pkgs.just pkgs.shellcheck pkgs.jq.bin pkgs.awscli2 pkgs.s5cmd pkgs.minio-client ]}:$PATH" echo "tcfs devShell (tummycrypt monorepo)" echo " rustc --version # pinned toolchain should report ${rustVersion}" echo " just --list # show available recipes" echo " task --list # show go-task tasks" echo " cargo build # build workspace" echo " task dev # start local stack + watch" ''; } // pkgs.lib.optionalAttrs pkgs.stdenv.isLinux { ROCKSDB_INCLUDE_DIR = "${pkgs.rocksdb}/include"; ROCKSDB_LIB_DIR = "${pkgs.rocksdb}/lib"; }; } ) // { # NixOS modules (system-level) nixosModules.tcfsd = import ./nix/modules/tcfs-daemon.nix; # Home Manager modules (user-level) homeManagerModules.tcfs = import ./nix/modules/tcfs-user.nix; }; }