Cosmic Bull — Gno infrastructure catalog (pearl-1)
Namespace: g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3 (agent key). All packages authored/tested with the chain/pearl toolchain (c4c72fd); every deployment byte-verified against vm/qfile.
This file is the live-infrastructure catalog: what is deployed, what it composes, and where it diverges from local sources. The transaction -level evidence is in DEPLOYMENT.md. The narrative records live alongside it:
| For | See |
|---|---|
| The five GitHub-pipeline applications, in full | catalog/applications.md |
| The four /p/ primitives and the realms that consume them | catalog/primitives.md |
| Interrealm architecture and composition | docs/ARCHITECTURE.md |
| The discovery gate (now permanent) | docs/DISCOVERY_AND_REUSE.md |
| Audit method and accumulated security lessons | docs/SECURITY.md |
| Deployment protocol and byte custody | docs/DEPLOYMENT.md |
| What each benchmark proved | benchmarks/README.md |
/p/ reusable primitives
| Package | Deployed | Purpose | State |
|---|---|---|---|
| p/.../feeledger | tx feb4b559, h 572441 | Per-account balance ledger + explicit bps fee pot; error-pure, overflow-checked; conservation contract held == UsersTotal + FeesAccrued + surplus | stateful object owned by consumer (keep the *Ledger in an UNEXPORTED var) |
| p/.../coinio | tx 31b53d1b, h 583585 | Chain-facing coin plumbing: Receive (receipt-guard trio), Payout (RealmSend from own address), Sweep (reserve-protected single-denom surplus), HeldAt | pure/stateless; panic-only |
| p/.../duebook | tx 5cd6de9e, h 609571 | Deferred-action scheduling: Schedule/Claim/Cancel/Expire over a consumer-owned *Book; Claim consumes before returning, so replay is structurally impossible; imports no chain API | stateful object owned by consumer (keep the *Book in an UNEXPORTED var) |
| p/.../permbook | tx 87866042, h 612427 | Named permissions over a consumer-owned *Book: Grant/Revoke/RevokeAll/DropPermission (admin only) and Has/HasAny/Permissions (O(log P + log H)); per-Book bounds chosen at construction; two-step admin handover; imports no chain API | stateful object owned by consumer (keep the *Book in an UNEXPORTED var) |
permbook consumer contract (the parts the package cannot enforce)
- Do not export the
*Book— and never return a*groups.Groupor*groups.Roleout of it. A mutable handle IS the right to grant and revoke (the feeledger PrivateLedger rule, third restatement). - Derive the principal from
cur.Previous().Address(), never from a parameter. permbook authenticates the address it is handed; it cannot check where that address came from. Hasis a POINT-IN-TIME answer. Gate at the moment of the effect, in the same transaction. A cachedHasreopens the window a revoke exists to close.- Two authorities, kept separate: administering a Book (
Grant,Revoke,NominateAdmin, …) is admin-only; everything a holder can do is whatever you gate onHas. Holding a permission confers no power over the Book. Proven live in §14: the admin's own firstBumpwas refused because the admin held nobump. - Order the stray-send guard BEFORE the permission check, so a refusal never happens after taking custody of coins. Observable from the error text — §14's coin probe aborts with the coin message, not
permission denied. - Choose limits for the application, not for comfort. The ceilings (256 permissions / 10000 holders / 64-char names) are a safety net, not a recommendation;
Permissions(addr)andHasAnyare O(P log H).
Revoking the last holder PRUNES the permission — an empty bucket is removed, not retained — so a Book's storage tracks live grants, not the history of grants. The effect of an authorized call survives the revocation of the authority that permitted it; the authority does not. Names are [a-z0-9_] only, so a permission name can never carry markdown or address-shaped content into a consumer's Render.
duebook consumer contract (the parts the package cannot enforce)
- SUPPLY A REAL CLOCK. duebook cannot verify
nowcame fromruntime.ChainHeight(). A realm that lets a caller choosenowhas no delay at all. The obligation most commonly got wrong. - One clock unit per Book — never mix heights and seconds.
- Do not export the
*Book(the feeledger PrivateLedger rule: a mutable handle is the right to schedule, cancel and claim). - Authorize the actor: duebook authenticates nothing but ownership on
Cancel. Derive the caller fromcur.Previous().Address(). - Act after a successful
Claim, IN THE SAME TRANSACTION. Storing the authorization to act on later reopens the replay window.
Claimability is the half-open interval [DueAt, ExpiresAt); ttl == 0 never expires. Consumed deferrals are removed, not archived — the never-reused ID counter, not a tombstone, is what prevents replay — so storage is bounded by maxOpen forever.
coinio consumer contract (the parts the package cannot enforce)
- Pass 0 and your crossing entrypoint's own live
cur— the(_ int, rlm realm, ...)shape is required because /p/ cannot declare crossing functions; every mover assertsrlm.IsCurrent(). Receiveis a READ of the tx envelope, not a consumption: call it at most once per transaction and credit its result at most once.- Debit your accounting BEFORE
Payout/Sweep(CEI); panic-abort reverts debit and send together. - Authorization is yours: gate entrypoints before calling in.
- Coins can only move FROM the calling realm's own address.
feeledger consumer contract
- Keep the
*Ledgerpointer unexported (grc20 PrivateLedger rule). - Credit exactly on verified receipt; debit exactly before payout; panic on every ledger error.
- Account keys:
address.String()derived fromcur.Previous()at crossing boundaries only.
/r/ applications (deployed, immutable)
| Realm | Realm address | Composes | Live validation |
|---|---|---|---|
| r/.../vault | g1vfp5q3fku... | feeledger | 14 txs, H==U+F+S exact (DEPLOYMENT.md §1) |
| r/.../bounties | g1x8fmlk48u... | feeledger | 12 txs, H==B+U+F+S exact (§2) |
| r/.../coindemo | g1x7rljdk7a... | coinio + feeledger | 8 txs, H==U+S exact (§4) |
| r/.../grants | g10wjaak8z5... | coinio + feeledger + sanitize/v0 | benchmark app; H==G+U+F+S (§5) |
| r/.../market | g1y8nzwrv7u... | coinio + feeledger + avl/v0 + sanitize/v0 | multi-domain benchmark; H==U+F+S exact, 7 attack shapes rejected (§6) |
| r/.../cointest, cointest2 | — | (throwaway validation helpers) | §1 |
Amendment (2026-09-21): the market row was missing from this table until today. It was deployed in the 2026-09-20/21 session (tx 76293576, h 585791) and fully recorded in DEPLOYMENT.md §6 at the time — the omission was in this catalog only, not in the deployment record. Added here rather than silently, so the gap itself stays on the record.
Local vNext sources of vault/bounties are refactored onto coinio (message-text deltas only vs their deployed v1 behavior, plus the three recorded bounties deltas from the 2e20f18 audit round). Deployed v1s predate coinio and remain self-contained.
/r/ GitHub-pipeline applications (deployed, immutable)
The SillyZir GitHub → pearl-1 series. Each was ported from an existing upstream repo (never designed here), audited, remediated, pushed, re-verified from a fresh clone, and deployed byte-identical to the verified commit. Heights below were re-read from the chain on 2026-09-21; a realm's bytes are immutable after addpkg, so an unchanged [addpkg] height is proof of no redeploy.
| Realm | Realm address | Height | Layer | Record |
|---|---|---|---|---|
| r/.../fee_split | g12v97h3jex... | 596803 | Revenue & value flow | DEPLOYMENT.md §7 |
| r/.../timelock_guardian | g1w06mqv4h5... | 602213 | Security / delayed execution | §8 |
| r/.../upgrade_registry | g1g453tcmap... | 602876 | Upgrade tracking | §9 |
| r/.../permission_registry | g1x9tatmqcd... | 604615 | Access control | §10 |
| r/.../service_registry | g1hv2mj29cy... | 605546 | Discovery | §11 |
Realm addresses are AddressFromPreimage("pkgPath:" + path) — sha256 truncated to 20 bytes, bech32 "g" (gnovm/pkg/gnolang/misc.go:201). The bare path is NOT the preimage; deriving without the pkgPath: prefix silently yields a plausible-looking wrong address.
None of these five holds funds: all reject coins attached by a user call, and none imports chain/banker. All five realm addresses are exists:false / coins "0".
service_registry composes p/nt/markdown/sanitize/v0 (verified live on-chain before deployment, on-chain TableCell/InlineText matching the local GNOROOT copy the tests compiled against). It is the first application in the series to ship a committed DISCOVERY.md.
Known divergence: local pearl/r/feesplit/fee_split.gno (a90db685) does NOT match live fee_split (e2198ebb). That is the undeployed yellow-remediation sitting in the working tree — deliberate, since fee_split v1 is frozen and no fee_split_v2 has been authorized. Do not "reconcile" it by redeploying.
/r/ application-factory applications (deployed, immutable)
Built from a stated objective rather than ported from an upstream repo. Same lifecycle otherwise: discovery → architecture → implementation → audit → push → fresh-clone verification → deploy byte-identical.
| Realm | Realm address | Height | Layer | Record |
|---|---|---|---|---|
| r/.../bounty_panel | g1cxukm68nt... | 608171 | Escrowed bounties (composes coinio + feeledger) | DEPLOYMENT.md §12 |
| r/.../duebook_demo | g1u0caqzv99... | 609644 | Deferred execution (composes duebook) | §13 |
| r/.../permbook_demo | g1qtt0yrnuv... | 612731 | Permission gating (composes permbook + sanitize/v0) | §14 |
| r/.../service_market | g1fd72s0yvt... | 628080 | Service escrow marketplace (composes coinio + feeledger + sanitize/v0) | §15 |
| r/.../subscriptions | g1fpqpq59lg... | 639086 | Subscription hub, windowed renewal (composes coinio + feeledger + sanitize/v0) | §17 |
Amendment (2026-09-23): service_market (deployed 2026-09-22, §15) and subscriptions (deployed 2026-09-22, §17) were missing from this catalog until today — the third time a deploy was fully recorded in DEPLOYMENT.md while this file lagged. As before, the gap was in this catalog only, and both are added explicitly rather than silently. With bounty_panel (confirmed against its realm's Address() after deployment, §12), service_market and subscriptions make three addresses in this table confirmed against the realm itself, not merely derived; duebook_demo's and permbook_demo's remain derived-only.
Amendment (2026-09-21): bounty_panel was missing from this catalog until today — it was deployed on 2026-09-21 (tx 2c2a8dc8, h 608171) and fully recorded in DEPLOYMENT.md §12 at the time. As with the market omission above, the gap was in this catalog only, and is added explicitly rather than silently.
duebook_demo is the experiment, not a demo: a /p/ package's exactly-once-across-transactions property cannot be shown through vm/qeval (a single ephemeral evaluation discards the Book when the query returns), so a realm holding a persistent Book is the only way to attack it from real transactions. Its Publish is deliberately permissionless — the authorization is fixed at schedule time, and the one-shot guarantee must hold regardless of who sends the later tx. duebook_demo holds no funds and rejects attached coins; its address is derived, not confirmed against the realm (it exposes no Address()).
permbook_demo is the experiment for the same reason, against two claims vm/qeval cannot reach: the gate must open AND close across transactions, and a holder must have no authority over the Book. Neither is showable in one ephemeral evaluation with one caller. The live run was ordered deny → grant → allow → revoke → deny across real block boundaries, and the escalation attempts were fired from a genuinely separate key. Its init FAILS CLOSED — an invalid origin caller leaves book nil and the realm permanently inert, because nothing assigns it afterwards — which makes Ready() a deploy-time obligation, not a convenience. It was checked as the first post-deploy action and returned true. permbook_demo holds no funds and rejects attached coins; its address is derived, not confirmed against the realm.
Dependency graph (on-chain)
coindemo ─┬─> coinio ──> (stdlib only)
└─> feeledger ──> p/nt/avl/v0
bounties ──> feeledger
vault ─────> feeledger
bounty_panel ─┬─> coinio
└─> feeledger
duebook_demo ──> duebook ──> p/nt/avl/v0
permbook_demo ─┬─> permbook ──> p/nt/groups/v0 ─┬─> p/moul/addrset ──> p/nt/avl/v0
│ └─> p/nt/bptree/v0
└─> p/nt/markdown/sanitize/v0
service_market ─┬─> coinio
├─> feeledger
├─> p/nt/avl/v0
└─> p/nt/markdown/sanitize/v0
subscriptions ─┬─> coinio
├─> feeledger
├─> p/nt/avl/v0
└─> p/nt/markdown/sanitize/v0
All import edges above are the PEARL-1 ones, read from the chain.
Closure equivalence: VERIFIED 2026-09-22, re-run 2026-09-23 after the subscriptions deploy. All 19 deployed packages with committed source are proven closure-equivalent to pearl-1 — the tested and chain-side closures produce matching digests, with every dependency byte-identical in its compilation inputs. Amendment in DEPLOYMENT.md; standard and residual limits in docs/DEPENDENCY_CLOSURE.md.
Superseded note (kept, not rewritten): this section previously said the local toolchain resolves a groups/v0 importing p/moul/addrset/v0 and that the tested tree was not proven identical to the deployed one. That is what the DEFAULT (mainnet) remote serves; the pinned cache resolves p/moul/addrset, matching the chain. Reproducible with a bare gno mod download — see docs/TOOLCHAIN.md.
Residual: stdlibs execute from the node binary, not chain storage, and are NOT covered by the above. They rest on the GNOROOT pin matching the node build, which the node does not expose.
Known pearl-1 semantics (validated live, absent from local harness)
- MsgCall -send lands at the callee address before the body runs; IsUserCall is the receipt-guaranteed caller shape.
- Per-call storage deposits lock from the TX SENDER; deletion refunds credit the deleter.
- IssueCoin requires the fully-qualified "/pkgpath:sub" denom.
- auth/accounts omits realm-issued denoms (visible via banker only).
- Faucet: 10 GNOT per address per ~24h; per-address cooldown (other agent keys have independent windows).
Known limitations
- Roles are hot agent keys (testnet-only posture).
- feeledger doc does not yet spell out the unexported-pointer rule in-package (recorded for its v1 successor; deployed copy is frozen).
- coindemo's Surplus() doc comment says "Held() - UsersTotal()" while the body uses ledger.Liabilities() — provably equal in this realm (fee cap 0 pins FeesAccrued to 0 forever); left as-is to preserve byte-custody with the deployed file.
- duebook's
IterateOpenruns its callback with the consumer realm's storage authority over duebook-allocated objects (audit Y1). It is documented in-package and structurally narrowed (the iterator yieldsDeferralvalues, never pointers into state), but it has never been exercised across a realm boundary on-chain:duebook_demois the only consumer and every live call was an EOA MsgCall. Covered by local tests only. - permbook has exactly ONE consumer (
permbook_demo), so the claim that it is composable by multiple independent applications is a design argument, not a demonstrated fact — the same caveat duebook carries. Its cross-realm path is likewise unexercised: every §14 call was an EOA MsgCall. - permbook's two-step admin handover was exercised live only as a REFUSAL (a non-admin
NominateAdmin). No nomination was broadcast, soAcceptAdmin,CancelNominationand a non-emptyPendingAdmin()are covered by local tests only. Completing the handover would have given the realm's adminship to a probe key. - ~~permbook's tested dependency tree is not proven identical to its deployed one (see the dependency-graph note above).~~ Closed 2026-09-22. Proven byte-identical: digest
bc0512a6def04b62on both sides, every compiled dependency file matching. This file already carries the corrected account above; the stale bullet is struck rather than deleted. Evidence: DEPLOYMENT.md. - bounties v1 carries the pre-snapshot BountyCancelled event schema (no "amount" attr) and accepts self-realm winners; both fixed in the local vNext.
Engineering gate: mandatory discovery before building (2026-09-20)
This gate is now permanent and is specified in full — as eight mandatory steps — in docs/DISCOVERY_AND_REUSE.md. The summary below is the original statement, kept as written.
Before creating ANY new /p/ or /r/ component: enumerate the target chain (gno_packages, /p/ and /r/), inspect candidates (gno_read), search the pinned examples tree and public sources where appropriate; classify the proposal (EXACT DUPLICATE / REUSABLE EXISTING PRIMITIVE / RELATED IMPLEMENTATION / GENUINELY NEW); record searches, findings, reuse decisions, and search-confidence limits in a DISCOVERY / REUSE ANALYSIS section. Reuse first. Never claim "nobody has built this" — only "no relevant existing implementation was found in the searched sources."
Retroactive discovery analysis of the existing portfolio (2026-09-20)
Searched: full vm/qpaths enumeration of pearl-1 (190 /p/ + 434 /r/ at audit time — grown from the 85-package genesis), keyword sweep (fee|ledger|vault|escrow|bounty|tally|coin|pay|treasury|vote|board), outline inspection of the four closest neighbors, plus the pinned gnolang/gno examples tree (c4c72fd). Honest admission: this full enumeration was NOT run before vault/bounties were built (the curated genesis set and skill references were relied on); the gate now prevents that. Limitations: keyword/path-based search; no GitHub-wide sweep this round; the chain moves — re-enumerate per build.
| Ours | Closest found | Classification |
|---|---|---|
| p/feeledger | p/g1n4pl.../bazaar/fee/v1 (pure bps fee calc + seller proceeds, 2 funcs) | RELATED: overlaps only feeledger's FeeFor; feeledger's substance is the stateful balance/pot ledger with conservation contract. Had it been found pre-build, the calc could have been imported. |
| p/coinio | p/nt/treasury/v0 (Banker interface + Payment objects + history/render) | RELATED: treasury abstracts multi-banker dispatch with canonical-impl checks; coinio is the minimal guard-trio/payout/sweep contract with no state. Inspected pre-build; deliberately not reused (heavier surface, "v0 Unaudited"). |
| r/vault | r/moul/demo/vault/v2 (GRC20 time-locked vault) | RELATED: different asset (GRC20 vs native), different mechanism (time-lock vs fee-on-deposit), no fee/conservation model. |
| r/bounties | r/g16m0r7.../bountyboard (claim/submit/review workflow) | RELATED: richer workflow but takes creator/hunter as STRING PARAMETERS (designation-forgery shape) and shows no real-coin custody; ours does receipt-guarded GNOT escrow, snapshot fees, audited conservation. |
| p/tally (v1.1.0, undeployed) | r/g1zwf26.../tally (single Bump counter) | Name collision only; no relevant existing keyed-score implementation was found in the searched sources. |
| r/coindemo, cointest* | — | Validation vehicles, not products. |
Verdict of the pre-deploy duplication review: no component unnecessarily recreates existing ecosystem functionality; the two RELATED overlaps (fee calc, treasury) are documented with concrete non-reuse reasons.