A CLI to manage git repositories over ssh
Find a file
2025-08-26 11:37:31 +02:00
.ssh feat(nts): add $NTS_CONFIG env variable to overwrite config directory 2025-05-27 11:33:17 +02:00
crates chore(nts): v1.4.0 2025-08-26 10:51:49 +02:00
docker feat: setup Docker container to run nts-srv watch 2025-06-02 13:11:58 +02:00
nix fix(hydraJobs): wrong input value 2025-08-26 11:37:31 +02:00
scripts chore(version): switch to podman for build & publish 2025-06-17 09:35:45 +02:00
.dockerignore wip: feat: add docker container setup 2025-05-22 10:55:53 +02:00
.envrc feat: added nix shell & packaging 2025-03-31 11:54:03 +00:00
.gitignore chore: setup extra dev shell for nts docker container 2025-07-29 16:04:18 +02:00
.nts-srv.toml feat(nts-srv+nextcloud): add option to share result folder with different user 2025-06-02 09:33:45 +02:00
.sops.yaml feat(nts-srv): add config command 2025-05-27 16:04:34 +02:00
Cargo.lock chore(nts): v1.4.0 2025-08-26 10:51:49 +02:00
Cargo.toml feat(nts-srv+nextcloud): add option to share result folder with different user 2025-06-02 09:33:45 +02:00
compose.yaml chore: setup extra dev shell for nts docker container 2025-07-29 16:04:18 +02:00
Dockerfile fix(docker): whitelist data directory after git is installed 2025-06-02 14:45:09 +02:00
flake.lock feat: added nix shell & packaging 2025-03-31 11:54:03 +00:00
flake.nix chore: clean up flake.nix with own nix/ folder 2025-07-07 10:35:20 +02:00
README.md docs(nts): add usage for 'nts move' 2025-08-26 10:47:30 +02:00

nts

nts is a single user git provider over ssh. It has build in single-threaded "CI" and therefore mainly focused on notes-syncing.

Client side

There is a client side cli (nts), aimed at providing wrapper commands to manage git repositories on a remote git host.

The main commands are mainly client side (create, remove, clone), while others rely on the server-side binary nts-srv (list and search).

Server side

The server-side binary enhances the client-side functionality. It additionally provides the option to run a "CI", which builds the git repositories (on change) via nix build and uploads the results to a ResultStore.

Security

nts tries to be as simple and light-weight as possible. Therefore it relys a lot on other technologies that I consider "already installed" (that play nice with my setup).

nts works mainly by executing commands directly on the server, saving git repositories directly on the server and building directly on the server without a lot of sandboxing (aka none).

You should therefore be carefull where to put your ssh keys (as usually) and take care of sandboxing by creating a seperate user for nts.

Usage (client side)

nts provides the basic utilities to manage git repositories on a remote server:

  • create
  • delete
  • move
  • list / search
  • clone

Create a repository

nts add PATH
nts new PATH

This will create a git repository on the remote host (by default under $HOST/data), however the data direcotry can be changed by the server-side environment variable: NTS_SRV_DATA.

The path can be as nested as one wants.

Under the hood this just calls git init --bare via ssh.

The path is always relative to the data directory and can start with ~/, ./, / or directly with the path (all mean the same thing).

Additionally one can pass the --clone flag, which will tell nts to directly clone the repository after creating it.

Delete a repository

nts rm PATH
nts remove PATH

This will delte a git repository on the remote host. The path specification is the same as in nts add.

Under the hood this just calls rm PATH via ssh.

Move a repository

nts mv SRC DEST
nts move SRC DEST

This will move a repository from one place to another.

Unter the hood this just calls mv SRC DEST via ssh. So it should behaive excatly like mv

List / Search repositories

This command relies on nts-srv present.

nts ls
nts list

Will list all git repositories currently present inside the data directory on the host.

nts query SEARCH
nts search SEARCH

Will filter the repository list to only the repositories matching the given SEARCH query. Currently the search string is fuzzy-matched, but this might change in the future to provide a better search experience.

Clone a repository

nts clone REMOTE [LOCAL]

This is essentially just a wrapper around git clone that allows to use the same PATH notation as in every other command.

It additionally also handles the host configuration the same as every other command and therefore makes cloning a bit easier.

Configuration

nts needs a bit of configuration to work correctly, mainly it needs to know which remote host to use.

The configuration file can be edited via:

nts config OPTION VALUE

The config file will be placed in one of the following direcotires (which ever is defined first):

  1. $NTS_CONFIG
  2. $LOCALAPPDATA/nts
  3. $XDG_CONFIG_HOME/nts
  4. $HOME/.config/nts

the config file is named config.json.

The following options are supported:

Hostname

via cli:

nts config host HOSTNAME

via config.json:

{
    "host": "HOSTNAME"
}

Specifies which ssh hostname to connect to. The host has to be setup with a ssh-key that needs no more authentication, as nts calls ssh internally.

Ssh config

via cli:

nts config ssh PATH

via config.json:

{
    "ssh_config": "PATH"
}

Overwrites the default ssh config file (~/.ssh/config) that will be used.

CI runner

The CI runner is a server-side feature that needs nts-srv installed and correctly setup (see docker documentation).

When activated, nts-srv will listen on changed inside the data directory and try to build git repositories on change with nix. For this to work the repository has to have a flake.nix which exposes packages.default (that derivation will be build).

Afterwards the contents will be uploaded to the ResultStore. If the derivation is only one file, it will be placed inside a folder, otherwise the folder structure of the result will be used.

Nextcloud

The nextcloud ResultStore uploads the build artifacts to a nextcloud instance. The path will be the same as the relative path of the git repository inside the data directory.

Optionally it will create a share with another user, which allows for an alt-account to manage the artifact repositories (for improved security).

Installation

nts works best when nts-srv is also installed (on the remote), but it can be used on its own (to some extend).

nts

The client side executable can be installed via cargo:

cargo install --git https://git.solarpunk.social/krauterbaquette/nts nts

Nix

You can also run nts via nix:

nix run git+https://git.solarpunk.social/krauterbaquette/nts

To include in your system configuration add:

{
    inputs = {
        nts = {
            url = "git+https://git.solarpunk.social/krauterbaquette/nts";
            inputs.nixpkgs.follows = "nixpkgs";
        };
    };
}

nts will be accessable via: inputs.nts.packages.${system}.default

nts-srv

The server side executable can be installed via cargo:

cargo install --git https://git.solarpunk.social/krauterbaquette/nts nts-srv

But has to be sym-linked to ~/.local/bin/nts-srv, because nts will only search there for it.

Default location of the installed binary is: $HOME/.cargo, see here

After installing nts-srv you can start the "CI" with nts-srv watch.

Nix

On NixOS maschines nts-srv can be installed "bare-metal", meaning it will run under a user on said maschine. You first have to include this repo as an flake-input:

{
    inputs = {
        nts = {
            url = "git+https://git.solarpunk.social/krauterbaquette/nts";
            inputs.nixpkgs.follows = "nixpkgs";
        };
    };
}

You can then import the module:

{ inputs, ... }: {
    flake.nixosConfigurations.HOSTNAME = {
        
        imports = [
            inputs.nts.nixosModules.nts-srv
        ];

        ... 

    };
}

This will expose a new service:

{
    flake.nixosConfigurations.HOSTNAME = {
        
        ...

        services.nts-srv = { ... };

    };
}

With the following options:

Option Default Description
enable false Wether to setup nts-srv
package self' .packages.server The nts-srv package to use
user.name nts Username which will run nts-srv and own the repos. A system user with the given username will be created
user.home /var/lib/nts Home directory for the user. The repos will be in the data subfolder.
user.authorizedKeyFile [ ] List of ssh public key files for said user
user.authorizedKeys [ ] List of ssh public keys for said user
config.noCI false This will disable the "CI" (not run nts-srv watch)
config.file { } Here you can specify the contents of the nts-srv config file as nix values (that get transformed to toml)

The environment will be setup by the nts-srv-init systemd service, which will afterwards start the nts-srv-ci service, given that config.noCI = false.

You can see the logs for the CI service with journalctl -u nts-srv-ci or systemctl status nts-srv-ci.

Docker

On any other distro that NixOS it is recommended to use the docker image provided at dock.woisek.de/tofubar/nts-srv.

This is currently not publicly available and I am stil searching for an option to publish my docker builds (other than docker hub).

