Skip to content

On-Disk Contract Spec (v1)

This specification defines the versioned on-disk data format and directory layout for Pibmo Relay stores. Any implementation agreeing with this contract can safely interoperate on a shared git transport store.


1. Directory Layout

A Relay store repository root contains two primary directory trees:

<store_root>/
├── mail/
│ └── <uuid>.md # Canonical Message files
└── roles/
└── <role>/ # Directory per team member seat
├── inbox/
│ └── <uuid>.ref # Pointer Ref files (unread/read)
├── sent/
│ └── <uuid>.ref # Sender's copy pointer Ref files
└── archive/
└── <uuid>.ref # Archived pointer Ref files
  • <uuid>: A non-empty, valid UUID string (36 characters, e.g. 0010e837-43d5-4f40-8b1e-976404987a1e).
  • <role>: A validated team member role identifier (matching ^[A-Za-z0-9_-]+$).

2. UUID Validation & Identity

  1. Format: Every UUID must be a valid RFC 4122 string matching: ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$
  2. Non-empty requirement: Empty strings, missing UUIDs, or filenames resulting in mail/.md or roles/<role>/*/.ref are strictly invalid.
  3. Case Sensitivity: Implementations must handle case hex characters gracefully; standard UUID generation uses lowercase hexadecimal characters.

3. Timestamp Format

All timestamps MUST use ISO 8601 UTC format with second-resolution and a terminal Z suffix: YYYY-MM-DDTHH:MM:SSZ (e.g., 2026-07-21T13:14:00Z). Fractional seconds and timezone offsets other than Z are not permitted.


4. Canonical Message Format

Location: mail/<uuid>.md

4.1 Structure

A canonical message consists of line-prefix key-value metadata (“frontmatter”) bounded by --- delimiters, followed by a blank line and the message body:

---
id: <uuid>
thread: <uuid>
in_reply_to: null|<uuid>
from: <role>
to: [<role_list>]
cc: [<role_list>]
type: <enum>
subject: <string>
created: <ISO8601Z>
---
<body>

4.2 Field Specifications & Order

Keys in the frontmatter must appear in fixed order:

  1. id: The UUID of this message.
  2. thread: The UUID of the conversation thread root. For a new top-level thread, thread equals id.
  3. in_reply_to: The UUID of the immediate parent message, or literal string null if top-level.
  4. from: Sender’s valid role identifier.
  5. to: Bracketed CSV list of recipient roles, e.g. [role1, role2].
  6. cc (Optional): Present if and only if at least one CC recipient is declared, e.g. [role3]. Placed directly after to.
  7. type: Enumerable string. Must be one of: info, request, report, ack, ping.
  8. subject: Single-line subject string.
  9. created: Timestamp string in ISO 8601 UTC format.

4.3 Immutability

Canonical message files are write-once. Once created under mail/<uuid>.md, their content is never modified or moved.


5. Pointer Ref Format

Location: roles/<role>/{inbox,sent,archive}/<uuid>.ref

5.1 Structure

A Ref file is a pointer carrying per-member state for a given message. It consists of exactly 4 lines in fixed key order:

id: <uuid>
read_at: null|<ISO8601Z>
status: unread|read|archived
claimed_by: null

5.2 Field Rules

  1. id: Matching the message UUID.
  2. read_at: Literal string null when unread, or ISO 8601 UTC timestamp when read. Write-once once set.
  3. status: Folder-consistent state:
    • In inbox/: status is unread or read.
    • In sent/: status is read.
    • In archive/: status is archived.
  4. claimed_by: Literal string null (reserved for future lease mechanics).

6. Directory vs. Status Consistency Invariants

  • Folder Authority: Physical location (inbox/ vs archive/) determines whether a message is archived.
  • Per-Member Scope: All mutation operations for a member occur exclusively under roles/<role>/.
  • Read State: Status transition is strictly idempotent (unread -> read). Re-reading preserves original read_at.