Files
hanzo.ai/components/home/SystemArchitecture.tsx

141 lines
6.9 KiB
TypeScript

import { YStack } from '@/components/ui'
import families from '@/lib/data/families.json'
import { Band, Head } from './band'
import { Caption, Cell, Chip, Host, LinkRow, Mono, Plate, Rule } from './plate'
/**
* The system, drawn as the index the binary keeps of itself.
*
* This section used to be six cards of running prose — a lede arguing about
* vendors and six paragraphs naming products — and its claim was "designed
* together". A reader cannot check that against a paragraph, and the stack
* drawing that preceded this was an ILLUSTRATION of the claim rather than
* evidence for it, which is the one thing plate.tsx's header rules out.
*
* THE ARTEFACT IS THE MOUNT INDEX. hanzoai/cloud is a single process; its
* composition root indexes every subsystem's declared route prefixes once at
* boot, and `MountPrefixes` states the one rule those prefixes follow — a
* subsystem owns `/v1/<name>` unless it declares otherwise (`subsystem.go`).
* That index is a product surface in its own right: `GET /v1/admin/subsystems`
* serves it as name + prefixes + enabled, and its own note says outright why the
* label has to exist — "the cloud binary is a SINGLE process mounting ~60
* subsystems, so every request already shares one service name"
* (`apps/admin/subsystems.go`). One process is what "one OS" MEANS here, so the
* picture is that index, grouped.
*
* THE VALUES ARE READ, AND THE PLATE SHOWS ITS WORKING. `lib/data/families.json`
* is written by `scripts/sync-catalog.mjs` out of `GET /v1/openapi.json` on each
* build, keyed by the first segment after `/v1/` — the same rule the composition
* root mounts on. So every prefix here is one the served document carries and
* every count is that family's operations. The GROUPING into four layers and two
* rails is ours; the arithmetic is not, and because each row prints the prefixes
* it sums, a reader can check the total against the paths beside it. A prefix
* the document stops carrying is dropped from its row rather than printed with a
* zero, which is the inclusion gate the catalog already runs on.
*
* THE RAILS SIT UNDER A RULE, not in the stack. Learning and governance are not
* a fifth and sixth layer — they cut across every one — and the two are the
* largest surfaces the binary serves after the model plane, which is the fact a
* card of adjectives could never carry.
*
* NO ARROWS, and that is the trade this made. The old drawing read upward with
* three glyphs between the cards, saying infrastructure carries intelligence
* carries execution. An index has no direction: it is what is mounted, not the
* order a request walks. The stack claim now lives in the row order alone, and
* the section is honest about which of the two it is drawing.
*
* THE SIX LINKS SURVIVE — each row is the link its card used to be, so nothing
* a reader could reach before is unreachable now. `LinkRow` (plate.tsx) gives a
* row its approach — quiet at rest, its name lifting under a pointer or a key —
* and a thumb-sized box on a coarse pointer, since a row that navigates is a
* control and not a link in a sentence.
*
* NO TOTAL IS PRINTED. Surfaces already captions the whole surface
* ("N operations, read from /v1/openapi.json") and a second copy of that number
* on one page is one fact with two homes. This caption states its own scope
* instead — how many of the document's families the plate names.
*/
/** Operations the served document carries under `/v1/<name>`, or 0 if none. */
const ops = (name: string): number =>
families.families.find((f) => f.name === name)?.operations ?? 0
/** A group of the index: what it is called, where it opens, what it mounts. */
type Group = { name: string; href: string; mounts: string[] }
/** The four layers, in the order the system stacks — the company on top. */
const LAYERS: Group[] = [
{ name: 'Company', href: 'https://hanzo.team', mounts: ['team', 'crm', 'books'] },
{ name: 'Intelligence', href: '/enso', mounts: ['ai', 'router', 'memory'] },
{ name: 'Execution', href: 'https://tabs.hanzo.ai', mounts: ['agents', 'sandboxes', 'tasks'] },
{ name: 'Infrastructure', href: 'https://cloud.hanzo.ai', mounts: ['platform', 'machines', 'deploy'] },
]
/** The two rails. Not layers — they answer for every one of the four. */
const RAILS: Group[] = [
{ name: 'Learn & adapt', href: 'https://insights.hanzo.ai', mounts: ['o11y', 'evals', 'experiments'] },
{ name: 'Trust & govern', href: '/security', mounts: ['iam', 'kms', 'compliance'] },
]
/** Only what the document serves, so the plate cannot print a path that 404s. */
const served = (g: Group) => g.mounts.filter((m) => ops(m) > 0)
const NAMED = [...LAYERS, ...RAILS].reduce((n, g) => n + served(g).length, 0)
/* The whole plate is mono — an index of route prefixes reads as one. The name
column is fixed so the prefixes align down the plate: "Infrastructure" is the
widest at 14 characters, which sets the measure. The prefixes are the only
thing allowed to truncate, and they do it with an ellipsis rather than
against the plate's own clip: a path cut mid-token reads as a broken value
where `…` reads as a column that ran out of room. */
function Mount({ group }: { group: Group }) {
const mounts = served(group)
return (
<LinkRow href={group.href}>
<Cell width={116} shrink={0} fontWeight="500">
{group.name}
</Cell>
<Mono grow={1} shrink={1} minW={0} whiteSpace="nowrap" overflow="hidden" textOverflow="ellipsis" color="var(--text-tertiary)">
{mounts.map((m) => `/v1/${m}`).join(' ')}
</Mono>
<Chip>{mounts.reduce((n, m) => n + ops(m), 0)} ops</Chip>
</LinkRow>
)
}
export default function SystemArchitecture() {
return (
<Band id="system">
<Head
eyebrow="The system"
title="One OS. Every layer of the company."
// Counted five times in one sentence — four, two, one, one, one — to describe
// a diagram sitting directly beneath it that draws every one of them. The span
// is the story: the top of the company down to the machines it runs on, with
// learning and governance touching every layer rather than sitting in the
// stack as another box.
lede="From the company down to the machines under it, with learning and governance across every layer."
/>
{/* The plate is the width its longest row needs (430px holds
"Infrastructure" and its three prefixes), centred under the head,
and the whole column on a phone. */}
<YStack mt={40} width="100%" maxW={430} mx="auto">
<Plate>
<Host>api.hanzo.ai</Host>
{LAYERS.map((g) => (
<Mount key={g.name} group={g} />
))}
<Rule>across every layer</Rule>
{RAILS.map((g) => (
<Mount key={g.name} group={g} />
))}
</Plate>
<Caption>
{NAMED} of {families.families.length} route families · read from /v1/openapi.json
</Caption>
</YStack>
</Band>
)
}