diff --git a/src/docusaurus.config.js b/src/docusaurus.config.js index 610f9903..08a6e19f 100644 --- a/src/docusaurus.config.js +++ b/src/docusaurus.config.js @@ -19,7 +19,7 @@ const config = { organizationName: 'peridio', projectName: 'peridio-docs', trailingSlash: false, - clientModules: ['./src/clientModules/copyInlineCode.js'], + clientModules: ['./src/clientModules/copyInlineCode.js', './src/clientModules/fieldNotesGate.js'], i18n: { defaultLocale: 'en', locales: ['en'], @@ -57,6 +57,30 @@ 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', + }, + }, + ], + // Injects into every + // /field-notes HTML page. Feed deletion happens in scripts/build.sh + // because postBuild hooks run concurrently across plugins. + './plugins/field-notes-preview-gate', ...(process.env.NODE_ENV === 'production' ? [ [ @@ -129,6 +153,12 @@ const config = { position: 'left', activeBasePath: 'developer-reference', }, + { + to: '/field-notes', + label: 'Field Notes', + position: 'left', + activeBasePath: 'field-notes', + }, { to: '/changelog/latest', label: 'Changelog', @@ -200,6 +230,35 @@ const config = { // document.documentElement.setAttribute('data-site-theme', 'avocado'); `, }, + { + // Synchronous /field-notes gate. Runs in before any paint, + // so visitors without the preview flag never see field-notes content. + // The companion clientModule (fieldNotesGate.js) handles the body + // class for the navbar link; this is just the redirect. + tagName: 'script', + attributes: {}, + innerHTML: ` + (function () { + if (typeof window === 'undefined') return; + var path = window.location.pathname; + if (path !== '/field-notes' && path.indexOf('/field-notes/') !== 0) return; + try { + var params = new URLSearchParams(window.location.search); + var flag = params.get('preview'); + if (flag === '1') { + window.sessionStorage.setItem('peridio:fn-preview', '1'); + return; + } + if (flag === '0') { + window.sessionStorage.removeItem('peridio:fn-preview'); + } else if (window.sessionStorage.getItem('peridio:fn-preview') === '1') { + return; + } + } catch (e) { /* sessionStorage unavailable — fall through to redirect */ } + window.location.replace('/'); + })(); + `, + }, { tagName: 'script', attributes: {}, @@ -234,6 +293,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-15-power-pull-orin-nx.mdx b/src/field-notes/2026-06-15-power-pull-orin-nx.mdx new file mode 100644 index 00000000..89a9fd02 --- /dev/null +++ b/src/field-notes/2026-06-15-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-15 +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/2026-06-16-rpi5-from-macos.mdx b/src/field-notes/2026-06-16-rpi5-from-macos.mdx new file mode 100644 index 00000000..dcff06b4 --- /dev/null +++ b/src/field-notes/2026-06-16-rpi5-from-macos.mdx @@ -0,0 +1,68 @@ +--- +title: 'Provisioning a Raspberry Pi from macOS natively, no Linux VM and no USB passthrough' +description: 'Flashing embedded boards from a Mac usually means a Linux VM and fragile USB passthrough. The Avocado Desktop preview provisions a Raspberry Pi 5 natively from macOS, so there is no VM to babysit and no passthrough to drop.' +date: 2026-06-16 +authors: [jschneck] +tags: [rpi5, provisioning, macos, usb, preview] +tested_against: 'Avocado [ENGINEER: preview version], Raspberry Pi 5, macOS host [ENGINEER: macOS version and chip, Apple Silicon or Intel]' +hn_title: 'Provisioning a Raspberry Pi from macOS natively, no Linux VM (Avocado Desktop preview)' +poster: jschneck +lift_for_blog: 'The Linux-VM-and-USB-passthrough dance for flashing boards from a Mac, removed: native provisioning on the laptop the engineer already owns.' +promote_to_track: 'Tutorial candidate: native macOS provisioning of a Pi 5 as the first device bring-up on the on-ramp.' +--- + +import TestedAgainst from '@site/src/components/TestedAgainst' + +
+