Hellas commits exact work, runs it locally or over an adversarial network, and
binds the result into a signed transcript. Execution identity is one canonical
ProgramManifest:
(evaluator, adaptor, content-addressed application root)
The two application identifiers are exact opaque strings. Hellas does not parse versions, negotiate compatibility, or consult a model/package registry.
The application owns the meaning of its root. The two currently modelled shapes intentionally have different trust boundaries:
| Application | Root and trusted computation | Outside that guarantee |
|---|---|---|
hellas/catena-gpu-0.0.1, causal-lm-0.0.1 |
Exact Catena program, entrypoint, content-addressed static objects and borrowed slices, state sizing, vocabulary, capacity, and token-native invocation/result | Model acquisition, provider GPU/backend choice, tokenizer, chat template, text decoding, and API presentation |
hellas/fetch-0.0.1, codex-responses-0.0.1 or openai-responses-0.0.1 |
A strict stateless Responses field set; typed reconstruction of provider-shaped JSON; stream=true and upstream store=false; the exact official Codex or OpenAI HTTPS endpoint; no redirects; and SSE response projection |
The truth, correctness, and availability of the adversarial upstream service; provider route label; credentials/auth-file location; and access policy |
For causal LM work, the request and transcript bind the manifest, input token
IDs, maximum output, explicit stop IDs, output token IDs, and termination.
--tokenizer is a caller-side lens that encodes prompt text and decodes the
verified IDs; it is not part of the Catena kernel claim. Hellas infers no stop
tokens from it.
Fetch is different. Its purpose is to attest the exact transformation around a remote request, so structuring and destructuring are inside that application's trusted path rather than presentation performed outside it. This proves which checks and transformation ran, not that the remote service's claims are true.
A platform-backed Assurance authenticates the Fetch application. The
ProducerSigned mode authenticates only the producer key and signed transcript;
it does not authenticate a running binary. The sealed Responses adaptor sends
store=false upstream and accepts text and client-executed function tools.
Work is the only execution protocol. This milestone supports payment-funded
channels. Owner and principal grants share the Work protocol, including
gateway --machine and sealed Fetch. Delegated project tracks remain a later milestone. Ticket execution, shadow verification, public retained-artifact
retrieval and CacheControl have been removed. Work-side local reproduction
remains available. Accepted paid inputs and results remain in the channel's
recovery/evidence journal, independently of gateway archive settings.
Human-readable settings live in examples/. Their paths are
local acquisition hints; canonical environment bytes contain only identities,
lengths, and ABI data.
hellas-cli environment build \
--program model.hex \
--settings examples/smollm2.environment.toml \
--out smollm2.environment
hellas-cli environment inspect --environment smollm2.environment
# Prove one environment and all of its referenced content are provider-ready.
hellas-cli environment verify \
--environment smollm2.environment \
--content-root /srv/hellas/content \
--content-index /var/lib/hellas/content.indexFor gateway, the caller selects the environment trust anchor before
any route starts. By default, the exact local --environment file bytes are
that anchor and the CLI derives their manifest ID. When a manifest ID was
distributed separately, pass --manifest-id <CONTENT_ID>; a file deriving
a different ID is rejected before network or GPU work. A provider never selects
either value.
A provider indexes ordinary runtime files and can accept any locally satisfiable supported environment; it need not register a model name:
hellas-cli --software-root serve \
--work-config /run/hellas/work.json \
--content-root /srv/hellas/contentWork execution strictly decodes the submitted manifest. The first binding opens
its root by exact local content ID and verifies every declared program/static
object; later executions for that exact manifest may reuse the immutable verified
binding without reopening or rehashing those files. Binding does not acquire content. The authorized worker is the final availability and integrity
boundary: before nonresident content enters the safe runtime, it reopens the
descriptor and enforces the exact ID and length; an already-resident exact
mapping is reused. The provider then compiles the Catena source for its visible
GPU device. Verified static files are lent by descriptor to a bounded
persistent safe-runtime session, so an already prepared program and weights are
reused across requests until the session is recycled or the service restarts.
There is no client-supplied gfx target or provider architecture allow-list.
Providers independently bound compilation with --gpu-compile-timeout-secs
and each complete generation with --gpu-execution-timeout-secs; expiry kills
the isolated worker process group and the next request starts a fresh session.
--gpu-max-generation-capacity is additionally capped at 524288 tokens so a
token transcripts stay within the canonical artifact size bound.
Indexed provider content is an immutable local-cache assumption. Hellas pins the verified read-only descriptor and detects path/inode replacement; it does not defend against a separate local process that already holds a writable descriptor to the same inode and mutates it concurrently.
Fetch route names are operator-defined routing labels. The sealed destination selects the trusted adaptor, fixed official endpoint, no-redirect HTTP driver, and response projector as one unit. A configuration cannot supply a URL or claim a different adaptor identity.
A provider route file contains a routes array. Each route has service,
method, destination and optional capabilities (model and output limits).
The former callers field is rejected; the mounted Work channel authorizes jobs.
Use serve --fetch-config FILE --work-config FILE to mount paid execution.
See paid Work for channel configuration and
HTTPS Fetch for provider credentials and egress.
For upstream APIs, HTTP Fetch routes preserve the vendor's
request, response and streaming formats. CLI gateways archive payloads by default;
--zdr or x-hellas-zdr: true disables application payload persistence.
The causal-LM gateway requires the same canonical environment and an explicit
presentation tokenizer. --model is only an API response label; when omitted,
the manifest ID is used.
Configure a funded pool using the paid gateway guide,
then pass --paid-work-config POOL together with --environment and
--tokenizer. The former in-process local route awaits owner grant funding.
It binds loopback by default. Non-loopback listening requires --allow-remote
and --bearer-token-file FILE; the private credential file is created once and
reused across restarts. Without a file, a fresh credential is shown on the
controlling terminal. The causal-LM backend accepts plain text at
/v1/completions and /v1/responses. Set --chat-template to enable the shared
model adapter for chat, reasoning and tool calls supported by that model.
The proxy Responses backend retains its explicit upstream semantics.
The unpaid attested Fetch Responses backend awaits grant funding. --responses-backend changes only /v1/responses; every other
route remains bound to the causal-LM environment, so --environment and
--tokenizer are still required.
The exposed routes are:
POST /v1/completions
POST /v1/responses
POST /v1/chat/completions
POST /v1/messages
Monitor discovery and peer health with hellas-cli monitor --timeout-secs 30.
The chain feature provides chain query, chain open, and chain close.
The indexer feature adds chain indexer follow; the validator feature adds
chain validator config, chain validator run, and
chain validator check-config.
chain query --rpc URL supports latest-block, state-root, finalization,
finalized-block, coin, edge, validators, coins-by-owner, and
edges-by-owner.
chain open reads each 32-byte secret scalar from --maker-key FILE and
--taker-key FILE; --maker-auth and --taker-auth accept webauthn or
native. Funding IDs use repeatable or comma-separated --maker-funding and
--taker-funding. Terms use --protocol, --timeout, and repeatable or
comma-separated --timeout-payout SETTLEMENT_KEY:VALUE. --terms-out FILE
writes the canonical reveal for a later timeout close.
chain close --kind mutual takes repeatable or comma-separated
--payout SETTLEMENT_KEY:VALUE plus both key and auth pairs. --kind timeout
takes the same committed payouts and --terms-file FILE, with no signer
options.
Stored ownership uses raw, untagged 33-byte settlement keys. P-256 and
secp256k1 keys can own stored coins. Legacy Transfer and MergeCoin
verification remains P-256-only, and genesis owner strings must decode as
P-256 keys when the validator config is loaded.
Given two P-256 key files whose settlement keys each own a 100-value coin:
RPC=ws://127.0.0.1:56946
MAKER_KEY=maker.key
TAKER_KEY=taker.key
MAKER_OWNER=maker-settlement-key
TAKER_OWNER=taker-settlement-key
MAKER_COIN=maker-coin-id
TAKER_COIN=taker-coin-id
OPEN=$(
cargo run --no-default-features --features chain -- chain open \
--rpc "$RPC" \
--maker-key "$MAKER_KEY" --maker-auth webauthn \
--taker-key "$TAKER_KEY" --taker-auth webauthn \
--maker-funding "$MAKER_COIN" --taker-funding "$TAKER_COIN" \
--protocol 1 --timeout 1000 \
--timeout-payout "$MAKER_OWNER:100" \
--timeout-payout "$TAKER_OWNER:100"
)
EDGE_ID=$(printf '%s\n' "$OPEN" | awk '$1 == "edge_id" { print $2 }')
# After the open finalizes:
PAYLOAD=$(cargo run --no-default-features --features chain -- \
chain query --rpc "$RPC" latest-block | awk '$1 == "payload" { print $2 }')
cargo run --no-default-features --features chain -- \
chain query --rpc "$RPC" edge --object-id "$EDGE_ID" --payload "$PAYLOAD"
cargo run --no-default-features --features chain -- chain close \
--rpc "$RPC" --edge-id "$EDGE_ID" --kind mutual \
--payout "$MAKER_OWNER:100" --payout "$TAKER_OWNER:100" \
--maker-key "$MAKER_KEY" --maker-auth webauthn \
--taker-key "$TAKER_KEY" --taker-auth webauthn
# After the close finalizes:
cargo run --no-default-features --features chain -- \
chain query --rpc "$RPC" coins-by-owner --owner "$MAKER_OWNER"
cargo run --no-default-features --features chain -- \
chain query --rpc "$RPC" coins-by-owner --owner "$TAKER_OWNER"The main outputs are .#cli (network client/node/gateway), .#cli-catena
(x86_64 Linux plus the Catena safe GPU runtime), and .#cli-validator.
Hellas imports only catena-lang from the Catena workspace, pinned to a
published Git revision in Cargo.toml and Cargo.lock. Nix vendors the same
locked dependency; a sibling Catena checkout is not required.
Enter an x86_64 Linux GPU development shell with:
nix develop .#rocm --no-write-lock-file # AMD
nix develop .#cuda --no-write-lock-file # NVIDIACatena selects a runtime inside an isolated worker. Use serve --gpu-backend auto|hip|cuda to choose one; auto chooses CUDA on a NixOS NVIDIA
host and HIP otherwise.
The NixOS module provisions the matching toolchain and device access:
services.hellas = {
enable = true;
workConfigFile = "/srv/hellas/work.json";
gpuBackend = "cuda"; # or "hip" / "auto"
contentRoots = [ "/srv/hellas/content" ];
};Keep models and compiler artifacts outside /nix/store; they are runtime data.
Fetch providers likewise use runtime files: set fetchConfigFile to the JSON
configuration path and environmentFile to a systemd environment file holding
provider-local secrets such as OPENAI_API_KEY. Do not put either file in a
Nix expression or in the store. In particular, never interpolate the file as a
Nix path and never use builtins.readFile on it: both operations expose its
contents during evaluation, before any module assertion or runtime validation
can protect it. Paid Fetch uses metadata-only journals; see
provider retention.
On Darwin, the Home Manager launch agent remains network-only but supports the same runtime-secret boundary:
programs.hellas = {
enable = true;
serve = {
enable = true;
workConfigFile = "/Users/alice/.config/hellas/work.json";
fetchConfigFile = "/Users/alice/.config/hellas/fetch.json";
environmentFile = "/Users/alice/.config/hellas/provider.env";
};
};Provision the environment file outside Nix and restrict it to the user, with no
group or other permission bits. The runtime wrapper resolves parent symlinks,
rejects the Nix store, opens the final component without following symlinks and
without blocking on special files, then requires that same opened descriptor to
name a regular file owned by the agent's effective user. Its
grammar is deliberately small: blank lines and # comments in column one are
allowed; every other line is NAME=VALUE, with names matching
[A-Za-z_][A-Za-z0-9_]*. Values are literal, so spaces, #, and = are kept
and quotes, escapes, substitutions, and shell commands have no special
meaning. Duplicate names, malformed lines, CR/NUL bytes, or any failed security
check stop the agent before Hellas runs. The launchd plist contains the absolute
file path, never its contents. As above, never use Nix interpolation or
builtins.readFile for this file; validation cannot undo an evaluation-time
secret leak.
Work on the kernel Quint models:
nix develop .#kernel
nix run .#check-kernel-models
nix run .#check-kernel-model-verifyThe default docker output is a network-only node image. It contains no local Catena or
GPU runtime and is tagged ghcr.io/hellas-ai/hellas:network. The derivation
streams a Docker archive to stdout:
$(nix build .#docker --print-out-paths) | docker load
nix run .#docker-push # network image onlyGPU images include Catena and the corresponding runtime compiler:
$(nix build .#docker-cuda --print-out-paths) | docker load
$(nix build .#docker-hip --print-out-paths) | docker load
# NVIDIA with a configured CDI device specification:
docker run --rm --device nvidia.com/gpu=all \
-v hellas-state:/var/lib/hellas -v /srv/hellas/content:/content:ro \
ghcr.io/hellas-ai/hellas:cuda --software-root --content-root /content
# AMD:
docker run --rm --device /dev/kfd --device /dev/dri \
-v hellas-state:/var/lib/hellas -v /srv/hellas/content:/content:ro \
ghcr.io/hellas-ai/hellas:hip --software-root --content-root /contentBoth GPU images default to serve with their matching backend. The host supplies
the GPU driver and model content stays on runtime volumes. On x86_64 Linux,
nix run .#docker-push-all publishes the network, CUDA, and HIP images.
A GPU asset owner uploads verified weights once and execution workers map them read-only. Worker replacement retains weights; asset pressure recreates the owner. HIP resident sharing requires version 7.15 or newer.
The gateway retains its local inference archive: --output-cache off, record
(reuse complete successful results and record misses), or replay-only (fail
on misses without execution). Paid completion and payment acknowledgement finish
before a successful result is recorded. Proxy requests also support recording.
The executor's separate replay cache and the cache administration RPC/CLI have
been removed.
The archive uses the existing Hellas store (--store-dir, HELLAS_STORE_DIR,
or ~/.hellas/store). One process owns a writable index. Stop the writer before
copying a complete store snapshot for offline replay. Recording persists payloads
and is incompatible with gateway ZDR mode. Agent tool execution is not cached.
hellas-cli --output-cache record gateway --responses-backend proxy --wrap opencode
hellas-cli --output-cache replay-only gateway --responses-backend proxy --wrap opencodeFor a Linux Nix agent build, use the overlay's pkgs.hellasLib.agent helpers:
let
agent = pkgs.hellasLib.agent;
inputs = {
name = "agent-change";
workspace = ./source;
prompt = builtins.readFile ./prompt.txt;
model = "your-provider-model";
hellas = pkgs.hellas.cli;
opencode = pkgs.opencode;
packages = [ pkgs.rustc pkgs.cargo ]; # tools available to the agent
gatewayArgs = [ "--responses-backend" "proxy" ];
};
in {
record = agent.mkAgentRecord inputs;
result = agent.mkAgentRun (inputs // { cache = ./agent-recordings; });
}Run the recorder executable with a writable store directory outside the Nix
build, with provider credentials in its runtime environment. Stop the writer, copy a complete store snapshot,
and use it as cache. mkAgentRun starts a replay-only gateway inside the
sandbox, runs OpenCode, and returns its resulting workspace. Both phases use
/build/workspace, the same pinned tools/configuration, and an explicit system
prompt date (default 1970-01-01). Changing inputs can produce a cache miss;
re-record them instead of giving the build network access. A replay reruns
tools and is only reproducible when those tools are deterministic too.
The helper uses OpenCode's generic OpenAI Responses client. Its endpoint must
accept that request shape; the stricter sealed codex-responses Fetch contract
is not a drop-in target. A zero OpenCode exit status alone is not success: the
runner also requires a completed session and rejects error events.
Recordings and generated files can contain private source or model output. Do not put credentials in Nix expressions, and do not publish recordings without reviewing them: Nix store contents are normally readable by all local users.
Available in the development shell:
cargo audit # security advisories
cargo outdated --workspace --root-deps-only # outdated deps
cargo update --workspace # update Cargo.lockThe otel Cargo feature is opt-in across the CLI (including Fetch, node,
gateway, indexer and validator commands). Default Nix packages, including
public musl builds, disable Hellas telemetry exporters. Request instrumentation
uses shared enabled/no-op implementations; ordinary operational logs remain.
Transitive dependencies still include metrics collection and telemetry SDK crates;
disabling otel does not remove those dependencies.
The exported NixOS, nix-darwin and Home Manager modules share otel options:
# NixOS node and gateway:
services.hellas.otel = {
enable = true;
collectorEndpoint = "http://127.0.0.1:4318";
sampleRate = 1.0;
};
# nix-darwin CLI or Home Manager CLI (including Darwin's launchd agent):
programs.hellas.otel.collectorEndpoint = "http://127.0.0.1:4318";
# Validator clusters use services.hellas-chain-validators.<name>.otel.A configured endpoint enables the feature on the module's default package;
otel.enable = false explicitly selects the build without telemetry. Custom
packages remain the caller's choice. collectorEndpoint is the standard OTLP
HTTP/protobuf base URL for traces and metrics; the existing endpoint option
continues to accept an exact trace URL. Home Manager and nix-darwin scope
variables to hellas-cli, rather than enabling unrelated applications.
Home Manager's programs.hellas.serve supplies the Darwin user service.
Direct package users can use cli.override { otel = true; } (also
cli-catena and cli-validator) and the standard OTEL_* environment variables.
One CLI SDK lifecycle owns traces and metrics, including validators. Native
operations follow the GenAI Development conventions at
c88d504ab3d9879f8e50d3cc87e69775e11db234; these conventions are still evolving.
Hellas-specific work/payment metadata uses hellas.*, with W3C TraceContext
in RPC metadata and HTTP headers. Prompts, model output, tool arguments and
credentials are not recorded. Log events stay in the local log sinks, rather
than being duplicated into OTLP. Normal shutdown flushes both providers;
existing forced-exit paths cannot guarantee a final flush.