Skip to content
Merged
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
9 changes: 8 additions & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,17 @@
url = "github:SourceOS-Linux/sourceos-boot";
flake = false;
};
hellgraph-src = {
url = "github:SocioProphet/hellgraph";
flake = false;
};
nixos-generators = {
url = "github:nix-community/nixos-generators";
inputs.nixpkgs.follows = "nixpkgs";
};
};

outputs = { self, nixpkgs, nixos-apple-silicon, sops-nix, lampstand-src, sourceos-syncd-src, sourceos-boot-src, nixos-generators }:
outputs = { self, nixpkgs, nixos-apple-silicon, sops-nix, lampstand-src, sourceos-syncd-src, sourceos-boot-src, hellgraph-src, nixos-generators }:
let
lib = nixpkgs.lib;
systems = [ "x86_64-linux" "aarch64-linux" ];
Expand All @@ -50,6 +54,7 @@
sourceos-syncd = pkgs.callPackage ./packages/sourceos-syncd/default.nix {
inherit sourceos-syncd-src;
};
hellgraph = pkgs.callPackage ./packages/hellgraph/default.nix { inherit hellgraph-src; };
sourceos-boot = pkgs.callPackage ./packages/sourceos-boot/default.nix {
inherit sourceos-boot-src;
};
Expand Down Expand Up @@ -152,6 +157,7 @@

nixosModules = {
sourceos-syncd = import ./modules/nixos/sourceos-syncd/default.nix;
hellgraph = import ./modules/nixos/hellgraph/default.nix;
# Public edition profiles. The installer (scripts/install-image.sh
# --edition) composes one of these with a freshly generated
# hardware-configuration.nix on the target — no per-machine config is
Expand Down Expand Up @@ -197,6 +203,7 @@
modules = [
sops-nix.nixosModules.sops
self.nixosModules.sourceos-syncd
self.nixosModules.hellgraph
./hosts/canary-x86_64/default.nix
];
};
Expand Down
15 changes: 14 additions & 1 deletion governance/adr/ADR-0001-nix-to-guix.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,20 @@
"parity_doc": "guix/NIX_BASELINE.md",
"status": "parity",
"phases": ["spike", "parity", "cutover", "done"],
"waivers": [],
"waivers": [
{
"path": "packages/hellgraph/default.nix",
"reason": "hellgraph package: Guix equivalent guix/packages/hellgraph.scm authored in the same PR (#326); parity table updated in guix/NIX_BASELINE.md",
"guix_equivalent": "guix/packages/hellgraph.scm",
"pr": "326"
},
{
"path": "modules/nixos/hellgraph/default.nix",
"reason": "hellgraph NixOS service module: Guix shepherd service equivalent guix/services/hellgraph.scm authored in the same PR (#326); parity table updated in guix/NIX_BASELINE.md",
"guix_equivalent": "guix/services/hellgraph.scm",
"pr": "326"
}
],
"policy": {"new_from": "forbid", "new_from_reason": "no new Nix while the estate migrates to Guix; the gate blocks only newly-ADDED .nix, not maintenance of existing ones"},
"gate": "scripts/adr_swap_gate.py (Firewall #1, CI-required); flip status to 'cutover' then 'done' to relax",
"provenance": "source-os#314 (guix/channels.scm + guix/system/workstation.scm) + #315 (desktop.scm + guix/NIX_BASELINE.md), MERGED 2026-08-02. As of 2026-08-04: 63 .nix vs 3 .scm on main — parity phase."
Expand Down
2 changes: 2 additions & 0 deletions guix/NIX_BASELINE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Status legend: ☐ not started · ◐ spiked · ☑ parity proven on a Linux run
| `lampstand` | `packages/search/lampstand.nix` | Guix package | ☐ |
| `sourceos-syncd` | `packages/sourceos-syncd/default.nix` | Guix package | ☐ |
| `sourceos-boot` | `packages/sourceos-boot/default.nix` | Guix package | ☐ |
| `hellgraph` | `packages/hellgraph/default.nix` | `guix/packages/hellgraph.scm` — copy-build-system, wraps Node.js entrypoints | ◐ |

## 2. Images (`flake.nix`, via `nixos-generators`)
| Nix image | Def | Guix equivalent (`guix system image -t …`) | Status |
Expand All @@ -32,6 +33,7 @@ Status legend: ☐ not started · ◐ spiked · ☑ parity proven on a Linux run
| mesh | `modules/nixos/mesh` | `guix/services/mesh.scm` | ☐ |
| sourceos-shell | `modules/nixos/sourceos-shell` | `guix/services/sourceos-shell.scm` | ☐ |
| sourceos-syncd | `modules/nixos/sourceos-syncd` | `guix/services/sourceos-syncd.scm` | ☐ |
| hellgraph | `modules/nixos/hellgraph` | `guix/services/hellgraph.scm` — shepherd service, superpeer opt-in, credential file | ◐ |

