Discovery and architecture — cosmicbull.net, first public version
The objective: build the first public version of cosmicbull.net as the human-facing interface to the verified Cosmic Bull ecosystem, "without creating a second source of truth", reusing the existing generated artifacts and live chain-derived information, and preserving every existing verification and provenance guarantee.
This is the §7 discovery record for that decision. It is the third in the series: DISCOVERY.md covered the catalog manifest, DISCOVERY_APIDOCS.md covered the API reference and deferred a site with a stated condition. That condition has now been met, and this record is what the deferral was waiting for.
Every measurement below was taken against live pearl-1 on 2026-09-22 and is reproducible with the commands shown. Where a claim is inherited from a search rather than measured, it says so.
1. Verdicts
| Shape | Verdict | |
|---|---|---|
| (a) | Reuse an existing ecosystem package explorer instead of building | NOT AVAILABLE — §3.1 |
| (b) | Embed / iframe gnoweb inside the site | IMPOSSIBLE — §4.1 |
| (c) | Fetch chain state from the browser at page load | REFUSED — §4.2 |
| (d) | Crawl gnoweb into a static mirror | REFUSED — §4.3 |
| (e) | A new on-chain realm backing the site | REFUSED — §4.4 |
| (f) | Commit the built HTML into the repository | REFUSED — §5.2 |
| (g) | A static site generated offline from the committed, already-verified artifacts | BUILD — §5 |
The shape of the answer is set by one measured fact and one design rule.
The measured fact is §4.1: gnoweb sends X-Frame-Options: DENY and no CORS headers at all. It cannot be embedded and it cannot be read from a browser on another origin. Whatever the site does with gnoweb, it does with hyperlinks.
The design rule is §5.1: the site is a pure function of committed artifacts and never talks to a chain. That is the whole answer to "no second source of truth" — not a policy anyone has to remember, but a property of the build.
2. What already exists, inside the project
Searched: catalog/portfolio.json, catalog/api/, catalog/*.md, docs/*.md, pearl/*.md, tools/, and all committed .gno under pearl/, p/, r/.
| Artifact | What it holds | Nature |
|---|---|---|
catalog/portfolio.json | 20 packages: path, kind, role, category, deploy tx + height + creator, gas, storage, primary_file_sha256, byte count, imports, dependencies, consumers, registry registration, limitations, record pointers | provenance fields chain-attested and proved by verify_catalog.py; summary / category / role / reusable / limitations human-authored |
catalog/api/*.md | 21 pages, 344 exported symbols — signatures, doc comments, types, methods, crossing flags | chain-derived from vm/qdoc by gen_api_docs.py |
pearl/DEPLOYMENT.md | 2,175 lines: 16 deployment sections, cost measurements, live-verification transcripts, adversarial results, closure amendments, explicit limitation sections | human-authored prose over measured facts |
catalog/applications.md, catalog/primitives.md | per-application and per-primitive narrative, audit dispositions | human-authored |
docs/*.md | architecture, security, deployment, toolchain, dependency-closure and discovery standards | human-authored |
tools/verify_catalog.py · gen_api_docs.py · verify_depclosure.py | the three gates, each with its own test suite | executable |
There is no HTML emitter anywhere in tools/ — confirmed by grep -rilE '<html|<!doctype|text/html' tools/, which returns nothing. Nothing in the project renders anything for a browser today.
.evidence/ holds the raw closure-gate runs but is gitignored, so it does not exist in a fresh clone. The site must not read it. The digests that matter are transcribed into pearl/DEPLOYMENT.md.
Classification: REUSABLE EXISTING PRIMITIVE, all of it. The site needs to author no facts whatsoever. Every column it wants already exists in a committed file. That is the finding that makes a small site possible.
3. What already exists, outside the project
3.1 Ecosystem explorers and registries
| Hit | What it is | Classification |
|---|---|---|
gnoweb on pearl — https://pearl.testnets.gno.land | The chain's own web interface. Serves all 20 of our packages today: rendered output, source browser, $help call builder, state explorer | REUSABLE EXISTING PRIMITIVE — link to it, never rebuild it |
| GnoScan — gnoscan.io | Block explorer. Network selector offers mainnet, staging and custom only. Pearl is not among them | NOT RELEVANT (wrong network, and a block explorer is not a package portfolio) |
| gnoverse/mygnoscan | Realm inspector — source, imports, dependents, callers, dependency graph. The most conceptually adjacent project found | RELATED IMPLEMENTATION — a dynamic service needing a tx-indexer and a database; whether pearl is configured was not verified |
moul/gno-contracts gnocontracts preview | Crawls a locally-booted gnodev into a static tree. Live at moul.github.io/gno-contracts-previews | RELATED IMPLEMENTATION — see §4.3 |
gnolang/gno PR #6194 misc/gnopreview | The upstream version of the same crawler, for per-PR previews. OPEN, unmerged | RELATED IMPLEMENTATION — not depend-able |
| moul/gnopm | Workspace version manager, not a public registry | NOT RELEVANT |
| gnoverse/gno-explorer | Aggregate counts; last push 2026-03-19, README still create-next-app boilerplate | NOT RELEVANT |
No public package-registry website for gno.land was found — nothing analogous to npmjs.com or pkg.go.dev. The curated gnoverse/awesome-gno list was read in full: its Tools section names gnodev, the debugger, GnoScan, tx-indexer, Supernova, editor plugins, gnopls, gno-mcp, gnobro and gnovanity, and contains no static site generator, no documentation-site tooling and no portfolio template.
No relevant existing implementation was found in the searched sources for the thing the site is actually for: presenting deployment records, transaction hashes, byte-match verification, audit dispositions and dependency-closure verdicts alongside the packages they describe. Every tool above shows what is deployed. None shows what was proved about it.
3.2 Limits of this search — stated, because they bound the verdict
gh search reposmatches a quoted multi-word string as a phrase, so several of the zero-result queries are partly an artifact of the query form. Topic enumeration and full org listings were used to compensate; a differently-named tool could still have been missed.gh search codeindexes only default branches of public repositories.- GitLab, Codeberg and non-English sources were not searched.
- Whether
mygnoscanhas pearl configured was not verified. - The ecosystem leg of this search was delegated and its negative results are inherited, not personally re-run. The pearl measurements in §4 were not delegated — every one was re-run first-hand before being relied on.
4. The four refusals and the one impossibility
4.1 Embedding gnoweb — IMPOSSIBLE, measured
curl -sS -o /dev/null -D - https://pearl.testnets.gno.land/r/<ns>/service_registry \
| grep -iE '^(x-frame-options|content-security-policy|referrer-policy)'
returns
content-security-policy: default-src 'self'; script-src 'self' https://sa.gno.services; ... form-action 'self'
referrer-policy: no-referrer
x-frame-options: DENY
DENY is unconditional — not SAMEORIGIN. No origin can iframe any gnoweb page. This is not a limitation to work around; it is the correct answer, and it settles the question of how the site relates to gnoweb: by hyperlink, and only by hyperlink.
4.2 Fetching chain data from the browser — REFUSED
Two facts, both measured:
curl -sS -o /dev/null -D - -H 'Origin: https://cosmicbull.net' \
'https://pearl.testnets.gno.land/r/<ns>/service_registry$source' | grep -ic '^access-control'
# -> 0
curl -sS -o /dev/null -D - -H 'Origin: https://cosmicbull.net' \
'https://rpc.pearl.testnets.gno.land:443/status' | grep -i '^access-control'
# -> access-control-allow-origin: *
So gnoweb is unreadable from a browser on another origin, and the RPC node is readable. A live-fetching site is therefore technically possible against the RPC.
It is refused anyway, and the reason is the objective's own constraint rather than a technical one. A page that queries the chain at load time can display a value that no gate ever checked. The portfolio's provenance fields are trustworthy precisely because verify_catalog.py proved them; a number fetched in the browser three months from now carries none of that and is indistinguishable on screen from one that does. That is exactly a second source of truth — the same fact, arriving by a second path, with no gate on it.
The live view already exists and is authoritative: it is gnoweb, one hyperlink away. Reproducing it in a weaker form on our own page would trade a verified claim for an unverified one and call it freshness.
There is a second reason, smaller but real: that access-control-allow-origin: * is infrastructure configuration, not a documented API contract. Where it is set — node or edge — could not be determined. Building a page that goes blank when someone changes a proxy header is a poor trade for data we already hold.
4.3 Crawling gnoweb into a static mirror — REFUSED
The precedent exists and works: gnocontracts preview and the unmerged upstream misc/gnopreview both crawl gnoweb into a self-contained static tree, and both have already paid for the hard parts — the render-argument page explosion, and noindex on pages that are near-duplicates of real gno.land pages.
It is refused here for three reasons:
- It is the second-source-of-truth problem in its purest form. A crawled mirror is a copy of gnoweb's output that begins drifting the instant it is written, looks exactly like the real thing, and has no gate that can detect the drift.
- It inverts the value. Those crawlers exist because their target chain is ephemeral — a
gnodevthat will not exist tomorrow. Ours is a live public testnet with a hosted gnoweb. The original is available; mirroring it is strictly worse than linking to it. - The upstream tool is unmerged and its input is a git diff, not a list of deployed paths. It could not be used as-is regardless.
What is adopted from that prior art is its URL-slugging discipline: no output path may contain $, : or &, because a URL tolerates them and a static host may not. The site's own URLs are plain directory paths for the same reason.
4.4 A new on-chain component — REFUSED
The objective says "Do not create a new on-chain registry or duplicate existing Gno infrastructure unless discovery establishes a genuine need."
Discovery establishes the opposite. The site is read-only over artifacts that already exist. It has no state, no users, no writes and no authority. There is nothing for a realm to hold.
DISCOVERY.md §5 already refused a catalog realm on four grounds, and DISCOVERY_APIDOCS.md §6 added a fifth. All five still apply. A sixth is specific to this objective: our service_registry is already live and already carries the four primitives' registrations, and portfolio.json already records them under registry — a fact verify_catalog.py checks as C11/C12. A site realm would be a third copy of a pointer that is already recorded twice and checked once.
Nothing is deployed by this objective. No bytes, no transactions, no registrations. §7a's Gate B does not apply, because there is no deploy.
5. What is built, and why it is the smallest thing
5.1 The central property: the build is offline and hermetic
tools/gen_site.py reads exactly three kinds of input, all committed:
catalog/portfolio.json -> every structured fact (provenance, deps, limitations)
catalog/api/*.md -> the chain-derived API reference
the committed *.md records -> deployment, audit, architecture, discovery prose
and writes site/. It opens no socket. It imports no networking module, and a test asserts that.
This is the architecture, not an implementation detail. It means:
- The site cannot show a fact that no gate has checked, because it has no way to obtain one.
- Freshness is the upstream generators' job, where the gates already are:
gen_api_docs.pyregenerates from the chain and--checkdetects drift;verify_catalog.pyproves the manifest against the chain;verify_depclosure.pyproves the dependency closure. The site inherits their verdicts and adds no new claim of its own. - Rebuilding is deterministic and reproducible from a fresh clone with no network, no keys and no chain.
The provenance guarantees are not merely "preserved". They are preserved structurally — the site has no path by which it could weaken them.
5.2 site/ is a build output, not a committed artifact — and why that differs from catalog/api/
catalog/api/ is committed, and checked for drift. Doing the same for site/ was considered and refused, because the two cases are not alike:
catalog/api/is generated from the chain, which is not in the repository. Committing it adds a genuinely new artifact — the API is readable offline, in a diff, and at a past commit. The--checkgate is what makes that copy safe.site/is generated from files already in the repository. Committing it would add a second rendering ofdocs/SECURITY.mdand everything else, carrying no information the source does not already carry.
So site/ is gitignored. The generator, its templates, its tests and its documentation are committed; the output is one command away. This removes the drift surface entirely rather than policing it — the smaller and stronger move.
The cost is that a production host must run one build command. That is a real cost and it is accepted; hosting is a separately-authorized step that has not been taken.
5.3 The markdown renderer is bounded by measurement, not by hope
Rendering the committed records means implementing markdown, which is a notorious place for bugs to live. The scope was therefore measured across all 39 committed .md files rather than assumed:
| Construct | Occurrences |
|---|---|
fenced code (bare / go / sh) | 471 / 395 / 14 |
| table rows | 844 |
| ATX headings (h1–h4 only) | 816 |
| unordered list (depth 0 / depth 1) | 543 / 32 |
| blockquote | 177 |
| thematic break | 129 |
| ordered list | 110 |
| indented code | 29 |
| inline code / bold / link / emphasis | 4,022 / 1,303 / 317 / 253 |
backslash escapes (\_ \[ \* \ <`) | 104 |
HTML comments (the GENERATED banner) | 21 |
| raw inline or block HTML | 0 |
That is the entire grammar the renderer must handle, and it is small. The one apparent <pkgpath> HTML tag in catalog/api/cointest2.md is a backslash escape emitted by vm/qdoc, not a tag.
Critically, this is enforced rather than recorded: the test suite re-derives that inventory from every committed .md on every run and fails on a construct the renderer does not handle. If a future record introduces a footnote or a nested blockquote, the tests break before the site silently renders it as literal text.
5.4 The one thing the site adds that nothing else has
For every package, the page prints the command that lets the reader check the central provenance claim themselves:
curl -sS 'https://pearl.testnets.gno.land/p/<ns>/feeledger$download&file=feeledger.gno' \
| shasum -a 256
# 8723 bytes, sha256 bb2e1058973d...
$download returns the deployed bytes verbatim. This was verified for all twenty packages: the fetched bytes' sha256 and length equal the primary_file_sha256 and primary_file_bytes recorded in portfolio.json, 20 matching, 0 mismatching.
That is the difference between a site that asserts its packages are verified and one that hands the visitor the means to confirm it against the chain in a single command, without trusting us at all. It costs no infrastructure — it is a curl line on a static page.
One operational note, learned by measurement: pearl's edge returns HTTP 403 to Python's default urllib User-Agent while serving curl normally. The commands printed on the site use curl for that reason.
6. gnoweb views — verified, including two that look real and are not
Measured on /r/<ns>/service_registry:
| Suffix | Status | Bytes | Verdict |
|---|---|---|---|
| (bare) | 200 | 50,832 | real — Render() |
$source | 200 | 122,730 | real — source browser |
$source&file=<f> | 200 | 287,774 | real — deep-links one file |
$help | 200 | 135,799 | real — godoc + maketx builder |
$help&func=<Name> | 200 | 56,085 | real — filters to one function |
$download&file=<f> | 200 | 19,781 | real — raw deployed bytes |
?state | 200 | 51,124 | real — state explorer |
$info | 200 | 50,837 | NOT A VIEW |
$funcs | 200 | 50,838 | NOT A VIEW |
$info and $funcs return HTTP 200 and within six bytes of the bare render page, because gnoweb dispatches on known webquery keys and silently falls through to the render view for anything else. A 200 is not evidence that a view exists. The site links none of them.
$help&func=<Name> is the most useful of these for the objective's "learn how to interact with it": it filters the call builder to one function — 56 KB against 135 KB for the unfiltered page. The site deep-links it from every exported function of every realm. It fails open on an unknown name, so the names are taken from the chain-derived API reference rather than typed.
7. Page structure
Six page kinds, each with a single job:
| Path | Job | Every fact from |
|---|---|---|
/ | What this is; the verification model in one screen; the counts | portfolio.json |
/packages/ | The full inventory, filterable, with kind, role, category, size, deps | portfolio.json |
/packages/<name>/ | Identity · provenance + the check-it-yourself command · API · dependencies · limitations · records · live links | portfolio.json + catalog/api/<name>.md |
/verification/ | What is proved, by which tool, and what is not covered | portfolio.json + the records |
/records/<doc>/ | The committed records, rendered | the .md files |
/start/ | How to import a primitive and how to call a realm | portfolio.json + the API reference |
Curated prose is labelled as curated wherever it appears, matching the manifest's own about field, which already says summary, category, role, reusable and limitations are human-authored and not chain-attested. A site that renders a chain-attested tx hash and a human-written summary in the same visual style would erase the distinction the whole project is built on.
8. Limits of this record
- The ecosystem search in §3 is delegated and inherited, and its negatives are bounded by §3.2. The pearl measurements in §4, §5.4 and §6 are first-hand.
- The
$downloadbyte-match in §5.4 proves the primary file matches. A package also holdsgnomod.toml, which always differs from the committed copy by the chain-appended[addpkg]stanza — that is expected, recorded indocs/TOOLCHAIN.md, and is why the manifest hashes the primary file. - The gnoweb suffix table in §6 is empirical, derived from status codes and response sizes against one realm on one chain on one day. It was not read from gnoweb's route table. A size-based verdict is weaker than reading the dispatcher; it is strong enough to decide which URLs to link and not strong enough to claim the list is exhaustive.
- PR #6191 remains open and unacted-on. If it merges as drafted, every gnoweb page the site links to acquires a "community realm" notice, because our packages sit under the address namespace rather than a registered name. This was reported after the previous objective and is unchanged. Registering a name is a chain write against a system realm affecting all twenty live packages at once; it is outside this objective's scope (§10) and remains a decision for a human.
- The site is not deployed. No DNS, no host, no production anything. It runs on localhost only, which is what was authorized.
9. Amendment, 2026-09-22 — the public-exposure hardening pass
Section 8's last limit said the site runs on localhost only. Before any future hosting, a public-exposure audit of the built output was performed, and the build hardened so the site is safe to publish without access to the project repository — which is private, a fact the first version's build did not account for: it pinned 152 GitHub links into that repository, every one a 404 for a public visitor and every one naming the private path.
Every rendered page was scanned (not assumed) for repository URLs, emails, machine paths, credential shapes, loopback URLs, endpoints and addresses. Findings, by category:
| Exposure | Category | Disposition |
|---|---|---|
Chain identifiers: pkgpaths, tx hashes, heights, digests, g1… addresses, gnoweb/RPC links | public chain data | kept — this is the site's substance |
Links to the five upstream application repositories (fee_split, timelock_guardian, upgrade_registry, permission_registry, service_registry) | intentionally public — verified "private": false via the GitHub API on 2026-09-22 | kept — authoritative source for the five pipeline applications |
github.com/gnolang/gno quoted as the GNOROOT Go module path | public upstream | kept |
| 152 generated links into the private project repository (footer, per-package source links, record headers, the verification tools table, record cross-links to non-record files) | private/repository-dependent | removed — replaced by unlinked file paths pinned to the build commit; sitemd renders a withheld link as its bare label |
The private repository's path in two lines of pearl/DEPLOYMENT.md prose | private/repository-dependent | redacted at render time to a visible marker, counted and disclosed on the affected page; the committed record is unchanged |
CLAUDE.md and docs/CONTRIBUTING.md rendered as records | sensitive operational — agent operating rules, authorship policy, and the only email addresses (personal and noreply) in any committed Markdown | withheld from the record set entirely |
Stale pages surviving in site/ from earlier builds | unnecessary exposure | pruned — a rebuild now converges the directory to exactly its own output, and refuses (exit 2) on a foreign file it never wrote |
What enforces it, rather than remembers it:
audit()S5 allows external links per-repository, not per-host — a GitHub link outside the five public repositories fails the build.audit()S8 scans every page's text for the private repository path, email-shaped strings, local home-directory paths (the macOS and Linux forms), credential-shaped strings (GitHub token prefixes,AKIA,xox*-,sk-, private-key blocks) and localhost/loopback/file URLs.- The test suite gained a public-exposure section (X1–X10) over the real build, with fixture-faithfulness in the preflight (the inputs really do contain the private path and the emails, so the withholding is exercised, not vacuous) and a sabotage case per S8 class.
Stated limits: S8 checks are shape checks — a secret that reads as prose has no greppable form; the real defense stays upstream (key material never enters the repository, CLAUDE.md §1). The five-repository allowlist encodes their visibility as measured on 2026-09-22; if one is ever made private, the allowlist is wrong until edited, and nothing detects that automatically. The repository remains private and the site remains undeployed; neither changed in this pass.
10. Amendment, 2026-09-22 — the site becomes the ecosystem's public identity
Same day, third pass, operator-directed: evolve the verified site into the public home of the Cosmic Bull ecosystem, without rebuilding it and without weakening §5's architecture. The build remains offline and hermetic; every new page is generated from the same committed inputs; nothing was retyped.
What changed, and what deliberately did not:
- Identity. The homepage now leads with the Cosmic Bull identity — "We Are All Powered By The Sun. 🌞", the Gno Application Factory, and the factory lifecycle (Discover → Compose → Build → Test → Verify → Deploy) — and "check, not just trust" follows it as a section rather than opening the page. The brand mark is 🐂 Cosmic Bull; the sun stays a homepage motif, asserted distinct from the brand by test. One dark cosmic theme with a sun-gold accent replaces the dual light/dark palette.
- Information architecture. Navigation is Overview · Applications · Packages · Get started · Verification · SHAMS, with the community icons (X, GitHub profile, Discord, Telegram) as a separate right-hand group. Applications (the 14 complete realms, plus the 2 validation helpers stated as such) and Packages (the 4 primitives, each showing which live applications compose it, from the manifest's
consumed_by) are separate indexes built from the same rows. Records moved under Verification — still fully rendered, one click deeper. - The community URLs are the operator's, verbatim, and were verified publicly reachable before linking (all four return 200 unauthenticated; the Discord invite resolves to the Cosmic Bull server; the GitHub link is the public profile, allowed by audit S5 as an exact URL so it cannot wave the private repository through).
- SHAMS exists as one honest future-facing page: a planned community and economic layer that does not exist yet — no token, nothing launched, nothing to acquire. Tests enforce the disclaimers on the page and the absence of sale and distribution language anywhere on the site (the banned vocabulary is enumerated in the test suite, deliberately not repeated here — a rendered record quoting a banned word would trip the guard that bans it). The operator's nav sketch showed a faucet/distribution entry; it was not implemented — a contribution faucet is explicitly out of scope and a nav entry would imply it exists.
- The AI-branding constraint (operator-directed): the site's own voice carries no AI/agent framing — banned phrases plus standalone "AI"/"agents" are asserted at zero over every authored page. The rendered records are the committed engineering record, shown verbatim under Verification; the constraint governs what the generator writes, not what the record says.
- Not done, by design: no token functionality of any kind, no deploy, no DNS, no visibility change, no new on-chain anything, no second source of truth. Suite grew 82 → 125 cases; API (36) and catalog (89, live pearl-1) suites re-run green.
11. Amendment, 2026-09-22 — brand order and the Airdrop / Faucet page
Operator-directed refinement, fourth pass, same constraints. §10 recorded that a faucet/distribution nav entry was not implemented; the operator has now explicitly directed one, informational only, so this amendment supersedes that line rather than rewriting it.
- Brand order: the mark is now Cosmic Bull 🐂 — the bull trails the name. The test asserts the order, not merely the presence.
- Navigation: the SHAMS entry is replaced by Airdrop / Faucet, routing to
/airdrop/— the public-facing home of the future contribution/distribution concept. The old/shams/page is gone (the §9 prune removed it from the output on the next build, as designed). - The page leads constructively — contributions (code, documentation, integrations, testing, bug reports, research, education) are valuable on their own terms, in the order software → ecosystem → contribution → distribution — with the not-live status unmistakable but not dominant: PLANNED / NOT LIVE badges at the top and one concise "Not live yet" inventory (no SHAMS token, no live airdrop, no live faucet, no claim mechanism, no distribution, nothing to purchase or trade). SHAMS keeps a concise future-framed section on that page and stays out of the top navigation. Nothing was implemented: no token, faucet, claim, wallet, or distribution functionality of any kind, and no claims about price, value, dates, supply, amounts or eligibility — the last five are now negative test assertions on the page itself.
- Guard change, stated: "airdrop" and "faucet" left the banned site-wide vocabulary — they are now the legitimate name of this one concept's page and nav entry. What replaced that protection is stronger where it matters: required not-live disclosures on the page, a no-promise ban on the page, and the unchanged site-wide ban on sale vocabulary. Suite: 125 → 138 cases.
12. Amendment, 2026-09-22 — the $SHAMS ticker and the minimal footer
Operator-directed, fifth pass. Two presentation rulings; no mechanism changed.
- The official ticker is
$SHAMS. Every reference to the future token/economic layer in the site's own voice now writes the dollar sign — the airdrop page's section heading, its not-live inventory, and the homepage's ecosystem section. Enforced by a naming test on the authored voice (bareSHAMSfollowed by token/layer/distribution is a failure); the rendered records are the committed engineering record, predate the ruling, and stay verbatim — this amendment is itself the example, since §10–§11 above spell the name as it was spelled then. The bare name remains legitimate only for the sun concept itself, and the page's wording now reads "named for the sun, the same idea that anchors the Cosmic Bull homepage". Page order is unchanged: participation concept, status, not-live inventory, the future-layer section at the bottom. - The footer is now minimal public chrome: the brand line (Cosmic Bull 🐂 · Gno Application Factory), the six site links, the four community icons, and one line stating the deployments shown are on the pearl-1 testnet. The long-form footer paragraph about how the site is generated, and the line naming the build tool and the repository's privacy, are gone from every page — presentation only. The verification model itself is untouched and remains fully explained on the Verification page; tests assert the old footer copy is absent site-wide and the new elements are present. Suite: 138 → 147 cases.
13. Amendment, 2026-09-22 — the Sun line removed from the homepage
Operator-directed, sixth pass: the homepage no longer opens with the Sun line that §10 introduced; the hero now leads directly with Gno Application Factory. The sun-gold accent and hero glow remain the visual motif, and the airdrop page's $SHAMS section now says simply "named for the sun". Test B1 inverted to pin the removal on the homepage (the historical amendments above still quote the line, which is why the assertion is homepage-scoped, not site-wide); B4 keeps the sun emoji off the page while still asserting the bull's position in the brand.
14. Amendment, 2026-09-22 — the bull image replaces the emoji
Operator-directed, seventh pass: the brand mark in the header and the footer is now the operator's Cosmic Bull artwork (a 400×400 JPEG, committed at tools/site_assets/CosmicBull.jpg, sha256 80f5b8e5d2fe…), rendered as a small circular image after the name; the bull emoji is gone from the site chrome. The image is copied verbatim into the build as bull.jpg — a local asset, deliberately: the same artwork exists on IPFS (CID bafkreiea6w4olux6nyaapncwkge42aot7mnf4rtgypr4qjzxnobcbkl33y), but a brand mark hotlinked through a public gateway inherits that gateway's availability, and two major gateways answered HTTP 429 during the upload's own verification. A missing asset is a build refusal, the copy is byte-checked by test (POS4), and B4/B4b now assert the image — not the emoji — trails the name in both header and footer. Suite: 148 → 150 cases.
15. Amendment, 2026-09-23 — the Build-with journey and the published artifacts
Operator-authorized after a third-party-reuse investigation whose finding was that the external developer journey fails not for missing material but for unpublished material: the manifest and API reference answer every discovery question, privately. Two additions, both presentation over existing gate-checked data:
- Machine-readable artifacts ship with the site.
catalog/portfolio.json→site/data/portfolio.jsonandcatalog/api/*.md→site/api/, verbatim byte copies of the authoritative committed files — the same onesverify_catalog.pyproves against the chain andgen_api_docs.py --checkholds tovm/qdoc. No second source of truth: byte-identity is asserted by test (AR1–AR3), stale copies are pruned on rebuild (AR4), and a new audit check S9 runs the private/operational leak scan over the artifact bytes before they ship (S9a/S9b pin it per class). - A "Build with Cosmic Bull" page (
/build/, in the primary nav): the eight-step third-party journey — objective, discovery (with the machine-readable pointers), when/when-not (from the manifest's own limitations), exact import lines, the full-API pages, the existingcoindemo/duebook_demo/permbook_demorealms designated as the canonical minimal consumers (no new example application was built — the investigation found the live demos already are the minimal patterns), the$download | shasumprovenance check with the recorded digest, and a composition sketch using the primitives' real call shapes. Content is drawn from the manifest; the sketch is the one authored block and it defers to the live demos as the authority.
The brand guard did its job during implementation: the first draft of the page said "for tooling and agents" and B7b refused the build; the shipped copy says "for tooling". Suite: 161 → 186 cases. The site remains localhost-only; deployment is a separate decision.
16. Amendment, 2026-09-23 — Get started merged into Build
Operator-directed navigation cleanup: the nav is now Overview · Applications · Packages · Build · Verification · Airdrop / Faucet. The former Get started page's one piece of unique content — the live-realm call guidance with gnoweb's generated gnokey maketx call builder — is now step 7 of the Build journey (the import and verify-before-integrating guidance already lived there); the start/ route is removed from the build and pruned from the output, with every internal link retargeted (nothing external links it: the site has never been deployed). All audited Build content is unchanged. Tests: B5 reordered; BW9–BW12 pin exactly-one-Build, the merged call guidance, and the route's absence. Suite: 187 → 193.
17. Amendment, 2026-09-23 — production hosting: Cloudflare Pages, private source
Operator-authorized end-to-end. The architecture, replacing the torn-down GitHub Pages deployment of the same day:
private repo → gen_site.py build → 194-test suite + exposure audit
→ wrangler pages deploy (Direct Upload) → Cloudflare Pages
→ cosmicbull.net (zone on Cloudflare, registrar GoDaddy)
Chosen because Direct Upload gives the cleanest possible privacy model — no git host is involved in hosting; the host receives only the built, audited artifact — and because Cloudflare's Universal SSL removes the certificate-queue failure mode that stalled the GitHub deployment (its Let's Encrypt cert never issued in over an hour with every health check green). The zone's nameservers moved to Cloudflare (the apex needs CNAME flattening GoDaddy DNS cannot do); GoDaddy remains registrar. The _dmarc TXT and _domainconnect CNAME records were carried over verbatim; the pre-migration GoDaddy zone is backed up.
Pipeline changes: deploy_site.sh's publish half is a wrangler Direct Upload gated on the full suite (credentials are runtime-only environment variables the script refuses to run without); the GitHub-Pages markers (CNAME, .nojekyll) are gone — the foreign-file guard flagged their stale copies during removal, as designed — and the one artifact the new host reads, _redirects, is emitted by the build (POS3c) carrying the canonical www → apex 301, so the deployed tree remains a pure function of the generator. HTTP→HTTPS is zone-level (Always Use HTTPS). Both hostnames are attached to the Pages project; DNS is two proxied CNAMEs to cosmicbull.pages.dev.
Verified at deploy time: all 86 servable files byte-identical on the Pages origin. Production verification on cosmicbull.net follows nameserver delegation and is recorded with the deployment.