Skip to content

chore: migrate to master, enforce ci - #1

Closed
georgewhewell wants to merge 116 commits into
masterfrom
grw/feat/aiter
Closed

chore: migrate to master, enforce ci#1
georgewhewell wants to merge 116 commits into
masterfrom
grw/feat/aiter

Conversation

@georgewhewell

Copy link
Copy Markdown
Contributor

No description provided.

Collapse the remote execution stack onto the canonical graph request shape, move quote discovery into the rpc crate, and remove the CLI-only discovery split. This also folds in the executor/client fixes needed to make the new path work end-to-end.
Document the deny-by-default serve flow, surface the active policy mode at startup, and add the Nix/docker packaging helpers that make the new deployment shape usable.
…s everywhere

OpenTelemetry plumbing is now opt-in via the `otel` feature on `hellas-cli`
(default off). With the feature off, none of opentelemetry / opentelemetry_sdk
/ opentelemetry-otlp / tracing-opentelemetry / reqwest compile, and the
trace-context propagation glue collapses to identity. Plain `tracing::info!`
/ `warn!` / etc. macros stay unconditional — they're no-op-cheap without a
subscriber.

Cfg surface is concentrated, not sprinkled:
- One function pair in `tracing_config.rs` (`install_with_otel`); registry
  composition stays cfg-free. `TracerGuard` newtype hides the
  `Option<SdkTracerProvider>` behind a cfg-gated field so `main.rs` drops the
  `if let Some(provider) = ...` dance around shutdown.
- `execution.rs`: cfg-swap of the `TracedChannel` type alias plus a
  `traced(channel)` helper collapses 8 `InterceptedService::new(channel,
  TraceContextInjector)` sites and avoids spreading cfg across the file.
- `serve/node.rs`: a single `traced_service<S>` helper replaces 5
  `trace_layer.layer(...)` sites.

iroh's internal `EndpointMetrics` are bridged into the existing
`prometheus-client` registry exposed at `/metrics`. The cli's `otel` feature
also enables `tonic-iroh-transport/metrics`, and `serve` attaches
`endpoint.metrics()` (clone of Arcs into live storage) via
`MetricsBundle::with_iroh`. The HTTP handler emits prometheus-client text
followed by iroh's OpenMetrics text in one well-formed response with a single
`# EOF` terminator. Verified end-to-end: `endpoint_socket_send_ipv4_total`
etc. show up alongside `hellas_*` counters.

Switch all TLS to rustls so the crate compiles in weird places (wasm,
cross-compile, no system openssl):
- Workspace `tonic-iroh-transport`: drop `["otel", "native-defaults"]`,
  pin to v0.9.2, use granular features `["tls-ring", "portmapper",
  "fast-apple-datapath"]`. v0.9.2 exposes the new passthroughs.
- Workspace `reqwest`: switch to `["rustls", "webpki-roots"]` (was
  `["rustls-native-certs"]`, which lacks an actual TLS provider — the cause
  of the `"invalid URL, scheme is not http"` symptom against
  jaeger.lsd-ag.ch).
- `opentelemetry-otlp`: add `"reqwest-rustls-webpki-roots"` so its internal
  `reqwest 0.12` (separate from our 0.13) gets a TLS provider too.
- `crates/executor/Cargo.toml`: `hf-hub = "0.5"` was secretly pulling
  `native-tls` -> `openssl-sys` via default features. Pin to
  `default-features = false, features = ["ureq"]`, matching `hellas-rpc`.
- Drop `pkgs.openssl` from `nix/default.nix`, `nix/docker.nix`,
  `nix/package.nix`. `ldd target/debug/hellas-cli` is now empty for
  `libssl`/`libcrypto` in both default and `candle,otel` builds.

Dev workflow:
- `rust-analyzer.toml` at workspace root pins RA's feature set to
  `["candle", "otel"]` so type-checking covers gated modules across
  editors. Replaces the abandoned `HELLAS_FEATURES` env var / cargo shim
  approach.
