diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a1190ec --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Zola build output +public/ + +# nix +result +result-* + +# editor / OS +.direnv/ +*.swp +*.swo +.DS_Store diff --git a/README.md b/README.md index 3567426..df6a994 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,56 @@ -# website +# hyperhive website -hyperhive marketing landing — Zola static site, Catppuccin-themed (will transfer to hyperhive/website per #502) \ No newline at end of file +Marketing landing for hyperhive, deployed at +[hyperhive.darkest.space](https://hyperhive.darkest.space). Tracks +[hyperhive#502](http://localhost:3000/hyperhive/hyperhive/issues/502). + +Built with [Zola](https://www.getzola.org/), the Rust static site +generator. Single-page landing for now; nav / blog / docs land via +follow-up issues in this repo if scope grows. + +## Build + +```sh +# one-shot build to ./public +nix build .#website +# → result/ is the dist, ready to drop under any static host + +# dev server (live reload on http://127.0.0.1:1111/) +nix develop +zola serve +``` + +## Layout + +``` +config.toml # zola config (single source of truth for site meta) +content/_index.md # landing page copy — edit here for prose changes +templates/ + base.html # base layout (head, footer, og tags) + index.html # landing template extending base +sass/ + main.scss # theme — Catppuccin Mocha + amber accent +static/ + favicon.svg # hyperhive hex motif (copy of dashboard branding) + hex-mark.svg # same SVG, used inline in the hero + hyperhive.svg # og:image +flake.nix # `nix build` → site dist, `nix develop` → zola shell +``` + +## Theme + +Catppuccin Mocha palette + the hyperhive amber from the swarm's +identity hex mark. Monospace identity throughout (same family as +the dashboard / agent terminals) so the website reads as part of +the same project, not a separate marketing artifact. + +Theme variables live in `sass/main.scss` (single source of truth). +The hex motif in the hero is the same SVG that ships on the +dashboard / forge / agent containers. + +## Repo provenance + +Currently scaffolded under `iris/website` because my agent forge +token doesn't have org-admin to create repos under `hyperhive/`. +Will be transferred to `hyperhive/website` once mara does the move +(see hyperhive#502 thread). diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..ec11b5a --- /dev/null +++ b/config.toml @@ -0,0 +1,34 @@ +# Zola config — hyperhive marketing landing (#502). +# +# Single-page landing for now; nav / blog / docs come later if scope grows. +# Theme lives in `sass/main.scss` and `templates/`; no external Zola theme +# import — the project's identity matches the dashboard / agent terminals +# closely enough that pulling in a third-party theme would just give us +# something to fight. + +base_url = "https://hyperhive.darkest.space" +title = "hyperhive" +description = "a swarm of claude agents in nspawn containers, with an operator-shaped trust boundary." + +# We don't need RSS / search / sitemaps for a single-page landing. Can +# flip these on later if blog content lands. +generate_feeds = false +build_search_index = false + +# Embed the SCSS pipeline; zola compiles `sass/*.scss` → `public/*.css`. +compile_sass = true + +# Markdown rendering defaults — landing has very little prose so the +# fancier syntax-highlight setup isn't needed yet. Easy to turn on later. +[markdown] +highlight_code = false +smart_punctuation = true + +[extra] +# Catppuccin Mocha palette + amber accent (matching the hex mark) is +# the project's identity. Single source of truth — `main.scss` reads +# these via Sass variables, not from this attrset (Zola doesn't pipe +# extras into Sass). Keeping them here as documentation only so the +# theme intent is reviewable in one place. +palette = "catppuccin-mocha" +accent = "amber" diff --git a/content/_index.md b/content/_index.md new file mode 100644 index 0000000..937a590 --- /dev/null +++ b/content/_index.md @@ -0,0 +1,9 @@ ++++ +title = "hyperhive" ++++ + +A swarm of Claude agents running in nspawn containers, gated by an +operator-shaped trust boundary. Each agent has a logical name, a +state directory, and a NixOS config — and talks to its peers +through a broker that the human at the dashboard can read, gate, +or interrupt. diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..c5e45df --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1779560665, + "narHash": "sha256-tpyBcxPpcQb8ukyNF7DoCwfSY3VPsxHoYwj00Cayv5o=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "64c08a7ca051951c8eae34e3e3cb1e202fe36786", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..73ada2c --- /dev/null +++ b/flake.nix @@ -0,0 +1,64 @@ +{ + # hyperhive marketing landing — Zola static site build (#502). + # + # `nix build` → `result/` is the `public/` Zola dist, ready to drop + # under any static-file host. No CI runner needed; the flake is the + # validation contract. + description = "hyperhive marketing landing"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { inherit system; }; + in { + packages.website = pkgs.stdenv.mkDerivation { + pname = "hyperhive-website"; + version = self.shortRev or "dev"; + + src = ./.; + + nativeBuildInputs = [ pkgs.zola ]; + + # Zola reads `config.toml` from CWD, writes `public/` next to it, + # which we then move to $out. The build is offline / hermetic — + # no network access needed at build time, which fits the nix + # sandbox without extra fetchurls. + buildPhase = '' + runHook preBuild + zola build --output-dir public + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + mkdir -p $out + cp -r public/. $out/ + runHook postInstall + ''; + + # Static-site dist; no shared libs to patchelf, no executables. + dontFixup = true; + }; + + # `nix build` with no attribute → the website dist. + packages.default = self.packages.${system}.website; + + # `nix develop` → a shell with zola for local iteration: + # `zola serve` runs the dev server with live reload. + devShells.default = pkgs.mkShell { + packages = [ pkgs.zola ]; + }; + + # `nix run` is the dev-server shortcut: `nix run . -- serve` + # boots Zola's hot-reload server on http://127.0.0.1:1111/. + apps.default = { + type = "app"; + program = "${pkgs.zola}/bin/zola"; + }; + }); +} diff --git a/sass/main.scss b/sass/main.scss new file mode 100644 index 0000000..5be2e4a --- /dev/null +++ b/sass/main.scss @@ -0,0 +1,249 @@ +// hyperhive marketing landing — theme (#502). +// +// Matches the dashboard / agent terminals visually so the website +// reads as part of the same family: Catppuccin Mocha palette + amber +// accent (matching the hex mark), banner-thin block-glyph headings, +// dashed dividers, glow text-shadow on titles. +// +// Single-file SCSS — landing is small enough that splitting into +// partials would be premature. Re-evaluate if /docs or /blog land. + +// ─── palette ─────────────────────────────────────────────────────── +// Catppuccin Mocha + the hyperhive amber from branding/hyperhive.svg. +// Copying them in (not importing) so this file stays self-contained. + +$base: #1e1e2e; +$mantle: #181825; +$crust: #11111b; + +$text: #cdd6f4; +$subtext1: #bac2de; +$subtext0: #a6adc8; +$overlay2: #9399b2; +$overlay1: #7f849c; +$overlay0: #6c7086; +$surface2: #585b70; +$surface1: #45475a; +$surface0: #313244; + +$rosewater: #f5e0dc; +$flamingo: #f2cdcd; +$pink: #f5c2e7; +$mauve: #cba6f7; +$red: #f38ba8; +$maroon: #eba0ac; +$peach: #fab387; +$yellow: #f9e2af; +$green: #a6e3a1; +$teal: #94e2d5; +$sky: #89dceb; +$sapphire: #74c7ec; +$blue: #89b4fa; +$lavender: #b4befe; + +// The hex-mark amber. Pure swarm identity colour. +$amber: #ffb300; +$amber-deep: #ff8f00; +$amber-glow: rgba(255, 179, 0, 0.45); + +// ─── globals ─────────────────────────────────────────────────────── + +* { box-sizing: border-box; } + +html, body { + margin: 0; + padding: 0; + background: $base; + color: $text; + // Same font stack as the dashboard's @hive/shared base.css — + // monospace identity reads as "this is the terminal swarm" instead + // of "this is a marketing site". Body copy stays readable at this + // weight because the lines are short. + font-family: ui-monospace, "JetBrains Mono", "Fira Code", + Menlo, Consolas, monospace; + font-size: 16px; + line-height: 1.55; + // Subtle background grid — same idea as the hex-mark's faint + // horizontal lines, but at body scope. Cheaper than a full SVG + // background. + background-image: + repeating-linear-gradient( + to bottom, + transparent 0, + transparent 23px, + rgba(255, 179, 0, 0.025) 23px, + rgba(255, 179, 0, 0.025) 24px + ); +} + +a { + color: $sky; + text-decoration: none; + border-bottom: 1px dashed transparent; + transition: border-color 0.15s, color 0.15s; +} +a:hover { color: $sapphire; border-bottom-color: $sapphire; } + +::selection { background: $amber-glow; color: $crust; } + +// ─── layout ──────────────────────────────────────────────────────── + +.shell { + max-width: 920px; + margin: 0 auto; + padding: 4rem 1.5rem 2rem; +} + +// ─── hero ────────────────────────────────────────────────────────── + +.hero { + display: grid; + grid-template-columns: minmax(180px, 280px) 1fr; + gap: 2rem; + align-items: center; + padding-bottom: 2rem; + border-bottom: 1px dashed $surface1; +} + +@media (max-width: 700px) { + .hero { grid-template-columns: 1fr; text-align: center; } + .hero-art { max-width: 220px; margin: 0 auto; } +} + +.hero-art svg { + width: 100%; + height: auto; + // Subtle pulse — only the outer ring + dashed orbit rotate. Quiet + // enough to read as "active" instead of "noisy". + animation: hex-orbit 24s linear infinite; +} +.hero-art:hover svg { + // Speed up on hover for a tiny "you got noticed" cue. No JS needed. + animation-duration: 8s; +} + +@keyframes hex-orbit { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +.hero-text h1 { + margin: 0 0 0.6rem; + font-size: 2.4rem; + font-weight: 700; + letter-spacing: 0.04em; + color: $amber; + text-shadow: + 0 0 4px $amber-glow, + 0 0 12px rgba(255, 143, 0, 0.25); +} + +.banner-glyph { + color: $amber-deep; + opacity: 0.65; + font-weight: 400; + letter-spacing: 0; +} + +.hero-tagline { + margin: 0 0 1.6rem; + font-size: 1.05rem; + color: $subtext1; +} + +.hero-cta { margin: 0; } + +.cta-primary { + display: inline-block; + padding: 0.5rem 1rem; + border: 1px solid $amber; + color: $amber; + border-bottom-color: $amber; + text-shadow: 0 0 6px $amber-glow; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; + transition: box-shadow 0.15s ease, color 0.15s, border-color 0.15s; +} +.cta-primary:hover { + color: $base; + background: $amber; + border-color: $amber; + border-bottom-style: solid; + box-shadow: 0 0 14px -2px $amber-glow; + text-shadow: none; +} + +// ─── prose ───────────────────────────────────────────────────────── + +.prose { + padding: 2rem 0; + color: $text; + max-width: 64ch; + font-size: 1.02rem; +} +.prose p { margin: 0 0 1rem; } + +// ─── three-column grid ──────────────────────────────────────────── + +.grid-3 { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; + padding: 2rem 0; + border-top: 1px dashed $surface1; +} + +@media (max-width: 760px) { + .grid-3 { grid-template-columns: 1fr; } +} + +.card { + padding: 1rem 1.2rem; + background: $mantle; + border: 1px solid $surface0; + border-radius: 4px; + transition: border-color 0.15s, box-shadow 0.15s; +} +.card:hover { + border-color: $amber; + box-shadow: 0 0 14px -6px $amber-glow; +} +.card h2 { + margin: 0 0 0.6rem; + font-size: 1.1rem; + letter-spacing: 0.04em; + color: $mauve; +} +.card-glyph { color: $amber; margin-right: 0.4em; } +.card p { + margin: 0; + font-size: 0.92rem; + color: $subtext1; + line-height: 1.5; +} + +// ─── footer ──────────────────────────────────────────────────────── + +.site-footer { + max-width: 920px; + margin: 3rem auto 1.5rem; + padding: 1.5rem 1.5rem 0; + text-align: center; + border-top: 1px dashed $surface1; +} + +.banner-thin { + margin: 1.5rem 0 0.5rem; + color: $amber-deep; + font-size: 0.95rem; + letter-spacing: 0.18em; + opacity: 0.7; +} + +.footer-links { + margin: 0; + font-size: 0.88rem; + color: $subtext0; +} +.footer-links .sep { margin: 0 0.4em; opacity: 0.5; } diff --git a/static/favicon.svg b/static/favicon.svg new file mode 100644 index 0000000..81d931c --- /dev/null +++ b/static/favicon.svg @@ -0,0 +1,97 @@ + + HyperHive + HyperHive icon — hexagonal hive, amber on dark + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/hex-mark.svg b/static/hex-mark.svg new file mode 100644 index 0000000..81d931c --- /dev/null +++ b/static/hex-mark.svg @@ -0,0 +1,97 @@ + + HyperHive + HyperHive icon — hexagonal hive, amber on dark + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/hyperhive.svg b/static/hyperhive.svg new file mode 100644 index 0000000..81d931c --- /dev/null +++ b/static/hyperhive.svg @@ -0,0 +1,97 @@ + + HyperHive + HyperHive icon — hexagonal hive, amber on dark + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..1029387 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,34 @@ + + + + + + {% block title %}{{ config.title }}{% endblock %} + + + {# Open Graph / social card. Reuses the SVG hex mark as the image. #} + + + + + + + + + + + +
+ {% block content %}{% endblock %} +
+ + + + diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..f3ebd2b --- /dev/null +++ b/templates/index.html @@ -0,0 +1,64 @@ +{% extends "base.html" %} + +{% block content %} + {# Hero — hex motif left, headline + tagline + CTA right. Stacks + vertically on narrow viewports via the .hero CSS grid. #} +
+ +
+

+ + hyperhive + +

+

{{ config.description }}

+

+ + ◆ explore the code → + +

+
+
+ + {# Landing prose — pulled from _index.md so non-engineers can edit + copy without touching templates. #} +
+ {{ section.content | safe }} +
+ + {# Three-column "what's inside" — quick orientation for visitors + who clicked through from a link without context. Copy here is + intentionally short; deep dives belong in /docs (future). #} +
+
+

the swarm

+

+ Each agent is a NixOS container with its own claude session, + a logical name, and a parent in the topology tree. Containers + come and go; the topology is the operator's facts file. +

+
+
+

the dashboard

+

+ One web page shows live agent state, the broker stream, the + approval queue, scheduled prompts, and the rebuild pipeline. + Everything that mutates the swarm passes through here. +

+
+
+

the boundary

+

+ Agents can ask, schedule, request approvals, and message peers + — but never spawn / destroy / mutate config themselves. Those + actions queue up and wait on the human at the dashboard. +

+
+
+{% endblock %}