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
2 changes: 2 additions & 0 deletions src/data/docsNav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ export const docsNav: NavItem[] = [
icon: '<svg viewBox="0 0 24 24"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>' },
{ label: 'Configuration', href: '/docs/configuration', slug: 'configuration',
icon: '<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>' },
{ label: 'Message of the Day', href: '/docs/motd', slug: 'motd',
icon: '<svg viewBox="0 0 24 24"><path d="M3 11l18-5v12L3 14v-3z"/><path d="M11.6 16.8a3 3 0 1 1-5.8-1.6"/></svg>' },
{ label: 'Integration', href: '/docs/integration', slug: 'integration',
icon: '<svg viewBox="0 0 24 24"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>' },
// Reference
Expand Down
2 changes: 1 addition & 1 deletion src/pages/docs/configuration.astro
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ const bodyContent = `<h1>Configuration</h1>
activePage="configuration"
canonicalPath="/docs/configuration"
prev={{ label: "Diagnostics", href: "/docs/diagnostics" }}
next={{ label: "Integration", href: "/docs/integration" }}
next={{ label: "Message of the Day", href: "/docs/motd" }}
>
<Fragment set:html={bodyContent} />
</DocLayout>
2 changes: 1 addition & 1 deletion src/pages/docs/integration.astro
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ for (const msg of inbox.messages || []) {
description="Integrate Pilot Protocol with OpenClaw, heartbeat patterns, webhook-driven agents, and custom workflows."
activePage="integration"
canonicalPath="/docs/integration"
prev={{ label: "Configuration", href: "/docs/configuration" }}
prev={{ label: "Message of the Day", href: "/docs/motd" }}
next={{ label: "Error Codes", href: "/docs/error-codes" }}
>
<Fragment set:html={bodyContent} />
Expand Down
83 changes: 83 additions & 0 deletions src/pages/docs/motd.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
import DocLayout from "../../layouts/DocLayout.astro";

const bodyContent = `<h1>Message of the Day</h1>
<p class="subtitle">A short notice from the Pilot Protocol team, shown ahead of every <code>pilotctl</code> command — for one UTC day at a time.</p>

<div class="toc">
<h4>On this page</h4>
<ul>
<li><a href="#what">What it is</a></li>
<li><a href="#how-it-works">How it works</a></li>
<li><a href="#output">Output: text vs JSON</a></li>
<li><a href="#config">Configuration</a></li>
<li><a href="#rules">Rules &amp; semantics</a></li>
</ul>
</div>

<h2 id="what">What it is</h2>

<p>The message of the day (MOTD) is a network-wide banner the Pilot Protocol team can surface for a single UTC calendar day. When a message is active, it is prepended to the output of <strong>every</strong> <code>pilotctl</code> command — used for maintenance windows, incident updates, and breaking-change heads-ups. Messages are managed centrally by the Pilot Protocol team; there is nothing to set up to receive them.</p>

<pre><code><span class="cmd">pilotctl</span> info
<span class="comment">Message of the day: overlay maintenance 22:00 UTC — expect ~5min blips</span>

&lt;normal pilotctl info output&gt;</code></pre>

<p>When no message is active for the current UTC day, output is unchanged.</p>

<h2 id="how-it-works">How it works</h2>

<p>Two rules drive the design: <code>pilotctl</code> must stay fast and never hit the network to render the banner, and the daemon must not make an on-demand call when a command runs. So the work is split:</p>

<ul>
<li>The <strong>daemon</strong> is the only component that touches the network. A background loop fetches the central feed every <code>--motd-interval</code> (default 15m), selects the entry dated for the current UTC day, holds it in memory, and mirrors it to <code>~/.pilot/motd.json</code>.</li>
<li><strong><code>pilotctl</code></strong> reads only that local mirror — one file read, no network, no IPC — and re-validates the UTC day on read, so a stale mirror (for example, the daemon was offline across midnight) never shows yesterday's message.</li>
</ul>

<pre><code>central feed ──poll──▶ pilot-daemon ──mirror──▶ ~/.pilot/motd.json
(Pilot team) (only net I/O) the local variable
│ read (no net, no IPC)
pilotctl &lt;any command&gt; ──▶ prepends banner</code></pre>

<p>No new binary ships — the poll is a goroutine inside <code>pilot-daemon</code>, modelled on the existing skill-reconciler loop.</p>

<h2 id="output">Output: text vs JSON</h2>

<p><strong>Text mode</strong> prepends a <code>Message of the day: &lt;text&gt;</code> line and a blank line, then the normal command output.</p>

<p><strong><code>--json</code> mode</strong> does not prepend text (that would break parsing). Instead the standard envelope carries a top-level <code>important_update</code> field:</p>

<pre><code><span class="cmd">pilotctl</span> --json info
<span class="comment"># { "status": "ok", "data": { ... }, "important_update": "overlay maintenance 22:00 UTC" }</span></code></pre>

