2 Commits

Author SHA1 Message Date
a5bc992590 fix(rpm): explicitly Provides user(cortex)/group(cortex)
Some checks failed
CI / Format, lint, build, test (push) Successful in 1m4s
CI / Build cortex SRPM (push) Successful in 44s
CI / Build neuron SRPM (push) Successful in 1m46s
CI / Publish cortex to COPR (push) Successful in 8m49s
CI / Publish neuron to COPR (push) Successful in 9m51s
CI / Bump version in source (push) Failing after 47s
dnf5 was silently rejecting neuron-0.1.3 with "Nothing to do" because
it had an unresolvable Requires. Inspection showed:

  Requires: user(cortex)               ← unversioned
  Provides: user(cortex) = <base64>    ← versioned only, no unversioned

rpm's sysusers provides-generator only emits the unversioned user()
provide when the u-line is minimal. Our sysusers.conf specifies GECOS,
home dir, and shell, which pushes the generator to versioned-only.
The matching Requires (auto-generated from %attr(,,cortex) on config
files) is unversioned, so resolution failed silently.

Explicitly declare Provides: user(cortex) and Provides: group(cortex)
to guarantee the unversioned forms exist. group(cortex) was already
emitted unversioned but adding it for symmetry and to protect against
future generator changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 12:04:19 +03:00
5a86f7cc16 ci: dump COPR per-chroot build logs to CI output
Previously the COPR publish steps only surfaced copr-cli's status
updates (pending/importing/running). When a build failed, diagnosing
required clicking through to the COPR web UI. Now we submit with
--nowait, watch the build, then use copr-cli download-build to fetch
each chroot's builder-live.log and cat them as collapsible ::group::
blocks in the CI output.

Logic is factored into .gitea/scripts/copr-build.sh so cortex and
neuron jobs share it. Both COPR jobs now check out the repo to access
the script.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 11:41:15 +03:00
16 changed files with 171 additions and 150 deletions

61
.gitea/scripts/copr-build.sh Executable file
View File

