Skip to content

Releases: notpop/hearth

Hearth v0.2.2-alpha

26 Apr 13:52

Choose a tag to compare

Hearth v0.2.2-alpha Pre-release
Pre-release

Hearth v0.2.2-alpha

Patch release. Three improvements:

Structured logging on both sides

Worker side emits these slog events with worker, job, kind, attempt, and elapsed fields:

INFO  task.received  worker=mac-1   job=ab12  kind=img2pdf  attempt=1
INFO  task.started   worker=mac-1   job=ab12  kind=img2pdf
INFO  task.done      worker=mac-1   job=ab12  kind=img2pdf  elapsed=27.3s
WARN  task.failed    worker=mac-1   job=ab12  kind=img2pdf  err="..."  elapsed=...

Coordinator (host) side emits:

INFO  job.submitted  job=ab12  kind=img2pdf
INFO  lease.granted  job=ab12  kind=img2pdf  worker=mac-1  attempt=1
INFO  job.complete   job=ab12  worker=mac-1  output_blobs=1
INFO  job.failed     job=ab12  worker=mac-1  err="..."  will_retry=true  next_run_at=...
INFO  job.cancelled  job=ab12

Operators can now answer "who picked up this job?" and "how long did it take?" by tailing logs. Switch to a JSON handler (runner.Options.Logger) for structured ingestion.

Pure Nix task runner (Justfile dropped)

Tasks moved from a Justfile to flake apps. Inside nix develop (or via direnv) you can just type build, test, enroll, etc. — shell functions are auto-defined. Outside the shell, use nix run .#<name>.

# In-shell (interactive)
nix develop
build
test
enroll --addr 192.168.1.10:7843 my-worker

# Out of shell (CI, scripts)
nix run .#build
nix run .#test
nix run .#release-build -- v0.2.2-alpha

packages.default — consume Hearth from another flake

inputs.hearth.url = "github:notpop/hearth";
# then in your devShell.packages:
hearth.packages.${system}.default

Useful if your project wants to standardize on Nix-based delivery rather than downloading release archives.

Verified

  • go vet ./..., go test ./..., go test -race ./...
  • Cross-compile to all 5 targets

License

MIT © 2026 notpop


Full Changelog: v0.2.1-alpha...v0.2.2-alpha

Full Changelog: v0.2.1-alpha...v0.2.2-alpha

v0.2.1-alpha

26 Apr 13:29

Choose a tag to compare

v0.2.1-alpha Pre-release
Pre-release

Hearth v0.2.1-alpha

Patch release. Two improvements that pave the way for "single-file worker deployment":

pkg/runner.RunFromBundleBytes

A new entry point that accepts the .hearth bundle as []byte rather than a filesystem path. Use it with go:embed to ship a single self-contained worker binary — no separate cert/key/addr file to manage on the target machine:

package main

import (
    _ "embed"
    "context"
    "log"
    "os"
    "os/signal"
    "syscall"

    "github.com/notpop/hearth/pkg/runner"
)

//go:embed worker.hearth
var bundle []byte

func main() {
    ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
    defer cancel()
    log.Fatal(runner.RunFromBundleBytes(ctx, bundle, myHandler{}))
}

runner.Run(ctx, runner.Options{...}) now requires exactly one of BundlePath or BundleBytes; passing both or neither returns a clear error.

Justfile

A conventional task surface lands at the repo root, designed to pair with the existing nix develop shell:

just              # list tasks
just build        # ./bin/hearth
just test         # full suite
just test-race
just cover
just lint
just proto        # regenerate gRPC stubs
just release-build v0.2.1-alpha

just is now in flake.nix's devshell packages so anyone with Nix gets it for free.

Verified

  • go vet ./...
  • go test ./...
  • go test -race ./...
  • Cross-compile to all 5 targets

License

MIT © 2026 notpop


Full Changelog: v0.2.0-alpha...v0.2.1-alpha

