Anyone with a key can comment on any Hypermedia document, from any node, without asking the document's owner. A comment is a small signed blob. It names the document and version it is about, carries a tree of blocks like a document body, and can reply to another comment. This page explains how comments are identified, edited, threaded and found.
The comment blob
A comment is a signed blob of type Comment. Each version of a comment is a whole snapshot. An edit publishes a new blob that replaces the old one. Documents work differently: they are built from a chain of Changes.
key | meaning |
|---|---|
| the envelope every blob carries: who wrote it and when |
| the document it targets; |
| the CIDs of the document heads the author was looking at; empty means "the document at this path" |
| the CID of the first comment of the thread; present on every reply |
| the CID of the comment this one replies to; omitted when it equals |
| a list of comment blocks: a block plus its children, recursively |
| present only on an edit or a deletion: the TSID of the comment being replaced |
|
|
| deprecated and ignored; old blobs may carry it |
The daemon verifies the signature, then indexes the target as a link, the thread links, every link inside the body, and a full-text row per block. It does not check whether the signer may comment, because no such permission exists.
Identity, edits and deletion
A comment's stable identity is <author>/<tsid>. The author is the signer's principal. The TSID is a timestamped id: 10 bytes, a 48-bit millisecond timestamp followed by the first 4 bytes of the SHA-256 of the blob, encoded base58btc into 14 or 15 characters. The first version of a comment derives its TSID from its own bytes. An edit or a tombstone carries that TSID in its id field, so all versions share one identity while each has its own CID.
Among the blobs that share a TSID, the live version is the one with the greatest timestamp. On a tie, the blob the node stored last wins. A blob with an empty body is a tombstone. The comment is deleted, and listings stop showing it. The history is kept, and ListCommentVersions returns every version.
Because identity includes the signer, looking up a comment by <author>/<tsid>, or listing its versions, matches only blobs signed by that author. A blob from another key that carries the same TSID does not change what that address returns.
Threads and discussions
A comment with no threadRoot starts a discussion. A reply names the discussion's first comment in threadRoot and the comment it answers in replyParent. When those are the same, replyParent is left out. A reply must carry threadRoot, or the daemon rejects it. When a reply arrives before its root or parent, the daemon stashes it and indexes it as soon as the missing comment lands. So out-of-order sync never loses a reply.
Which version was commented on
A comment records the document version its author saw, and readers show it under that version. The daemon resolves the comment to the document's genesis Change through that version. So comment counts and the latest-comment pointer survive when a document is moved or republished. The path can change, but the genesis cannot. A comment whose version is empty attaches to whatever document currently lives at the path.
Block and range comments
To comment on one block, or on a selection inside it, a client wraps the comment body in an Embed block whose link is the target with a fragment: hm://<space>/<path>?v=<version>#<blockId> for a block, or #<blockId>[start:end] for a range of its text in Unicode code points. The version is pinned on purpose. A later edit could move the offsets, so the quote always shows the text the author selected. The comment's own text follows as the embed's children.
Because the quote is an ordinary link inside the body, the daemon indexes it like any other link. So a block comment shows up as a citation of that block. The InteractionSummary request reports per-block comment and citation counts that the app uses to mark commented blocks in the margin.
Citations and backlinks
Every hm:// link the daemon meets while indexing becomes a link record from the source blob to the target resource. The link can sit in a document block, an annotation, or a comment body. The record is, tagged with the source block, the fragment and the version. A pinned version marks the link as exact. A link that follows the latest marks its version as a suggested minimum. ListCitations on a target returns these records as citations, ordered by when the citing blob arrived locally. The order ignores the blob's claimed time, so a late-arriving old blob can never hide a newer citation on the next page. Four links from one version count as four citations.
The older ListEntityMentions call is deprecated. Use ListCitations.
Mentions
A mention is an inline embed: an Embed annotation over a placeholder character in a block's text, with mentionKind set to account or document and the link pointing at the person or the page. Documents and comments use the same mechanism, described in Blocks. The daemon indexes the mention as a link from the comment to the account. This link drives "mentions of me" and the mention trigger of a Seed Agent. The MentionCandidates request ranks who or what to suggest while you type.
Visibility
A comment carries its own visibility. By convention it copies its target's: clients set Private when commenting on a private document. A private comment is visible in two spaces, the author's and the target's. Over peer sync, the comment goes to peers authenticated as either account, as a WRITER in either space, or as either space's site server. On a public-only node, HTTP serves it only to the target space's owner and root-level writers, so that node may refuse the author. Its visibility passes on to the file blobs it links, such as attached images, and to nothing else. Private documents are still changing. Privacy has the current state.
No moderation yet
Nothing checks authorization on comments. Any key can publish a comment on any document, and a node that syncs the document syncs its comments. So no site owner can silence a reply on their own page. There is also no moderation. A site owner cannot hide or remove someone else's comment today, and comment spam has appeared on public sites. Removing or revoking comments is on the team's launch list and is not built as of September 2026. Until then there are two weak mitigations. A document can hide its activity panel with the showActivity metadata key. The web of trust described in Permissions is the planned long-term filter.
Working with comments
In the Seed app
Open a document's discussion from the comments panel, reply inside a thread, or select text and comment on it to create a range comment. Your own comments show edit and delete actions. The Seed app signs comments with your account key, or with a linked device key on a linked device.
CLI
seed-cli comment list hm://<space>/<path> # every comment on a document
seed-cli comment discussions hm://<space>/<path> # grouped into threads
seed-cli comment get <author>/<tsid>
seed-cli comment create hm://<space>/<path> --body "Nice." --key mykey
seed-cli comment create 'hm://<space>/<path>#<blockId>' --file reply.md # a block comment
seed-cli comment create hm://<space>/<path> --reply <author>/<tsid> --body "Agreed."
seed-cli comment edit <author>/<tsid> --body "Edited."
seed-cli comment delete <author>/<tsid> # publishes a tombstoneBodies are markdown, parsed into blocks with the same dialect documents use. See the CLI guide. A web URL of a comment page works wherever an id is accepted.
SDK
createComment, updateComment and deleteComment in the SDK build the signed blob on the client and return the blobs for client.publish. createComment takes the target id and version, optional replyCommentVersion and rootReplyCommentVersion, an optional quoting target with a block id and code-point range, and a visibility. The signing pattern is the one every blob uses: encode with the signature zeroed, sign, fill, encode. See the SDK guide.
Web API
request | what it returns |
|---|---|
| one comment by |
| every comment on a target, with the authors' metadata |
| the same comments grouped into threads, plus discussions on other documents that cite this one |
| comments elsewhere that quote or link this document |
| by author, edit history, reply count |
| every link record pointing at a resource |
| counts of comments, citations, changes and children, per document and per block |
Publishing goes through PublishBlobs with a blob the SDK signed. The daemon's gRPC Comments service offers CreateComment, UpdateComment and DeleteComment signed with a key the daemon holds. The Seed API guide catalogues all of it.
Agents
The read verb accepts hm://<doc>/:comments for a whole discussion and hm://<author>/<tsid> for one comment with its thread, and returns a ready reply call. The write verb with options.action: "comment" posts a comment on a target, with replyTo for a reply. comment.update and comment.delete edit and tombstone. Seed Agents fire on document-comment and user-mention triggers, so mentioning an agent in a comment summons it. External agents use the CLI commands above. Building with agents covers keys and attribution.
Where this is going
As of September 2026 the open items are moderation (an owner revoking or hiding comments on their documents), human-friendly comment URLs under the target's namespace, reactions (designed, then put on hold), and notifications that treat an edit as an edit instead of a new comment. Roadmap collects the wider plans.
See also
Blocks: the body of a comment and block fragments.
Documents: what a comment targets.
URLs: comment addresses.
Permissions: contacts and the web of trust.
Privacy: private comments.
Identity: who signs a comment.
Schema pages: comment, block/comment.
Seed API: ListComments, ListDiscussions, ListCitations.
Do you like what you are reading? Subscribe to receive updates.
Unsubscribe anytime