From b4b8304a4f23ba62ce9a7115fa87d0e9e1957cee Mon Sep 17 00:00:00 2001 From: Alex Godoroja Date: Mon, 15 Jun 2026 16:50:05 -0700 Subject: [PATCH 1/2] docs: add Message of the Day page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document the message-of-the-day (MOTD) feature: an operator-published banner shown ahead of every pilotctl command for one UTC day at a time. Covers what it is, the poll-and-mirror design (daemon fetches the feed, pilotctl reads the local mirror — no per-command network call), text vs --json output (the important_update envelope field), publishing/clearing via the pilot-motd repo, the --motd-feed-url / --motd-interval flags, and UTC semantics. - src/pages/docs/motd.astro: full docs page - src/pages/plain/docs/motd.astro: plain-text mirror - src/data/docsNav.ts: sidebar entry under Operations - configuration.astro / integration.astro: splice prev/next chain Built locally with `npm run build` (301 pages, clean). --- src/data/docsNav.ts | 2 + src/pages/docs/configuration.astro | 2 +- src/pages/docs/integration.astro | 2 +- src/pages/docs/motd.astro | 115 +++++++++++++++++++++++++++++ src/pages/plain/docs/motd.astro | 68 +++++++++++++++++ 5 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 src/pages/docs/motd.astro create mode 100644 src/pages/plain/docs/motd.astro diff --git a/src/data/docsNav.ts b/src/data/docsNav.ts index 9fc0444..be37ee3 100644 --- a/src/data/docsNav.ts +++ b/src/data/docsNav.ts @@ -63,6 +63,8 @@ export const docsNav: NavItem[] = [ icon: '' }, { label: 'Configuration', href: '/docs/configuration', slug: 'configuration', icon: '' }, + { label: 'Message of the Day', href: '/docs/motd', slug: 'motd', + icon: '' }, { label: 'Integration', href: '/docs/integration', slug: 'integration', icon: '' }, // Reference diff --git a/src/pages/docs/configuration.astro b/src/pages/docs/configuration.astro index 7823f09..caadf4b 100644 --- a/src/pages/docs/configuration.astro +++ b/src/pages/docs/configuration.astro @@ -213,7 +213,7 @@ const bodyContent = `

Configuration

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" }} > diff --git a/src/pages/docs/integration.astro b/src/pages/docs/integration.astro index cd97286..78b155f 100644 --- a/src/pages/docs/integration.astro +++ b/src/pages/docs/integration.astro @@ -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" }} > diff --git a/src/pages/docs/motd.astro b/src/pages/docs/motd.astro new file mode 100644 index 0000000..7cab335 --- /dev/null +++ b/src/pages/docs/motd.astro @@ -0,0 +1,115 @@ +--- +import DocLayout from "../../layouts/DocLayout.astro"; + +const bodyContent = `

Message of the Day

+

A short, operator-published notice shown ahead of every pilotctl command — for one UTC day at a time.

+ + + +

What it is

+ +

The message of the day (MOTD) is a network-wide banner the Pilot Protocol team can publish for a single UTC calendar day. When a message is active, it is prepended to the output of every pilotctl command. Use it for maintenance windows, incident updates, and breaking-change heads-ups.

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

When no message is published for the current UTC day, output is unchanged.

+ +

How it works

+ +

Two rules drive the design: pilotctl 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:

+ +
    +
  • The daemon is the only component that touches the network. A background loop fetches the feed every --motd-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 (for example, the daemon was offline across midnight) never shows yesterday's message.
  • +
+ +
pilot-motd repo (motd.json)  ──poll──▶  pilot-daemon  ──mirror──▶  ~/.pilot/motd.json
+   the "DB": one JSON file                (only net I/O)              the local variable
+                                                                          │ read (no net, no IPC)
+                                                                          ▼
+                                          pilotctl <any command>  ──▶  prepends banner
+ +

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

+ +

Output: text vs JSON

+ +

Text mode prepends a Message of the day: <text> line and a blank line, then the normal command output.

+ +

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

+ +
pilotctl --json info
+# { "status": "ok", "data": { ... }, "important_update": "overlay maintenance 22:00 UTC" }
+ +

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

+ +

Publishing a message

+ +

The source of truth is a single static file, motd.json, in the pilot-motd repository. The daemon fetches it raw from the default branch, so committing to main is the whole publish workflow — every daemon picks the change up on its next poll.

+ +
# in the pilot-motd repo — defaults to today (UTC)
+scripts/set-motd.sh "overlay maintenance 22:00 UTC — expect ~5min blips"
+
+# schedule ahead for a specific UTC day
+scripts/set-motd.sh "v2 cutover — see #ops" 2026-07-01
+
+git add motd.json && git commit -m "motd: maintenance notice" && git push
+ +

The feed shape (schema_version: 1):

+ +
{
+  "schema_version": 1,
+  "messages": [
+    { "date": "2026-06-15", "text": "overlay maintenance 22:00 UTC", "id": "maint-0615" }
+  ]
+}
+ +

Clearing a message

+ +

Clearing is first-class — an empty MOTD is a valid update. Any of these remove the banner within one poll interval, then commit motd.json:

+ +
scripts/clear-motd.sh             # clear today's (UTC) entry
+scripts/clear-motd.sh 2026-07-01  # clear a specific day
+scripts/clear-motd.sh --all       # remove every entry
+ +

Configuration

+ +

Daemon flags (also settable via pilotctl daemon start):

+ +
pilot-daemon --motd-feed-url <url>     # feed location; empty disables polling entirely
+pilot-daemon --motd-interval 15m       # how often to re-fetch (default 15m)
+ +

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.

+ +

Rules & semantics

+ +
    +
  • UTC days. date is a UTC calendar day (YYYY-MM-DD); a message is active only on that day. pilotctl re-checks the day on read, so a message never lingers past its UTC day.
  • +
  • One per day. Keep at most one entry per day. If several share a day, the daemon takes the first non-blank one.
  • +
  • Forward-dating. Entries dated in the future are ignored until their day arrives, so you can stage announcements ahead of time.
  • +
  • Fail-safe. Non-2xx responses and parse errors are non-fatal: the daemon keeps its last good mirror and logs at debug level. An unknown schema_version is rejected rather than mis-parsed.
  • +
`; +--- + + + diff --git a/src/pages/plain/docs/motd.astro b/src/pages/plain/docs/motd.astro new file mode 100644 index 0000000..e81b0a1 --- /dev/null +++ b/src/pages/plain/docs/motd.astro @@ -0,0 +1,68 @@ +--- +import PlainLayout from '../../../layouts/PlainLayout.astro'; +--- + + +

