Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

160 changes: 160 additions & 0 deletions site/src/components/blog/ForestHero.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
---
// Original art for blog post #6 (exploration 0246). Where the earlier nature
// heroes looked DOWN into soil (MycelialHero) and ACROSS an ocean (DustHero),
// this one looks at a patch of ground at eye level and shows the choice the
// essay is about: a flat monocrop field on the left (identical sprouts in dead
// straight rows, bare soil) versus a layered food forest on the right (canopy →
// understory → shrub → herb → ground cover, many species, no rows). The
// cosmic-X glows as the sun feeding the living half — the recurring "X as the
// brightest node" motif. All inline SVG, no external assets, so the page ships
// nothing third-party (Self-Audit parity with #1–#5).
interface Props {
title: string
deck: string
date: string
readingMinutes: number
tags: string[]
}
const { title, deck, date, readingMinutes, tags } = Astro.props

// The monoculture: identical crops on a rigid grid. Uniformity is the point —
// a thousand of the same plant, one bad day from collapse.
const fieldRows = [236, 268, 302, 336]
const fieldCrop = fieldRows.flatMap((y, r) =>
Array.from({ length: 7 }, (_, c) => ({ x: 58 + c * 66, y, r }))
)

// The food forest: hand-placed layers, varied heights and greens, nothing in
// rows. Canopy high, understory below it, shrubs lower, herb dots at the floor.
const canopy = [
{ cx: 612, cy: 150, rx: 46, ry: 30, fill: '#1f6b3f' },
{ cx: 712, cy: 132, rx: 58, ry: 38, fill: '#175a34' },
{ cx: 838, cy: 146, rx: 52, ry: 34, fill: '#1f6b3f' },
{ cx: 948, cy: 158, rx: 44, ry: 28, fill: '#175a34' }
]
const understory = [
{ cx: 664, cy: 196, rx: 30, ry: 20, fill: '#2f7d4f' },
{ cx: 776, cy: 200, rx: 34, ry: 22, fill: '#2f7d4f' },
{ cx: 898, cy: 198, rx: 28, ry: 19, fill: '#368a57' }
]
const shrubs = [
{ cx: 600, cy: 238, rx: 20, ry: 14, fill: '#3f9d63' },
{ cx: 726, cy: 244, rx: 24, ry: 15, fill: '#3f9d63' },
{ cx: 852, cy: 240, rx: 22, ry: 14, fill: '#46a86c' },
{ cx: 960, cy: 236, rx: 18, ry: 12, fill: '#3f9d63' }
]
const trunks = [
{ x: 612, y: 178 },
{ x: 712, y: 168 },
{ x: 838, y: 178 },
{ x: 948, y: 184 }
]
const herbs = Array.from({ length: 22 }, (_, i) => ({
x: 566 + i * 21,
y: 286 + ((i * 5) % 18),
o: 0.4 + ((i * 7) % 5) * 0.1
}))
---

<section class="relative overflow-hidden border-b border-border bg-surface/20 dark:bg-[#070d09]">
<svg
class="pointer-events-none absolute inset-0 h-full w-full"
viewBox="0 0 1040 360"
preserveAspectRatio="xMidYMid slice"
aria-hidden="true"
>
<defs>
<linearGradient id="fhsky" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#0a140d" />
<stop offset="60%" stop-color="#0c1812" />
<stop offset="100%" stop-color="#0e1f15" />
</linearGradient>
<linearGradient id="fhfield" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#6b5224" />
<stop offset="100%" stop-color="#3d2f14" />
</linearGradient>
<linearGradient id="fhforest" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#123b22" />
<stop offset="100%" stop-color="#08240f" />
</linearGradient>
<radialGradient id="fhsun" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#fde68a" stop-opacity="0.95" />
<stop offset="45%" stop-color="#fbbf24" stop-opacity="0.38" />
<stop offset="100%" stop-color="#fbbf24" stop-opacity="0" />
</radialGradient>
<linearGradient id="fhx" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#fde68a" />
<stop offset="100%" stop-color="#34d399" />
</linearGradient>
</defs>

<rect width="1040" height="360" fill="url(#fhsky)" />

<!-- the sun, low and warm, falling on the living half -->
<g transform="translate(548 70)">
<circle cx="0" cy="0" r="116" fill="url(#fhsun)" />
</g>

