Engineers working at a bank of monitors running build and release infrastructure

A HoruSphere Group company

Build operations for game studios.

Horu CLI instruments Unity and Unreal builds wherever they already run — a developer shell, Jenkins, GitHub Actions, TeamCity, GitLab. Horu CI is the native build system around the same engine. One Go binary and one horu.yaml produce the first useful report, with no server, no Docker and no account in the way.

Horu CLI is at MVP. Horu CI is in development. We are onboarding a small number of studios as design partners, one build lane at a time.

Getting started

your existing pipeline no Horu server required
horu init --engine unity --platform windows macos --template starter
horu build --platform windows --configuration release
horu report --format text,json,html --out ./out/horu

Three commands and one config file, added to a build lane you already have. The orchestrator stays where it is.

Three properties we will not trade away.

Server-optional

The CLI is a native Go binary. It does not need Docker, an account, a database or a hosted service to produce a report. If Horu disappeared tomorrow your pipeline would still run — that constraint is deliberate, and it is what makes the first evaluation cheap.

Engine-aware

Build targets, host filtering, cache keys derived from Packages/manifest.json and ProjectSettings/ProjectVersion.txt, and log rule packs aimed at Unity licensing, IL2CPP and Addressables failures. Generic CI treats these as your problem.

Evidence-first

A green badge is not a release decision. Every run writes one report directory: stage timings, cache behaviour and miss reasons, sanitation findings, package manifests, artifact checksums, and the failure anchor if there was one.

Architecture

Three ways to run one build engine.

Horu CLI is not a thin API client wrapped around a service. It is the build engine, and it is the same code in all three positions: a developer running a build locally, an existing orchestrator calling it as a step, and a Horu CI runner executing it natively.

That is why moving between them is a change of launcher rather than a migration. The records come out the same shape whichever one started the run, so history collected during evaluation is not thrown away when a team decides Horu should own orchestration.

See the product boundary

Developer shell Local run, no network Existing CI Jenkins, GitHub Actions, TeamCity, GitLab Horu CI runner Native execution Horu CLI build engine init · build · run cache · package · report One build report Timings, cache, sanitation, manifests, checksums
The engine is the product surface. Local runs, external orchestrators and native runners all execute the same commands and write the same records.

The build report

What one Horu build actually records.

Not a status. A chain of linked facts, written in order by six commands, that survives the run and can be read by a person or a machine afterwards.

  1. source context
  2. stage timing
  3. runner and tool inventory
  4. logs and failure anchors
  5. cache reports
  6. sanitation reports
  7. package manifests
  8. artifact checksums
  9. report outputs

1.0 Init

Fail in ten seconds, not forty minutes.

Long engine stages fail late for cheap reasons: a missing rsync, an unset UNITY_LICENSE, a disk with no room for the cook step. horu init checks the environment before any of that starts, and scaffolds horu.yaml from an engine template if the project does not have one yet.

Naming note, because it comes up: the command is init, not doctor. Verifying a machine before a release-critical operation is not a support activity.

scaffold and check
horu init --engine unity --platform windows macos \
     --template steam-windows
  • windows
  • macos
  • linux
  • android
  • ios
  • webgl
tools.required
Executables the stage assumes are on PATH — git, the engine binary, rsync, SDK utilities, signing and upload CLIs.
environment.required
Variables that must be set, such as UNITY_LICENSE. An unset licence is a five-second failure, not a thirty-minute one.
disk.min_free_gb
A floor checked before the run, so a cook or package step does not die halfway through for want of space.
profile
Resolved automatically: ci when CI=true and that profile exists, otherwise default. One config file serves both machines.
ProjectVersion.txt
Unity starter targets read the Editor version from the project, so macOS and Windows builds do not need a unity binary resolvable on PATH.

2.0 Build

Run the target matrix, filtered by host.

Each entry in build.targets carries a hosts field. horu build filters to what the current machine can actually produce, so a Windows runner does not start a macOS target and discover the problem at the end. If nothing compatible survives the filter the command fails rather than reporting a hollow success.

  1. horu build --dry-run Headless validation of the resolved targets. Checks the shape of the run without invoking the engine.
  2. horu build --configuration release Selects a configuration and narrows the matrix through build.targets[].configurations.
  3. horu build --platform macos --configuration release Non-dry-run builds require an explicit --platform, and must target that platform. Silence is not a default here.
  4. horu build --platform macos --configuration release --clean Disables cache restore and save for this run, which is how you get a clean baseline to compare against.

Two lines worth grepping for

A successful non-dry-run prints Build artifact output: target=<id> path=<path>, so the next stage does not have to guess where the output landed. Every build also prints workspace_affinity_key=<key>, which an external scheduler can use to route repeat jobs back to a machine that already has a warm workspace.

3.0 Run

Turn engine log noise into a failure anchor.

A failed Unity build is usually thousands of lines of asset-import chatter wrapped around one meaningful error. horu run wraps a stage, streams stdout and stderr live while capturing them, collapses known noise by pattern, and classifies what went wrong against a rule pack. The left column is configuration you write once. The right column is what the run records.

Rule pack, from horu.yaml
logs:
  unity:
    engine: unity
    collapse_patterns:
      - "^Refreshing native plugins"
      - "^Asset import worker"
    failure_packs:
      - unity-licensing
      - unity-il2cpp
      - unity-addressables
Recorded by horu run
stage
The stage name you wrapped, so timings are comparable across runs.
timing
Wall time for the wrapped command, kept per stage rather than per job.
exit state
Process exit preserved as a stable exit code the orchestrator can branch on.
classification
Build, asset, package, test, validation or infrastructure — a taxonomy, not a boolean.
failure anchor
The first meaningful failure line, surfaced instead of the last thousand repeated ones.