- `nix/default.nix:88`: `hellas-run` wrapper drops
  `--features "${HELLAS_FEATURES:-candle}"` in favor of explicit
  `--features candle`.

Build matrix: all four cli feature combos compile (`{}`, `candle`, `otel`,
`candle,otel`); workspace check + clippy clean; HTTPS OTLP connect now
succeeds (DNS to jaeger.lsd-ag.ch is environmental).
- expose individual check-{fmt,clippy,sort,test} apps for matrix dispatch
- add `cargo test --workspace` check (default features)
- workflow enumerates check-* apps from the flake, runs each on
  `[self-hosted, shared]`; gates with `CI passed` aggregate job
- opt-in `cache.hellas.ai` substituter via flake.nix nixConfig
writeShellApplication strips PATH down to runtimeInputs only, so cargo's
default linker invocation (`cc`) was failing on the runner.
Single source of truth in nix/ci.nix is now an attrset
{ name -> { check, fix? } }. Exposed flat as `.#ci.<system>.commands`
for the GitHub Actions matrix to enumerate. CI runs each command via
`nix develop -c` so the dev shell is the runtime environment — no
more per-check writeShellApplication wrappers with hand-curated input
lists.

Workflow gains a `devshell` warmup job (clean failure surface for
env issues) and drops `--accept-flake-config` (runner daemon already
trusts cache.hellas.ai; flake nixConfig is for downstream users).
`nix run .#fix` previously fell back to running the check command
for entries without a `fix` field — so it ran cargo test (slow) and
cargo outdated (heuristic) during fix mode. Now those are filtered
out entirely; fix only runs entries with an explicit fix variant.
Mechanical changes from `nix run .#fix`: rustfmt across the workspace,
cargo-sort across all Cargo.toml files, and clippy's --fix for the
auto-resolvable lints (collapsible if/let chains).
EnqueueError / StartExecutionError now wrap ExecuteJob in Box (was
~232 bytes inline). PreparedRoute / OpaquePreparedRoute box the
RemoteDirect variant which held a ~1KB RemoteExecution. The remaining
variant-size disparity in the route enums is annotated with
`#[allow(clippy::large_enum_variant)]` — the variants are heterogeneous
by nature and the enum lives only briefly during execution setup.
After `CI passed`, on push only, build the slow targets in parallel:

  static-x86_64   cross.x86_64-linux-musl.cli
  static-aarch64  cross.aarch64-linux-musl.cli
  docker-cpu      default cpu image
  docker-cuda     alias of docker-cuda12-sm89 (new)

Matrix is driven from `.#ci.<sys>.builds` (same data-driven pattern as
`commands`). `Extended builds passed` is a separate gate from
`CI passed` so branch protection can require them independently.
@georgewhewell
georgewhewell force-pushed the grw/feat/aiter branch 7 times, most recently from 120cd35 to 9f36f95 Compare May 11, 2026 15:39
@hellasbot

hellasbot commented Jun 2, 2026

Copy link
Copy Markdown

Hydra reported failures for this PR.

Failed builds:

Update: hellas/node-pr-1, eval #760, head 2f5014522228: newer evaluation started.

georgewhewell added a commit that referenced this pull request Sep 11, 2026
…_FINDINGS #1 + #5)

The old `ExecutionRuntime` was a bag-of-Options that lied about what
it supported. `default().with_secret_key(sk)` produced a remote-
capable-looking value that couldn't dial — the `secret_key` field
was stored but never used to build an endpoint. All three remote
callers (llm, opaque, gateway) constructed exactly this dead shape.
`with_registry` existed for "external code that builds its own
registry", which nothing in-tree does. `RemoteNodeTarget` stored
`Vec<SocketAddr>` as a parallel structure to iroh's own
`EndpointAddr`, and the `endpoint_addr()` adapter built to bridge
the two was `#[allow(dead_code)]` because no call site reached it.

This commit collapses the three pieces:

1. `ExecutionRuntime` shrinks to two fields:
   ```
   struct ExecutionRuntime {
       #[cfg(feature = "hellas-executor")]
       local_executor: Option<ExecutorHandle>,
       remote: Option<RemoteRpc { endpoint, registry }>,
   }
   ```
   `RemoteRpc` owns the bound iroh `Endpoint` and the
   `ServiceRegistry` built atop it as one unit — no half-built
   state. Constructors:
   - `local(handle)` for local-only.
   - `remote(secret_key, seed_targets).await?` for remote-only.
   - `with_remote(self, sk, seed).await?` adds remote dialing to
     an existing (typically local) runtime; used by the
     verify-against-local path.
   Deleted: `secret_key` field, `with_secret_key`, `with_registry`,
   `require_registry`. They were the inert composition the review
   flagged.

2. New helper `remote_transport<S: ServiceMarker>(&self, target)
   -> anyhow::Result<IrohTransport>`. Every `*Direct::quote_*` /
   `*Direct::opaque_*` call site now goes through this one path
   instead of replicating
   `registry.pool::<S>().transport(target.node_id).await.map_err(|e|
   anyhow!(e).context(...))` boilerplate per service. ~7 sites
   collapse from 4-6 lines each to 1.

3. `RemoteNodeTarget` becomes just `{ addr: EndpointAddr }` — the
   address-bundle iroh expects at `Endpoint::connect` time.
   Direct CLI-supplied IPs go into `EndpointAddr::from_parts(id,
   ip_hints)` at the CLI parse boundary (in `ExecutionRoute::remote`)
   and never enter `PeerManager` / `PeerDirectory`. iroh owns
   routing — `presets::N0` already configures pkarr/DNS lookup, so
   `EndpointId`-only routing works when iroh can resolve the peer.
   `RemoteNodeTarget::node_id()` is the only accessor.
   Deleted: `node_addrs: Vec<SocketAddr>` field, the dead
   `endpoint_addr()` helper, the `TransportAddr` thread-through.

4. `wire/src/iroh/pool.rs`: `Pool::connection` and `Pool::transport`
   now take `impl Into<EndpointAddr>`. The internal `endpoint.connect`
   already accepted `impl Into<EndpointAddr>`, so this is a clean
   widening. Cache keys remain `EndpointId` — hints are dial-time
   only, not part of the cache identity (an `EndpointAddr` with
   stale IPs reuses a live connection cached under the same
   `EndpointId`). `From<EndpointId> for EndpointAddr` (existing in
   iroh-base) handles the migration so all old `pool.transport(id)`
   call sites still compile after the change.

5. Three broken call sites fixed:
   - `cli/commands/llm.rs:107`: `default().with_secret_key(sk)` →
     `ExecutionRuntime::remote(sk, vec![]).await?` (or
     `local(...).with_remote(sk, vec![]).await?` when verify_local
     is set).
   - `cli/commands/opaque.rs:53`: same.
   - `cli/commands/gateway/state.rs:100`: `from_options` is now
     `async fn`; `mod.rs` updated to `.await?` it.

Behavioural change: `hellas execute --remote`, `hellas llm`, and
`hellas gateway` now actually dial when given a `--node-id`. Before
this commit they always errored out with "no iroh ServiceRegistry
is configured on the runtime".

Tests:
- `cargo test -p hellas-cli --features hellas-executor` → 58
  (unchanged — the runtime-construction tests stayed in shape
  through the refactor).
- `cargo test -p hellas-wire --all-features` → 57.
- `cargo test -p hellas-rpc --all-features --lib` → 73.
- `cargo build --workspace` clean (no warnings from our crates).

Diffstat: 6 files, +180 / -156. Net: **+24 LOC** — modestly
subtractive after accounting for the new `with_remote` builder
required by the verify-against-local composition. The "delete
dead helpers + collapse boilerplate into `remote_transport`"
budget delivered ~80 LOC reduction; that's mostly offset by the
new builder + the dedicated `RemoteRpc` struct + doc comments.
Net LOC isn't the only measure: the runtime can no longer be
constructed in a broken state, and a previously-bug-prone area
(pool dial boilerplate) is now factored through one helper.
georgewhewell added a commit that referenced this pull request Sep 11, 2026
… rule

