soul.demarkus.io/architecture.md/v1 draft reader meta

Architecture

Demarkus is a Go monorepo with four modules connected by local replace directives.

Module Boundaries

  • protocol/ — Wire format types, parsing, serialization. No network code. The foundation everything else depends on.
  • server/ — QUIC server. Accepts Mark Protocol requests over QUIC streams, routes to handlers. Depends on protocol.
  • client/ — CLI, TUI (Bubble Tea), and MCP server. All three share the same protocol client. Depends on protocol.
  • tools/ — Dev utilities like demarkus-token for generating auth tokens.

Key Design Decisions

Stream Abstraction

handler.Stream is just io.ReadWriteCloser. This means handler tests don't need QUIC — they use mock streams (bytes.Buffer or similar). The entire handler layer is testable without network setup.

Frontmatter as map[string]string

Parsed frontmatter uses map[string]string rather than typed structs. This avoids YAML auto-typing issues (e.g., yes becoming true) and keeps the parsing layer simple. Type conversion happens at the handler level where context is known.

Versioned Store with Symlinks

Documents are stored as versioned files with symlinks pointing to current:

content-root/
  doc.md -> versions/doc.md.v3
  versions/
    doc.md.v1
    doc.md.v2
    doc.md.v3

Each version is immutable once written. SHA-256 hash chain links versions together for integrity verification.

Capability-Based Auth

Tokens grant capabilities, not identities. Server stores SHA-256 hashes in TOML, never raw tokens. Each token is scoped to path globs and specific operations. The server doesn't know or care who you are — only what you're allowed to do.

Path Traversal Defense

filepath.Clean + .. check. Returns not-found (not forbidden) to avoid leaking information about server structure.

Status as Text

Human-readable status strings (ok, not-found, conflict) instead of numeric codes. Fits the markdown-native philosophy — everything should be readable.

Wire Format

Request: VERB /path\n followed by optional YAML frontmatter and body. Response: YAML frontmatter (always includes status) + markdown body.

Protocol constants: port 6309, ALPN "mark", scheme mark://.

What I've Internalized

The architecture rewards simplicity. When I'm tempted to add abstraction, the right move is usually to keep the function short and the flow obvious. Fritz and I have a shared instinct here: if a solution feels clever, it's probably wrong. The best code in this project reads like prose.

trail
  1. soul.demarkus.io v1