Comments & Annotations

Dune's comment system lets your editorial team leave notes directly on pages in the admin panel. Comments are stored in data/comments/ alongside your content, visible only inside the admin, and never exposed on the public site.

Because comment files are git-tracked, they survive deploys, appear in your repository history, and are included in content backups. They are the right place to record editorial decisions, flag issues for a teammate, and track sign-off across a review cycle.

Leaving a comment

Open any page in the admin editor and find the Comments panel in the sidebar. Type your message and press Post. Comment bodies support Markdown formatting.

To reply to an existing comment, click Reply beneath it. Threads nest one level deep — replies to replies are not supported.

@mentions

Type @ followed by a username anywhere in your comment body to notify a team member. When the comment is saved, Dune extracts all usernames matching [a-zA-Z0-9_-]+ and stores them in the mentions field. Mentioned users see an unread count badge in the admin navigation.

Editing a comment re-extracts mentions from the updated body, so adding or removing an @username from an edit is reflected immediately.

Resolve workflow

Any team member can mark a comment thread as resolved to indicate the issue has been addressed or the discussion is closed. Resolved comments remain visible in the panel but are visually de-emphasised.

Resolving a comment sets resolved: true and records the resolver's username in resolvedBy and the resolution timestamp in resolvedAt. A resolved thread can be reopened by posting a reply or toggling the resolved state directly.

Editing and deleting

Users can edit or delete their own comments. Users with the editor or admin role can edit or delete any comment on the site.

Storage

Data Location Persisted
Comment records {admin.dataDir}/comments/{encodedPath}.json Yes — git-tracked
Mention read state {admin.runtimeDir}/mention-reads/{username}.json No — ephemeral

encodedPath is derived from the page's sourcePath by replacing / with __ and . with -dot-. For example, 02.blog/01.hello-world becomes 02-dot-blog__01-dot-hello-world.

Mention read state is machine-local and does not need to be committed or backed up.

Comment structure

interface PageComment {
  id: string;              // hex ID
  pageSourcePath: string;
  author: string;          // display name
  authorUsername: string;
  body: string;            // Markdown-compatible
  createdAt: number;       // Unix ms
  updatedAt: number;       // Unix ms
  resolved: boolean;
  resolvedBy?: string;     // username of resolver
  resolvedAt?: number;     // Unix ms
  parentId?: string;       // set on replies; omitted on top-level comments
  mentions?: string[];     // @usernames extracted at save time
}

API reference

All endpoints require authentication. The {path} segment is the URL-encoded sourcePath of the page — for example, 02.blog%2F01.hello-world.

Method Path Description
GET /admin/api/pages/{path}/comments List all comments for a page.
POST /admin/api/pages/{path}/comments Create a new comment.
PATCH /admin/api/pages/{path}/comments/{id} Edit a comment body. Requires own comment or editor/admin role.
DELETE /admin/api/pages/{path}/comments/{id} Delete a comment. Requires own comment or editor/admin role.
POST /admin/api/pages/{path}/comments/{id}/resolve Toggle the resolved state of a comment.
GET /admin/api/comments/mentions List all @mentions for the authenticated user.
POST /admin/api/comments/mentions/read Mark a set of mention IDs as read.

Create a comment

POST /admin/api/pages/02.blog%2F01.hello-world/comments
{
  "body": "This intro paragraph needs revision. @jane can you rewrite it?",
  "parentId": null
}

parentId is optional. Omit it or set it to null for a top-level comment. Set it to an existing comment's id to post a reply.

List comments response

{
  "items": [
    {
      "id": "a1b2c3d4e5f6",
      "pageSourcePath": "02.blog/01.hello-world",
      "author": "Bob Smith",
      "authorUsername": "bob",
      "body": "This intro needs revision. @jane can you rewrite it?",
      "createdAt": 1742000000000,
      "updatedAt": 1742000000000,
      "resolved": false,
      "mentions": ["jane"]
    }
  ],
  "total": 1
}

Mentions

GET /admin/api/comments/mentions

Returns all comments in which the authenticated user is mentioned, each wrapped in a read-state envelope:

[
  {
    "comment": {
      "id": "a1b2c3d4e5f6",
      "pageSourcePath": "02.blog/01.hello-world",
      "author": "Bob Smith",
      "authorUsername": "bob",
      "body": "This intro needs revision. @jane can you rewrite it?",
      "createdAt": 1742000000000,
      "updatedAt": 1742000000000,
      "resolved": false,
      "mentions": ["jane"]
    },
    "read": false
  }
]

To mark mentions as read, POST an array of comment IDs:

POST /admin/api/comments/mentions/read
{ "ids": ["a1b2c3d4e5f6"] }