Used inside Jenkins or GitHub Actions, horu run replaces the defensive scripting that accumulates in build files, while leaving the existing orchestrator in charge.

4.0 Cache

Cache behaviour as a record, not runner folklore.

Keys are derived from files you name, not from a convention buried in a runner image. Entries are split per configured path so one bad save does not poison the whole workspace, and high-churn directories are excluded on purpose. Compression defaults to none, because the archive overhead frequently costs more than the transfer it saves.

key.files Packages/manifest.json ProjectSettings/ProjectVersion.txt ProjectSettings/EditorBuildSettings.asset cache key per profile paths Library exclude Library/Bee Library/ShaderCache compression: none — no archive overhead on either side of the run one entry per configured path, rather than a single monolithic archive
Cache key composition for a Unity library profile. Naming the inputs is what makes a miss explainable.
restore / save
Attempted by default whenever cache profiles are configured. --clean opts a run out.
warm no-op
Restore is skipped when the paths are already present, and save is skipped when that key is already stored. A no-op is faster than a correct copy.
accelerator_endpoint
When set, Unity Accelerator flags are passed to the engine while Horu still owns restore and save. The two are not mutually exclusive.
invalidate
horu cache --invalidate drops an entry deliberately, and the reason is part of the report rather than tribal knowledge.

5.0 Package

Stop shipping files that were never meant to ship.

Sanitation is a package step with a report attached, not a shell one-liner somebody remembers to add to the release lane. remove strips known junk. forbid fails the run when an internal directory or a development config is present in the output. Both decisions are written down, so the answer to “how did that get in the build” is a lookup rather than an investigation.

Manifests carry a sha256 checksum per artifact, which is what makes a later promotion decision about a specific build rather than about a branch name.

Read the Horu CLI reference

remove

Stripped silently. Junk that has no business in a shipped tree.

  • **/.DS_Store
  • **/Thumbs.db
  • **/*.tmp
  • **/*.log

forbid

Fails the run. Presence is a defect, not a cleanup task.

  • **/InternalOnly/**
  • **/*Development*.json

manifest

Written for every package, and used by later gates.

  • mode: fail
  • manifest: true
  • checksum: sha256
release lane
horu package --path ./Builds/Windows \
     --profile steam-release --sanitize --manifest --validate

6.0 Report

One report, four ways to read it.

Every command contributes to a single build report directory. The console summary is for the CI log. JSON is for machines and for upload. HTML is for the person who has to explain the failure to someone who was not there. GitHub annotations put the anchor on the pull request, where the decision actually happens.

Upload is additive. The report is useful with no server in the picture, and a team can run Horu CLI for months before deciding whether history is worth hosting.

  • text
  • json
  • html
  • github
report--upload is optional
horu report --format text,json,html --out ./out/horu
# on GitHub Actions, annotate the run
horu report --format github,json,html --out ./out/horu
# send the same records to Horu CI for history
horu report --format json --out ./out/horu --upload
console
A summary short enough to read in a CI log without scrolling.
json
Structured records, and the payload Horu CI ingests.
html
A local file you can attach to a ticket or archive as a build artifact.
annotations
GitHub Actions annotations pointing at the failure anchor.
exit codes
Stable across versions, because pipelines branch on them.

Status

What is built, and what is only written down.

We publish this split because a pre-1.0 build tool that hides its edges is not worth adopting. Everything on the right is documented and specified. None of it is shipping yet, and no part of the argument above depends on it.

Implemented today

  • Horu CLI with init, build, run, cache, package and report
  • Shared build modules behind both the CLI and the runner, so the two cannot drift apart
  • Horu CI service: build records, pipeline records, manual runs, source-event triggers, queue items
  • Runner pools, leases and heartbeats, with host, container and test executors
  • Artifact upload, manifest validation, log upload, stage records, rerun, retry and cancel
  • Release candidates and release gate evaluation
  • Raw CLI report upload with list and detail history

Documented, not yet built

  • Deep Unity and Unreal rule packs beyond the MVP diagnostic baseline
  • Remote cache restore and save, with cache evidence to match
  • Horu CI runners orchestrating the shared build modules as their native execution path
  • Web views for reports, cache analytics and package output
  • ReleaseOps, PM, Wiki and VCS as separate product surfaces

See how this is sequenced

Surface area

Facts you can check, rather than outcomes you cannot.

We have no customer metrics to show you and will not invent any. These are statements about what the product is.

6 Commands in the v1 CLI surface. Behaviour is controlled by flags and horu.yaml, not by a growing verb list.
4 Report formats: console text, JSON, HTML and GitHub Actions annotations.
6 Unity scaffold target platforms: windows, macos, linux, android, ios, webgl.
4 External CI systems supported as launchers: Jenkins, GitHub Actions, TeamCity, GitLab.
Go One static binary for the CLI, and a runner agent that registers, leases, streams logs and uploads artifacts without Docker.
sha256 Checksum written into every package manifest, so promotion decisions name a build rather than a branch.

Products

Two products now. Four documented and deferred.

The deferred four are real designs, not roadmap decoration — and they stay deferred until build evidence is good enough to be worth building on.

Documented, deferred

  • Horu ReleaseOps Planned

    Release candidates and promotion gates

  • Horu PM Planned

    Evidence-backed production management

  • Horu Wiki Planned

    Evidence-linked studio knowledge

  • Horu VCS Planned

    Source intelligence for builds

Open the full product index

Pick one painful build lane.

That is the whole ask. Add the binary and a config file to a single Unity or Unreal lane, compare failure diagnosis and cache visibility before and after, and decide from there. We onboard a small number of studios at a time so that early reports get read by the people who wrote the tool.