Adversarial panel (Codex/gpt-5.3-codex-spark, Grok, nemotron-3-ultra)
reviewed 8b5cd03..HEAD. Confirmed findings fixed:

- StakeBondTerms commits challenge_margin (window + inclusion +
  finality, blocks); JobAcceptanceContext::covered_by is the admission
  rule (1 <= p_j <= max_job_price, terminal_deadline + margin <=
  bond timeout) and FraudArtifact::binds re-checks it — closes the
  stall-past-timeout escape's policy gap (Grok #3, Codex #1).
- Kernel open rejects party-controlled treasuries (TreasuryIsParty):
  treasury == provider would collapse the slash penalty from S to A
  (Grok #2).
- Kernel open rejects max_job_price == 0 (JobPriceCapZero): kills the
  A = C_disp strict-incentive degeneracy (Grok #7).
- CloseKindSet::decode refuses no-exit sets (defense in depth, Grok #12).
- Bond e2e uses STAKE_BOND_PROTOCOL, not the payment code (Grok #13).
- preverified-seals feature doc: process-local cache, single-node dev
  only, never multi-validator (Grok #1 — by design, now explicit).

Rejected with evidence: cross-bond artifact replay (binds checks
bond_edge == public.edge_id), seal-omits-treasury (acceptance digest
commits bond_terms which commits it), SealPublicInputs malleability
(terms hash-checked against edge pre-verify), VoucherBook stale race
(kernel payload hash binds the edge; older voucher pays provider less),
stake==net-value as fee bug (plan defines S net).
georgewhewell added a commit that referenced this pull request Sep 11, 2026
…_FINDINGS #1 + #5)

The old `ExecutionRuntime` was a bag-of-Options that lied about what
it supported. `default().with_secret_key(sk)` produced a remote-
capable-looking value that couldn't dial — the `secret_key` field
was stored but never used to build an endpoint. All three remote
callers (llm, opaque, gateway) constructed exactly this dead shape.
`with_registry` existed for "external code that builds its own
registry", which nothing in-tree does. `RemoteNodeTarget` stored
`Vec<SocketAddr>` as a parallel structure to iroh's own
`EndpointAddr`, and the `endpoint_addr()` adapter built to bridge
the two was `#[allow(dead_code)]` because no call site reached it.

This commit collapses the three pieces:

1. `ExecutionRuntime` shrinks to two fields:
   ```
   struct ExecutionRuntime {
       #[cfg(feature = "hellas-executor")]
       local_executor: Option<ExecutorHandle>,
       remote: Option<RemoteRpc { endpoint, registry }>,
   }
   ```
   `RemoteRpc` owns the bound iroh `Endpoint` and the
   `ServiceRegistry` built atop it as one unit — no half-built
   state. Constructors:
   - `local(handle)` for local-only.
   - `remote(secret_key, seed_targets).await?` for remote-only.
   - `with_remote(self, sk, seed).await?` adds remote dialing to
     an existing (typically local) runtime; used by the
     verify-against-local path.
   Deleted: `secret_key` field, `with_secret_key`, `with_registry`,
   `require_registry`. They were the inert composition the review
   flagged.

2. New helper `remote_transport<S: ServiceMarker>(&self, target)
   -> anyhow::Result<IrohTransport>`. Every `*Direct::quote_*` /
   `*Direct::opaque_*` call site now goes through this one path
   instead of replicating
   `registry.pool::<S>().transport(target.node_id).await.map_err(|e|
   anyhow!(e).context(...))` boilerplate per service. ~7 sites
   collapse from 4-6 lines each to 1.

3. `RemoteNodeTarget` becomes just `{ addr: EndpointAddr }` — the
   address-bundle iroh expects at `Endpoint::connect` time.
   Direct CLI-supplied IPs go into `EndpointAddr::from_parts(id,
   ip_hints)` at the CLI parse boundary (in `ExecutionRoute::remote`)
   and never enter `PeerManager` / `PeerDirectory`. iroh owns
   routing — `presets::N0` already configures pkarr/DNS lookup, so
   `EndpointId`-only routing works when iroh can resolve the peer.
   `RemoteNodeTarget::node_id()` is the only accessor.
   Deleted: `node_addrs: Vec<SocketAddr>` field, the dead
   `endpoint_addr()` helper, the `TransportAddr` thread-through.

4. `wire/src/iroh/pool.rs`: `Pool::connection` and `Pool::transport`
   now take `impl Into<EndpointAddr>`. The internal `endpoint.connect`
   already accepted `impl Into<EndpointAddr>`, so this is a clean
   widening. Cache keys remain `EndpointId` — hints are dial-time
   only, not part of the cache identity (an `EndpointAddr` with
   stale IPs reuses a live connection cached under the same
   `EndpointId`). `From<EndpointId> for EndpointAddr` (existing in
   iroh-base) handles the migration so all old `pool.transport(id)`
   call sites still compile after the change.

5. Three broken call sites fixed:
   - `cli/commands/llm.rs:107`: `default().with_secret_key(sk)` →
     `ExecutionRuntime::remote(sk, vec![]).await?` (or
     `local(...).with_remote(sk, vec![]).await?` when verify_local
     is set).
   - `cli/commands/opaque.rs:53`: same.
   - `cli/commands/gateway/state.rs:100`: `from_options` is now
     `async fn`; `mod.rs` updated to `.await?` it.

Behavioural change: `hellas execute --remote`, `hellas llm`, and
`hellas gateway` now actually dial when given a `--node-id`. Before
this commit they always errored out with "no iroh ServiceRegistry
is configured on the runtime".

Tests:
- `cargo test -p hellas-cli --features hellas-executor` → 58
  (unchanged — the runtime-construction tests stayed in shape
  through the refactor).
- `cargo test -p hellas-wire --all-features` → 57.
- `cargo test -p hellas-rpc --all-features --lib` → 73.
- `cargo build --workspace` clean (no warnings from our crates).

Diffstat: 6 files, +180 / -156. Net: **+24 LOC** — modestly
subtractive after accounting for the new `with_remote` builder
required by the verify-against-local composition. The "delete
dead helpers + collapse boilerplate into `remote_transport`"
budget delivered ~80 LOC reduction; that's mostly offset by the
new builder + the dedicated `RemoteRpc` struct + doc comments.
Net LOC isn't the only measure: the runtime can no longer be
constructed in a broken state, and a previously-bug-prone area
(pool dial boilerplate) is now factored through one helper.
georgewhewell added a commit that referenced this pull request Sep 11, 2026
… rule

Adversarial panel (Codex/gpt-5.3-codex-spark, Grok, nemotron-3-ultra)
reviewed 8b5cd03..HEAD. Confirmed findings fixed:

- StakeBondTerms commits challenge_margin (window + inclusion +
  finality, blocks); JobAcceptanceContext::covered_by is the admission
  rule (1 <= p_j <= max_job_price, terminal_deadline + margin <=
  bond timeout) and FraudArtifact::binds re-checks it — closes the
  stall-past-timeout escape's policy gap (Grok #3, Codex #1).
- Kernel open rejects party-controlled treasuries (TreasuryIsParty):
  treasury == provider would collapse the slash penalty from S to A
  (Grok #2).
- Kernel open rejects max_job_price == 0 (JobPriceCapZero): kills the
  A = C_disp strict-incentive degeneracy (Grok #7).
- CloseKindSet::decode refuses no-exit sets (defense in depth, Grok #12).
- Bond e2e uses STAKE_BOND_PROTOCOL, not the payment code (Grok #13).
- preverified-seals feature doc: process-local cache, single-node dev
  only, never multi-validator (Grok #1 — by design, now explicit).

Rejected with evidence: cross-bond artifact replay (binds checks
bond_edge == public.edge_id), seal-omits-treasury (acceptance digest
commits bond_terms which commits it), SealPublicInputs malleability
(terms hash-checked against edge pre-verify), VoucherBook stale race
(kernel payload hash binds the edge; older voucher pays provider less),
stake==net-value as fee bug (plan defines S net).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants