Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,35 @@ Or from a checkout: `docker compose up -d` (see `docker-compose.yml`).
2. Attach a **volume** mounted at `/data`.
3. That's it — the server listens on Railway's `PORT` automatically.

### NixOS
Add this repository as a flake input and add the module to your configuration:
```nix
{
crosspoint-sync.url = "github:rogierknoester/crosspoint-sync";
crosspoint-sync.inputs.nixpkgs.follows = "nixpkgs";
...
}: {
nixosConfigurations = {
myServer = nixpkgs.lib.nixosSystem {
...
modules = [
./configuration.nix
crosspoint-sync.nixosModules.crosspoint-sync
];
};
};
}
```
Comment thread
rogierknoester marked this conversation as resolved.

Now you can enable it in your `configuration.nix`:
```nix
services.crosspoint-sync = {
enable = true;
port = 8080;
registration = true;
};
```

### Bare Node (≥ 22.13)

```sh
Expand Down
61 changes: 61 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
description = "crosspoint-sync flake";

inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
};

outputs =
{
nixpkgs,
flake-utils,
...
}:
{
nixosModules = {
crosspoint-sync = import ./nix/module.nix;
};
}
// flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = import nixpkgs { inherit system; };
crosspoint-sync = pkgs.callPackage ./nix/package.nix { };
in
{
packages = {
inherit crosspoint-sync;
default = crosspoint-sync;
};
}
);
}
150 changes: 150 additions & 0 deletions nix/module.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
{
pkgs,
lib,
config,
...
}:

let

inherit (lib)
mkOption
mkEnableOption
mkIf
types
;
cfg = config.services.crosspoint-sync;
crosspoint-sync = pkgs.callPackage ./package.nix { };

filename =
types.addCheck types.str (v: v != "" && !(lib.hasInfix "/" v) && v != "." && v != "..")
// {
description = "a filename; cannot traverse directories or be an absolute path";
};
in
{

options.services.crosspoint-sync = {
enable = mkEnableOption "Enable crosspoint-sync server";

port = mkOption {
type = types.port;
description = "Port to run crosspoint-sync server on";
default = 8080;
};
Comment thread
coderabbitai[bot] marked this conversation as resolved.

databaseFile = mkOption {
type = filename;
description = "Filename of the SQLite database in the state directory; ";
default = "crosspoint.db";
};
Comment thread
coderabbitai[bot] marked this conversation as resolved.

registration = mkOption {
type = types.bool;
description = "Have registration enabled or not";
default = true;
};

tokenEncryptionKeyFile = mkOption {
type = types.nullOr types.str;
description = "Path to the secret that contains the encryption key";
};

authRateLimit = mkOption {
type = types.int;
description = "Per-IP limit on registrations";
default = 30;
};

user = mkOption {
type = types.str;
description = "User to run crosspoint-sync with";
default = "crosspoint-sync";
};

group = mkOption {
type = types.str;
description = "Group to run crosspoint-sync with";
default = "crosspoint-sync";
};

};

config = mkIf cfg.enable {
systemd.services.crosspoint-sync = {
description = "crosspoint-sync server";
wantedBy = [ "multi-user.target" ];

serviceConfig = {
Type = "simple";
ExecStart =
if (cfg.tokenEncryptionKeyFile != null) then
pkgs.writeShellScript "crosspoint-sync-credential-loader" ''
export TOKEN_ENC_KEY="$(cat "$CREDENTIALS_DIRECTORY/TOKEN_ENC_KEY_FILE")"
exec ${lib.getExe crosspoint-sync}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
''
else
"${lib.getExe crosspoint-sync}";
Restart = "on-failure";

User = cfg.user;
Group = cfg.group;
LoadCredential = mkIf (
cfg.tokenEncryptionKeyFile != null
) "TOKEN_ENC_KEY_FILE:${cfg.tokenEncryptionKeyFile}";
StateDirectory = "crosspoint-sync";
ProtectSystem = "strict";
ProtectHome = true;
NoNewPrivileges = true;
PrivateDevices = true;
PrivateTmp = true;
PrivateUsers = !(cfg.port < 1024);
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectKernelLogs = true;
ProtectControlGroups = true;
ProtectClock = true;
ProtectHostname = true;
ProtectProc = "invisible";
ProcSubset = "pid";
RestrictNamespaces = true;
RestrictSUIDSGID = true;
LockPersonality = true;
UMask = "0077";
RemoveIPC = true;
AmbientCapabilities = if (cfg.port < 1024) then "CAP_NET_BIND_SERVICE" else lib.mkForce "";
CapabilityBoundingSet = if (cfg.port < 1024) then "CAP_NET_BIND_SERVICE" else lib.mkForce "";
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
];
SystemCallFilter = [ "@system-service" ];
SystemCallErrorNumber = "EPERM";
RestrictRealtime = true;

};
Comment thread
coderabbitai[bot] marked this conversation as resolved.

environment = {
PORT = builtins.toString cfg.port;
DATABASE_PATH = "/var/lib/crosspoint-sync/${cfg.databaseFile}";
REGISTRATION_DISABLED = if !cfg.registration then "true" else "false";
AUTH_RATE_LIMIT_PER_MINUTE = builtins.toString cfg.authRateLimit;
};

};

networking.firewall.allowedTCPPorts = [ cfg.port ];

users = {
users.crosspoint-sync = mkIf (cfg.user == "crosspoint-sync") {
description = "crosspoint-sync service user";
isSystemUser = true;
group = cfg.group;
};

groups.crosspoint-sync = mkIf (cfg.group == "crosspoint-sync") { };
};

};

}
35 changes: 35 additions & 0 deletions nix/package.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{ pkgs, lib }:

with pkgs;
buildNpmPackage {
pname = "crosspoint-sync";
version = "git";
src = ../.;

npmDeps = importNpmLock {
npmRoot = ../.;
};

npmConfigHook = importNpmLock.npmConfigHook;

buildPhase = ''
npm run build
'';

installPhase = ''
runHook preInstall
mkdir -p $out/lib $out/bin
cp -r package.json dist node_modules assets migrations $out/lib/
makeWrapper ${lib.getExe nodejs} $out/bin/crosspoint-sync \
--add-flags "$out/lib/dist/index.js"
runHook postInstall
'';

nativeBuildInputs = [ pkgs.makeWrapper ];

meta = {
description = "Lightweight KoSync Server for Syncing Crosspoint/CrossInk stats & progress";
homepage = "https://github.com/crosspoint-reader/crosspoint-sync";
mainProgram = "crosspoint-sync";
};
}