Noah's NUR repo
Nix 75%
Shell 19%
2%
JavaScript 2%
Python 2%
Makefile <1%

README.md

ngp-nur #

chiefnoah's NUR package repository.

Packages #

This repository currently exports:

  • chicago95-theme
  • celld
  • dir2opds
  • dnscontrol
  • foundryvtt_14_367
  • fx
  • janet-lsp
  • memphis98-icon-theme
  • opencode-v2
  • rea
  • retro-5-classic98-openbox-theme
  • windows-classic-theme
  • windows98-lxqt-theme
  • rcsh-language-server
  • tree-sitter-rcsh

Usage #

After this repository is registered in community NUR as chiefnoah, packages can be installed from the NUR namespace:

nix shell github:nix-community/NUR#legacyPackages.x86_64-linux.repos.chiefnoah.rcsh-language-server

Or from a NixOS configuration using the NUR overlay:

environment.systemPackages = [
  pkgs.nur.repos.chiefnoah.rcsh-language-server
];

REA #

The rea package provides the rea and rea-agents commands, the stdio MCP server, and the bundled skill. It pins the npm release and runtime dependencies.

nix run github:chiefnoah/ngp-nur#rea -- --help

REA uses environment variables and client-specific MCP configuration, not a standalone configuration file. No separate Home Manager module is necessary. Use the existing OpenCode module:

{ inputs, pkgs, ... }:
let
  rea = inputs.ngp-nur.packages.${pkgs.stdenv.hostPlatform.system}.rea;
in
{
  home.packages = [ rea ];
  programs.opencode = {
    enable = true;
    settings.mcp.rea = {
      type = "local";
      command = [ "${rea}/bin/rea" "mcp" ];
      enabled = true;
    };
  };
}

For other MCP clients, set the command to ${rea}/bin/rea and the arguments to [ "mcp" ]. Pass provider environment variables through that client. For an existing Ghidra installation, set GHIDRA_INSTALL_DIR and JAVA_HOME.

The package does not install analysis providers or change agent configuration. Static JavaScript analysis needs no provider. Native analysis requires a separate Hopper, Ghidra, or IDA installation. Use Nix to update the package, not rea update. Avoid rea setup for Home Manager-managed client files because it edits those files and registers an npm command instead of the Nix executable.

NixOS Modules #

celld #

The celld module runs named celld application fleets as hardened systemd services. By default, all instances share one native, single-node Garage service and its credentials. Each instance gets a bucket matching its name. No container runtime is used:

{ inputs, ... }:
{
  imports = [ inputs.ngp-nur.nixosModules.celld ];

  services.celld.instances.hello = {
    port = 8082;
    projects.hello = ./worker;
    primaryProject = "hello";
  };
}

The project directory must contain wrangler.json or wrangler.jsonc. It is copied to the Nix store, deployed before celld starts, and redeployed when its source changes. Do not put secrets in project sources because Nix store paths are readable by local users.

An instance may contain several Wrangler projects connected by service bindings. Set primaryProject to the project that receives incoming requests. Other projects are deployed first and the primary project is deployed last:

services.celld.instances.application = {
  port = 8082;
  listenAddress = "0.0.0.0";
  advertise = "odin:8082";

  projects = {
    authentication = {
      root = ./my-application;
      config = "workers/authentication";
    };
    api = {
      root = ./my-application;
      config = "workers/api";
    };
  };
  primaryProject = "api";
};

Using a shared source root allows Workers to import packages elsewhere in a monorepo. A plain path is shorthand for root = path; config = ".";.

The shared Garage service listens on 127.0.0.1:3900 by default. Configure it once through services.celld.garage; the module creates every instance bucket and grants the shared key access automatically:

services.celld.garage = {
  apiAddress = "127.0.0.1:3900";
  rpcAddress = "127.0.0.1:3901";
  environmentFile = config.age.secrets.celld-garage.path;
};

Module-managed Garage has replication factor 1 and provides no redundancy. For a production fleet, an instance can override the shared default with external S3-compatible storage:

{ config, inputs, ... }:
{
  imports = [ inputs.ngp-nur.nixosModules.celld ];

  services.celld.instances.production = {
    port = 8080;
    s3 = {
      bucket = "s3://celld-production";
      endpoint = "https://garage.example.com";
      region = "garage";
    };
    environmentFiles = [ config.age.secrets.celld-s3.path ];
    listenAddress = "0.0.0.0";
    advertise = "node-a.internal:8080";

    projects.api = ./worker;
    primaryProject = "api";
  };
}

The S3 credentials file should define AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. Instance environmentFiles are loaded after the shared Garage credentials, so they may override credentials for that instance. Peer traffic is not protected by TLS; advertised addresses should remain on a trusted private network or encrypted overlay.

DNSControl #

The dnscontrol module generates provider-neutral DNSControl configuration and provides dnscontrol-preview and dnscontrol-push commands. Import it from the flake and declare providers and zones:

{ config, inputs, ... }:
{
  imports = [ inputs.ngp-nur.nixosModules.dnscontrol ];

  ngp.dnscontrol = {
    enable = true;

    environmentFiles = [ config.age.secrets.porkbun.path ];
    providers.porkbun = {
      type = "PORKBUN";
      settings = {
        api_key = "$PORKBUN_API_KEY";
        secret_key = "$PORKBUN_SECRET_API_KEY";
      };
    };

    zones."example.com" = {
      registrar = "porkbun";
      dnsProviders = [ "porkbun" ];
      records = [
        {
          type = "A";
          args = [
            "@"
            "192.0.2.1"
          ];
          ttl = 600;
        }
        {
          type = "MX";
          args = [
            "@"
            10
            "mail.example.com."
          ];
        }
      ];
    };
  };
}

Secrets may be supplied as a complete runtime credentialsFile, through environmentFiles, or as individual providers.<name>.secretFiles.<field> paths. These options accept runtime strings such as agenix paths and do not copy secret contents to the Nix store.

Run sudo dnscontrol-preview before applying changes with sudo dnscontrol-push. DNS is never changed during NixOS activation. An optional ngp.dnscontrol.apply service and timer can automate pushes after the configuration has been reviewed.

Local Checks #

Run the same restricted evaluation check used by NUR:

nix-env -f . -qa \* --meta --xml \
  --allowed-uris https://static.rust-lang.org \
  --option restrict-eval true \
  --option allow-import-from-derivation true \
  --drv-path --show-trace \
  -I nixpkgs=$(nix-instantiate --find-file nixpkgs) \
  -I "$PWD"

Build cacheable outputs locally:

nix-build ci.nix -A cacheOutputs

Spindle cache uploads #

The Spindle build uploads ci.nix outputs to Misaki's NCPS cache over HTTPS. The same cache serves those paths at https://cache.ngp.computer.

Set NIX_CACHE_PUSH_PASSWORD as a Tangled repository secret before the first build. Use the ci account password from /tmp/opencode/nix-cache-push-password. Remove that local file after you save the secret in Tangled.

Deploy the Misaki configuration from ~/repos/nixos before you run this workflow. Its nix-cache-push.age file holds the hashed Basic auth password.

Spindle passes repository secrets to every workflow step. This workflow runs on pushes, schedules, and manual requests; it does not run on pull requests. Odin's Spindle workflow timeout is two hours for the three nixpkgs builds.

Registering With NUR #

Open a pull request against nix-community/NUR that adds this repository to repos.json:

{
  "repos": {
    "chiefnoah": {
      "url": "https://github.com/chiefnoah/ngp-nur"
    }
  }
}

Only commit the repos.json change in the NUR pull request. The community NUR automation updates repos.json.lock.