@@ -0,0 +1,61 @@
#!/bin/bash
# Submit an SRPM to COPR, watch the build, and dump per-chroot build logs
# to stdout so they are captured in CI output.
#
# Usage: copr-build.sh <project> <srpm> [srpm...]
# Example: copr-build.sh helexa/cortex ./cortex-0.1.2-1.fc43.src.rpm
set -o pipefail
PROJECT="$1"
shift
if [ -z "$PROJECT" ] || [ "$#" -eq 0 ]; then
echo "usage: $0 <project> <srpm> [srpm...]" >&2
exit 2
fi
# Submit without waiting; capture the build ID from stdout.
SUBMIT_OUT=$(copr-cli build --nowait "$PROJECT" "$@")
echo "$SUBMIT_OUT"
BUILD_ID=$(echo "$SUBMIT_OUT" | grep -oP 'Created builds: \K[0-9]+' | head -n1)
if [ -z "$BUILD_ID" ]; then
echo "error: could not parse build ID from copr-cli output" >&2
exit 1
fi
echo
echo "Build $BUILD_ID submitted to $PROJECT"
echo "Follow live: https://copr.fedorainfracloud.org/coprs/build/$BUILD_ID"
echo
# Watch the build; captures status transitions to stdout. Exit non-zero
# on build failure, but defer propagating that until after we've fetched
# logs so the CI output contains diagnostics either way.
if copr-cli watch-build "$BUILD_ID"; then
STATUS=0
else
STATUS=$?
fi
# Fetch per-chroot results (logs + rpms). Anonymous download — no auth needed.
mkdir -p copr-logs
copr-cli download-build --dest copr-logs "$BUILD_ID" || {
echo "warning: failed to download build artifacts" >&2
}
# Dump each chroot's builder-live.log as a collapsible group.
for chroot_dir in copr-logs/*/; do
[ -d "$chroot_dir" ] || continue
chroot=$(basename "$chroot_dir")
log="${chroot_dir}builder-live.log"
if [ -f "$log" ]; then
echo
echo "::group::${chroot} builder-live.log"
cat "$log"
echo "::endgroup::"
fi
done
exit "$STATUS"

View File

@@ -24,6 +24,19 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Cache cargo registry and target
uses: actions/cache@v4
with:
path: |
~/.cargo/bin
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Ensure sccache with S3 support - name: Ensure sccache with S3 support
env: env:
RUSTC_WRAPPER: "" RUSTC_WRAPPER: ""
@@ -53,8 +66,6 @@ jobs:
if: startsWith(github.ref, 'refs/tags/v') if: startsWith(github.ref, 'refs/tags/v')
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Determine version - name: Determine version
id: version id: version
@@ -68,12 +79,6 @@ jobs:
sed -i '/\[workspace\.package\]/,/\[/{ s/^version = ".*"/version = "'"${VERSION}"'"/ }' Cargo.toml sed -i '/\[workspace\.package\]/,/\[/{ s/^version = ".*"/version = "'"${VERSION}"'"/ }' Cargo.toml
sed -i "s/^Version:.*/Version: ${VERSION}/" cortex.spec sed -i "s/^Version:.*/Version: ${VERSION}/" cortex.spec
- name: Generate changelog entry
uses: https://git.lair.cafe/actions/rpm-changelog@v1
with:
spec: cortex.spec
version: ${{ steps.version.outputs.VERSION }}
- name: Generate source tarball - name: Generate source tarball
run: | run: |
set -ex set -ex
@@ -113,8 +118,6 @@ jobs:
if: startsWith(github.ref, 'refs/tags/v') if: startsWith(github.ref, 'refs/tags/v')
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Determine version - name: Determine version
id: version id: version
@@ -126,37 +129,31 @@ jobs:
run: | run: |
VERSION="${{ steps.version.outputs.VERSION }}" VERSION="${{ steps.version.outputs.VERSION }}"
sed -i '/\[workspace\.package\]/,/\[/{ s/^version = ".*"/version = "'"${VERSION}"'"/ }' Cargo.toml sed -i '/\[workspace\.package\]/,/\[/{ s/^version = ".*"/version = "'"${VERSION}"'"/ }' Cargo.toml
sed -i "s/^Version:.*/Version: ${VERSION}/" helexa-neuron.spec sed -i "s/^Version:.*/Version: ${VERSION}/" neuron.spec
- name: Generate changelog entry
uses: https://git.lair.cafe/actions/rpm-changelog@v1
with:
spec: helexa-neuron.spec
version: ${{ steps.version.outputs.VERSION }}
- name: Generate source tarball - name: Generate source tarball
run: | run: |
set -ex set -ex
VERSION="${{ steps.version.outputs.VERSION }}" VERSION="${{ steps.version.outputs.VERSION }}"
tar czf /tmp/helexa-neuron-${VERSION}.tar.gz \ tar czf /tmp/neuron-${VERSION}.tar.gz \
--transform "s,^\.,helexa-neuron-${VERSION}," \ --transform "s,^\.,neuron-${VERSION}," \
--exclude='./target' \ --exclude='./target' \
--exclude='./.git' \ --exclude='./.git' \
--exclude='*.tar.gz' \ --exclude='*.tar.gz' \
--exclude='*.src.rpm' \ --exclude='*.src.rpm' \
. .
mv /tmp/helexa-neuron-${VERSION}.tar.gz . mv /tmp/neuron-${VERSION}.tar.gz .
- name: Vendor Rust dependencies - name: Vendor Rust dependencies
run: | run: |
VERSION="${{ steps.version.outputs.VERSION }}" VERSION="${{ steps.version.outputs.VERSION }}"
cargo vendor vendor/ cargo vendor vendor/
tar czf helexa-neuron-${VERSION}-vendor.tar.gz vendor/ tar czf neuron-${VERSION}-vendor.tar.gz vendor/
rm -rf vendor/ rm -rf vendor/
- name: Build SRPM - name: Build SRPM
run: | run: |
rpmbuild -bs helexa-neuron.spec \ rpmbuild -bs neuron.spec \
--define "_sourcedir $(pwd)" \ --define "_sourcedir $(pwd)" \
--define "_srcrpmdir $(pwd)" --define "_srcrpmdir $(pwd)"
@@ -171,34 +168,40 @@ jobs:
runs-on: fedora runs-on: fedora
needs: srpm-cortex needs: srpm-cortex
steps: steps:
- uses: actions/checkout@v4
- name: Download SRPM - name: Download SRPM
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
with: with:
name: srpm-cortex name: srpm-cortex
- name: Publish to COPR - name: Configure copr-cli
uses: https://git.lair.cafe/actions/copr-publish@v1 run: |
with: mkdir -p ~/.config
project: helexa/helexa echo "${{ secrets.COPR_CONFIG }}" > ~/.config/copr
srpm: "*.src.rpm"
copr-config: ${{ secrets.COPR_CONFIG }} - name: Submit build to COPR
run: bash .gitea/scripts/copr-build.sh helexa/cortex *.src.rpm
copr-neuron: copr-neuron:
name: Publish neuron to COPR name: Publish neuron to COPR
runs-on: fedora runs-on: fedora
needs: srpm-neuron needs: srpm-neuron
steps: steps:
- uses: actions/checkout@v4
- name: Download SRPM - name: Download SRPM
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
with: with:
name: srpm-neuron name: srpm-neuron
- name: Publish to COPR - name: Configure copr-cli
uses: https://git.lair.cafe/actions/copr-publish@v1 run: |
with: mkdir -p ~/.config
project: helexa/helexa echo "${{ secrets.COPR_CONFIG }}" > ~/.config/copr
srpm: "*.src.rpm"
copr-config: ${{ secrets.COPR_CONFIG }} - name: Submit build to COPR
run: bash .gitea/scripts/copr-build.sh helexa/neuron *.src.rpm
bump-version: bump-version:
name: Bump version in source name: Bump version in source
@@ -206,43 +209,21 @@ jobs:
needs: [copr-cortex, copr-neuron] needs: [copr-cortex, copr-neuron]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Determine version - name: Stamp version and push
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT"
- name: Stamp version
run: |
VERSION="${{ steps.version.outputs.VERSION }}"
sed -i '/\[workspace\.package\]/,/\[/{ s/^version = ".*"/version = "'"${VERSION}"'"/ }' Cargo.toml
sed -i "s/^Version:.*/Version: ${VERSION}/" cortex.spec
sed -i "s/^Version:.*/Version: ${VERSION}/" helexa-neuron.spec
cargo check --workspace 2>/dev/null || true
- name: Generate cortex changelog entry
uses: https://git.lair.cafe/actions/rpm-changelog@v1
with:
spec: cortex.spec
version: ${{ steps.version.outputs.VERSION }}
- name: Generate helexa-neuron changelog entry
uses: https://git.lair.cafe/actions/rpm-changelog@v1
with:
spec: helexa-neuron.spec
version: ${{ steps.version.outputs.VERSION }}
- name: Commit and push
env: env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: | run: |
VERSION="${{ steps.version.outputs.VERSION }}" VERSION="${GITHUB_REF#refs/tags/v}"
sed -i '/\[workspace\.package\]/,/\[/{ s/^version = ".*"/version = "'"${VERSION}"'"/ }' Cargo.toml
sed -i "s/^Version:.*/Version: ${VERSION}/" cortex.spec
sed -i "s/^Version:.*/Version: ${VERSION}/" neuron.spec
cargo check --workspace 2>/dev/null || true
git config user.name "Gitea Actions" git config user.name "Gitea Actions"
git config user.email "actions@git.lair.cafe" git config user.email "actions@git.lair.cafe"
git add Cargo.toml Cargo.lock cortex.spec helexa-neuron.spec git add Cargo.toml Cargo.lock cortex.spec neuron.spec
if git diff --cached --quiet; then if git diff --cached --quiet; then
echo "Nothing to commit for ${VERSION}" echo "Version already at ${VERSION}"
else else
git commit -m "chore: bump version to ${VERSION}" git commit -m "chore: bump version to ${VERSION}"
git remote set-url origin "https://gitea-actions:${GITEA_TOKEN}@git.lair.cafe/helexa/cortex.git" git remote set-url origin "https://gitea-actions:${GITEA_TOKEN}@git.lair.cafe/helexa/cortex.git"

