A comment can now reference a separate Post that carries the full body
(long text, attachments, rich formatting). The inline comment's `content`
becomes a short preview string; the referenced post propagates through the
normal CDN and readers fetch it lazily when rendering the expanded view.
Type change:
- `InlineComment` gains `ref_post_id: Option<PostId>` (#[serde(default)]).
When None, `content` is the full comment text (v0.6.1 shape — unchanged on
the wire). When Some, `content` is the preview.
Signature binding:
- `crypto::sign_comment` / `verify_comment_signature` now take
`ref_post_id: Option<&PostId>`. The signed digest appends
`b"ref:" || ref_post_id` only when a ref is present, so plain comments
produce the same digest as the v0.6.1 scheme and remain verifiable
without a migration. When a ref is present the signature binds it, so a
peer can't strip or swap the reference without re-signing.
Storage:
- `comments` table gets a `ref_post_id BLOB` column (nullable). Added to
both the CREATE TABLE statement and a conditional ALTER TABLE migration
so upgraded DBs pick it up automatically.
- `store_comment`, `get_comments`, `get_comments_with_tombstones` read and
write the column.
Node API:
- `comment_on_post` stays as the plain-comment entry point (calls the
inner helper with `ref_post_id = None`).
- New `comment_on_post_with_ref(post_id, preview, ref_post_id)` for rich
comments. Both share a single inner helper that signs, stores, and
propagates via BlobHeaderDiff.
connection.rs BlobHeaderDiff handler passes `comment.ref_post_id.as_ref()`
to the signature verify so forged or rewritten refs are rejected.
Tests: new crypto test asserting the signature binds ref_post_id (strip /
swap / drop all fail); new storage test asserting ref_post_id roundtrips
through live + tombstone reads. 116 / 116 core tests pass.
Client-side UX (pulling the ref post on expand, composing rich comments)
is frontend work that will land with the next UI iteration.