Superseded: the Hanzo GitHub App pushes a webhook, so the forge tracks GitHub
without a per-repo workflow. This file called git.hanzo.ai/api/v1/.../mirror-sync
— a Gitea API for a system we no longer drive — and would sit inert in every repo.
One mechanism, in one place, instead of ~350 copies of a cron.
git.hanzo.ai mirrors this repo by PULL on a ~10-minute interval, and arcd runs
CI/CD there — so every push waited out that interval before anything built.
This asks Gitea to pull HEAD immediately.
Latency only: the repo already mirrors via the App webhook, so a missing
HANZO_GIT_TOKEN or a failed curl is non-fatal and never fails the push.
Idempotent (mirror-sync just pulls HEAD) and concurrency-coalesced.
This pipeline could not work on this fleet and had not produced an image in
weeks, while looking idle rather than broken.
buildctl-daemonless.sh: command not found (exit 127). Jobs run in
catthehacker/ubuntu:act-24.04 per GIT_RUNNER_LABELS; there is no buildctl
in it. It also read secrets.GIT_CLONE_TOKEN, which exists on neither the
repo nor the hanzoai org on git.hanzo.ai.
The kubectl patch step failed the same way (kubectl: command not found) and
was futile anyway: cd.hanzo.ai selfHeal restores the App CR from the pin in
hanzoai/universe on its next poll. Rollout is a reviewed pin there, never a
CI side effect — so the deploy step is removed, not repaired.
Replaced with the shape proven on hanzoai/playground, the only one of these
whose build job has ever gone green here: setup-buildx + login-action +
build-push-action, using GHCR_USER/GHCR_TOKEN/GH_PAT which DO exist as org
secrets. Keeps playground's imagetools verification, because build-push-action
can exit 0 before the manifest resolves.
runs-on named a label no runner on git.hanzo.ai advertises. The four online
git-runners offer hanzo-build-linux-amd64 (plus ubuntu-latest/22.04/24.04);
hanzo-linux-amd64 and hanzo-deploy-linux-amd64 are offered by NOTHING there
(the latter is a GitHub ARC pool, not a forge runner).
A job asking for an unavailable label does not fail — it queues forever and
cancels at the 24h timeout, which reads as 'CI is slow', not 'CI is broken'.
This was latent while the forge repo had actions disabled and the pipeline
could not fire at all. Enabling it made the bug reachable, so it is fixed in
the same pass.
The detail page tells maintainers to paste a 'Deploy on Hanzo' badge into their
README, but the Dockerfile's COPY list is explicit and badge.svg was not on it.
The asset 404'd, so every README that adopted the snippet would render a broken
image. It ships with the page that advertises it.
Co-authored-by: Hanzo Dev <[email protected]>
The gallery could only search and deploy: a card had no way in, so the only way
to learn what an app actually was meant leaving for GitHub. Cards now open
#app=<id> — a real URL, so a specific app can be linked, shared and reloaded,
and Back closes it because the hash is the single source of truth for what is
open.
The panel answers what a tile cannot: which containers a deploy starts and
which images they pull, read from the blueprint's own docker-compose.yml rather
than restated from the catalog row. Verified against the real corpus — 400 of
400 blueprints parse, 812 services, none silently empty. A blueprint without
compose simply omits the section instead of implying nothing runs.
Two things the catalog needed and had nowhere to put:
- "Add it to your repo" — the markdown badge a maintainer pastes into their
README so their own users can deploy in one click. badge.svg ships here so
the snippet is not a dead link, and it carries no external font because
READMEs render where remote CSS is blocked.
- "Suggest an app" — submitting was previously undocumented; it is now a
prefilled issue from the catalog itself and from the footer.
Native <dialog>, so focus trapping, Esc and inertness are the platform's job.
Still no framework, still no innerHTML: the panel is built with the DOM API, so
a hostile description cannot inject.
Co-authored-by: Hanzo Dev <[email protected]>
`crs/templates-blueprints.yaml` was a second copy of a declaration that only
counts in one place. Its own header says "LAND THIS via hanzoai/universe", and it
had already drifted two releases behind the live CR (sha-f45d1da here vs
sha-402659d deployed) — so the copy that looks authoritative in this repo was
telling anyone who read it the wrong thing, and editing it would have deployed
nothing.
Declared state lives in hanzoai/universe (infra/k8s/operator/crs/), which is what
Hanzo CD renders and applies. One home, one answer.
Measured on the live page at 390px: the category chips render 32px tall and the
theme button 34px, under the ~44px a thumb reliably hits. They are the primary
controls on that page — filtering the catalogue is the whole interaction.
Scoped to `@media (pointer: coarse)`, so the dense row a mouse wants is untouched.
Verified on the live page with the rule injected: touch 32/34 -> 44/44, mouse
unchanged at 32/34. One chip class, one adaptation, no second component.
Merge the two half-storefronts into ONE canonical page:
- KEEP the educational story (hero · beliefs · makers band).
- FOLD IN the searchable 1,030-app launcher below it: search box, 16
data-driven category chips, paginated card grid (Deploy + Source),
load-more, #q=&tag= URL sync, / and Esc keys.
- STRIP every public compute-share percentage — the makers band now
promises a share metered per second, paid automatically, no number.
- ONE reconciled stylesheet: theme-aware (light default, dark via OS +
[data-theme] toggle persisted to localStorage), responsive 390→1680,
no horizontal body scroll. Editorial sections converted from hardcoded
black/white to design tokens.
- ONE JS: theme bootstrap (blocking in <head>, no flash) + launcher.
DOM-built, never innerHTML; logos degrade to a monogram on error.
meta.json + blueprints (the machine contract the PaaS loader reads) are
untouched. Verified headless under the exact prod CSP at 390 + 1280:
hero + real cards render, search + chip filter work, Deploy href =
platform.hanzo.ai/templates?deploy=<id>, zero CSP/JS errors, zero '%',
theme toggle persists, scrollWidth<=innerWidth.
Elevate the storefront from a working grid to a destination, without regressing
the resilient base (DOM-built, CSP-clean, per-item try/catch, logo→monogram).
- Editorial hero: "The world runs on open source. Deploy any of it in one click."
Static HTML story (hero · beliefs band · makers band) first-paints instantly and
survives a /meta.json failure — only the catalog below needs data.
- Philosophy band: one binary/one API · own everything, no lock-in · earn when
others run it. Honest (Techstars '17, real 20%, real 1,030).
- Curated exploration, not a wall of 1030: 12 hand-picked "Start here" spotlights
+ 8 browse-domains (each a curated tag-set, one intersection filter) with live
counts and plain-English intros. Search stays the power path.
- Economics first-class: Deploy → platform, "Earn 20% →" → console/authors.
- 1030-grid deferred behind IntersectionObserver (renders on scroll/interaction),
paginated, lazy logos. Motion is opt-in + reduced-motion-safe.
CSP-clean: external app.js/catalog.css only, data: favicon+mark, zero inline
handlers (grep=0), zero securitypolicyviolations verified under production CSP.
The inline img onerror= handler was blocked by script-src 'self' → console-error
storm on all 1030 cards + dead-feeling page. Rebuild: cards built via DOM API
(never innerHTML — XSS-safe, CSP-clean), logo error handled via addEventListener
(graceful monogram fallback), render wrapped so one bad row is skipped not fatal,
graceful catalog-load failure. Apple-tier CSS polish (calm monochrome, refined
hover, rhythm). Reliable + resilient by construction.
Aggregates five OSS one-click/self-hosting catalogs into the Dokploy blueprint
shape the Hanzo PaaS consumes (meta.json array + blueprints/<id>/{template.toml,
docker-compose.yml,meta.json,logo}):
Dokploy 500 (MIT, verbatim) · Coolify 174 (Apache-2.0) · CapRover 161
(Apache-2.0) · CasaOS 85 (Apache-2.0) · Runtipi 110 (GPL-3.0)
Deduped by app identity; each source + app keeps its upstream license
(ATTRIBUTION.md). Deterministic aggregators + a hard validator (every
template.toml parses with the platform's toml lib, every compose parses as YAML,
meta.json is the expected array, and an over-HTTP fetch probe exercises all
1030) — 0 hard failures. Served natively by hanzoai/static on DOKS (no CDN/Pages/
Vercel); CI builds on the hanzo-build-linux-amd64 arc pool; operator App CR in
crs/ serves it at templates.hanzo.ai.