View File

@@ -125,8 +125,7 @@ automatically. Clippy warnings must be resolved, not suppressed with
- One or more GPU nodes running mistral.rs on port 8080 - One or more GPU nodes running mistral.rs on port 8080
- Optionally a metrics-only node (no GPU) for Prometheus/Grafana - Optionally a metrics-only node (no GPU) for Prometheus/Grafana
- Each node runs `mistralrs serve` on port 8080 - Each node runs `mistralrs serve` on port 8080
- Gateway listens on port 31313 (API) and 31314 (metrics) - Gateway listens on port 8000 (API) and 9100 (metrics)
- neuron listens on port 13131 on each GPU host
- TLS terminated at gateway or via nginx; internal traffic is plaintext over WireGuard - TLS terminated at gateway or via nginx; internal traffic is plaintext over WireGuard
## Conventions ## Conventions
@@ -381,7 +380,7 @@ processes (one process per loaded model, each on its own port).
## neuron API ## neuron API
neuron exposes an HTTP API on port 13131 that cortex polls and calls. neuron exposes an HTTP API on port 9090 that cortex polls and calls.
``` ```
GET /discovery GET /discovery
@@ -425,8 +424,8 @@ endpoint. cortex.toml shrinks to:
```toml ```toml
[gateway] [gateway]
listen = "0.0.0.0:31313" listen = "0.0.0.0:8000"
metrics_listen = "0.0.0.0:31314" metrics_listen = "0.0.0.0:9100"
[eviction] [eviction]
strategy = "lru" strategy = "lru"
@@ -434,15 +433,15 @@ defrag_after_cycles = 50
[[neurons]] [[neurons]]
name = "beast" name = "beast"
endpoint = "http://beast.hanzalova.internal:13131" endpoint = "http://beast.hanzalova.internal:9090"
[[neurons]] [[neurons]]
name = "benjy" name = "benjy"
endpoint = "http://benjy.hanzalova.internal:13131" endpoint = "http://benjy.kosherinata.internal:9090"
[[neurons]] [[neurons]]
name = "quadbrat" name = "quadbrat"
endpoint = "http://quadbrat.hanzalova.internal:13131" endpoint = "http://quadbrat.hanzalova.internal:9090"
``` ```
On startup and periodically, cortex calls `GET /discovery` and On startup and periodically, cortex calls `GET /discovery` and
@@ -522,7 +521,7 @@ cortex/
│ │ └── metrics.rs # prometheus exporter (unchanged) │ │ └── metrics.rs # prometheus exporter (unchanged)
│ ├── neuron/ # node plane (replaces cortex-agent) │ ├── neuron/ # node plane (replaces cortex-agent)
│ │ └── src/ │ │ └── src/
│ │ ├── main.rs # binary entrypoint, axum server on :13131 │ │ ├── main.rs # binary entrypoint, axum server on :9090
│ │ ├── discovery.rs # nvidia-smi, device enumeration │ │ ├── discovery.rs # nvidia-smi, device enumeration
│ │ ├── health.rs # runtime GPU polling │ │ ├── health.rs # runtime GPU polling
│ │ ├── api.rs # HTTP handlers for /discovery, /models, etc. │ │ ├── api.rs # HTTP handlers for /discovery, /models, etc.
@@ -596,24 +595,16 @@ placement matching can be added incrementally.
Completed. Both packages have RPM specs, systemd units, and example configs. Completed. Both packages have RPM specs, systemd units, and example configs.
CI builds parallel SRPMs on tag push and publishes to separate COPR repos. CI builds parallel SRPMs on tag push and publishes to separate COPR repos.
- `cortex.spec` — installs the `cortex` binary. Package name keeps the - `cortex.spec` `helexa/cortex` COPR: binary, systemd unit, config files
short `cortex` because no Fedora package collides with it. - `neuron.spec``helexa/neuron` COPR: binary, systemd unit, config
- `helexa-neuron.spec` — installs the `neuron` binary under package name
`helexa-neuron`. Renamed from bare `neuron` to avoid collision with
Fedora's NEURON neural-simulation package
(https://src.fedoraproject.org/rpms/neuron); binary, systemd unit,
system user, and config dir all stay named `neuron` since those are
project-local contexts.
- `data/cortex.service`, `data/neuron.service` — systemd units - `data/cortex.service`, `data/neuron.service` — systemd units
- `cortex.example.toml`, `neuron.example.toml`, `models.example.toml` - `cortex.example.toml`, `neuron.example.toml`, `models.example.toml`
- CI: parallel `srpm-cortex` + `srpm-neuron` jobs, then parallel COPR - CI: parallel `srpm-cortex` + `srpm-neuron` jobs, then parallel COPR publish
publish to a single project `helexa/helexa` hosting both packages.
Install: Install:
```sh ```sh
dnf copr enable helexa/helexa dnf copr enable helexa/cortex && dnf install cortex # gateway host
dnf install cortex # gateway host dnf copr enable helexa/neuron && dnf install neuron # GPU nodes
dnf install helexa-neuron # GPU nodes
``` ```
### Phase 11: llama.cpp harness stub ### Phase 11: llama.cpp harness stub

8
Cargo.lock generated
View File

@@ -351,7 +351,7 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]] [[package]]
name = "cortex-cli" name = "cortex-cli"
version = "0.1.12" version = "0.1.2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@@ -366,7 +366,7 @@ dependencies = [
[[package]] [[package]]
name = "cortex-core" name = "cortex-core"
version = "0.1.12" version = "0.1.2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@@ -381,7 +381,7 @@ dependencies = [
[[package]] [[package]]
name = "cortex-gateway" name = "cortex-gateway"
version = "0.1.12" version = "0.1.2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"axum", "axum",
@@ -1184,7 +1184,7 @@ dependencies = [
[[package]] [[package]]
name = "neuron" name = "neuron"
version = "0.1.12" version = "0.1.2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",

View File

@@ -8,7 +8,7 @@ members = [
] ]
[workspace.package] [workspace.package]
version = "0.1.12" version = "0.1.2"
edition = "2024" edition = "2024"
license = "GPL-3.0-or-later" license = "GPL-3.0-or-later"
repository = "https://git.lair.cafe/helexa/cortex" repository = "https://git.lair.cafe/helexa/cortex"