← Docs index

+ +

Message of the Day

+ +

The message of the day (MOTD) is a network-wide banner the Pilot Protocol team can publish for a single UTC calendar day. When a message is active, it is prepended to the output of every pilotctl command. Use it for maintenance windows, incident updates, and breaking-change heads-ups. When no message is published for the current UTC day, output is unchanged.

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

How it works

+

The daemon is the only component that touches the network. A background loop fetches the 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.

+ +

Output: text vs JSON

+

Text mode prepends a "Message of the day: <text>" 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.

+
pilotctl --json info
+# { "status": "ok", "data": { ... }, "important_update": "overlay maintenance 22:00 UTC" }
+ +

Publishing a message

+

The source of truth is a single static file, motd.json, in the pilot-motd repository. The daemon fetches it raw from the default branch, so committing to main is the whole publish workflow — every daemon picks the change up on its next poll.

+
# in the pilot-motd repo — defaults to today (UTC)
+scripts/set-motd.sh "overlay maintenance 22:00 UTC — expect ~5min blips"
+
+# schedule ahead for a specific UTC day
+scripts/set-motd.sh "v2 cutover — see #ops" 2026-07-01
+
+git add motd.json && git commit -m "motd: maintenance notice" && git push
+

Feed shape (schema_version 1):

+
{
+  "schema_version": 1,
+  "messages": [
+    { "date": "2026-06-15", "text": "overlay maintenance 22:00 UTC", "id": "maint-0615" }
+  ]
+}
+ +

Clearing a message

+

Clearing is first-class — an empty MOTD is a valid update. Any of these remove the banner within one poll interval, then commit motd.json:

+
scripts/clear-motd.sh             # clear today's (UTC) entry
+scripts/clear-motd.sh 2026-07-01  # clear a specific day
+scripts/clear-motd.sh --all       # remove every entry
+ +

Configuration

+

Daemon flags (also settable via pilotctl daemon start):

+
pilot-daemon --motd-feed-url <url>     # feed location; empty disables polling entirely
+pilot-daemon --motd-interval 15m       # how often to re-fetch (default 15m)
+

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.

+ +

Rules and semantics

+
    +
  • UTC days: date is a UTC calendar day (YYYY-MM-DD); a message is active only on that day. pilotctl re-checks the day on read, so a message never lingers past its UTC day.
  • +
  • One per day: keep at most one entry per day. If several share a day, the daemon takes the first non-blank one.
  • +
  • Forward-dating: entries dated in the future are ignored until their day arrives.
  • +
  • Fail-safe: non-2xx responses and parse errors are non-fatal; the daemon keeps its last good mirror. An unknown schema_version is rejected rather than mis-parsed.
  • +