## 4. Checks / tests (`tests/`, `flake.nix` `checks`)
The primary gate is Layer-1 deterministic boot; the contract/smoke suite asserts
Expand Down
56 changes: 56 additions & 0 deletions guix/packages/hellgraph.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
;;; SourceOS Guix package — HellGraph graph engine.
;;;
;;; Parity target for packages/hellgraph/default.nix.
;;; HellGraph ships a committed, self-contained `ts/dist` with zero runtime
;;; npm dependencies — the daemon runs on `node` alone. We install bin/ + ts/
;;; preserving the relative layout (bin/*.mjs → ../ts/dist/index.mjs) and
;;; wrap the two public entrypoints.
;;;
;;; Build:
;;; guix time-machine -C guix/channels.scm -- \
;;; build -f guix/packages/hellgraph.scm

(define-module (sourceos packages hellgraph)
#:use-module (guix packages)
#:use-module (guix build-system copy)
#:use-module (guix licenses)
#:use-module (gnu packages node))

(define-public hellgraph
(package
(name "hellgraph")
(version "0.1.0")
;; Source is the hellgraph repo's top-level (bin/ + ts/ are the install targets).
;; The caller substitutes the actual origin (local or git fetch) when building.
(source #f)
(build-system copy-build-system)
(arguments
`(#:install-plan
'(("bin" "libexec/hellgraph/bin")
("ts" "libexec/hellgraph/ts"))
#:phases
(modify-phases %standard-phases
(add-after 'install 'wrap-entrypoints
(lambda* (#:key inputs outputs #:allow-other-keys)
(let* ((out (assoc-ref outputs "out"))
(node (string-append (assoc-ref inputs "node") "/bin/node"))
(lib (string-append out "/libexec/hellgraph")))
(for-each
(lambda (ep)
(let ((wrapper (string-append out "/bin/" ep))
(target (string-append lib "/bin/" ep ".mjs")))
(mkdir-p (string-append out "/bin"))
(call-with-output-file wrapper
(lambda (p)
(format p "#!/bin/sh\nexec ~a ~a \"$@\"\n" node target)))
(chmod wrapper #o755)))
'("hellgraph-superpeer" "hellgraph-agent-ingest")))
#t)))))
(inputs
(list node))
(synopsis "HellGraph AtomSpace graph engine — always-on local graph service")
(description
"HellGraph serves the canonical graph over HTTP (local-only by default;
p2p superpeer mode opt-in via HELLGRAPH_BOOTSTRAP_KEY). Ships a committed,
pre-built @code{ts/dist} with no npm runtime dependencies.")
(license expat)))
78 changes: 78 additions & 0 deletions guix/services/hellgraph.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
;;; SourceOS Guix service — HellGraph always-on graph service.
;;;
;;; Parity target for modules/nixos/hellgraph/default.nix.
;;; Creates a shepherd service (Guix's init-service abstraction) that mirrors
;;; the NixOS module: local-only by default, p2p superpeer opt-in via a
;;; credential file, extra environment passthrough.
;;;
;;; Usage in a system config:
;;; (use-modules (sourceos services hellgraph))
;;; ...
;;; (services (cons* (service hellgraph-service-type
;;; (hellgraph-configuration
;;; (hellgraph hellgraph-pkg)
;;; (superpeer? #f)))
;;; %base-services))

(define-module (sourceos services hellgraph)
#:use-module (gnu services)
#:use-module (gnu services shepherd)
#:use-module (guix gexp)
#:use-module (guix records)
#:use-module (ice-9 match)
#:export (hellgraph-configuration
hellgraph-service-type))

;;; Configuration record — mirrors the NixOS module options.
(define-record-type* <hellgraph-configuration>
hellgraph-configuration
make-hellgraph-configuration
hellgraph-configuration?
;; The hellgraph package (must export hellgraph-superpeer in bin/).
(hellgraph hellgraph-configuration-hellgraph)
;; OPT-IN: join the p2p superpeer mesh. Default off — sovereign local graph.
(superpeer? hellgraph-configuration-superpeer?
(default #f))
;; Path to a file holding HELLGRAPH_BOOTSTRAP_KEY when superpeer? is #t.
;; The service reads it at start so the key never appears in the environment
;; of the process table.
(bootstrap-key-file hellgraph-configuration-bootstrap-key-file
(default #f))
;; Extra environment variables forwarded to hellgraph-superpeer
;; (e.g. port/store overrides).
(extra-environment hellgraph-configuration-extra-environment
(default '())))

;;; Build the shepherd service from the configuration.
(define (hellgraph-shepherd-service config)
(match-record config <hellgraph-configuration>
(hellgraph superpeer? bootstrap-key-file extra-environment)
(list
(shepherd-service
(documentation "HellGraph always-on graph service (local-only; p2p superpeer opt-in)")
(provision '(hellgraph))
(requirement '(networking))
(start
#~(make-forkexec-constructor
(list (string-append #$hellgraph "/bin/hellgraph-superpeer"))
#:environment-variables
(append
'#$extra-environment
(if '#$superpeer?
(if '#$bootstrap-key-file
(list (string-append "HELLGRAPH_BOOTSTRAP_KEY="
(call-with-input-file
'#$bootstrap-key-file
(lambda (p) (string-trim-right
(get-string-all p))))))
'())
(list "HELLGRAPH_SUPERPEER_DISABLED=1")))))
(stop #~(make-kill-destructor))))))

(define hellgraph-service-type
(service-type
(name 'hellgraph)
(extensions
(list (service-extension shepherd-root-service-type
hellgraph-shepherd-service)))
(description "Run the HellGraph graph engine as an always-on system service.")))
6 changes: 6 additions & 0 deletions hosts/canary-x86_64/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@
};

# ── sourceos-syncd ───────────────────────────────────────────────────────────
# ── HellGraph always-on graph service (local-only; p2p superpeer opt-in) ──────
sourceos.hellgraph = {
enable = true;
package = self.packages.x86_64-linux.hellgraph;
};

sourceos.syncd = {
enable = true;
package = syncdPkg;
Expand Down
63 changes: 63 additions & 0 deletions modules/nixos/hellgraph/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{ config, lib, pkgs, ... }:

let
cfg = config.sourceos.hellgraph;
in
{
options.sourceos.hellgraph = {
enable = lib.mkEnableOption "HellGraph always-on graph service (local-only; p2p superpeer opt-in)";

package = lib.mkOption {
type = lib.types.package;
description = "The hellgraph package to run.";
};

superpeer = {
enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "OPT-IN: join the p2p superpeer mesh. Default off — the graph service is local-only and sovereign.";
};
bootstrapKeyFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = "Path to a file holding HELLGRAPH_BOOTSTRAP_KEY (read via LoadCredential) when p2p is enabled.";
};
};

extraEnvironment = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = { };
description = "Extra environment for startSuperPeerFromEnv (e.g. port/store overrides).";
};
};

config = lib.mkIf cfg.enable {
systemd.services.hellgraph = {
description = "HellGraph always-on graph service (local-only; p2p superpeer opt-in)";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];

# local-only by default: no bootstrap key means startSuperPeerFromEnv serves the local graph
# only. p2p federation is opt-in and the key is supplied via a systemd credential, never inline.
environment = cfg.extraEnvironment;

serviceConfig = {
Type = "simple";
DynamicUser = true;
StateDirectory = "hellgraph";
ExecStart = "${cfg.package}/bin/hellgraph-superpeer";
Restart = "always";
RestartSec = 5;
# hardening
NoNewPrivileges = true;
ProtectSystem = "strict";
ProtectHome = true;
PrivateTmp = true;
} // lib.optionalAttrs (cfg.superpeer.enable && cfg.superpeer.bootstrapKeyFile != null) {
LoadCredential = [ "bootstrap-key:${cfg.superpeer.bootstrapKeyFile}" ];
Environment = [ "HELLGRAPH_BOOTSTRAP_KEY_FILE=%d/bootstrap-key" ];
};
};
};
}
33 changes: 33 additions & 0 deletions packages/hellgraph/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{ lib, stdenvNoCC, nodejs, makeWrapper, hellgraph-src }:

# HellGraph ships a COMMITTED, self-contained `ts/dist` (package.json has ZERO runtime dependencies;
# the dist imports only `node:` builtins + relative paths), so there is no npm build and no
# node_modules — the daemon runs on `node` alone. We install bin/ + ts/ preserving their relative
# layout (bin/*.mjs import ../ts/dist/index.mjs) and wrap the entrypoints that exist on main.
stdenvNoCC.mkDerivation {
pname = "hellgraph";
version = "0.1.0";
src = hellgraph-src;

nativeBuildInputs = [ makeWrapper ];
dontConfigure = true;
dontBuild = true;

installPhase = ''
runHook preInstall
mkdir -p $out/libexec/hellgraph $out/bin
cp -r bin ts $out/libexec/hellgraph/
for entry in hellgraph-superpeer hellgraph-agent-ingest; do
makeWrapper ${nodejs}/bin/node $out/bin/$entry \
--add-flags $out/libexec/hellgraph/bin/$entry.mjs
done
runHook postInstall
'';

meta = {
description = "HellGraph AtomSpace graph engine — always-on local graph service (serves the graph over HTTP; p2p superpeer opt-in via HELLGRAPH_BOOTSTRAP_KEY).";
license = lib.licenses.mit;
platforms = lib.platforms.linux;
mainProgram = "hellgraph-superpeer";
};
}
Loading