View File

@@ -88,8 +88,8 @@ WantedBy=multi-user.target
```toml ```toml
# cortex.toml # cortex.toml
[gateway] [gateway]
listen = "0.0.0.0:31313" listen = "0.0.0.0:8000"
metrics_listen = "0.0.0.0:31314" metrics_listen = "0.0.0.0:9100"
[eviction] [eviction]
strategy = "lru" # lru | priority strategy = "lru" # lru | priority
@@ -143,7 +143,7 @@ cortex serve --config cortex.toml
cortex status cortex status
# list all models across nodes # list all models across nodes
curl http://localhost:31313/v1/models curl http://localhost:8000/v1/models
``` ```
## License ## License

View File

@@ -3,11 +3,11 @@
# Copy to cortex.toml and adjust for your environment. # Copy to cortex.toml and adjust for your environment.
# #
# Environment variable overrides use CORTEX_ prefix with __ separators: # Environment variable overrides use CORTEX_ prefix with __ separators:
# CORTEX_GATEWAY__LISTEN=0.0.0.0:31313 # CORTEX_GATEWAY__LISTEN=0.0.0.0:9000
[gateway] [gateway]
listen = "0.0.0.0:31313" listen = "0.0.0.0:8000"
metrics_listen = "0.0.0.0:31314" metrics_listen = "0.0.0.0:9100"
[eviction] [eviction]
strategy = "lru" strategy = "lru"