<!-- left: the monocrop field — bare soil, dead-straight furrows -->
<rect x="0" y="200" width="520" height="160" fill="url(#fhfield)" />
<g stroke="#2c2210" stroke-width="1.4" opacity="0.5">
<line x1="-20" y1="224" x2="540" y2="214" />
<line x1="-20" y1="256" x2="540" y2="248" />
<line x1="-20" y1="292" x2="540" y2="286" />
<line x1="-20" y1="330" x2="540" y2="326" />
</g>
<!-- identical sprouts on a rigid grid; every one the same -->
<g stroke="#9bbf4a" stroke-width="2" stroke-linecap="round" fill="none" opacity="0.9">
{
fieldCrop.map((s) => (
<g transform={`translate(${s.x} ${s.y})`}>
<line x1="0" y1="0" x2="0" y2="-12" />
<line x1="0" y1="-7" x2="-6" y2="-13" />
<line x1="0" y1="-7" x2="6" y2="-13" />
</g>
))
}
</g>

<!-- right: the food forest — layered, varied, nothing in rows -->
<rect x="520" y="200" width="520" height="160" fill="url(#fhforest)" />
<g stroke="#3a2a12" stroke-width="5" stroke-linecap="round" opacity="0.6">
{trunks.map((t) => <line x1={t.x} y1={t.y} x2={t.x} y2={t.y + 56} />)}
</g>
<g>
{canopy.map((e) => <ellipse cx={e.cx} cy={e.cy} rx={e.rx} ry={e.ry} fill={e.fill} />)}
{understory.map((e) => <ellipse cx={e.cx} cy={e.cy} rx={e.rx} ry={e.ry} fill={e.fill} />)}
{shrubs.map((e) => <ellipse cx={e.cx} cy={e.cy} rx={e.rx} ry={e.ry} fill={e.fill} />)}
</g>
<g fill="#5cc07e">
{herbs.map((h) => <circle cx={h.x} cy={h.y} r="3.2" opacity={h.o} />)}
</g>

<!-- the cosmic-X, the brightest node, riding in the sun over the forest -->
<g transform="translate(548 70)">
<g stroke="url(#fhx)" stroke-width="5" stroke-linecap="round" transform="translate(-11 -13)">
<line x1="0" y1="0" x2="22" y2="26" />
<line x1="22" y1="0" x2="0" y2="26" />
</g>
</g>
</svg>

<div class="relative mx-auto max-w-3xl px-6 pb-14 pt-44 lg:pt-52">
<div class="flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-emerald-200/80">
<time datetime={date}>{date}</time>
<span aria-hidden="true">·</span>
<span>{readingMinutes} min read</span>
{
tags.map((tag) => (
<span class="rounded-full border border-emerald-400/30 bg-emerald-500/10 px-2 py-0.5 font-mono lowercase text-emerald-200">
{tag}
</span>
))
}
</div>
<h1 class="mt-4 text-4xl font-bold tracking-tight text-white drop-shadow lg:text-5xl">
{title}
</h1>
<p class="mt-4 max-w-2xl text-lg leading-relaxed text-gray-300">{deck}</p>
</div>
</section>
56 changes: 56 additions & 0 deletions site/src/components/blog/HonestGarden.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
// The honesty beat (exploration 0246), modeled on HonestDesert / HonestStar /
// HonestMycelium. This essay maps a farming discipline onto software, and the
// mapping is tidy enough to be suspicious — so we say plainly where it strains.
// We take the design intuitions, not the fairy tale: code isn't scarce like
// soil, software doesn't self-heal like a forest, an ecological metaphor can
// quietly excuse a monopoly, and permaculture itself is more contested than its
// confidence lets on.
const rows = [
{
isnt: 'We won’t pretend code is scarce the way soil is.',
is: 'A field obeys thermodynamics; software copies for free. So “yield” and “waste” are borrowed words here, not equations. The yield we mean is problems solved and people served; the waste we refuse is the behavioral exhaust harvested from you — not a deprecated library, which costs nothing to leave lying around.'
},
{
isnt: 'We won’t pretend software ecosystems heal themselves.',
is: 'A forest self-regulates over evolutionary time. A package registry does not — left-pad, the XZ Utils backdoor, and Log4Shell are what “self-healing” actually looks like in software: it doesn’t, without funded, vigilant maintenance. The commons is real, but its upkeep is finite and depletable, and calling it “free” is how it gets strip-mined.'
},
{
isnt: 'We won’t let the metaphor launder power.',
is: 'Calling a dominant platform a “climax species” makes capture sound like nature taking its course. It isn’t. Monopoly is built from network effects, acquisitions, and regulatory capture — choices, not succession. The forest is a model to design by, not an alibi for who’s already won.'
},
{
isnt: 'We won’t oversell permaculture itself.',
is: 'Its productivity claims are thinner on peer-reviewed evidence than its confidence suggests, and it carries some guru-and-certificate baggage. We’re borrowing a way of thinking about regenerative design — store, recycle, diversify, decentralize — not claiming a settled science.'
}
]
---

