mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-30 14:58:08 +00:00
141 lines
6.9 KiB
TypeScript
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>
|
|
)
|
|
}
|