SurfDoc Format Specification
The definitive reference for the .surf document format. Version 0.5.0.
SurfDoc is a typed document format designed for AI/human collaboration. It uses structured block directives, embedded data, and presentation hints — while remaining readable as plain text in any editor. A SurfDoc without a renderer degrades gracefully to readable plain text. With a renderer, it becomes an interactive, visually polished document with structured data, conversations, task tracking, and cross-references.
This specification defines 41 block types across 8 categories, the front matter schema, the rendering model, graceful degradation behavior, and the security architecture. The reference parser is surf-parse, a Rust crate with 378+ tests.
1. Design Principles
- Backward-compatible. Every standard Markdown document is a valid SurfDoc. SurfDoc adds typed block directives; it never breaks existing files.
- Three-layer architecture. Every document has a content layer (prose), a data layer (structured, typed, queryable), and a presentation layer (layout, styling). Each layer is accessible independently.
- Graceful degradation. A SurfDoc opened in a plain text editor, GitHub, or any markdown renderer is fully readable. Blocks degrade to fenced content with visible labels. No information is lost.
- AI-native. An AI agent can read, write, and modify a SurfDoc without special tooling. The format is text-based, the syntax is simple, and the block types are semantic.
- File-based and portable. A SurfDoc is a
.surffile. It lives on disk, in git, in any filesystem. It is not a database row, not a cloud-only format, not proprietary. - Schema-validated. Block types have defined attributes. A renderer or linter can validate that a
::datablock has a format, a::turnblock has a participant, etc. - Renderable at multiple fidelities. Plain text → styled markdown → rich interactive → print/PDF. Same source, four fidelity levels.
2. File Format
| Property | Value |
|---|---|
| Extension | .surf (preferred) or .md (compatible) |
| MIME type | text/surf (proposed), falls back to text/markdown |
| Encoding | UTF-8, no BOM |
| Line endings | LF (Unix). CRLF accepted, normalized to LF on parse. |
A .surf file signals to tooling that it should be rendered with SurfDoc block support. Renaming .surf to .md produces a fully readable markdown file (graceful degradation).
3. Front Matter
SurfDoc uses YAML front matter delimited by ---. Front matter is optional but recommended.
---
title: "Document Title"
type: doc
version: 1
created: 2026-02-10
updated: 2026-02-10
author: "Brady Davis"
status: draft
scope: workspace
tags: [architecture, surfdoc]
---
Required Fields by Document Type
| Type | Required Fields |
|---|---|
doc | title, type |
guide | title, type, confidence, updated |
conversation | title, type, status, participants |
plan | title, type, status, created |
agent | title, type |
preference | title, type |
4. Block Syntax
SurfDoc blocks use the :: directive syntax. Container blocks open with ::blocktype[attrs] and close with ::. Leaf directives fit on a single line.
Container Block (multi-line)
::blocktype[attr1=value attr2="quoted value"]
Content inside the block.
Multiple lines allowed.
::
Leaf Directive (single-line)
::blocktype[attr1=value]{Inline content}
Nesting
Blocks can nest. Inner blocks use additional colons. Maximum nesting depth: 4 levels.
::columns[count=2]
:::column
Left column content.
:::
:::column
Right column content.
:::
::
Attribute Syntax
| Syntax | Meaning |
|---|---|
[key=value] | Unquoted value (no spaces) |
[key="quoted value"] | Quoted value (spaces allowed) |
[key=true] | Boolean |
[key=42] | Number |
[flag] | Boolean flag (presence = true) |
5. Core Blocks
The foundation blocks for structured content: callouts, decisions, summaries, figures, quotes, and code.
::callout
Styled callout box for important information with type-based theming.
Attributes
| Attribute | Values | Default |
|---|---|---|
type | info, warning, danger, success, tip, note | info |
title | String | Auto from type |
collapsible | Boolean | false |
Source
::callout[type=warning]
This deployment will cause 5 minutes of downtime.
::
Rendered
This deployment will cause 5 minutes of downtime.
Degrades to
> **Warning:** This deployment will cause 5 minutes of downtime.::decision
Highlighted decision record with status tracking.
Attributes
| Attribute | Values | Default |
|---|---|---|
status | proposed, accepted, rejected, superseded | proposed |
date | ISO date | None |
deciders | Comma-separated names | None |
Source
::decision[status=accepted date=2026-02-10]
**GTK4 native** — 5MB binary, native look and feel.
::
Rendered
GTK4 native — 5MB binary, native look and feel.
Degrades to
> **Decision (accepted, 2026-02-10):** GTK4 native — 5MB binary, native look and feel.::summary
Token-efficient summary for AI loading. Renderers may show this collapsed with an expand affordance.
Attributes
| Attribute | Values | Default |
|---|---|---|
| No attributes | ||
Source
::summary
This document covers the GTK vs Electron decision for desktop.
We chose GTK4 for binary size and native feel.
::
Rendered
This document covers the GTK vs Electron decision for desktop. We chose GTK4 for binary size and native feel.
Degrades to
*This document covers the GTK vs Electron decision for desktop. We chose GTK4 for binary size and native feel.*::figure
Image or diagram with caption and optional source attribution.
Attributes
| Attribute | Values | Default |
|---|---|---|
src | File path or URL | Required |
caption | String | None |
alt | String | Caption or empty |
width | CSS value | 100% |
Source
::figure[src=brand/diagrams/ecosystem.png caption="Product Ecosystem" alt="Diagram showing product relationships"]
::
Rendered
Product Ecosystem
Degrades to