<section class="not-prose my-12">
<div class="rounded-2xl border border-border bg-surface/30 p-6 dark:bg-surface/40 lg:p-8">
<h3 class="text-xl font-bold tracking-tight text-gray-900 dark:text-white">
An honest garden
</h3>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
A metaphor that flatters itself is just more marketing. Here’s where the
land-to-software analogy actually strains.
</p>

<ul class="mt-6 space-y-5">
{
rows.map((row) => (
<li class="grid gap-3 sm:grid-cols-[1fr_1.3fr] sm:gap-5">
<div class="flex gap-2.5">
<span class="select-none text-gray-400 dark:text-gray-600">✕</span>
<p class="text-sm leading-relaxed text-gray-500 dark:text-gray-400">{row.isnt}</p>
</div>
<div class="flex gap-2.5">
<span class="select-none text-emerald-500">✓</span>
<p class="text-sm leading-relaxed text-gray-700 dark:text-gray-200">{row.is}</p>
</div>
</li>
))
}
</ul>
</div>
</section>
147 changes: 147 additions & 0 deletions site/src/components/blog/PrincipleWheel.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
---
// The signature diagram for post #6 (exploration 0246) — the "one diagram" slot
// (cf. HydrostaticBalance, ThreeNervousSystems, DustBridge). David Holmgren's
// twelve permaculture principles are traditionally drawn as a spiral of petals;
// here a twelve-spoke wheel with the cosmic-X at the hub, and then a row-by-row
// mapping that groups the twelve into the essay's four "movements" and cashes
// each one out in an xNet commitment. Mirrors the DustBridge idiom (a small
// inline-SVG schematic over a mapping list, not-prose on a bordered surface) so
// the posts read as one publication. Inline SVG only — no external assets.

// Twelve nodes evenly around the hub, numbered the way Holmgren numbers them.
const cx = 240
const cy = 150
const R = 112
const wheel = Array.from({ length: 12 }, (_, i) => {
const a = ((-90 + i * 30) * Math.PI) / 180
return { n: i + 1, x: cx + R * Math.cos(a), y: cy + R * Math.sin(a) }
})

// The four movements, three principles each, with the xNet receipt for each.
const movements = [
{
title: 'Store what you grow',
rows: [
{ p: 'Catch & store energy', x: 'Your device holds the master copy — your harvest, in your barn, working offline' },
{ p: 'Produce no waste', x: 'No behavioral surplus to harvest; nothing stranded when you export and leave' },
{ p: 'Use renewable resources', x: 'Open standards over proprietary lock-in: a did:key you mint, a signed open log' }
]
},
{
title: 'Let it regulate itself',
rows: [
{ p: 'Observe & interact', x: 'Telemetry off by default — observed only with consent, scrubbed so it can’t fingerprint' },
{ p: 'Self-regulation & feedback', x: 'No infinite scroll or engagement ranking; a build check fails on dark patterns' },
{ p: 'Small & slow solutions', x: 'Local-first, run-it-yourself hubs, a calm motion vocabulary — the small web' }
]
},
{
title: 'Plant a polyculture',
rows: [
{ p: 'Use & value diversity', x: 'Any framework, many languages — and the freedom to fork; only the name is protected' },
{ p: 'Integrate, don’t segregate', x: 'Federation and interop by shared rules, not a wall around the commons' },
{ p: 'Patterns before details', x: 'One open protocol is the pattern; every app and feature is a detail grown on top' }
]
},
{
title: 'Tend the edges, design for change',
rows: [
{ p: 'Use edges & the marginal', x: 'The edge is your device — that’s where the data and the compute live' },
{ p: 'Respond to change', x: 'The right to leave: when a platform turns, you walk out with everything intact' },
{ p: 'Obtain a yield', x: 'Not a manifesto — working software today; the yield is your own audience and space' }
]
}
]
---

