Files
zooqueen 75c4f1f641 One product answers with one name, route, command and docs page
The CLI lines on the product pages were written from the product name -- hanzo
dns zones, hanzo kms keys -- and several were not commands the binary has. So the
command moves into lib/product.ts, which assembles the rest from what is already
stated: the catalog names the product and its route, pages.json names what the
catalog does not carry, and products-metadata answers for docs, where null means
docs publishes nothing and the link drops.

Every CLI line in that table was run against the binary. A product absent from it
has no command and its page prints none; nothing composes a verb out of a name.
Both blocks that draw it import the Product type rather than declaring their own,
which leaves the table the only place a command can be written.

The catalog also moves out of package.json into pnpm-workspace.yaml, where pnpm
reads it, so the versions are stated once.
2026-09-06 23:18:24 -07:00

80 lines
4.0 KiB
TypeScript

'use client'
import { ArrowRight, FileCode, GitBranch, Layers, RefreshCw, Settings, ShieldCheck, Workflow } from 'lucide-react'
import { ProductLanding } from '@/components/product/ProductLanding'
import { ProductFooter } from '@/components/products/ProductFooter'
/**
* Hanzo Operator, through the ONE product kit.
*
* The largest cluster left: TWELVE pages — engine, gateway, guard, ingress,
* jin, metrics, o11y, operator, sentinel, skills, telemetry, visor — at 85%+
* identical className sets, each a hero, a six-card grid and a call to action
* written out again. All twelve pass one typed props object to
* `ProductLanding`. See LLM.md, "Tailwind is NOT gone".
*
* EVERY WORD IS THE ONE THAT WAS HERE. ProductFooter came out of the final
* card's `max-w-3xl` wrapper, where it had been rendering narrower than on
* every other product page.
*/
export default function HanzoOperatorPage() {
return (
<>
<ProductLanding
badge={"hanzoai/operator"}
badgeIcon={Settings}
title={"Hanzo Operator"}
tagline={"One binary that runs the stack on Kubernetes"}
lede={"Declare a Service, a Datastore, a Gateway or an Ingress. The Deployment, StatefulSet, PVC, autoscaler, disruption budget, network policy and KMS-synced secret behind it get created — and stay that way, because one loop keeps checking."}
ctas={[
{ label: "View on GitHub", href: "https://github.com/hanzoai" },
]}
what={{
title: "Declare the thing, not the thirty objects",
sub: "One reconcile loop, an API group you choose at install, and Kinds that already know what a service in this stack needs.",
pillars: [
{
icon: Layers,
title: "Kinds that know the stack",
body: "Service covers most workloads. Datastore covers the stateful ones and dispatches on its type, with SQL, KV, DocDB and S3 as thin facades over it. Gateway, Ingress, DNS, Base, IAM, KMS and MPC are their own Kinds, because each one wires up differently.",
},
{
icon: ShieldCheck,
title: "Secrets come from KMS",
body: "A KMSSecret is materialized beside the workload and synced from kms.hanzo.ai. What lives in git is the path to a secret, never the secret.",
},
{
icon: RefreshCw,
title: "Drift is reverted, and said out loud",
body: "Every sweep reads each object before it applies, then logs what it did to it: created, reverted, or already in sync. The resync interval is per-resource and clamped into a safe band, so a bad number can't turn the loop into a hot loop.",
},
{
icon: GitBranch,
title: "Git is a source, and it never deletes",
body: "Point it at a repo and it applies what's there, on a tight poll a push webhook can interrupt for an instant sweep. A file removed from git is left alone — the plan type has no delete case, so pruning isn't something a bug can cause.",
},
{
icon: Workflow,
title: "Every write is attributable",
body: "Server-side apply under its own field manager, so a git-driven apply never fights a per-Kind reconcile. Turn on autoscaling and the Deployment ships with no replica count at all — the autoscaler owns that field, alone.",
},
{
icon: FileCode,
title: "Install is one command",
body: "operator install lays down the CRDs, namespace, RBAC and Deployment. operator up does that and applies your resources behind it. Both are server-side apply, so running either again changes nothing.",
},
],
}}
finalCta={{
title: "Get started with Hanzo Operator",
buttons: [
{ label: "View on GitHub", href: "https://github.com/hanzoai" },
],
}}
/>
<ProductFooter slug={"operator"} name={"Operator"} />
</>
)
}