*Product Ecosystem*::quote
Attributed quotation with source citation.
Attributes
| Attribute | Aliases | Default |
|---|---|---|
by | attribution, author | None |
cite | source | None |
Source
::quote[by="Peter Thiel" cite="Zero to One"]
Every moment in business happens only once.
::
Rendered
Every moment in business happens only once.
Degrades to
> Every moment in business happens only once.
>
> — Peter Thiel, *Zero to One*::code
Enhanced code block with language, file path, and highlighting metadata.
Attributes
| Attribute | Values | Default |
|---|---|---|
lang | Language identifier | None |
file | Source file path | None |
highlight | Line numbers/ranges | None |
lines | Line range | None |
title | Display title | None |
runnable | Boolean | false |
diff | Boolean | false |
Source
::code[lang=rust file=src/sync/mod.rs]
pub fn run_sync(opts: &SyncOpts) -> Result<SyncReport> {
let repo_root = find_repo_root()?;
let config = config::load_config(&repo_root)?;
}
::
Rendered
Degrades to
```rust
pub fn run_sync(opts: &SyncOpts) -> Result<SyncReport> {
let repo_root = find_repo_root()?;
let config = config::load_config(&repo_root)?;
}
```6. Data Blocks
Blocks that embed structured, queryable data inside documents.
::data
Structured data with schema, supporting table, CSV, and JSON formats.
Attributes
| Attribute | Values | Default |
|---|---|---|
id | Unique identifier | Auto-generated |
format | table, csv, json | table |
sortable | Boolean | false |
filterable | Boolean | false |
chart | bar, line, pie, none | none |
source | File path | None |
Source
::data[id=competitors format=table sortable]
| Company | Funding | Users | Threat |
|---------|---------|--------|--------|
| Linear | $52M | 10K+ | High |
| Jira | Public | 250K+ | Medium |
::
Rendered
| Company | Funding | Users | Threat |
|---|---|---|---|
| Linear | $52M | 10K+ | High |
| Jira | Public | 250K+ | Medium |
Degrades to
| Company | Funding | Users | Threat |
|---------|---------|--------|--------|
| Linear | $52M | 10K+ | High |
| Jira | Public | 250K+ | Medium |::metric
Single metric display with label, value, trend, and optional change indicator.
Attributes
| Attribute | Values | Default |
|---|---|---|
label | String | Required |
value | String | Required |
trend | up, down, flat | None |
unit | String | None |
change | String (percentage or absolute) | None |
period | String | None |
Source
::metric[label="MRR" value="$2,400" trend=up]
Rendered
Degrades to
**MRR:** $2,400::timeline
Chronological sequence of events with dates and descriptions.
Attributes
| Attribute | Values | Default |
|---|---|---|
| No attributes | ||
Source
::timeline
- 2026-01: Beta launch
- 2026-02: First paying customers
- 2026-03: Angel round
::
Rendered
Degrades to
- 2026-01: Beta launch
- 2026-02: First paying customers
- 2026-03: Angel round::chart
Data visualization with bar, line, pie, area, and scatter chart types.
Attributes
| Attribute | Values | Default |
|---|---|---|
type | bar, line, pie, area, scatter | Required |
title | String | None |
x | Column name for x-axis | First column |
y | Column name(s) for y-axis | Remaining columns |
stacked | Boolean | false |
Source
::chart[type=bar title="Revenue by Quarter"]
| Quarter | Revenue |
|---------|---------|
| Q1 2026 | $3,000 |
| Q2 2026 | $8,000 |
::
Rendered
Degrades to
*[Chart: Revenue by Quarter]*
| Quarter | Revenue |
|---------|---------|
| Q1 2026 | $3,000 |
| Q2 2026 | $8,000 |7. Conversation Blocks
Blocks that structure multi-party discussions and decision alternatives.
::turn
A single participant's contribution in a threaded conversation.
Attributes
| Attribute | Values | Default |
|---|---|---|
participant | String | Required |
time | ISO timestamp | None |
role | human, ai, system | Inferred |
model | Model identifier | None |
Source
::turn[participant=brady time=2026-02-10T05:00Z]
Should we go native per platform?
::
Rendered
Should we go native per platform?
Degrades to
### Brady — 2026-02-10 05:00
Should we go native per platform?::alternatives
Structured comparison of options considered, with pros, cons, and verdict.
Attributes
| Attribute | Values | Default |
|---|---|---|
| No attributes | ||
Source
::alternatives
| Option | Pros | Cons | Verdict |
|----------|-----------------|---------------|-------------|
| GTK4 | 5MB, native | Linux-first | **Selected**|
| Electron | Cross-platform | 150MB, heavy | Rejected |
::
Rendered
| Option | Pros | Cons | Verdict |
|---|---|---|---|
| GTK4 | 5MB, native | Linux-first | Selected |
| Electron | Cross-platform | 150MB, heavy | Rejected |
Degrades to
| Option | Pros | Cons | Verdict |
|----------|-----------------|---------------|-------------|
| GTK4 | 5MB, native | Linux-first | **Selected**|
| Electron | Cross-platform | 150MB, heavy | Rejected |8. Task Blocks
Actionable task lists and progress indicators embedded in documents.
::tasks
Actionable task list with assignees, status tags, and dependency tracking.
Attributes
| Attribute | Values | Default |
|---|---|---|
source | inline, surfdoc, github | inline |
project | External project ID | None |
filter | Query string | None |
Source
::tasks[source=inline]
- [x] Build parser crate @brady #done
- [ ] Add chart rendering @brady #in-progress
- [ ] Publish to crates.io @brady
::
Rendered
Degrades to
- [x] Build parser crate @brady #done
- [ ] Add chart rendering @brady #in-progress
- [ ] Publish to crates.io @brady::progress
Visual progress indicator with label, value, and maximum.
Attributes
| Attribute | Values | Default |
|---|---|---|
value | Number | Required |
label | String | None |
max | Number | 100 |
Source
::progress[value=65 label="Sprint 3 completion" max=100]
Rendered
Degrades to
**Sprint 3 completion:** 65%9. Reference Blocks
Cross-references, embeds, and citations that link documents together.
::embed
Embed content from another SurfDoc or external file with optional section targeting.
Attributes
| Attribute | Values | Default |
|---|---|---|
src | File path | Required |
section | Heading to extract | Entire file |
mode | full, preview, link | preview |
lines | Line range | None |
Source
::embed[src=.context/docs/architecture.md section="## Stack" mode=preview]
Degrades to
See: [architecture.md — Stack](.context/docs/architecture.md)::footnote
Document-level footnote with citation for evidence-tagged claims.
Attributes
| Attribute | Values | Default |
|---|---|---|
id | Footnote identifier | Required |
Source
::footnote[id=1]
Gartner, "Magic Quadrant for PM Tools," 2025. Tier 1 source.
::
Degrades to
[^1]: Gartner, "Magic Quadrant for PM Tools," 2025. Tier 1 source.10. Layout Blocks
Blocks that control content arrangement: columns, tabs, collapsibles, and dividers.
::columns
Multi-column layout with nested :::column children or --- separators.
Attributes
| Attribute | Values | Default |
|---|---|---|
count | 2–4 | 2 |
gap | sm, md, lg | md |
ratio | Ratios like 2:1 or 1:1:1 | Equal |
Source
::columns
:::column
Left column content.
:::
:::column
Right column content.
:::
::
Rendered
Degrades to
Left column content.
Right column content.::tabs
Tabbed content sections where headings within the block define tab panels.
Attributes
| Attribute | Values | Default |
|---|---|---|
| No attributes — tabs derived from ## or ### headings | ||
Source
::tabs
## Linux
Install via PPA: `sudo apt install surf-browser`
## macOS
Install via Homebrew: `brew install surf-browser`
::
Rendered
Install via PPA: sudo apt install surf-browser
Degrades to
## Linux
Install via PPA: `sudo apt install surf-browser`
## macOS
Install via Homebrew: `brew install surf-browser`::details
Collapsible content section with a title toggle.
Attributes
| Attribute | Values | Default |
|---|---|---|
title | String | None |
open | Boolean | false |
Source
::details[title="Implementation details" open=false]
The sync engine uses SHA-256 content hashing.
::
Rendered
Implementation details
The sync engine uses SHA-256 content hashing.
Degrades to
<details>
<summary>Implementation details</summary>
The sync engine uses SHA-256 content hashing.
</details>::divider
Thematic break with optional centered label.
Attributes
| Attribute | Values | Default |
|---|---|---|
label | String | None |
Source
::divider[label="Phase 2"]
Rendered
Phase 2
Degrades to
---11. Landing Page Blocks
Web generation blocks for building complete websites from .surf files. These blocks are used inside ::page containers.
::site
Site-level configuration: domain, theme, accent color, nav, and footer text.
Attributes
| Attribute | Values | Default |
|---|---|---|
domain | Domain name | None |
Source
::site[domain=example.com]
name: My App
theme: dark
accent: #6366f1
::Degrades to
**Site Configuration:** example.com, theme: dark::page
Defines a route and its layout for web generation.
Attributes
| Attribute | Values | Default |
|---|---|---|
route | URL path | Required |
layout | hero, cards, pricing, docs, landing, split, gallery | landing |
title | Page title | First heading |
description | Meta description | First paragraph |
sidebar | Boolean | false |
Source
::page[route=/ layout=hero]
# Welcome to My App
Build faster with less.
::cta[label="Get Started" href=/signup primary]
::Degrades to
# Welcome to My App
Build faster with less.
[Get Started](/signup)::cta
Call-to-action button with primary/secondary styling and optional icon.
Attributes
| Attribute | Values | Default |
|---|---|---|
label | Button text | Required |
href | URL or path | Required |
primary | Boolean | false |
icon | Built-in icon name | None |
Source
::cta[label="Get Started" href=/signup primary icon=arrow-right]Rendered
Degrades to
[Get Started](/signup)::hero-image
Full-width hero visual for landing page headers.
Attributes
| Attribute | Values | Default |
|---|---|---|
src | Image path or URL | Required |
alt | Alt text | None |
Source
::hero-image[src=assets/screenshot.png alt="App screenshot"]Degrades to
::testimonial
Customer testimonial with author, role, and company attribution.
Attributes
| Attribute | Aliases | Default |
|---|---|---|
author | name | None |
role | title | None |
company | org | None |
Source
::testimonial[author="Jane Dev" role="Engineer" company="Acme"]
Replaced 3 tools for me.
::Rendered
“Replaced 3 tools for me.”
Degrades to
> Replaced 3 tools for me.
> — Jane Dev, Engineer at Acme::faq
Accordion FAQ where questions are defined by headings within the block.
Attributes
| Attribute | Values | Default |
|---|---|---|
| No attributes — questions from ### headings | ||
Source
::faq
### Is my data encrypted?
Yes — AES-256 at rest, TLS in transit.
### Can I self-host?
Yes. Docker image available.
::Degrades to
### Is my data encrypted?
Yes — AES-256 at rest, TLS in transit.
### Can I self-host?
Yes. Docker image available.::pricing-table
Pricing comparison table with tier columns.
Attributes
| Attribute | Values | Default |
|---|---|---|
| No attributes — content is a pipe-delimited table | ||
Source
::pricing-table
| | Free | Pro | Team |
|---|---|---|---|
| Price | $0 | $4.99/mo | $8.99/seat/mo |
| Notes | Unlimited | Unlimited | Unlimited |
::Degrades to
| | Free | Pro | Team |
|---|---|---|---|
| Price | $0 | $4.99/mo | $8.99/seat/mo |::logo-cloud
Horizontal strip of partner or customer logos with optional heading.
Attributes
| Attribute | Values | Default |
|---|---|---|
title | String | None |
Source
::logo-cloud[title="Trusted by"]
- assets/logos/acme.svg
- assets/logos/initech.svg
::Degrades to
**Trusted by**: acme.svg, initech.svg::subscribe
Email subscription form with custom action URL and placeholder text.
Attributes
| Attribute | Values | Default |
|---|---|---|
action | Form action URL | None |
placeholder | Input placeholder | you@email.com |
Source
::subscribe[action=https://api.example.com/newsletter placeholder="you@email.com"]
Get notified when we launch.
::Degrades to
Get notified when we launch. Subscribe: https://api.example.com/newsletter::countdown
Event countdown timer with target date and label.
Attributes
| Attribute | Values | Default |
|---|---|---|
date | ISO date | Required |
label | String | None |
Source
::countdown[date=2026-03-15 label="Launch day"]Degrades to
**Launch day:** 2026-03-15::style
Presentation overrides: accent color, fonts, spacing. No selectors, no cascade.
Attributes
| Attribute | Values | Default |
|---|---|---|
accent | Hex color | #6366f1 |
font | Font preset name | system |
heading-font | Font preset name | Inherits font |
body-font | Font preset name | Inherits font |
Source
::style
accent: #2563EB
heading-font: montserrat
body-font: inter
::Degrades to
*Ignored entirely — content renders without styling overrides.*::css
Raw CSS escape hatch for custom styling beyond ::style presets.
Attributes
| Attribute | Values | Default |
|---|---|---|
| No attributes — body is raw CSS | ||
Source
::css
.custom-thing { border: 2px dashed red; }
::Degrades to
*Ignored entirely.*::form
Form with input fields and a submit action.
Attributes
| Attribute | Values | Default |
|---|---|---|
submit | Form action URL | None |
Source
::form[submit=/api/contact]
Name: text
Email: email
Message: textarea
::Degrades to
**Contact form:** /api/contact
- Name (text)
- Email (email)
- Message (textarea)::gallery
Image gallery with configurable column count and lightbox.
Attributes
| Attribute | Values | Default |
|---|---|---|
columns | Number (1-6) | 3 |
Source
::gallery[columns=3]
- assets/img1.jpg
- assets/img2.jpg
- assets/img3.jpg
::Degrades to


13. Inline Extensions
Inline extensions use single-colon syntax within text for evidence tags, status badges, person references, and knowledge references.
| Extension | Syntax | Degrades to |
|---|---|---|
:evidence | :evidence[tier=1 source="Gartner 2025"] | [verified — Gartner 2025] |
:status | :status[value=shipped] | [shipped] |
:person | :person[name="Brady Davis" role=founder] | Brady Davis |
:ref | :ref[path=.context/docs/arch.md title="Architecture"] | [Architecture](arch.md) |
14. Rendering Model
SurfDoc defines four rendering fidelities. The same source file produces progressively richer output.
| Fidelity | Environment | Output |
|---|---|---|
| 1. Plain Text | Any text editor (vim, nano) | Raw syntax visible, fully readable |
| 2. Markdown | GitHub, VS Code preview | Tables, links, headings rendered; block directives as text |
| 3. SurfDoc Renderer | SurfDoc workspace, Surf Browser | Interactive: sortable tables, chat bubbles, charts, tabs |
| 4. Export | surf export | Publication-quality PDF, HTML, DOCX |
Rendering Rules
- Unknown blocks are preserved. A renderer that does not understand a block type renders it as a fenced section with the block name as a label.
- Attributes are informational. A renderer that does not understand an attribute ignores it.
- Content is king. The content layer MUST always be accessible regardless of rendering fidelity.
- Progressive enhancement. Each fidelity level adds to the previous one.
15. Graceful Degradation
Every block type defines its degradation behavior. A SurfDoc without a renderer MUST be at least as useful as the equivalent plain markdown document.
| Block | Fidelity 1 (Text) | Fidelity 2 (Markdown) |
|---|---|---|
::callout | > **Warning:** text | Blockquote with bold label |
::decision | > **Decision:** text | Styled blockquote |
::summary | *text* | Italic paragraph |
::data (table) | Markdown table | Markdown table |
::metric | **label:** value | Bold label + value |
::chart | Table + label | Table + label |
::turn | ### Name — time | Heading + paragraph |
::tasks | - [x] task @name | Checkbox list |
::columns | Sequential sections | Sequential sections |
::tabs | Sequential headings | Sequential sections |
::details | Section with heading | <details> tag |
::embed | Link to source | Link to source |
::cta | [label](href) | Markdown link |
::style | Ignored | Ignored |
16. AI Interaction Model
SurfDoc is designed for AI agents to read, write, and modify without special tooling. The syntax is text-based, and block types are semantic.
::ai-context
Metadata block recording the AI context window state when content was generated.
Attributes
| Attribute | Values | Default |
|---|---|---|
model | Model identifier | None |
tokens | Token count | None |
loaded | Boolean | false |
Source
::ai-context[model=opus tokens=2400 loaded=true]
Context window state when this section was written.
::Degrades to
*[AI context: opus, 2400 tokens]*::ai-generated
Marks content as AI-generated with model, date, and human review status.
Attributes
| Attribute | Values | Default |
|---|---|---|
model | Model identifier | None |
date | ISO date | None |
reviewed | Boolean | false |
Source
::ai-generated[model=opus date=2026-02-10 reviewed=false]
This section was generated by AI and has not been human-reviewed.
::Degrades to
*[AI-generated, opus, 2026-02-10, not reviewed]* This section was generated by AI.17. Document Types
The front matter type field maps to ARDS artifact types. Types are advisory — any block can appear in any document type.
| Type | Purpose | Key Blocks |
|---|---|---|
doc | Evergreen knowledge | ::data, ::callout, ::related |
guide | Living how-to | ::callout[type=tip], ::code, ::tabs, ::details |
conversation | Decision record | ::turn, ::alternatives, ::decision, ::tasks |
plan | Time-stamped strategy | ::timeline, ::metric, ::tasks, ::data |
agent | Agent definition | ::data, ::callout |
preference | User config | ::data[format=json] |
report | Analysis output | ::chart, ::metric, ::data, ::summary |
proposal | RFC-style proposal | ::alternatives, ::decision, ::tasks, ::related |
incident | Incident record | ::timeline, ::turn, ::callout[type=error], ::tasks |
review | Structured review | ::turn, ::data, ::decision |
18. Relationship to ARDS
SurfDoc is the document format layer of the ARDS ecosystem. Every ARDS artifact is a SurfDoc. But SurfDoc is not limited to ARDS — it works independently as a general-purpose document format.
ARDS v1-3: File structure + discovery order + artifact types
|
ARDS v4: Knowledge-context unification + scope model
|
SurfDoc: Document format that makes artifacts rich, interactive, and AI-nativeYou do not need ARDS to use SurfDoc. You do not need SurfDoc to use ARDS. They are better together.
19. Export Targets
| Target | Description |
|---|---|
| Professional typography. Charts as vector graphics. Layout blocks produce actual columns. | |
| HTML | Full interactivity. Sortable tables, collapsible sections, tab switching, chart rendering. |
| DOCX | Word-compatible. Tables, headings, styled callouts. Charts as embedded images. |
| Slide Deck | Experimental. Top-level headings become slides. ::columns become slide layouts. |
20. Tooling
surf CLI
surf render <file> # Render to terminal
surf render <file> --html # Render to HTML
surf validate <file> # Validate syntax and front matter
surf convert <file> # Auto-annotate markdown with blocks
surf new doc "Architecture" # Create new SurfDoc
Parser Library (surf-parse)
Reference parser in Rust. 378+ tests, 35 block types implemented. Also available as npm package, Python package, and WASM module.
let doc = surf_parse::parse(content);
let html = doc.doc.to_html(); // Rendered HTML
let blocks = doc.doc.blocks(); // Structured block tree
21. Web Generation
SurfDoc files can generate complete websites. A .surf file with ::site and ::page blocks contains everything needed to produce a multi-route static site.
surf build site.surf --out dist/ # Static HTML+CSS+JS
surf serve site.surf # Local dev server with hot reload
surf pack site.surf --out site.html # Single portable HTML file
Layout Types
| Layout | Description |
|---|---|
hero | Full-viewport splash with centered content and CTA |
cards | Responsive card grid (auto 1-2-3 columns) |
pricing | Pricing comparison with highlighted tier |
docs | Sidebar navigation + content area |
landing | Vertically stacked full-width sections |
split | Two-panel side-by-side (text + image) |
gallery | Image/project grid with lightbox |
22. Self-Contained Bundling
SurfDoc supports bundling all assets into a single .surfpack file (ZIP archive) or a single .html file with all content inlined.
document.surfpack
+-- index.surf # The main document
+-- manifest.json # Asset inventory with checksums
+-- assets/
+-- images/
+-- fonts/surf pack --html produces one HTML file containing all content, CSS, base64-encoded assets, and the embedded runtime. The original .surf source is recoverable via surf unpack.
23. Computed Data Layer
Data blocks can reference other blocks and compute derived values using =function(#block-id, 'column') syntax.
| Function | Purpose |
|---|---|
=sum(#id, 'col') | Sum of column values |
=avg(#id, 'col') | Average |
=count(#id) | Row count |
=max(#id, 'col') | Maximum |
=min(#id, 'col') | Minimum |
=last(#id, 'col') | Last row value |
Computed values are not Turing-complete — no loops, no conditionals. They degrade to their last-evaluated static value.
24. Security Model
SurfDoc is designed to be received from untrusted sources. The security model assumes every document is adversarial.
Core Principles
- Inert by default. Nothing executes on open, load, render, or view.
- No ambient authority. No cookies, no session tokens, no implicit credentials.
- Typed block tree, not arbitrary DOM. Unknown types render as plain text.
- Fixed visual semantics. A
::callout[type=warning]always looks like a warning. - Explicit permission model. Execution, network, and filesystem require per-document consent.
- Deterministic rendering. Same input always produces the same visual output.
Permission Categories
| Permission | Default | Grant Mechanism |
|---|---|---|
execute | Denied | Per-block consent dialog |
network | Denied (cross-origin) | Same-origin auto; cross-origin consent |
filesystem | Denied | Per-file consent dialog |
clipboard | Allowed (read-only) | Automatic for user-initiated copy |
print | Allowed | Automatic |
25. Context-Budget Loading
SurfDoc defines a loading priority protocol for AI agents operating under token constraints. Any block can declare a priority attribute (0-4).
| Priority | Content | Loading |
|---|---|---|
| 0 | Front matter + ::summary blocks | Always loaded first |
| 1 | Core content | Loaded early |
| 2 | Supporting content | On demand |
| 3 | Supplementary | If budget permits |
| 4 | Appendices, historical | Only if specifically needed |
26. Surf Browser
SurfDoc defines a browser architecture for native rendering with a dual-engine design.
Surf Browser
+-- SurfEngine (Rust + GPU) <- .surf files
| +-- surf-parse <- Parser
| +-- surf-layout <- Block tree to positioned rectangles
| +-- surf-render <- GPU rendering (wgpu/skia)
|
+-- WebView (platform WebKit) <- .html sites
| +-- Full web compatibility
|
+-- AI Layer
+-- Block-tree query API
+-- Summarization
+-- Knowledge graphNo DOM. SurfEngine uses a typed, immutable block tree. No XSS. No script injection vector. Deterministic rendering. Same .surf always produces the same visual output.
27. Executable Code
Documents contain code, and code should be runnable. The runnable attribute on ::code blocks activates execution, turning a .surf file into a Jupyter-class notebook that degrades to a readable text file.
::kernel
Declares the execution environment (runtime, packages, sandbox level) for a language.
Attributes
| Attribute | Values | Default |
|---|---|---|
lang | Language identifier | Required |
env | Environment name | None |
runtime | Runtime version | System default |
packages | Package list | None |
sandbox | strict, network, filesystem, unrestricted | strict |
Source
::kernel[lang=python env=analysis]
runtime: python3.12
packages: [numpy, pandas, matplotlib]
sandbox: strict
::Degrades to
*[Kernel: python3.12, packages: numpy, pandas, matplotlib]*::output
Captures the result of executing a ::code[runnable] block.
Attributes
| Attribute | Values | Default |
|---|---|---|
for | ID of source ::code block | Required |
timestamp | ISO 8601 timestamp | Auto |
exit | Exit code (0 = success) | Auto |
format | text, html, image, table, chart | text |
truncated | Boolean | Auto |
Source
::output[for=analysis timestamp="2026-02-10T12:00:00Z" exit=0]
Mean quarterly revenue: $12,000
Growth: 633%
::Degrades to
```
Mean quarterly revenue: $12,000
Growth: 633%
```28. Accessibility
SurfDoc is accessible by construction, not by remediation. If a .surf file is valid, it is accessible. Every block type defines a narration strategy, reading modes, and semantic navigation.
Block Narration Contract
Every block type MUST define a narration strategy. Adding a new block type without defining its narration is a compilation error in the reference implementation.
Reading Modes
| Mode | Behavior |
|---|---|
| Overview | Announces document structure: section count, block types, task completion |
| Type-Filtered | Read only blocks of a given type (e.g., decisions only, metrics only) |
| Skim | High-signal blocks only: summaries, callouts, metrics, decisions |
| Conversation | Natural language queries about the document content |
Semantic Navigation
| Key | Target |
|---|---|
| H | Next heading |
| D | Next ::decision |
| M | Next ::metric |
| T | Next ::tasks |
| A | Next ::callout (warnings first) |
| C | Next ::cta action |
| S | Next ::summary |