Files
zeekayandGitHub 949608fd9d i18n: pages and components c–l draw their copy through @hanzo/i18n (#67)
Calculator, captable, careers, cloud, compare, contact and contact-sales,
customers, dashboards, dataroom, datastore, dev, download, enso,
enterprise, extension, family, integrations, investors, kai, learn,
legal and their components say their words through t() in client code,
<T> in server components and phrase() where copy is held as data; one
sentence is one key, figures and links its arguments.

Rebased on main: in every conflict main's file stands (its copy, layout
and styling) and only the wrapping is added back, including copy main
added since.

Claude-Session: https://claude.ai/code/session_015XYKLjZtDTJEnMo4eK3AFH
2026-10-30 01:15:31 -07:00

106 lines
5.9 KiB
TypeScript

'use client'
import type { ReactNode } from 'react'
import { ArrowRight, Eye, GitBranch, KeyRound, Lock, RefreshCw, Shield, Terminal } from 'lucide-react'
import { OSSRevenueBanner } from '@/components/oss/OSSRevenueBanner'
import { ProductLanding } from '@/components/product/ProductLanding'
import { ProductFooter } from '@/components/products/ProductFooter'
import { useI18n } from '@hanzo/i18n/react'
/**
* KMS, through the ONE product kit.
*
* One of NINE pages — console, insights, kms, kv, mq, pubsub, sql, storage,
* stream — that shared a class set 100%: the same hero, stat row, six-card
* grid, terminal and call to action, written out nine times in ~310 utility
* classes each. They now pass one typed props object to `ProductLanding` and
* carry zero layout classes. See LLM.md, "Tailwind is NOT gone", for the rule.
*
* EVERY WORD IS THE ONE THAT WAS HERE, the four stat tiles included: 28 of the
* group's 36 carry a port, a version, a licence or a coding the prose does not
* (measured), so unlike /ledger's four they are not restatements and they stay.
*
* One fix the move makes by construction: ProductFooter sat INSIDE the final
* card's `max-w-4xl` wrapper on all nine, so the OSS attribution and deploy
* block rendered narrower here than on every other product page.
*/
export default function KMSPage({ source }: { source: ReactNode }) {
const { t } = useI18n()
return (
<>
<ProductLanding
badge={"kms.hanzo.ai"}
badgeIcon={KeyRound}
title={"Hanzo KMS"}
tagline={t("Secrets management")}
lede={t("Hanzo KMS is where the credentials your code needs actually live, so they stop living in a file somebody committed. The SDK seals a value on your machine before it goes anywhere: your passphrase becomes a key through Argon2id, that key becomes an organization key through HKDF-SHA256, and the value is sealed with AES-256-GCM. The organization key never leaves the client. What the server stores is a blob it cannot read.")}
ctas={[
{ label: t("Open KMS"), href: "https://kms.hanzo.ai", icon: ArrowRight },
{ label: t("Documentation"), href: "https://docs.hanzo.ai/docs/openapi/kms/" },
]}
what={{
title: t("What it holds and how it holds it"),
sub: t("A secret is a value under a path, a name and an environment, inside one organization. That is the whole model."),
pillars: [
{
icon: Lock,
title: t("Sealed before it leaves you"),
body: t("Argon2id turns a passphrase into a key, HKDF-SHA256 turns that into the organization's key, and AES-256-GCM seals the value. The organization key stays on the client. Sharing with a colleague wraps that same key to their public key using a hybrid post-quantum exchange, so nobody has to send anybody a secret to share one."),
},
{
icon: GitBranch,
title: t("Into the process, not into a file"),
body: t("Run a command with the secrets already in its environment, export a dotenv when a tool insists on one, or let the Kubernetes operator keep a Secret in step with what KMS holds. The CLI also reads your repository and its git history looking for values that escaped."),
},
{
icon: RefreshCw,
title: t("Replacing one is deliberate"),
body: t("Rotate is a command, and a write states the version it means to replace — so a second writer who read the old value is refused rather than quietly winning. Signing keys generate, sign and rotate through the same API, backed by threshold MPC."),
},
{
icon: Eye,
title: t("Who read it, when, and why"),
body: t("Reads and writes land in an append-only record written by a single writer off the request path, so keeping it never slows a fetch. An AI agent's read is attributed to that agent by name, not to whichever human's key it borrowed."),
},
{
icon: Terminal,
title: t("One key, several languages"),
body: t("A CLI for a laptop and for CI. SDKs in Go, Node and Python over the same routes. In-cluster callers can take the binary ZAP transport instead of HTTP, and it enforces the identical token and role checks."),
},
{
icon: Shield,
title: t("Fail closed, or do not start"),
body: t("Every call carries a Hanzo IAM token verified against a cached JWKS; HMAC and alg none are refused outright. The organization comes from the verified token, never from a field the caller sets. Outside development the daemon will not boot without an issuer, an audience and a JWKS URL — there is no accidental open mode. A secret can be marked so that any agent read waits for a person to approve it, or is refused."),
},
],
}}
stats={{
items: [
{ value: "AES-256", label: t("Sealed on your machine") },
{ value: "K8s", label: t("Secrets kept in step") },
{ value: t("Audit"), label: t("Every read recorded") },
{ value: "MIT", label: t("Run it yourself") },
],
}}
code={{
head: { title: t("Nothing lands on disk") },
name: "terminal",
source,
}}
finalCta={{
title: t("Take them out of the repo"),
sub: t("Use it hosted, or run the same binary yourself. The source is MIT and the client is where the encryption happens either way."),
buttons: [
{ label: t("Open KMS"), href: "https://kms.hanzo.ai", icon: ArrowRight },
{ label: t("View on GitHub"), href: "https://github.com/hanzoai/kms" },
],
}}
>
<OSSRevenueBanner />
</ProductLanding>
<ProductFooter slug={"kms"} name={"KMS"} />
</>
)
}