+ +

Related

+ + +
From 51b147d9c397500a550bd3ef482467c78a8ab225 Mon Sep 17 00:00:00 2001 From: Alex Godoroja Date: Mon, 15 Jun 2026 17:10:19 -0700 Subject: [PATCH 2/2] docs(motd): remove publishing instructions, keep feature behavior Publishing is restricted to maintainers with push access to the feed; the public docs no longer describe how to post/clear a message. The page still covers what MOTD is, the poll-and-mirror design, text vs --json output, daemon configuration, and UTC/self-clearing semantics. --- src/pages/docs/motd.astro | 60 ++++++++------------------------- src/pages/plain/docs/motd.astro | 38 ++++----------------- 2 files changed, 21 insertions(+), 77 deletions(-) diff --git a/src/pages/docs/motd.astro b/src/pages/docs/motd.astro index 7cab335..f715d53 100644 --- a/src/pages/docs/motd.astro +++ b/src/pages/docs/motd.astro @@ -2,7 +2,7 @@ import DocLayout from "../../layouts/DocLayout.astro"; const bodyContent = `

Message of the Day

-

A short, operator-published notice shown ahead of every pilotctl command — for one UTC day at a time.

+

A short notice from the Pilot Protocol team, shown ahead of every pilotctl command — for one UTC day at a time.

On this page

@@ -10,8 +10,6 @@ const bodyContent = `