In the mean time you can compile the docker image yourself.

You can use docker-compose with a setup like this:

services:
  nts-srv:
    container_name: nts-srv
    restart: unless-stopped
    image: dock.woisek.de/tofubar/nts-srv:latest
    ports:
      # allow ssh connections to port 22
      - "127.0.0.1:3030:22"
    environment:
      # start nts-srv watch
      NTS_SRV_CI: "true"
    volumes: 
      # set ssh public key
      - type: bind
        source: .ssh/id_local_ed25519.pub
        target: /home/nts/.ssh/authorized_keys
      # set nts config -> important for CI config
      - type: bind
        source: .nts-srv/docker.toml
        target: /home/nts/.config/nts-srv/config.toml
      # create data volume for easier backups
      - ./data.docker:/home/nts/data

The supportet tags are named after the nts-srv version (e.g. version 1.2.2 has the tag name 1.2.2). There is also the latest tag which points to the currently latest version.

Bare metal

A bare metal setup is not recommended (other than with NixOS). If you still want to check out nix/module.nix and try to translate that to "normal linux".

Configuration (nts)

nts can be configured via the cli with nts config. See:

nts config --help

The configuration is saved inside a config.json file, in one of these direcotries:

  1. $NTS_CONFIG
  2. $XDG_CONFIG_HOME/nts
  3. $HOME/.config/nts

They will be searched in this order and the first one that is found will be used.

The config.json file can contain the following values:

{
    version: 1, // [constant]
    host: String, // ssh host to connect to e.g. notes
    ssh_config: Option<String> // path to ssh config to use [default: ~/.ssh/config]
}

version specifies the config file version for better migration later on.

The host must be set and have an identitation file configured, so nts can connect to the host without user interaction.

Configuration (nts-srv)

nts-srv can be configured (in theory) via the cli with nts-srv config. see:

nts-srv config --help

However most of the time you want to create the config.toml before hand. It must be placed inside one of the following direcotries:

  1. $NTS_SRV_CONFIG
  2. $XDG_CONFIG_HOME/nts-srv
  3. $HOME/.config/nts-srv

The config.toml file can contain the following values:

version = 1 # [constant]
build = Option<String> # base directory for CI builds (they will be placed inside "$build/result") [default: $HOME]
skip_nix_gc = Option<bool> # if true `nts watch` wont start a garbage collect thread [default: false]
nix_exe = Option<String> # path to nix executable [default: nix]
[nextcloud] # use nextcloud as a ResultStore for `nts watch` [optional]
url = String # base url of the nextcloud instance (http://example.nextcloud.com)
user = String # username to use for the upload
share_to = Option<String> # when set, the uploaded folder will be shared (readonly) with the given username
# eiter password or password_file must be set
password = Eiter<String> # password for the user account
password_file = Eiter<String> # file containing the user password. The fill will be trimmed at the start & end

Keep in mind, that this way your password can be read in clear text. For now this is the best solution i could come up with, but you should make sure that the config file / password file can only be ready by your user.

Why toml and json?

The nts configuration file was created way before the need for an nts-srv config file. At this time i basically only knew json config file. By the time nts-srv got its config file I discovered the beauty and simplicity of toml config files so nts-srv got one.

nts stil has the json file because of backwards compatability (which is the best joke i've ever told), but most likly this will change in the future when I become interested in creating a migration system.

I am the only user btw

Development

It is recommended the use the nix package manager while developing.

There are three devShells available:

  1. default - contains rust tools for development
  2. nts - setup a local dev config for nts which can connect to a local docker container
  3. nts-srv - setup a local dev config for nts-srv. This way you can run nts-srv on the local maschine or run the docker container

Docker container

Run:

docker compose up --build

to start a local nts-srv container on 127.0.0.1:3030. When inside the nts devShell you can then

rm .ssh/known_hosts # remove old public key
ssh local -F .ssh/config echo hello # save the new public key
cargo r --bin nts -- list # use nts with the local docker container

When not using nix you can see inside the nix/dev.nix on how to recreate the envrionment.

This project uses sops to save credentials for a test nextcloud account. You might setup your own there / ask for access.

Publish

To create a new version you can run nix run .#version, if you have write access to the docker registry.

See nix run .#version -- --help for usage information.