Skip to content

Cosmos Config Manifest

How cross-cutting config (newsletter URL, feedback link, parent site, etc.) lives in one place per repo, gets vendored to all 5 manifests, and propagates to every consumer with zero code changes.

Sush's principle (5 May 2026)

"Whatever direction we take the newsletter is one of many — a single place to update changes should reflect everywhere."

The problem this solves

Before 5 May 2026, the newsletter URL https://guided-weekly.beehiiv.com/subscribe was hardcoded in: - Earth's homepage form (layouts/index.html) - Earth's homepage form's submit handler (separate <script> in the same file) - Shift's /wire/ form (already broken — submitting to a dead /wire/signup endpoint) - The newsletter pitch text was hardcoded too ("Cloud & AI insights, every Sunday — in 2 minutes. Free.")

When Sush wanted to evaluate switching from Beehiiv to Buttondown (or anything else), there was no single place to flip. He'd have to remember every place the URL appeared, AND keep the marketing pitch consistent across all of them.

The fix: elevate cross-planet config into the cosmos manifest itself, alongside the planets list.

The schema

Every planet's planets.json / planets.toml now has a top-level [cosmos] block:

[cosmos]
newsletter_url    = "https://guided-weekly.beehiiv.com/subscribe"
newsletter_label  = "A Guide to Cloud Newsletter"
newsletter_pitch  = "Cloud & AI insights, every Sunday — in 2 minutes. Free."
feedback_url      = "https://www.aguidetocloud.com/about/"
parent_site       = "https://www.aguidetocloud.com/"

[[planets]]
# … planets list (see Cosmos Nav Playbook)
Field Type Used by
newsletter_url string Form submit URL — opens in popup with email pre-filled
newsletter_label string Display text for the link/anchor (e.g., "A Guide to Cloud Newsletter")
newsletter_pitch string Marketing pitch shown above the signup form
feedback_url string "Send feedback" links across planets (currently still hardcoded in many places — future cleanup)
parent_site string Cross-planet "back to home" links (Brain Bar uses this for its parent_site Hugo param too)

Where it's vendored

Five files. When you change any field, change all five in the same commit:

Repo Path Format
aguidetocloud-revamp data/planets.toml TOML
aguidetocloud-revamp brainbar/data/planets.toml TOML
guided src/data/planets.json JSON
shift src/data/planets.json JSON
plainai content/planets.json JSON

Where it's consumed

Currently consuming newsletter_url + newsletter_pitch + newsletter_label

File What it does
aguidetocloud-revamp/layouts/index.html Homepage newsletter section. Renders newsletter_pitch as <p>, sets data-newsletter-url attribute on the section. Inline JS reads from the attribute, builds ${newsletter_url}?email=${email} and opens in popup
shift/src/pages/wire/index.astro The /wire/ page. Reads cosmos block in frontmatter. Form button posts to newsletter_url via JS popup. Visible label uses newsletter_label

Not yet consuming (future cleanup candidates)

The feedback_url field is currently hardcoded in many places across the four repos (e.g., https://www.aguidetocloud.com/about/ and https://www.aguidetocloud.com/feedback/ show up scattered across templates). A future session could refactor these to read from the manifest. Search:

grep -ri "aguidetocloud.com/feedback\|aguidetocloud.com/about" --include="*.html" --include="*.astro" --include="*.mjs"

Same for parent_site — Hugo Brain Bar reads it from [Params].parent_site in hugo.toml, which duplicates with the manifest. Consolidate when convenient.

How to swap newsletter platform (Beehiiv → Buttondown / Substack / etc)

This is the workflow you set out to make easy. Here's the full procedure, ordered by safety:

Pre-swap checklist

  1. Confirm new platform's URL pattern — does it accept ?email= pre-fill? If yes, drop-in replacement. If no, you'll need a small JS update to convert email → query string in the new format.
  2. Test the new URL manually — sign up with a test address, confirm the new platform handles it cleanly.
  3. Check email export — does the existing Beehiiv list export to the new platform? If you're migrating subscribers, do that first; if you're starting fresh, the URL swap is enough.

The swap (5-file edit, one commit per repo)

For each of the 5 manifest files above:

# BEFORE
newsletter_url   = "https://guided-weekly.beehiiv.com/subscribe"
newsletter_label = "A Guide to Cloud Newsletter"

# AFTER (e.g., switching to Buttondown)
newsletter_url   = "https://buttondown.email/aguidetocloud"
newsletter_label = "A Guide to Cloud Newsletter"

Commit message template:

chore(cosmos): switch newsletter URL from Beehiiv to Buttondown

Updates [cosmos].newsletter_url across all 5 planet manifests in the same
commit. No code changes — every consumer reads from manifest:
- Earth homepage form (layouts/index.html)
- Shift /wire/ form (pages/wire/index.astro)
- Future: any new newsletter signup on any planet

Old URL: https://guided-weekly.beehiiv.com/subscribe
New URL: https://buttondown.email/aguidetocloud

Subscriber migration: [link to Buttondown import job / fresh start note]

Push each repo separately. Cloudflare deploys each automatically.

Post-swap verification

  1. Hard-refresh https://www.aguidetocloud.com/ — submit a test email, confirm popup opens to the new URL
  2. Hard-refresh https://shift.aguidetocloud.com/wire/ — same test
  3. Search the deployed HTML to confirm no leftover Beehiiv URLs:
    curl -s https://www.aguidetocloud.com/ | grep -i beehiiv
    curl -s https://shift.aguidetocloud.com/wire/ | grep -i beehiiv
    # Should return nothing
    

What if the new platform needs a different request format?

Some platforms want POST instead of GET, or different field names, or auth headers. If ?email= doesn't work:

  1. Update the JS in aguidetocloud-revamp/layouts/index.html (the <script> after the newsletter section)
  2. Update the JS in shift/src/pages/wire/index.astro (the <script is:inline define:vars={{ newsletterUrl }}>)
  3. Both already read newsletter_url from the manifest, so only the request shape changes, not the URL source

Keep the URL still in the manifest — even if the request format changes, the URL itself is still one source.

Plain AI's intentional opt-out

Plain AI has [cosmos] newsletter_url in its manifest for cross-planet consistency, but Plain AI's /about/ page explicitly states:

"It's free. There's no signup, no ads, no newsletter. The point is just for it to be useful."

This is a brand promise. Don't add a newsletter form to Plain AI without re-evaluating its identity. The manifest field is there so future code (e.g., a "more from the cosmos" footer) can mention the universe-wide newsletter without breaking Plain AI's "no newsletter" stance.

What else could use the manifest pattern?

Sush asked: "now I have to think what other things we can do this way so it's efficient."

High-value candidates already identified:

🎯 Strong candidates (add when you next touch them)

Candidate Current state Why manifest
Social media URLs (YouTube, LinkedIn, Ko-fi) Hardcoded in Earth's homepage <section class="zh-who">, Shift's footer, Brain Bar's about, Plain AI's footer If you ever rebrand a handle, every site needs updating
OG default image Hardcoded in each repo's <meta property="og:image"> defaults Centralized branding refresh
Brand monograms / first letters Earth has A, Plain AI has P, etc. — hardcoded in nav/wordmark CSS Could be part of [planets] entries (would need to ensure each planet's local register can override)
Author info ("Susanth Sutheesh, Microsoft NZ") Hardcoded in JSON-LD schemas, multiple about pages, footer Author data block in cosmos manifest
Site analytics ID (GA / Plausible / etc.) Hardcoded in baseof.html where present If you switch analytics, one update

🟡 Maybe candidates (proceed cautiously)

Candidate Tension
Brand colors Each planet INTENTIONALLY has different colors (cosmos philosophy: own atmosphere). Don't centralize — would tempt visual parity drift
Featured tools Already has a manifest (toolkit_nav.toml) — separate concern from cosmos
Email contact Currently hello@aguidetocloud.com in a few places. Worth centralizing if you ever change it
Currency / pricing Already has dedicated manifests (licence_picker/plans.toml) per-tool. Don't merge into cosmos manifest — different lifecycle

🔴 Don't centralize

Things that MUST stay per-planet (cosmos philosophy: each atmosphere is its own world):

  • Fonts (Inter on Earth, Iowan on Shift, Inter on Plain AI, JetBrains Mono on Brain Bar)
  • Visual color palettes (indigo / red / cyan-purple-pink / phosphor)
  • Voice register (Sush's English on Earth, newspaper on Shift, plain-English-explainer on Plain AI, terminal on Brain Bar)
  • Animation rules (Zen restraint on Earth, tape-ticker on Shift, gradients on Plain AI)
  • Page templates (each planet picks its own structure)

The general "cosmos manifest" pattern

If you want to apply this pattern to something else, here's the template:

  1. Identify the cross-cutting concern that's currently scattered. It must:
  2. Have ONE canonical value used by multiple worlds
  3. Change rarely (otherwise centralizing isn't worth the friction of editing 5 files)
  4. Not be a visual decision (those belong to each planet)
  5. Decide where it lives in the manifest — add a new top-level block (like [cosmos]) or add fields to an existing block
  6. Update all 5 manifests in the same commit — set the field, write a short comment explaining why
  7. Update consumers to read from the manifest:
  8. Hugo: (index hugo.Data "planets").<block>.<field>
  9. Astro: import data from '../../data/planets.json' then data.<block>.<field>
  10. Plain AI build: JSON.parse(await fs.readFile(...)) then data.<block>.<field>
  11. Document the field in this doc + the Cosmos Nav Playbook under "Schema"

When NOT to use this pattern

The manifest is the right tool when: - The data is shared across multiple repos - The data is truly identical across repos (newsletter URL, feedback link) - Changing it requires updating multiple consumers

It's the WRONG tool when: - Data is per-repo only (use that repo's own data file) - Data changes frequently (the 5-file edit cost is non-trivial) - Data is a visual decision (cosmos philosophy → keep per planet) - Data has structure too complex for the format (use a dedicated file)


Document established: 5 May 2026. The day Sush said "single place to update changes — reflect everywhere" and we baked it into the architecture.