Repository source: docs/specs/canonical_post_record_v1.md
Canonical Post Record V1
This document defines the canonical post record shape for the current implementation slice.
Scope
One ASCII text file represents one post.
Canonical post files live in
records/posts/.Sample files use the filename stem as the local
Post-ID.This slice defines thread roots, replies, board tags, and typed root-thread metadata.
File Structure
A post file has two parts:
A contiguous header block at the top of the file
A body separated from the headers by one blank line
Headers use this form:
Key: Value
Required Headers
Post-ID: unique identifier for the post within the repositoryCreated-At: RFC 3339 UTC timestamp such as2026-04-10T12:00:00ZBoard-Tags: space-separated board tags
Conditional Headers
Thread-ID: required for replies, omitted for thread rootsParent-ID: required for replies, omitted for thread rootsAuthor-Identity-ID: optional canonical identity reference for authored postsSubject: optional, mainly useful for thread rootsThread-Type: optional for thread roots, omitted for replies
Typed Root Headers
Typed root metadata is reserved for thread roots. Replies must not include Thread-Type
or subtype-specific headers.
The first supported typed root is Thread-Type: task. Task roots add:
Task-Status: short task state label such asproposedTask-Presentability-Impact: decimal rating from0to1Task-Implementation-Difficulty: decimal rating from0to1Task-Depends-On: optional space-separated root post IDs for task dependenciesTask-Sources: optional semicolon-separated provenance references
Body Rules
The body is plain ASCII text.
The body may include quoting and plain links.
The body should remain understandable without rendering.
Thread Semantics
A thread root is a post with no
Thread-IDand noParent-ID.A reply references the thread root through
Thread-ID.A reply references its immediate parent through
Parent-ID.Created-Atis authoritative canonical time for the post and must not be inferred from file mtimes or git history.When
Author-Identity-IDis present, it must be an ASCII token matching the retained identity form such asopenpgp:<lowercase-fingerprint>.When
Author-Identity-IDis absent, readers fall back to anonymous author labeling such asguestunless another retained rule applies.A typed root thread is still a normal thread root. Replies remain ordinary replies.
For this slice, authoritative task state lives on the task root post. Future slices may add append-only task-update records instead of overloading replies.
Structured Approval Replies
The retained approval/vouching slice reuses normal replies rather than inventing a separate approval record family.
Approval replies use these additional conventions:
Board-Tagsinclude bothidentityandapprovalThread-IDmust match the target profile bootstrap threadParent-IDmust match the target profile bootstrap postAuthor-Identity-IDidentifies the approving userthe body contains a deterministic line using the form
Approve-Identity-ID: openpgp:<lowercase-fingerprint>
These replies are canonical posts, but V1 readers treat them as hidden from board/activity feeds because they carry the identity tag.
Example Thread Root
Post-ID: root-001
Created-At: 2026-04-10T12:00:00Z
Board-Tags: general meta
Subject: First thread
Hello world.
Example Task Thread Root
Post-ID: T01
Created-At: 2026-04-10T12:05:00Z
Board-Tags: planning
Subject: Publish raw planning files and debug views in the web UI
Thread-Type: task
Task-Status: proposed
Task-Presentability-Impact: 0.94
Task-Implementation-Difficulty: 0.34
Task-Sources: todo.txt; docs/plans/forum_feature_splitting_checklist.md
Expose todo.txt, ideas.txt, and raw object/debug views from the interface.
Example Reply
Post-ID: reply-001
Created-At: 2026-04-10T12:06:00Z
Board-Tags: general meta
Thread-ID: root-001
Parent-ID: root-001
Author-Identity-ID: openpgp:0168ff20eb09c3ea6193bd3c92a73aa7d20a0954
This is a reply.
Example Approval Reply
Post-ID: reply-approval-001
Created-At: 2026-04-10T12:07:00Z
Board-Tags: identity approval
Thread-ID: bootstrap-20260410120000-a1b2c3d4
Parent-ID: bootstrap-20260410120000-a1b2c3d4
Author-Identity-ID: openpgp:0168ff20eb09c3ea6193bd3c92a73aa7d20a0954
Approve-Identity-ID: openpgp:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Portable timestamps for legacy imports
Legacy posts without a Created-At header may have a companion file at
records/post-timestamps/<Post-ID>.json. It has exactly three string fields:
post_id, record_sha256 (lowercase SHA-256 of the unchanged post bytes), and
created_at (RFC 3339 UTC, YYYY-MM-DDTHH:MM:SSZ). The metadata is at most 4 KiB.
The filename must match post_id, and the checksum must match the post.
The repository reader uses valid companion metadata before its legacy Git/mtime
fallback. Invalid companion metadata is an error. An explicit Created-At header
remains authoritative. The instance importer requires companion metadata for
legacy posts, recovering it from isolated source Git history when possible; it
never uses extraction mtime or the destination import commit date. Metadata and
its legacy post are imported together and participate in conflict/recovery checks.
The original post and detached signatures are not rewritten. These dates carry
source provenance, not a new cryptographic attestation.
Optional image reference
An image attachment is one canonical Image-v1: <sha256> <png|jpg> <bytes>
<width> <height> <percent-encoded-description> line in the body (all on one
line). The lowercase SHA-256 identifies immutable original bytes, independent of
host or storage provider. Numeric values are positive decimal integers; the
description is optional; omit its preceding space when absent. When present,
it is UTF-8 encoded with RFC 3986 percent encoding, at most 500 bytes before
encoding. The signed body binds all metadata. Existing text readers can preserve
the reference without image support; no read-model schema change is required.
Only one reference is supported per post. Uploads require a verified approved
author and FORUM_IMAGE_CONTENT_ENABLED; JPEG/PNG are limited to 5 MiB and
4096 pixels per side. Storage is outside the repository, with a separate image
backup required for restoration. Images are not public presentation assets.