View File

@@ -1,5 +1,5 @@
Name: cortex Name: cortex
Version: 0.1.12 Version: 0.1.2
Release: 1%{?dist} Release: 1%{?dist}
Summary: Inference gateway for multi-node GPU clusters Summary: Inference gateway for multi-node GPU clusters
@@ -22,14 +22,11 @@ BuildRequires: systemd-rpm-macros
Requires(pre): shadow-utils Requires(pre): shadow-utils
Requires: systemd Requires: systemd
# systemd-rpm-macros ships a unit dep generator that parses User=/Group= # rpm's sysusers provides-generator only emits versioned user(cortex) when
# from our .service file and emits Requires: user(cortex)/group(cortex). # the u-line has GECOS/home/shell fields. %attr(,,cortex) in %files emits
# rpm's sysusers provides-generator emits the unversioned form for groups # an unversioned Requires: user(cortex), so we provide it explicitly.
# but only a versioned user(cortex) = <base64> for users with GECOS/home/
# shell. Provide the unversioned user(cortex) explicitly so dnf can resolve
# the auto-generated Requires. Without this, dnf5 silently filters the
# package and reports "Nothing to do".
Provides: user(cortex) Provides: user(cortex)
Provides: group(cortex)
%description %description
Cortex is a Rust reverse-proxy that sits in front of multiple inference Cortex is a Rust reverse-proxy that sits in front of multiple inference
@@ -56,9 +53,9 @@ cargo build --release -p cortex-cli
install -Dm755 target/release/cortex %{buildroot}%{_bindir}/cortex install -Dm755 target/release/cortex %{buildroot}%{_bindir}/cortex
install -Dm644 data/cortex.service %{buildroot}%{_unitdir}/cortex.service install -Dm644 data/cortex.service %{buildroot}%{_unitdir}/cortex.service
install -Dm644 data/cortex-sysusers.conf %{buildroot}%{_sysusersdir}/cortex.conf install -Dm644 data/cortex-sysusers.conf %{buildroot}%{_sysusersdir}/cortex.conf
install -dm755 %{buildroot}%{_sysconfdir}/cortex install -dm750 %{buildroot}%{_sysconfdir}/cortex
install -Dm644 cortex.example.toml %{buildroot}%{_sysconfdir}/cortex/cortex.toml install -Dm640 cortex.example.toml %{buildroot}%{_sysconfdir}/cortex/cortex.toml
install -Dm644 models.example.toml %{buildroot}%{_sysconfdir}/cortex/models.toml install -Dm640 models.example.toml %{buildroot}%{_sysconfdir}/cortex/models.toml
%pre %pre
%sysusers_create_compat %{_builddir}/%{name}-%{version}/data/cortex-sysusers.conf %sysusers_create_compat %{_builddir}/%{name}-%{version}/data/cortex-sysusers.conf
@@ -78,10 +75,10 @@ install -Dm644 models.example.toml %{buildroot}%{_sysconfdir}/cortex/models.toml
%{_bindir}/cortex %{_bindir}/cortex
%{_unitdir}/cortex.service %{_unitdir}/cortex.service
%{_sysusersdir}/cortex.conf %{_sysusersdir}/cortex.conf
%dir %{_sysconfdir}/cortex %dir %attr(750,root,cortex) %{_sysconfdir}/cortex
%config(noreplace) %{_sysconfdir}/cortex/cortex.toml %config(noreplace) %attr(640,root,cortex) %{_sysconfdir}/cortex/cortex.toml
%config(noreplace) %{_sysconfdir}/cortex/models.toml %config(noreplace) %attr(640,root,cortex) %{_sysconfdir}/cortex/models.toml
%changelog %changelog
* Wed Apr 15 2026 Rob Thijssen <grenade@rob.tn> - 0.1.0-1 * Tue Apr 15 2026 Rob Thijssen <grenade@rob.tn> - 0.1.0-1
- Initial package - Initial package