Message of the Day

  • What it is
  • How it works
  • Output: text vs JSON
  • -
  • Publishing a message
  • -
  • Clearing a message
  • Configuration
  • Rules & semantics
  • @@ -19,29 +17,29 @@ const bodyContent = `

    Message of the Day

    What it is

    -

    The message of the day (MOTD) is a network-wide banner the Pilot Protocol team can publish for a single UTC calendar day. When a message is active, it is prepended to the output of every pilotctl command. Use it for maintenance windows, incident updates, and breaking-change heads-ups.

    +

    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 — 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.

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

    When no message is published for the current UTC day, output is unchanged.

    +

    When no message is active for the current UTC day, output is unchanged.

    How it works

    Two rules drive the design: pilotctl 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:

      -
    • The daemon is the only component that touches the network. A background loop fetches the feed every --motd-interval (default 15m), selects the entry dated for the current UTC day, holds it in memory, and mirrors it to ~/.pilot/motd.json.
    • +
    • The daemon is the only component that touches the network. A background loop fetches the central feed every --motd-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 (for example, the daemon was offline across midnight) never shows yesterday's message.
    -
    pilot-motd repo (motd.json)  ──poll──▶  pilot-daemon  ──mirror──▶  ~/.pilot/motd.json
    -   the "DB": one JSON file                (only net I/O)              the local variable
    -                                                                          │ read (no net, no IPC)
    -                                                                          ▼
    -                                          pilotctl <any command>  ──▶  prepends banner
    +
    central feed  ──poll──▶  pilot-daemon  ──mirror──▶  ~/.pilot/motd.json
    + (Pilot team)             (only net I/O)              the local variable
    +                                                           │ read (no net, no IPC)
    +                                                           ▼
    +                              pilotctl <any command>  ──▶  prepends banner

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

    @@ -56,38 +54,9 @@ const bodyContent = `

    Message of the Day

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

    -

    Publishing a message

    - -

    The source of truth is a single static file, motd.json, in the pilot-motd repository. The daemon fetches it raw from the default branch, so committing to main is the whole publish workflow — every daemon picks the change up on its next poll.

    - -
    # in the pilot-motd repo — defaults to today (UTC)
    -scripts/set-motd.sh "overlay maintenance 22:00 UTC — expect ~5min blips"
    -
    -# schedule ahead for a specific UTC day
    -scripts/set-motd.sh "v2 cutover — see #ops" 2026-07-01
    -
    -git add motd.json && git commit -m "motd: maintenance notice" && git push
    - -

    The feed shape (schema_version: 1):

    - -
    {
    -  "schema_version": 1,
    -  "messages": [
    -    { "date": "2026-06-15", "text": "overlay maintenance 22:00 UTC", "id": "maint-0615" }
    -  ]
    -}
    - -

    Clearing a message

    - -

    Clearing is first-class — an empty MOTD is a valid update. Any of these remove the banner within one poll interval, then commit motd.json:

    - -
    scripts/clear-motd.sh             # clear today's (UTC) entry
    -scripts/clear-motd.sh 2026-07-01  # clear a specific day
    -scripts/clear-motd.sh --all       # remove every entry
    -

    Configuration

    -

    Daemon flags (also settable via pilotctl daemon start):

    +

    Operators running a daemon can tune or disable polling:

    pilot-daemon --motd-feed-url <url>     # feed location; empty disables polling entirely
     pilot-daemon --motd-interval 15m       # how often to re-fetch (default 15m)
    @@ -97,15 +66,14 @@ scripts/clear-motd.sh --all # remove every entryRules & semantics
      -
    • UTC days. date is a UTC calendar day (YYYY-MM-DD); a message is active only on that day. pilotctl re-checks the day on read, so a message never lingers past its UTC day.
    • -
    • One per day. Keep at most one entry per day. If several share a day, the daemon takes the first non-blank one.
    • -
    • Forward-dating. Entries dated in the future are ignored until their day arrives, so you can stage announcements ahead of time.
    • -
    • Fail-safe. Non-2xx responses and parse errors are non-fatal: the daemon keeps its last good mirror and logs at debug level. An unknown schema_version is rejected rather than mis-parsed.
    • +
    • 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.
    • +
    • Self-clearing. When the active message is withdrawn, the banner disappears on its own within one poll interval — no action needed on your machine.
    • +
    • Fail-safe. 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.
    `; --- +

    ← Docs index

    Message of the Day

    -

    The message of the day (MOTD) is a network-wide banner the Pilot Protocol team can publish for a single UTC calendar day. When a message is active, it is prepended to the output of every pilotctl command. Use it for maintenance windows, incident updates, and breaking-change heads-ups. When no message is published for the current UTC day, output is unchanged.

    +

    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.

    pilotctl info
     Message of the day: overlay maintenance 22:00 UTC — expect ~5min blips
    @@ -15,48 +15,24 @@ Message of the day: overlay maintenance 22:00 UTC — expect ~5min blips
     <normal pilotctl info output>

    How it works

    -

    The daemon is the only component that touches the network. A background loop fetches the 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.

    +

    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.

    Output: text vs JSON

    Text mode prepends a "Message of the day: <text>" 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.

    pilotctl --json info
     # { "status": "ok", "data": { ... }, "important_update": "overlay maintenance 22:00 UTC" }
    -

    Publishing a message

    -

    The source of truth is a single static file, motd.json, in the pilot-motd repository. The daemon fetches it raw from the default branch, so committing to main is the whole publish workflow — every daemon picks the change up on its next poll.

    -
    # in the pilot-motd repo — defaults to today (UTC)
    -scripts/set-motd.sh "overlay maintenance 22:00 UTC — expect ~5min blips"
    -
    -# schedule ahead for a specific UTC day
    -scripts/set-motd.sh "v2 cutover — see #ops" 2026-07-01
    -
    -git add motd.json && git commit -m "motd: maintenance notice" && git push
    -

    Feed shape (schema_version 1):

    -
    {
    -  "schema_version": 1,
    -  "messages": [
    -    { "date": "2026-06-15", "text": "overlay maintenance 22:00 UTC", "id": "maint-0615" }
    -  ]
    -}
    - -

    Clearing a message

    -

    Clearing is first-class — an empty MOTD is a valid update. Any of these remove the banner within one poll interval, then commit motd.json:

    -
    scripts/clear-motd.sh             # clear today's (UTC) entry
    -scripts/clear-motd.sh 2026-07-01  # clear a specific day
    -scripts/clear-motd.sh --all       # remove every entry
    -

    Configuration

    -

    Daemon flags (also settable via pilotctl daemon start):

    +

    Operators running a daemon can tune or disable polling:

    pilot-daemon --motd-feed-url <url>     # feed location; empty disables polling entirely
     pilot-daemon --motd-interval 15m       # how often to re-fetch (default 15m)

    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.

    Rules and semantics

      -
    • UTC days: date is a UTC calendar day (YYYY-MM-DD); a message is active only on that day. pilotctl re-checks the day on read, so a message never lingers past its UTC day.
    • -
    • One per day: keep at most one entry per day. If several share a day, the daemon takes the first non-blank one.
    • -
    • Forward-dating: entries dated in the future are ignored until their day arrives.
    • -
    • Fail-safe: non-2xx responses and parse errors are non-fatal; the daemon keeps its last good mirror. An unknown schema_version is rejected rather than mis-parsed.
    • +
    • 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.
    • +
    • Self-clearing: when the active message is withdrawn, the banner disappears on its own within one poll interval — no action needed on your machine.
    • +
    • 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.

    Related