<section class="not-prose my-12">
<div class="rounded-2xl border border-border bg-surface/30 p-6 dark:bg-surface/40 lg:p-8">
<h3 class="text-xl font-bold tracking-tight text-gray-900 dark:text-white">
Twelve principles, one design
</h3>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Holmgren's twelve permaculture principles, grouped into four movements — and
what each one becomes when you build a web instead of a garden.
</p>

<div class="mt-6 grid items-center gap-6 md:grid-cols-[300px_1fr]">
<!-- the wheel: twelve numbered nodes spoked to the cosmic-X hub -->
<svg
class="mx-auto w-full max-w-[300px]"
viewBox="0 0 480 300"
role="img"
aria-label="A twelve-spoke wheel: the twelve permaculture principles arranged around a central cosmic-X hub."
>
<defs>
<radialGradient id="pwhub" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#fde68a" stop-opacity="0.9" />
<stop offset="55%" stop-color="#34d399" stop-opacity="0.18" />
<stop offset="100%" stop-color="#34d399" stop-opacity="0" />
</radialGradient>
<linearGradient id="pwx" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#fde68a" />
<stop offset="100%" stop-color="#34d399" />
</linearGradient>
</defs>

<g stroke="#34d399" stroke-width="1.2" opacity="0.35">
{wheel.map((w) => <line x1={cx} y1={cy} x2={w.x} y2={w.y} />)}
</g>
<circle cx={cx} cy={cy} r={R} fill="none" stroke="#34d399" stroke-width="1" opacity="0.25" />

{
wheel.map((w) => (
<g>
<circle
cx={w.x}
cy={w.y}
r="16"
class="fill-emerald-50 dark:fill-[#0e1f15]"
stroke="#34d399"
stroke-width="1.6"
/>
<text
x={w.x}
y={w.y + 4.5}
text-anchor="middle"
font-size="13"
font-weight="700"
class="fill-emerald-700 dark:fill-emerald-300"
>
{w.n}
</text>
</g>
))
}

<!-- the cosmic-X hub: the brightest node -->
<circle cx={cx} cy={cy} r="40" fill="url(#pwhub)" />
<g stroke="url(#pwx)" stroke-width="5" stroke-linecap="round" transform={`translate(${cx - 11} ${cy - 13})`}>
<line x1="0" y1="0" x2="22" y2="26" />
<line x1="22" y1="0" x2="0" y2="26" />
</g>
</svg>

<!-- the mapping: four movements, three principles each -->
<div class="space-y-5">
{
movements.map((m) => (
<div>
<h4 class="text-sm font-semibold tracking-tight text-emerald-700 dark:text-emerald-300">
{m.title}
</h4>
<ul class="mt-2 space-y-2">
{m.rows.map((row) => (
<li class="grid gap-1 sm:grid-cols-[1fr_1.5fr] sm:gap-4 sm:items-baseline">
<p class="text-sm font-medium leading-snug text-gray-900 dark:text-white">{row.p}</p>
<p class="text-sm leading-snug text-gray-600 dark:text-gray-300">{row.x}</p>
</li>
))}
</ul>
</div>
))
}
</div>
</div>
</div>
</section>
10 changes: 10 additions & 0 deletions site/src/data/blog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ export interface BlogPost {
}

export const posts: BlogPost[] = [
{
slug: 'the-forest-and-the-field',
title: 'The Forest and the Field',
description:
'Industrial farming strips the soil to exhaustion and trucks fertility back in by the ton. Surveillance capitalism does the same to the web. Permaculture is the discipline for growing land that feeds itself — and its principles are, almost furrow for furrow, how you regenerate a digital commons instead of strip-mining one.',
pubDate: '2026-06-30T15:00:00Z',
author: 'xNet',
tags: ['essay', 'philosophy', 'nature'],
readingMinutes: 14
},
{
slug: 'the-desert-that-feeds-the-forest',
title: 'The Desert That Feeds the Forest',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"id": "2026-06-28-new-essay-the-forest-and-the-field",
"date": "June 28, 2026",
"title": "New essay: The Forest and the Field",
"summary": "A new essay on applying permaculture's design principles to the open web — regenerating the digital commons instead of strip-mining it.",
"highlights": [],
"tags": ["platform"]
}
Loading
Loading