Hearth v0.2.0-alpha

26 Apr 12:44

Choose a tag to compare

Hearth v0.2.0-alpha Pre-release
Pre-release

Hearth v0.2.0-alpha

Second alpha. Significant API and ergonomics improvements. Wire protocol and bundle format unchanged from v0.1.0-alpha so existing bundles still work.

Ergonomics

  • hearth coordinator is now zero-config on first run. Auto-creates the CA at ~/.hearth/ca/, issues a server cert, and writes a local admin.hearth bundle to the data directory. The 6-step Quick Start collapses to 2.
  • CLI auto-discovers the admin bundle. submit, status, cancel, and nodes walk --bundle flag → $HEARTH_BUNDLE./.hearth/admin.hearth~/.hearth/admin.hearth. No --bundle needed when running on the coordinator host.
  • hearth submit --blob <path> uploads files as input blobs. Repeatable. Closes the gap that previously forced users to write Go code for any non-trivial submission.
  • hearth cancel <job-id> for cancelling running or queued jobs. Workers receive the cancel signal via the next heartbeat and unwind their handler context.

New public API: pkg/client

External Go projects can now talk to a coordinator without shelling out to the CLI or hand-rolling protobuf:

c, _ := client.Connect(ctx, "/path/to/admin.hearth")
defer c.Close()

id, _ := c.Submit(ctx, job.Spec{Kind: "img2pdf", Blobs: []job.BlobRef{ref}})
_ = c.Watch(ctx, id, func(j job.Job) { /* ... */ })

Sentinel errors are exposed for switch-on-error patterns:

errors.Is(err, client.ErrNotFound)            // 404
errors.Is(err, client.ErrInvalidArgument)     // 400
errors.Is(err, client.ErrInvalidTransition)   // already terminal
errors.Is(err, client.ErrUnauthenticated)     // bundle rejected

Progress reporting

Long-running handlers can report progress, which surfaces in hearth status --watch:

func (h myHandler) Handle(ctx context.Context, in worker.Input) (worker.Output, error) {
    for i, page := range pages {
        in.Report(float64(i)/float64(len(pages)), fmt.Sprintf("page %d/%d", i+1, len(pages)))
        process(page)
    }
    return worker.Output{}, nil
}

Reports go through an atomic and piggyback on the next heartbeat, so calling Report in a tight loop is safe and cheap.

Worker reliability

  • Auto-reconnect with exponential backoff. Workers no longer exit on transient Lease failures; they back off (1s → 30s) and retry. A coordinator restart looks like a long delay followed by reconnection.

WatchJob is push-based

Replaced the 200ms server-side poll with an in-memory pub/sub. Updates are delivered in the same coordinator goroutine that wrote the state, so latency drops to "milliseconds" from "up to 200ms".

Programmer-intuitive defaults

Spec zero values now do what most callers expect:

Field Before After
MaxAttempts: 0 1 attempt 3 attempts
BackoffPolicy{} undefined {1s, 60s, ×2, 0.1 jitter}
Multiplier: 0 constant backoff 2.0 (default)

Each field's behaviour is documented in godoc on pkg/job.Spec.

Validation and error clarity

  • Handler.Kind() and Spec.Kind now must match ^[a-z0-9._-]+$ (≤ 64 chars). Validation runs at handler registration and at Submit time, with messages that tell users exactly what to fix. Prevents --kinds CSV parsing bugs and keeps room for future namespacing like media.transcode.
  • Job.State gains IsActive() and IsRetryable() predicates alongside the existing IsTerminal(). Each State const now has a doc comment.
  • Job.Attempt is documented as 1-based (the first delivery has Attempt == 1).

Documentation

  • New docs/USAGE.md and docs/USAGE.ja.md covering all four use cases (worker, submitter, mixed, CLI), handler contract, error patterns, blob I/O, progress, and troubleshooting.
  • READMEs trimmed to ~129 lines each, focused on "what is it / get running / build your own worker".

