From 71b69f102774fde6a2f36f65944bcf599995747e Mon Sep 17 00:00:00 2001 From: Bill Brock Date: Mon, 15 Jun 2026 10:12:23 -0500 Subject: [PATCH 01/14] feat: add field notes section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A second blog instance at /field-notes for dense, dated engineering notes — distinct from the docs-plugin Changelog. Includes: - @docusaurus/plugin-content-blog instance with id 'field-notes', RSS+Atom+JSON feeds, no left sidebar - Swizzled BlogPostItems renders a single-column list (title, dek from `description` frontmatter, monospace meta line with indigo hashtag tags, hairline dividers) - TestedAgainst component for the per-note "Tested against: Avocado X / JetPack Y / boards" badge - Field-notes layout fills the width that the left-nav would occupy on other tabs; right-hand TOC kept and restyled subtle - Geist + Geist Mono fonts and indigo accent, all scoped to .plugin-id-field-notes so other tabs (Product Docs, Hardware, Developer Reference, Changelog) are unchanged - Notepad icon in the navbar - _template.mdx, authors.yml, CONTRIBUTING.md, and a worked example (2026-06-30-power-pull-orin-nx) included Editorial frontmatter (`hn_title`, `poster`, `lift_for_blog`, `promote_to_track`) lives in the source for greppability but never renders to HTML. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/docusaurus.config.js | 33 +++++ .../2026-06-30-power-pull-orin-nx.mdx | 40 +++++ src/field-notes/CONTRIBUTING.md | 59 ++++++++ src/field-notes/_template.mdx | 38 +++++ src/field-notes/authors.yml | 5 + src/src/components/TestedAgainst/index.jsx | 13 ++ .../TestedAgainst/styles.module.css | 21 +++ src/src/css/custom.css | 140 ++++++++++++++++++ src/src/theme/BlogPostItems/index.js | 75 ++++++++++ src/src/theme/BlogPostItems/styles.module.css | 79 ++++++++++ 10 files changed, 503 insertions(+) create mode 100644 src/field-notes/2026-06-30-power-pull-orin-nx.mdx create mode 100644 src/field-notes/CONTRIBUTING.md create mode 100644 src/field-notes/_template.mdx create mode 100644 src/field-notes/authors.yml create mode 100644 src/src/components/TestedAgainst/index.jsx create mode 100644 src/src/components/TestedAgainst/styles.module.css create mode 100644 src/src/theme/BlogPostItems/index.js create mode 100644 src/src/theme/BlogPostItems/styles.module.css diff --git a/src/docusaurus.config.js b/src/docusaurus.config.js index 610f9903..de47c8df 100644 --- a/src/docusaurus.config.js +++ b/src/docusaurus.config.js @@ -57,6 +57,26 @@ const config = { breadcrumbs: true, }, ], + [ + '@docusaurus/plugin-content-blog', + { + id: 'field-notes', + routeBasePath: 'field-notes', + path: 'field-notes', + blogTitle: 'Field Notes', + blogDescription: 'Dense technical notes from the Peridio engineering team.', + showReadingTime: true, + postsPerPage: 20, + blogSidebarCount: 0, + authorsMapPath: 'authors.yml', + exclude: ['CONTRIBUTING.md', '_template.mdx'], + feedOptions: { + type: 'all', + title: 'Peridio Field Notes', + description: 'Engineering notes from Peridio', + }, + }, + ], ...(process.env.NODE_ENV === 'production' ? [ [ @@ -129,6 +149,12 @@ const config = { position: 'left', activeBasePath: 'developer-reference', }, + { + to: '/field-notes', + label: 'Field Notes', + position: 'left', + activeBasePath: 'field-notes', + }, { to: '/changelog/latest', label: 'Changelog', @@ -234,6 +260,13 @@ const config = { href: 'https://fonts.googleapis.com/css2?family=Montserrat:wght@100..900&family=Space+Grotesk:wght@300..700&family=Spline+Sans:wght@300..700&display=swap', }, }, + { + tagName: 'link', + attributes: { + rel: 'stylesheet', + href: 'https://fonts.googleapis.com/css2?family=Geist:wght@300..700&family=Geist+Mono:wght@400;500;600&display=swap', + }, + }, { tagName: 'link', attributes: { diff --git a/src/field-notes/2026-06-30-power-pull-orin-nx.mdx b/src/field-notes/2026-06-30-power-pull-orin-nx.mdx new file mode 100644 index 00000000..4aa52421 --- /dev/null +++ b/src/field-notes/2026-06-30-power-pull-orin-nx.mdx @@ -0,0 +1,40 @@ +--- +title: "Pulling the power 200 times mid-OTA on an Orin NX" +description: "200 scripted power cuts at random points during an A/B OTA update. Zero bricks at steady state — and the two early failures weren't where we expected." +date: 2026-06-30 +authors: [jschneck] +tags: [ota, rollback, jetson, orin-nx] +tested_against: "Avocado 1.0.0, JetPack 7.2, Orin NX 16GB" +hn_title: "We cut power 200 times mid-update on a Jetson. Here's what bricked." +poster: jschneck +lift_for_blog: "Zero bricks across 200 forced failures — the 100-device-wall fear, quantified, for the budget holder." +promote_to_track: "Tutorial 4 candidate — the 'break it on purpose' step." +--- + +import TestedAgainst from '@site/src/components/TestedAgainst'; + + + +**TL;DR.** We scripted 200 power cuts at random points during an A/B OTA update on an Orin NX. Zero bricks — every device booted the last-known-good slot. Two early runs did fail, and the cause wasn't the bootloader. + +{/* truncate */} + +## What this is + +Peridio builds Avocado OS, an immutable embedded Linux runtime for devices like NVIDIA Jetson. Its updates are A/B and atomic: a failed or interrupted update should always fall back to the previously working slot. We wanted to know how literally true "always" is, so we tried to break it on purpose. + +## How we did it + +[Relay-controlled PSU, the update payload, the randomized power-cut harness, commands + output.] + +## What we learned + +[200 forced interruptions, zero bricks at steady state; what the boot logs show on recovery.] + +## What didn't work + +[Two early failures traced to a data-partition sync gap, not the OS slots; the fix and why it mattered.] + +## Reproduce it + +[Link to the power-cycle harness repo + step-by-step.] diff --git a/src/field-notes/CONTRIBUTING.md b/src/field-notes/CONTRIBUTING.md new file mode 100644 index 00000000..bce1aa17 --- /dev/null +++ b/src/field-notes/CONTRIBUTING.md @@ -0,0 +1,59 @@ +# Contributing to Field Notes + +Field Notes is a stream of dense, dated, engineer-voice technical entries. It's the source of truth — marketing repurposes strong notes into blog posts downstream, and the best ones get sequenced into a curated learning track later. + +Field Notes is *true*. The blog is *pretty*. Don't mix them up. + +## How to write a note + +Start by copying `_template.mdx`: + +```bash +cp _template.mdx 2026-MM-DD-short-slug.mdx +``` + +The filename must start with `YYYY-MM-DD-` so it sorts correctly and the date matches frontmatter. + +## Frontmatter fields + +| Field | Required | Rendered | Purpose | +|---|---|---|---| +| `title` | yes | yes | The note title | +| `date` | yes | yes | The dated contract — "true as of this date" | +| `authors` | yes | yes | Real engineer keys from `authors.yml` | +| `tags` | yes | yes | Target (`orin-nx`, `imx`, `rpi5`…) + topic (`ota`, `yocto`, `security`, `mcp`…) | +| `tested_against` | when applicable | yes (via ``) | Avocado + JetPack/board versions | +| `hn_title` | no | **no** | Suggested HN submission title | +| `poster` | no | **no** | Engineer who submits to HN and is on-call in comments for ~24h | +| `lift_for_blog` | no | **no** | One-line business-case angle for the blog (champion → approver) | +| `promote_to_track` | no | **no** | Flag + note if this is curated-track on-ramp material | + +`hn_title`, `poster`, `lift_for_blog`, and `promote_to_track` are editorial metadata. They never render to readers, but they live in frontmatter so the team can grep them: + +```bash +grep -l "^hn_title:" field-notes/*.mdx +grep -l "^promote_to_track: \"[^\"]" field-notes/*.mdx +``` + +## HN-readiness rules + +- **TL;DR first.** Two sentences: what you did, what happened. Above everything else. +- **Cold-reader intro.** Assume the reader has never heard of Peridio or Avocado. Two or three sentences orient a stranger, then go dense. +- **Result-first title.** Concrete and specific. "We cut power 200 times mid-update on a Jetson. Here's what bricked." beats "Improving OTA resilience on embedded Linux." +- **No hype.** No "revolutionary," no "blazing fast," no "10x." +- **Include what didn't work.** Honesty about dead ends is the single biggest credibility signal on HN. + +## Posting to HN / Reddit / Lobsters + +- **Disclose.** When you submit, state plainly in the first comment: "I work on this." Non-negotiable. It prevents the astroturf backlash that buries an otherwise good post. +- **Be on-call for ~24h.** The `poster` is the engineer who submits *and* answers comments. Don't post and disappear. +- **Select, don't spray.** HN throttles domains that self-submit too often and flags obvious marketing. Post only genuinely strong notes; let the rest live in the feed. A few great ones a quarter beats a steady drip. + +## Light review gate + +Before merging a note, one rotating engineering owner confirms: + +1. The reproducible core actually reproduces on a fresh setup. +2. The versions and tags in `tested_against` are accurate. + +This is a correctness check, not editorial polish. Field Notes is true; the blog is pretty. diff --git a/src/field-notes/_template.mdx b/src/field-notes/_template.mdx new file mode 100644 index 00000000..f4dc8547 --- /dev/null +++ b/src/field-notes/_template.mdx @@ -0,0 +1,38 @@ +--- +title: "" +description: "" # one-line dek; shown under the title on the index +date: YYYY-MM-DD +authors: [] # keys from authors.yml +tags: [] # target + topic +tested_against: "" # free text for greppability; also pass to the component below +hn_title: "" # suggested HN submission title; concrete, result-first, no hype +poster: "" # engineer who posts + answers comments for ~24h +lift_for_blog: "" # one-line business-case angle (champion → approver) +promote_to_track: "" # on-ramp candidate? note the target tutorial +--- + +import TestedAgainst from '@site/src/components/TestedAgainst'; + + + +**TL;DR.** Two sentences: what you did, what happened. + +## What this is + +Assume the reader has never heard of Peridio or Avocado. Orient a stranger in 2–3 sentences, then go dense. + +## How it works / what we did + +The meat: commands, config, output. + +## What we learned + +The payoff. The result that matters. + +## What didn't work + +Required when there were dead ends. Honesty here is the single biggest credibility signal on HN. + +## Reproduce it + +Steps, or a link to a runnable repo/gist. Include whenever the result can be reproduced. diff --git a/src/field-notes/authors.yml b/src/field-notes/authors.yml new file mode 100644 index 00000000..2073f59a --- /dev/null +++ b/src/field-notes/authors.yml @@ -0,0 +1,5 @@ +jschneck: + name: Justin Schneck + title: CTO & creator of Avocado OS + url: https://github.com/mobileoverlord + page: true diff --git a/src/src/components/TestedAgainst/index.jsx b/src/src/components/TestedAgainst/index.jsx new file mode 100644 index 00000000..14353822 --- /dev/null +++ b/src/src/components/TestedAgainst/index.jsx @@ -0,0 +1,13 @@ +import React from 'react' +import styles from './styles.module.css' + +export default function TestedAgainst({ avocado, jetpack, boards = [] }) { + return ( +
+ Tested against:{' '} + {avocado && Avocado {avocado}}{' '} + {jetpack && JetPack {jetpack}}{' '} + {boards.length > 0 && · {boards.join(', ')}} +
+ ) +} diff --git a/src/src/components/TestedAgainst/styles.module.css b/src/src/components/TestedAgainst/styles.module.css new file mode 100644 index 00000000..11022af5 --- /dev/null +++ b/src/src/components/TestedAgainst/styles.module.css @@ -0,0 +1,21 @@ +.testedAgainst { + margin: 0 0 1.5rem 0; + padding: 0.6rem 0.9rem; + border: 1px solid var(--ifm-color-emphasis-300); + border-left: 3px solid var(--ifm-color-emphasis-500); + border-radius: 4px; + background-color: var(--ifm-background-surface-color); + color: var(--ifm-color-emphasis-800); + font-size: 0.9rem; + line-height: 1.5; +} + +.testedAgainst code { + font-size: 0.85em; + padding: 0.1rem 0.4rem; + margin: 0 0.15rem; +} + +.boards { + color: var(--ifm-color-emphasis-700); +} diff --git a/src/src/css/custom.css b/src/src/css/custom.css index c84e169d..b7403ef3 100644 --- a/src/src/css/custom.css +++ b/src/src/css/custom.css @@ -320,6 +320,12 @@ html[data-theme="dark"] .navbar { mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke-width='1.5' stroke='currentColor'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M17.25 6.75 22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3-4.5 16.5'/%3E%3C/svg%3E"); } +/* Field Notes — notepad (document-text) icon */ +.navbar__link[href="/field-notes"]::before { + -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke-width='1.5' stroke='currentColor'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z'/%3E%3C/svg%3E"); + mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke-width='1.5' stroke='currentColor'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z'/%3E%3C/svg%3E"); +} + /* Changelog — list icon */ .navbar__link[href^="/changelog"]::before { -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke-width='1.5' stroke='currentColor'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M8.25 6.75h12M8.25 12h12m-12 5.25h12M3.75 6.75h.007v.008H3.75V6.75Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0ZM3.75 12h.007v.008H3.75V12Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm-.375 5.25h.007v.008H3.75v-.008Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z'/%3E%3C/svg%3E"); @@ -1086,3 +1092,137 @@ html[data-theme="dark"] .code-copy-tooltip { background: #e5e5e5; color: #1a1a1e; } + +/* ============================================================================= + FIELD NOTES — layout migration to dense single-column. + All rules scoped to body class .plugin-id-field-notes. Verified: this is + the only blog instance on the site, and the swizzled BlogPostItems only + renders on blog routes. + ============================================================================= */ + +:root { + --field-notes-accent: #4f46e5; +} + +html[data-theme="dark"] { + --field-notes-accent: #818cf8; +} + +/* Geist for body type, Geist Mono for meta + code */ +.plugin-id-field-notes, +.plugin-id-field-notes .markdown, +.plugin-id-field-notes h1, +.plugin-id-field-notes h2, +.plugin-id-field-notes h3, +.plugin-id-field-notes h4, +.plugin-id-field-notes h5, +.plugin-id-field-notes h6, +.plugin-id-field-notes p, +.plugin-id-field-notes li { + font-family: "Geist", "Inter", sans-serif; +} + +.plugin-id-field-notes code, +.plugin-id-field-notes kbd, +.plugin-id-field-notes pre { + font-family: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, monospace; +} + +/* Layout: drop the col--offset-1 and let the article fill the area that + would normally hold the left nav. TOC stays on the right. */ +.plugin-id-field-notes .container > .row { + display: flex; + flex-wrap: wrap; + gap: 2.5rem; +} + +.plugin-id-field-notes main.col { + flex: 1 1 0; + min-width: 0; + max-width: none; + margin: 0 !important; + padding: 0 !important; +} + +.plugin-id-field-notes .col--2 { + flex: 0 0 220px; + max-width: 220px; + padding: 0 !important; +} + +@media (max-width: 996px) { + .plugin-id-field-notes main.col, + .plugin-id-field-notes .col--2 { + flex: 0 0 100%; + max-width: 100%; + } +} + +/* ----- DETAIL PAGE ----- */ +/* "FIELD NOTES" eyebrow above the post title */ +.plugin-id-field-notes article > header > h1::before { + content: "FIELD NOTES"; + display: block; + font-family: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: 0.7rem; + font-weight: 600; + letter-spacing: 0.14em; + color: var(--field-notes-accent); + margin-bottom: 0.45rem; +} + +.plugin-id-field-notes article > header > h1 { + font-size: 1.875rem; + font-weight: 600; + line-height: 1.25; + margin-bottom: 0.5rem; + letter-spacing: -0.01em; +} + +.plugin-id-field-notes article > header time { + font-size: 0.875rem; + color: var(--ifm-color-emphasis-700); +} + +.plugin-id-field-notes article > header > div:first-of-type { + margin: 0.25rem 0 0.75rem 0 !important; +} + +/* The inner author row uses bootstrap .row too. Stop our outer flex + override from leaking down to it. */ +.plugin-id-field-notes article > header .row { + display: block !important; + margin-top: 0.25rem !important; + margin-bottom: 0.75rem !important; + gap: 0 !important; +} + +.plugin-id-field-notes article > header [class*="authorCol"] { + padding: 0; +} + +/* Section headings inside the article */ +.plugin-id-field-notes .markdown h2 { + font-size: 1.5rem; + font-weight: 600; + margin-top: 2rem; + margin-bottom: 0.75rem; +} + +.plugin-id-field-notes .markdown h3 { + font-size: 1.125rem; + font-weight: 600; + margin-top: 1.5rem; +} + +/* ----- TOC (kept, but subtle) ----- */ +.plugin-id-field-notes .table-of-contents { + font-family: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: 0.78rem; + border-left: 1px solid var(--ifm-color-emphasis-200); +} + +.plugin-id-field-notes .table-of-contents__link:hover, +.plugin-id-field-notes .table-of-contents__link--active { + color: var(--field-notes-accent); +} diff --git a/src/src/theme/BlogPostItems/index.js b/src/src/theme/BlogPostItems/index.js new file mode 100644 index 00000000..f835f99a --- /dev/null +++ b/src/src/theme/BlogPostItems/index.js @@ -0,0 +1,75 @@ +import React from 'react' +import Link from '@docusaurus/Link' +import { BlogPostProvider } from '@docusaurus/plugin-content-blog/client' +import styles from './styles.module.css' + +function formatDate(iso) { + const d = new Date(iso) + return d.toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric', + timeZone: 'UTC', + }) +} + +function ReadingTime({ minutes }) { + if (!minutes) return null + const m = Math.max(1, Math.round(minutes)) + return {m} min read +} + +function FieldNoteRow({ post }) { + const { metadata, frontMatter } = post + const { permalink, title, date, readingTime, authors = [], tags = [] } = metadata + const dek = metadata.description || frontMatter.description || '' + const authorNames = authors.map((a) => a.name).filter(Boolean) + + return ( +
+

+ {title} +

+ {dek ?

{dek}

: null} +
+ + {authorNames.length > 0 && ( + <> + · + {authorNames.join(', ')} + + )} + {readingTime ? ( + <> + · + + + ) : null} + {tags.length > 0 && ( + + {tags.map((t) => ( + + #{t.label} + + ))} + + )} +
+
+ ) +} + +export default function BlogPostItems({ items }) { + return ( +
+ {items.map(({ content: BlogPostContent }) => ( + + + + ))} +
+ ) +} diff --git a/src/src/theme/BlogPostItems/styles.module.css b/src/src/theme/BlogPostItems/styles.module.css new file mode 100644 index 00000000..1c690919 --- /dev/null +++ b/src/src/theme/BlogPostItems/styles.module.css @@ -0,0 +1,79 @@ +/* The single-column list view for /field-notes. All styles here only apply + inside the swizzled BlogPostItems, which only renders on field-notes + index / tag / author / archive pages (the only blog instance on the site). */ + +.list { + width: 100%; + margin: 0; + padding: 0; +} + +.row { + padding: 1.5rem 0 1.75rem; + border-bottom: 1px solid var(--ifm-color-emphasis-200); +} + +.row:first-child { + padding-top: 0.5rem; +} + +.row:last-child { + border-bottom: none; +} + +.title { + font-size: 1.375rem; + font-weight: 600; + line-height: 1.3; + letter-spacing: -0.01em; + margin: 0 0 0.5rem 0; +} + +.title a { + color: var(--ifm-heading-color); + text-decoration: none; +} + +.title a:hover { + color: var(--field-notes-accent); + text-decoration: none; +} + +.dek { + font-size: 0.95rem; + line-height: 1.55; + color: var(--ifm-color-emphasis-800); + margin: 0 0 0.65rem 0; +} + +.meta { + font-family: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: 0.8rem; + color: var(--ifm-color-emphasis-600); + display: flex; + flex-wrap: wrap; + gap: 0.4rem; + align-items: baseline; + line-height: 1.6; +} + +.sep { + color: var(--ifm-color-emphasis-400); +} + +.tags { + display: inline-flex; + flex-wrap: wrap; + gap: 0.6rem; + margin-left: 0.2rem; +} + +.tag { + color: var(--field-notes-accent); + text-decoration: none; +} + +.tag:hover { + text-decoration: underline; + text-underline-offset: 2px; +} From 181d70ec57692061ffc0f69a6d68acb8268ccd22 Mon Sep 17 00:00:00 2001 From: Bill Brock Date: Tue, 16 Jun 2026 05:31:09 -0500 Subject: [PATCH 02/14] docs(field-notes): add purpose, goals, and in-scope to contributing guide Co-Authored-By: Claude Opus 4.7 (1M context) --- src/field-notes/CONTRIBUTING.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/field-notes/CONTRIBUTING.md b/src/field-notes/CONTRIBUTING.md index bce1aa17..23b831ae 100644 --- a/src/field-notes/CONTRIBUTING.md +++ b/src/field-notes/CONTRIBUTING.md @@ -1,9 +1,25 @@ # Contributing to Field Notes -Field Notes is a stream of dense, dated, engineer-voice technical entries. It's the source of truth — marketing repurposes strong notes into blog posts downstream, and the best ones get sequenced into a curated learning track later. +## Purpose + +Field Notes is the surface where a skeptical embedded engineer sees real, reproducible proof that Avocado OS does the hard things — and it's the raw source that feeds HN, Reddit, and everything we repurpose downstream. Field Notes is *true*. The blog is *pretty*. Don't mix them up. +## Goals + +- **Build engineer trust and create internal champions** — be where an IC sees the proof and decides to bring us in. +- **Demonstrate the product doing hard things, reproducibly** — demos and hands-on guides (flashing containers on a Jetson, OTA rollback, JetPack migration) an engineer can run themselves. +- **Be the primary-source artifact for the engineer channels** — written to go raw to HN/Reddit and to rank for technical long-tail. +- **Feed the rest of the system** — the source material marketing lifts into blog narratives and that gets promoted into the curated track. + +## In scope + +- Technical developer content. +- Raw demos (clips and terminal casts). +- Reproducible how-tos and standalone guides (e.g. "flashing containers on Jetson"). +- "What we tried, what broke" experiments. + ## How to write a note Start by copying `_template.mdx`: From 4a63eeb97246f6853694eca198e206c34e6d1e15 Mon Sep 17 00:00:00 2001 From: Bill Brock Date: Tue, 16 Jun 2026 06:04:41 -0500 Subject: [PATCH 03/14] docs(field-notes): add rpi5-from-macos draft Drafted note on running the Avocado Desktop preview end-to-end on a Raspberry Pi 5 from macOS. Contains [ENGINEER: ...] placeholders for versions, command output, the agent diff, and the honest "what fought me" section to be filled in after a real run. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../2026-06-16-rpi5-from-macos.mdx | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 src/field-notes/2026-06-16-rpi5-from-macos.mdx diff --git a/src/field-notes/2026-06-16-rpi5-from-macos.mdx b/src/field-notes/2026-06-16-rpi5-from-macos.mdx new file mode 100644 index 00000000..4711d1e1 --- /dev/null +++ b/src/field-notes/2026-06-16-rpi5-from-macos.mdx @@ -0,0 +1,66 @@ +--- +title: "Provisioning a Raspberry Pi 5 from my Mac, no Yocto, no Linux box" +description: "An embedded dev's first run of the Avocado Desktop preview: install, build, and provision a Raspberry Pi 5 from macOS, no Linux build host. Including the part that didn't go to plan." +date: 2026-06-16 +authors: [jschneck] +tags: [rpi5, yocto, provisioning, sdk, agent] +tested_against: "Avocado [ENGINEER: preview version], Raspberry Pi 5, macOS host [ENGINEER: macOS version]" +hn_title: "I built and provisioned a Pi 5 from macOS with no Linux host (Avocado Desktop preview)" +poster: jschneck +lift_for_blog: "The Yocto tax, a dedicated OS team and a Linux build host, replaced by a Homebrew install and three commands on the Mac the engineer already owns." +promote_to_track: "Zero-to-Avocado on-ramp candidate: the install, build, provision run on a Pi 5 from macOS is the obvious first tutorial." +--- + +import TestedAgainst from '@site/src/components/TestedAgainst'; + + + +**TL;DR.** I run embedded for a living and I have never believed the "build your whole OS from your laptop" pitch, least of all from a Mac. The Avocado Desktop preview took a Raspberry Pi 5 from install to provisioned in three commands on macOS, with no Linux build host in sight. [ENGINEER: confirm it ran clean end to end, and the measured time.] It also fought me once. [ENGINEER: keep this line only if it actually did, and say how.] + +{/* truncate */} + +## Why I bothered + +If you have shipped embedded Linux you know the tax. You are the OS developer, you babysit a Yocto build for hours, and you need a dedicated Linux host to do it. I have spent more of my life than I would like watching a build sit at 35 percent. + +Quick orientation for anyone who has not seen this before. Peridio makes Avocado OS, an immutable embedded Linux runtime shipped as a binary distribution, for boards like the Raspberry Pi 5 and NVIDIA Jetson. Instead of compiling an OS from source, you compose a runtime out of extensions: prebuilt ones for common hardware, custom ones that pull in your own cross-compiled code. Avocado Desktop is the local tool that runs that on your machine. The developer preview is what is rolling out now, ahead of the Avocado 1.0 release. [ENGINEER: confirm the preview rollout status and timing you want to state.] + +The claim that made me skeptical was the part where you do the whole thing from a Mac. So I tried it. + +## Getting the preview + +Early access is a Homebrew install on macOS. [ENGINEER: the exact brew tap and install command, plus the early-access link or signup that gates the preview. Note prerequisites, for example Xcode command line tools or a minimum macOS version.] + +[ENGINEER: paste the install output, or the first thing that went sideways. macOS-specific setup is exactly where I expected friction, so if it was clean, that itself is worth saying.] + +## Three commands + +The workflow is install, build, provision. + +Install. [ENGINEER: the exact command and output. This pulls the SDK and resolves the dependencies for each extension.] + +Build. [ENGINEER: the exact command and output. Each extension becomes an image and they compose into one root filesystem. No containers, so none of the runtime container overhead, and I can drop in kernel modules or a custom kernel if I need to.] + +Provision. [ENGINEER: the exact command and the device monitor output. In the demo it prompts before overwriting the board, you confirm, and it streams the device output back to the host. Note the interface and any board prep, for example a particular cable or boot step.] + +[ENGINEER: paste the first boot log from the Pi 5, so this section is a result and not a story.] + +## The agent did the annoying part + +The preview ships an LLM agent that can read the package database, edit the project, drive the build, and provision. I gave it the kind of task I usually lose an afternoon to: add GStreamer and the kernel modules for a USB webcam to the app extension. + +[ENGINEER: paste the exact prompt, what the agent actually added (packages and kernel modules), and the resulting extension diff. Did it get it right on the first try or did you have to correct it? The honest answer here is worth more than a clean one.] + +## Where it fought me + +This is a preview, and a Field Note with no rough edges is a press release. [ENGINEER: required, the real one. Where did it break, stall, or surprise you? Candidates: the macOS setup, the agent picking the wrong or an incomplete set of packages, provisioning needing a board-specific step the one-command flow hid, or the Yocto comparison not being a fair fight (Yocto does not build natively on macOS, so any speed claim needs a defined Linux host and target). If it genuinely did not break, say what it does not do yet.] + +## Would I reach for it again + +[ENGINEER: the honest verdict after a real run. What it replaced in my workflow, what time it actually saved (real numbers, not the keynote's), and where it is still preview-rough. If you want the Jetson angle, note that this run was a Pi 5. Jetson is supported but I have not provisioned one yet, so do not imply I did.] + +## Get in and try it + +Developer preview, macOS, Homebrew. [ENGINEER: the brew tap and install command again as a clean copy-paste block, the early-access link or signup, and a minimal step list for install, build, provision on a Pi 5.] + +Docs and the rest of the Peridio ecosystem are at docs.peridio.com. [ENGINEER: confirm the preview landing URL.] From a76589590d2454839d564b0ba59b23901979fee6 Mon Sep 17 00:00:00 2001 From: Bill Brock Date: Tue, 16 Jun 2026 06:09:47 -0500 Subject: [PATCH 04/14] docs(field-notes): rewrite rpi5 note in creator voice, embed demo video MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Justin built Avocado Desktop — this note documents his own lightning demo, not a third-party engineer's first run. Reworked TL;DR, the "Why I built it" section, "Where it still fights me", and "Where this is going" so the voice matches. Embedded the YouTube lightning demo at the top with a responsive 16:9 iframe. Co-Authored-By: Claude Opus 4.7 (1M context) --- ....mdx => 2026-06-15-power-pull-orin-nx.mdx} | 0 .../2026-06-16-rpi5-from-macos.mdx | 48 +++++++++++-------- 2 files changed, 29 insertions(+), 19 deletions(-) rename src/field-notes/{2026-06-30-power-pull-orin-nx.mdx => 2026-06-15-power-pull-orin-nx.mdx} (100%) diff --git a/src/field-notes/2026-06-30-power-pull-orin-nx.mdx b/src/field-notes/2026-06-15-power-pull-orin-nx.mdx similarity index 100% rename from src/field-notes/2026-06-30-power-pull-orin-nx.mdx rename to src/field-notes/2026-06-15-power-pull-orin-nx.mdx diff --git a/src/field-notes/2026-06-16-rpi5-from-macos.mdx b/src/field-notes/2026-06-16-rpi5-from-macos.mdx index 4711d1e1..f559476d 100644 --- a/src/field-notes/2026-06-16-rpi5-from-macos.mdx +++ b/src/field-notes/2026-06-16-rpi5-from-macos.mdx @@ -1,37 +1,47 @@ --- title: "Provisioning a Raspberry Pi 5 from my Mac, no Yocto, no Linux box" -description: "An embedded dev's first run of the Avocado Desktop preview: install, build, and provision a Raspberry Pi 5 from macOS, no Linux build host. Including the part that didn't go to plan." +description: "A lightning demo of Avocado Desktop: install, build, and provision a Raspberry Pi 5 from macOS, no Linux host. Including the part of my own preview that still fights me." date: 2026-06-16 authors: [jschneck] tags: [rpi5, yocto, provisioning, sdk, agent] tested_against: "Avocado [ENGINEER: preview version], Raspberry Pi 5, macOS host [ENGINEER: macOS version]" hn_title: "I built and provisioned a Pi 5 from macOS with no Linux host (Avocado Desktop preview)" poster: jschneck -lift_for_blog: "The Yocto tax, a dedicated OS team and a Linux build host, replaced by a Homebrew install and three commands on the Mac the engineer already owns." +lift_for_blog: "The Yocto tax — a dedicated OS team and a Linux build host — replaced by a Homebrew install and three commands on the Mac the engineer already owns." promote_to_track: "Zero-to-Avocado on-ramp candidate: the install, build, provision run on a Pi 5 from macOS is the obvious first tutorial." --- import TestedAgainst from '@site/src/components/TestedAgainst'; +
+