View File

@@ -23,7 +23,7 @@ enum Commands {
/// Print the fleet status (models, nodes, health). /// Print the fleet status (models, nodes, health).
Status { Status {
/// Gateway API endpoint to query. /// Gateway API endpoint to query.
#[arg(short, long, default_value = "http://localhost:31313")] #[arg(short, long, default_value = "http://localhost:8000")]
endpoint: String, endpoint: String,
}, },
} }

View File

@@ -22,9 +22,9 @@ fn default_models_path() -> String {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GatewaySettings { pub struct GatewaySettings {
/// Address to listen on for API requests (e.g. "0.0.0.0:31313") /// Address to listen on for API requests (e.g. "0.0.0.0:8000")
pub listen: String, pub listen: String,
/// Address to listen on for Prometheus metrics (e.g. "0.0.0.0:31314") /// Address to listen on for Prometheus metrics (e.g. "0.0.0.0:9100")
pub metrics_listen: String, pub metrics_listen: String,
} }
@@ -50,7 +50,7 @@ pub enum EvictionStrategy {
pub struct NeuronEndpoint { pub struct NeuronEndpoint {
/// Human-readable node name (e.g. "beast") /// Human-readable node name (e.g. "beast")
pub name: String, pub name: String,
/// Base URL of the neuron daemon (e.g. "http://beast.internal:13131") /// Base URL of the neuron daemon (e.g. "http://beast.internal:9090")
pub endpoint: String, pub endpoint: String,
} }
@@ -70,8 +70,8 @@ impl Default for GatewayConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
gateway: GatewaySettings { gateway: GatewaySettings {
listen: "0.0.0.0:31313".into(), listen: "0.0.0.0:8000".into(),
metrics_listen: "0.0.0.0:31314".into(), metrics_listen: "0.0.0.0:9100".into(),
}, },
eviction: EvictionSettings { eviction: EvictionSettings {
strategy: EvictionStrategy::Lru, strategy: EvictionStrategy::Lru,

View File

@@ -6,7 +6,7 @@ use std::collections::HashMap;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct NodeState { pub struct NodeState {
pub name: String, pub name: String,
/// Base URL of the neuron daemon (e.g. "http://beast.internal:13131"). /// Base URL of the neuron daemon (e.g. "http://beast.internal:9090").
pub endpoint: String, pub endpoint: String,
pub healthy: bool, pub healthy: bool,
pub models: HashMap<String, ModelEntry>, pub models: HashMap<String, ModelEntry>,

View File

@@ -17,7 +17,7 @@ pub struct NeuronConfig {
} }
fn default_port() -> u16 { fn default_port() -> u16 {
13131 9090
} }
impl NeuronConfig { impl NeuronConfig {
@@ -33,7 +33,7 @@ impl NeuronConfig {
impl Default for NeuronConfig { impl Default for NeuronConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
port: 13131, port: 9090,
harnesses: vec![], harnesses: vec![],
} }
} }

View File

@@ -1,3 +0,0 @@
g neuron - -
u neuron - "Neuron GPU node daemon" /var/lib/neuron /sbin/nologin
m neuron neuron

View File

@@ -5,11 +5,11 @@ Wants=network-online.target
[Service] [Service]
Type=simple Type=simple
ExecStart=/usr/bin/neuron --config /etc/neuron/neuron.toml ExecStart=/usr/bin/neuron --config /etc/cortex/neuron.toml
Restart=on-failure Restart=on-failure
RestartSec=5 RestartSec=5
User=neuron User=cortex
Group=neuron Group=cortex
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View File

@@ -1,11 +1,11 @@
# neuron.example.toml — example configuration # neuron.example.toml — example configuration
# #
# Copy to /etc/neuron/neuron.toml and adjust for your environment. # Copy to /etc/cortex/neuron.toml and adjust for your environment.
# #
# Environment variable overrides use NEURON_ prefix with __ separators: # Environment variable overrides use NEURON_ prefix with __ separators:
# NEURON_PORT=13131 # NEURON_PORT=9090
port = 13131 port = 9090
# -- Harnesses --------------------------------------------------------------- # -- Harnesses ---------------------------------------------------------------
# Each [[harnesses]] entry declares an inference engine managed by neuron. # Each [[harnesses]] entry declares an inference engine managed by neuron.

View File

@@ -1,10 +1,7 @@
Name: helexa-neuron Name: neuron
Version: 0.1.12 Version: 0.1.2
Release: 1%{?dist} Release: 1%{?dist}
Summary: Per-node GPU discovery and harness management daemon for cortex Summary: Per-node GPU discovery and harness management daemon for cortex
# Package name disambiguates from Fedora's existing "neuron" package
# (NEURON neural simulation environment from Yale). Binary, systemd
# unit, and system user are still called "neuron" for brevity.
License: GPL-3.0-or-later License: GPL-3.0-or-later
URL: https://git.lair.cafe/helexa/cortex URL: https://git.lair.cafe/helexa/cortex
@@ -25,14 +22,11 @@ BuildRequires: systemd-rpm-macros
Requires(pre): shadow-utils Requires(pre): shadow-utils
Requires: systemd Requires: systemd
# systemd-rpm-macros ships a unit dep generator that parses User=/Group= # rpm's sysusers provides-generator only emits versioned user(cortex) when
# from our .service file and emits Requires: user(neuron)/group(neuron). # the u-line has GECOS/home/shell fields. %attr(,,cortex) in %files emits
# rpm's sysusers provides-generator emits the unversioned form for groups # an unversioned Requires: user(cortex), so we provide it explicitly.
# but only a versioned user(neuron) = <base64> for users with GECOS/home/ Provides: user(cortex)
# shell. Provide the unversioned user(neuron) explicitly so dnf can resolve Provides: group(cortex)
# the auto-generated Requires. Without this, dnf5 silently filters the
# package and reports "Nothing to do".
Provides: user(neuron)
%description %description
Neuron is a per-node daemon for cortex inference clusters. It discovers Neuron is a per-node daemon for cortex inference clusters. It discovers
@@ -57,12 +51,12 @@ cargo build --release -p neuron
%install %install
install -Dm755 target/release/neuron %{buildroot}%{_bindir}/neuron install -Dm755 target/release/neuron %{buildroot}%{_bindir}/neuron
install -Dm644 data/neuron.service %{buildroot}%{_unitdir}/neuron.service install -Dm644 data/neuron.service %{buildroot}%{_unitdir}/neuron.service
install -Dm644 data/neuron-sysusers.conf %{buildroot}%{_sysusersdir}/neuron.conf install -Dm644 data/cortex-sysusers.conf %{buildroot}%{_sysusersdir}/neuron.conf
install -dm755 %{buildroot}%{_sysconfdir}/neuron install -dm750 %{buildroot}%{_sysconfdir}/cortex
install -Dm644 neuron.example.toml %{buildroot}%{_sysconfdir}/neuron/neuron.toml install -Dm640 neuron.example.toml %{buildroot}%{_sysconfdir}/cortex/neuron.toml
%pre %pre
%sysusers_create_compat %{_builddir}/%{name}-%{version}/data/neuron-sysusers.conf %sysusers_create_compat %{_builddir}/%{name}-%{version}/data/cortex-sysusers.conf
%post %post
%systemd_post neuron.service %systemd_post neuron.service
@@ -79,9 +73,9 @@ install -Dm644 neuron.example.toml %{buildroot}%{_sysconfdir}/neuron/neuron.toml
%{_bindir}/neuron %{_bindir}/neuron
%{_unitdir}/neuron.service %{_unitdir}/neuron.service
%{_sysusersdir}/neuron.conf %{_sysusersdir}/neuron.conf
%dir %{_sysconfdir}/neuron %dir %attr(750,root,cortex) %{_sysconfdir}/cortex
%config(noreplace) %{_sysconfdir}/neuron/neuron.toml %config(noreplace) %attr(640,root,cortex) %{_sysconfdir}/cortex/neuron.toml
%changelog %changelog
* Wed Apr 15 2026 Rob Thijssen <grenade@rob.tn> - 0.1.0-1 * Tue Apr 15 2026 Rob Thijssen <grenade@rob.tn> - 0.1.0-1
- Initial package - Initial package