<p>The same field is added to error envelopes. The daemon also surfaces the current value as <code>motd</code> inside <code>pilotctl info</code>.</p>

<h2 id="config">Configuration</h2>

<p>Operators running a daemon can tune or disable polling:</p>

<pre><code><span class="cmd">pilot-daemon</span> --motd-feed-url &lt;url&gt; <span class="comment"># feed location; empty disables polling entirely</span>
<span class="cmd">pilot-daemon</span> --motd-interval 15m <span class="comment"># how often to re-fetch (default 15m)</span></code></pre>

<p>The <code>PILOT_MOTD_URL</code> environment variable overrides the feed URL. The mirror lives next to the daemon identity (normally <code>~/.pilot/motd.json</code>), which is where <code>pilotctl</code> looks.</p>

<h2 id="rules">Rules &amp; semantics</h2>

<ul>
<li><strong>UTC days.</strong> A message is active only on its UTC calendar day. <code>pilotctl</code> re-checks the day on read, so a message never lingers past its UTC day.</li>
<li><strong>Self-clearing.</strong> When the active message is withdrawn, the banner disappears on its own within one poll interval — no action needed on your machine.</li>
<li><strong>Fail-safe.</strong> Network errors and malformed responses are non-fatal: the daemon keeps its last good mirror and logs at debug level, so a transient fetch failure never blocks a command.</li>
</ul>`;
---
<DocLayout
title="Message of the Day"
description="The notice banner shown ahead of every pilotctl command, for one UTC day at a time."
activePage="motd"
canonicalPath="/docs/motd"
prev={{ label: "Configuration", href: "/docs/configuration" }}
next={{ label: "Integration", href: "/docs/integration" }}
>
<Fragment set:html={bodyContent} />
</DocLayout>
44 changes: 44 additions & 0 deletions src/pages/plain/docs/motd.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
import PlainLayout from '../../../layouts/PlainLayout.astro';
---
<PlainLayout title="Message of the Day — Pilot Protocol (plain)" description="The notice banner shown ahead of every pilotctl command, for one UTC day at a time." canonical="https://pilotprotocol.network/plain/docs/motd/">

<p><a href="/plain/docs/">&larr; Docs index</a></p>

<h1>Message of the Day</h1>

<p>The message of the day (MOTD) is a network-wide banner the Pilot Protocol team can surface for a single UTC calendar day. When a message is active, it is prepended to the output of every pilotctl command. Use cases: maintenance windows, incident updates, breaking-change heads-ups. Messages are managed centrally by the Pilot Protocol team; there is nothing to set up to receive them. When no message is active for the current UTC day, output is unchanged.</p>

<pre><code>pilotctl info
Message of the day: overlay maintenance 22:00 UTC — expect ~5min blips

&lt;normal pilotctl info output&gt;</code></pre>

<h2>How it works</h2>
<p>The daemon is the only component that touches the network. A background loop fetches the central feed every interval (default 15m), selects the entry dated for the current UTC day, holds it in memory, and mirrors it to ~/.pilot/motd.json. pilotctl reads only that local mirror — one file read, no network, no IPC — and re-validates the UTC day on read, so a stale mirror never shows yesterday's message. No new binary ships; the poll is a goroutine inside pilot-daemon.</p>

<h2>Output: text vs JSON</h2>
<p>Text mode prepends a "Message of the day: &lt;text&gt;" line, then the normal output. In --json mode the standard envelope carries a top-level important_update field instead (prepending text would break parsing). The same field is added to error envelopes, and the daemon surfaces the current value as motd inside pilotctl info.</p>
<pre><code>pilotctl --json info
# &#123; "status": "ok", "data": &#123; ... &#125;, "important_update": "overlay maintenance 22:00 UTC" &#125;</code></pre>

<h2>Configuration</h2>
<p>Operators running a daemon can tune or disable polling:</p>
<pre><code>pilot-daemon --motd-feed-url &lt;url&gt; # feed location; empty disables polling entirely
pilot-daemon --motd-interval 15m # how often to re-fetch (default 15m)</code></pre>
<p>The PILOT_MOTD_URL environment variable overrides the feed URL. The mirror lives next to the daemon identity (normally ~/.pilot/motd.json), which is where pilotctl looks.</p>

<h2>Rules and semantics</h2>
<ul>
<li>UTC days: a message is active only on its UTC calendar day. pilotctl re-checks the day on read, so a message never lingers past its UTC day.</li>
<li>Self-clearing: when the active message is withdrawn, the banner disappears on its own within one poll interval — no action needed on your machine.</li>
<li>Fail-safe: network errors and malformed responses are non-fatal; the daemon keeps its last good mirror, so a transient fetch failure never blocks a command.</li>
</ul>

<h2>Related</h2>
<ul>
<li><a href="/docs/configuration">Configuration</a></li>
<li><a href="/docs/diagnostics">Diagnostics</a></li>
</ul>

</PlainLayout>
Loading