Verified

  • go vet ./...
  • go test ./... (full suite)
  • go test -race ./...
  • staticcheck -checks=U1000 (no unused code)
  • Cross-compile to darwin/{arm64,amd64}, linux/{arm64,amd64}, windows/amd64 (CGO-free, ~6–7 MB)

Coverage on new code:

  • pkg/job: 100%
  • pkg/client: 83%
  • pkg/runner: 86%
  • internal/wire: 100%
  • internal/app/coordinator: 90%
  • internal/app/workerrt: 90%

Pre-release audit fixes

A fresh-context review surfaced and fixed:

  • A broken no-op in streamReader.Close (was assigning a method value instead of calling it)
  • A doc/code drift in runner.Options.LeaseTTL defaults
  • Dead code: ServerOptions.WatchPoll, DialContextOption, Client.Conn()
  • Defensive checks for unset Spec.LeaseTTL and unset progress_reported_at_ns columns
  • CLI commands now honour Ctrl-C during long blob uploads
  • Conformance test coverage for Progress storage paths

License

MIT © 2026 notpop

Full Changelog: v0.1.0-alpha...v0.2.0-alpha

Hearth v0.1.0-alpha

26 Apr 00:04

Choose a tag to compare

Hearth v0.1.0-alpha Pre-release
Pre-release

Hearth v0.1.0-alpha — initial alpha release

Distributed task runner for home networks. Single Go binary, mTLS by default, single-file worker enrollment.

This is an alpha release — wire protocol, public Go API, and bundle format may still change.

What's in this release

  • Coordinator — durable SQLite-WAL queue, FS content-addressable blob store, mDNS service publication, mTLS gRPC server.
  • Worker runtime — pluggable Handler interface, lease + heartbeat protocol, automatic re-queue on lease expiry.
  • CLI (hearth) — ca init, enroll, coordinator, worker, submit, status, nodes, version.
  • Single-file bundle<name>.hearth (~1 KB tar.gz) carries everything a new machine needs to join.

A reference handler (examples/img2pdf, PNG/JPEG → PDF) and a runnable worker
binary (examples/img2pdf/cmd/img2pdf-worker) live in the source tree to
demonstrate the pattern, but are not shipped as release binaries — build them
yourself with go build ./examples/img2pdf/cmd/img2pdf-worker if you want it.

Installing from binaries

Download the archive for your platform below, extract, and put hearth somewhere on your $PATH.

# example: macOS Apple Silicon
tar xzf hearth-v0.1.0-alpha-darwin-arm64.tar.gz
./hearth version

Verify with:

sha256sum -c checksums.txt   # Linux
shasum -a 256 -c checksums.txt   # macOS

Quick start

hearth ca init
hearth enroll --addr coordinator.local:7843 my-laptop
hearth coordinator --listen 0.0.0.0:7843 --data ./.hearth   # on the always-on host

See the README for the full guide
(日本語).

Known limitations

  • CancelJob server-side returns Unimplemented (worker-side cancel via heartbeat hook is in place, the public RPC is not).
  • WatchJob is implemented as server-side polling, not push-based.
  • CLI submit does not yet accept blob-file inputs (use the Go SDK).
  • Worker reconnect on coordinator restart is not yet automated.

Verified

  • go test ./... (full suite, ~10 s)
  • go test -race ./...
  • go vet ./...
  • Cross-compile to darwin/{arm64,amd64}, linux/{arm64,amd64}, windows/amd64 (CGO-free, ~12–18 MB binaries)
  • End-to-end gRPC over real TCP + mTLS with handshake verification
  • CLI smoke test (CA init → enroll → coordinator → submit → status → nodes)

License

MIT © 2026 notpop

Full Changelog: https://github.com/notpop/hearth/commits/v0.1.0-alpha

Full Changelog: https://github.com/notpop/hearth/commits/v0.1.0-alpha