author
.uverse.addressauthor is the only address allowed to write. Deliberately a constant rather than transferable ownership: this is one person's blog, and the same address is the only one the chain lets redeploy the realm anyway, so a second owner could publish posts it could never restore after a wipe.
Value
<gnolang.StringValue>
realmPath
untyped stringrealmPath is this realm's own path, used to build the links Render emits.
Value
"gno.land/r/moul/blog"
maxSlugLen
untyped bigintmaxSlugLen bounds a slug so a permalink stays typable and a key stays cheap.
Value
(64 <untyped> bigint)
excerptLen
untyped bigintexcerptLen is how much of a post body the index shows.
Value
(200 <untyped> bigint)
introKey
untyped stringintroKey is the manifest key of the index header. It starts with '!', which validSlug rejects, so it can never collide with a post.
Value
"!intro"
introSlug
untyped stringintroSlug is refused as a post slug. Not because this realm needs it: the intro lives in its own variable. It is refused so that the local half stays representable, where a post is \<slug>.md and the header is intro.md. A post published at this slug by a hand-written transaction would have no file it could live in, and tools/gnoblog would report it as an extra forever.
Value
"intro"
post
typeValue
blog.post
posts
*v0.Tree// slug -> \*post
- OID
- 0214cf…221c:58
posts details
order
*v0.Tree// "\<date>/\<slug>" -> \*post
- OID
- 0214cf…221c:61
order details
intro
string// markdown shown above the index
Value
"# moul's blog\n\nNotes on gno.land, from someone building on it. Short, specific, mostly about\nthings I made for myself and then found out other people wanted too.\n\nNothing here is official. It is one person's working notes."
rev
int// total writes accepted, bumped by every mutation
Value
5
assertAuthor
func()- OID
- 0214cf…221c:65
assertAuthor details
validSlug
func(slug string) boolvalidSlug reports whether slug is a legal permalink: 1..maxSlugLen bytes of \[a-z0-9.\_-]. The character set excludes ':' and '/' so a slug can never break out of its own render path, and excludes uppercase so a post has exactly one URL.
- OID
- 0214cf…221c:67
validSlug details
validDate
func(date string) boolvalidDate reports whether date is exactly YYYY-MM-DD. The format is not cosmetic: \`order\` relies on lexical order being chronological order, so a date of any other shape would silently sort into the wrong place.
- OID
- 0214cf…221c:68
validDate details
orderKey
func(date string, slug string) stringorderKey is the key a post takes in \`order\`. The slug is appended so two posts published on the same day do not collide.
- OID
- 0214cf…221c:69
orderKey details
splitTags
func(tags string) []stringsplitTags normalizes a comma-separated tag list: trimmed, lowercased, deduplicated, empties dropped. It returns the canonical string that gets stored, so the hash a client computes locally matches the one on chain.
- OID
- 0214cf…221c:70
splitTags details
joinTags
func(tags []string) string- OID
- 0214cf…221c:71
joinTags details
record
func(title string, date string, tags string, body string) stringrecord is the canonical serialization a post hashes to. Both this realm and tools/gnoblog build it the same way, which is what lets one Manifest() read decide whether a local file needs a transaction at all.
- OID
- 0214cf…221c:72
record details
hashOf
func(s string) string- OID
- 0214cf…221c:73
hashOf details
Set
func(slug string, title string, date string, tags string, body string)Set creates or replaces the post at slug. This is the ordinary publish: one post, one transaction, idempotent. Changing the date moves the post in the index, so the old order entry is removed before the new one is written.
- OID
- 0214cf…221c:74
Set details
Append
func(slug string, body string)Append adds to the end of a post's body. It exists for a body too large to fit in one transaction: Set the first chunk, Append the rest. An ordinary edit uses Set, which is idempotent and therefore safe to retry.
- OID
- 0214cf…221c:75
Append details
Delete
func(slug string)Delete removes a post and its index entry.
- OID
- 0214cf…221c:76
Delete details
SetIntro
func(body string)SetIntro replaces the markdown shown above the index. Passing "" restores the built-in header.
- OID
- 0214cf…221c:77
SetIntro details
Get
func(slug string) stringGet returns a post's raw markdown body, or "" when there is no such post.
- OID
- 0214cf…221c:78
Get details
Count
func() intCount returns the number of published posts.
- OID
- 0214cf…221c:79
Count details
Revision
func() intRevision returns the total number of writes this realm has accepted. It changes on every mutation, so a client can cheaply tell "nothing moved".
- OID
- 0214cf…221c:80
Revision details
Manifest
func() stringManifest returns one tab-separated line per post, plus one for the intro: <slug>\t<rev>\t<date>\t<len of body>\t<sha256 of the canonical record> It is the diff surface tools/gnoblog reads with a single vm/qeval: the hash covers the title, date, tags and body together, so one read says exactly which local file needs a transaction without downloading a single body. The intro is reported under the key "!intro" with an empty date. '!' is not a legal slug character, so that row can never be confused with a post.
- OID
- 0214cf…221c:81
Manifest details
linkPrefix
untyped stringlinkPrefix is what a permalink starts with. gnoweb addresses a realm's render path with ':' after the package path, so this is realmPath with the leading "gno.land" dropped and the separator appended.
Value
"/r/moul/blog:"
indexLink
untyped stringindexLink is the index, which is the realm root and therefore has NO ':'. Spelled separately rather than reusing linkPrefix, because linkPrefix with an empty path produces a trailing colon and a link nobody wants to paste.
Value
"/r/moul/blog"
defaultIntro
untyped stringdefaultIntro is the header shown before SetIntro has ever been called, i.e. right after the first deploy or after a redeploy wiped the state.
Value
"# moul's blog\n\nNotes on gno.land, from the person building on it.\n"
Render
func(path string) stringRender serves three views: "" the index, newest first "<slug>" one post "t/<tag>" every post carrying a tag Render wraps every view in the notice blocks from gno.land/r/moul/config: a warning, a changelog line or a bit of news, set either on every realm of moul's at once or on this one alone, plus a pause banner if this realm is ever held back. Both are empty by default, so an unconfigured chain renders exactly what it rendered before. The path is passed explicitly rather than using config's zero-argument helpers. Both work, but this realm already states its own path for its permalinks, and a constant cannot be wrong.
- OID
- 0214cf…221c:82
Render details
render
func(path string) string- OID
- 0214cf…221c:84
render details
header
func() string- OID
- 0214cf…221c:85
header details
renderIndex
func() string- OID
- 0214cf…221c:86
renderIndex details
renderTag
func(tag string) string- OID
- 0214cf…221c:87
renderTag details
renderPost
func(slug string) string- OID
- 0214cf…221c:88
renderPost details
entry
func(p *blog.post) stringentry is one row of a listing: linked title, byline, excerpt.
- OID
- 0214cf…221c:89
entry details
byline
func(p *blog.post) stringbyline is the date plus the post's tags as links, on one line.
- OID
- 0214cf…221c:90
byline details
hasTag
func(p *blog.post, tag string) bool- OID
- 0214cf…221c:91
hasTag details
flattenLinks
func(s string) stringflattenLinks rewrites "\[text](url)" to "text". An excerpt is plain text: ui.Excerpt escapes what it is given, so a markdown link survives into the index as its own source, backslashes and all, and the reader sees \`\[mygnoscan](https://...)\` where a sentence should be. Shipped exactly that way on 2026-09-23 and it was the first thing on the page. Dropping the URL rather than keeping it bare is deliberate: the title beside it is already a link to the post, and a second URL in a two-line preview is noise. Unbalanced brackets are left alone and escaped as before, because a half-parsed link is worse than an unparsed one.
- OID
- 0214cf…221c:92
flattenLinks details
firstLine
func(body string) stringfirstLine returns the first non-empty, non-heading line of a body, which is what an excerpt should show: a post that opens with "## Something" would otherwise preview its own heading.
- OID
- 0214cf…221c:93