Prototype
Substrate loaded
Untrusted document
Welcome to Folio
Slide 1 of 4

Your day, tools, and notes can live in one document.

Folio is a document-based workspace. Instead of keeping thoughts in one app, tasks in another, and communication somewhere else, Folio lets the document become the surface where all of that work comes together.

You can read it as plain text, but parts of that text can also render as live widgets. The goal is simple: keep the document readable, but make it powerful enough to act like a working environment.

The core idea
Write once as text. Render when useful. Keep the document understandable even without the widgets.
Slide 2 of 4

Tasks stay in the text, but become interactive when rendered.

In Folio, a task is not hidden inside an app database. It is a line in the document. When rendered, that line becomes a checkbox with metadata like due dates, priority, and blockers.

Text in the document
::task[send-followup]{due=today priority=high status=todo} ::task[prepare-notes]{due=tomorrow blocked-by=send-followup status=todo}
Rendered task widget
::task
send follow-up
due today high todo
prepare notes
due tomorrow blocked
Slide 3 of 4

Email and calendar come into the document without pretending they live there.

Some things still belong to remote systems. Your inbox and calendar stay in their own backends, but Folio can project them into the document so you can review them alongside notes, tasks, and analysis.

Text in the document
::cal[today]{view=agenda} ::end ::email[inbox]{filter=unread} ::end
Rendered calendar + inbox
::cal
09:00
Venue planning call
30 min · pending
14:00
Budget check-in
45 min · accepted
::email
Mira Patel 08:42
Updated rehearsal notes
Sent the revised arrangement and marked two bars for review.
Sent the revised arrangement and marked two bars for review. If you are happy with it, I will send the score to the rest of the group before noon.
Jordan Lee 07:58
Need confirmation before noon
Can you confirm the final timing before I send the venue update?
Can you confirm the final timing before I send the venue update? I can hold the room until noon, but after that they may release it.
Slide 4 of 4

The document underneath is always visible.

Use the code icon in the header to switch between rendered widgets and the full text substrate. That makes Folio useful for both exploration and inspection: you can see the live interface, then see the exact text that drives it.

Full text substrate
Morning planning ::email[inbox]{filter=unread} Follow up with the venue after rehearsal numbers are confirmed. ::task[confirm-venue]{due=today priority=high status=todo}
Same section, rendered

Morning planning

::email
Avery Gomez 09:10
Venue confirmation
We can hold the room until 4pm if you confirm today.
We can hold the room until 4pm if you confirm today. If you want the extra rehearsal setup, I need the final headcount before lunch.

Follow up with the venue after rehearsal numbers are confirmed.

::task
confirm venue
due today high todo

The default document is already loaded for you. Explore it first. If you want a profile-specific opening later, open Generate document from the menu, then add local directives yourself as you work.

Generate Document

Describe the person and context you want Folio to generate for. Saving this form will replace the current document with a profile-specific opening date and prose.

Contacts

Add the people who shape your day: coworkers, clients, collaborators, family, teachers, friends, or community members. Their roles help the generator produce more realistic email, chat, and calendar activity.

Remote Provider

Folio generates mirrored data and full documents through a remote OpenAI-compatible API. These settings are saved with the generation flow.

No saved API key.

Provider status

If no API key is saved, generation will stop and ask you to configure the remote provider first.

Folio Usage Manual

What is Folio?

Folio is a document-based personal computing environment. Owned state such as tasks, source blocks, tables, and local computation lives directly in the text document. Mirrored state such as calendar, email, and chat is projected into that document so you can act on it in context without pretending the remote backend lives in the file.

Every change you make — checking a task, editing a table cell, filing an email, accepting a calendar event — becomes a text edit. For owned state, the document is the system of record. For mirrored state, the document keeps the projection parameters, local notes, and a durable journal of what you did.

Toolbar

New
Reset to the example seed document.
Save
Download the current document as a .folio.txt file.
Load
Open a previously saved document from disk.
Clear persisted document
Remove all Folio local state stored in this browser, including saved documents, history, snapshots, and trust grants, then reload the app.
Generate document
Open one modal for both profile and remote provider settings, save them together, and immediately generate a new opening date and prose for the current document.
Trust document
Toggle whether the current document may access mirrored capabilities. Untrusted documents open with all mirrored access denied.

More Docs

For implementation details, architecture mapping, and the main JS modules in the prototype, use these companion docs:

Directives

Directives are lines that start with :: and summon live widgets into the document. They are readable as plain text even without a renderer. Projection-style directives can render after Enter or blur; content-bearing blocks still require ::end.

::meta — Lifecycle Metadata

An owned-state directive for document metadata. ::meta[status]{value=active} controls lifecycle state. active allows normal widget activation, dormant keeps mirrored widgets in fallback text unless opened manually, and archived leaves the document in text-first mode by default.

::spawn — Next Document

Creates the next document and a roll-up document by reference. The generated next document carries open tasks forward as cross-document ::note transclusions instead of copying them. compact done removes completed tasks from the current document as an explicit text mutation.

::spawn[next]{doc=daily:2026-03-24 title="Tuesday, 24 March 2026." after-status=dormant}

::cal — Calendar

Renders an agenda from the calendar backend. The directive line selects the projection; the body stores only local log lines and sync annotations. You can insert a projection-only calendar view as a single line and render it with Enter or blur; add ::end only when you want to keep a local body. If no mirrored calendar data exists yet, the first render may generate it through the remote provider. Pending events show accept / decline buttons; clicking one updates the backend first, then journals the confirmed action into the document.

::cal[today]{view=agenda}
::end

::email — Email

Inbox (::email[inbox]{filter=unread}) shows filtered messages with a file action that logs to the document. Thread (::email[thread-id]{limit=3}) renders a conversation with a reply bar. Inbox and thread projections can render after Enter or blur without ::end; if mirrored mail is empty, the first render may generate it through the remote provider. Draft (::email[draft]{to=... subject=...}) is a compose form whose body is the message text, stays local, does not invoke the remote provider, and still requires ::end. Sending replaces the block with a sent log line; discarding deletes it.

::contact — Contacts

A contact card with key = value fields in the body (name, email, role, org, phone, notes). Click edit to modify fields inline. email and call buttons log actions to the document.

::contact[sara.chen]
name = Sara Chen
email = sara@example.com
::end

::note — Notes

A transclusion block. source=type:id points at another block in the document, and optional field=... selects a structured field from that block. A pure transclusion note can render after Enter or blur without ::end; if you want an inline note body, keep it as a full block and close it with ::end. Editing the rendered note writes back to the source block; freeze replaces the transclusion with literal text.

::note[finance-contact]{source=contact:finance.team field=notes}
::end

::table — Tables

A text-native table with pipe-delimited rows. If editable=true, click any cell to edit it. Edits are written back to the document text. If linked to a ::py block via source=, the value is the source block id: ::table[budget]{source=q3-check} reads from ::py[q3-check]. The table reads exported variables from the Python block's runtime context, not from print() output. By default it looks for result (then table_result); use source-var= to override that variable name. Accepted source shapes are either a list of row objects or an object with headers and rows. re-sync re-runs the source block and rewrites the table body from the latest computed output; the divergence protocol tracks manual edits against that computed source:

  • Synchronised — table matches source output.
  • Diverged — cells edited, "edited" badge shown. Use re-sync to discard edits or detach to break the link.
::table[budget]{source=analysis editable=true}
| Region | Q1 | Q2 |
| North | 3500 | 4200 |
::end
::py[analysis]{run=auto}
result = [
  {"region": "North", "q1": 3500, "q2": 4200},
  {"region": "South", "q1": 2000, "q2": 2100}
]
::end

::py — Python

A computation block that runs inside a document-scoped Python context. Blocks execute top to bottom, sharing state through one namespace for the document. By default, Python can read owned-state namespaces such as table.query({'id': 'budget'}), note.query(...), and task.query(...). Mirrored namespaces such as calendar or email require both a trusted document and explicit grants. Click run to re-evaluate the document context through this block; run=auto evaluates automatically on load and after document changes.

::py[analysis]{run=auto}
result = [
  {'region': 'North', 'q1': 3500, 'q2': 4200},
  {'region': 'South', 'q1': 2000, 'q2': 2100}
]
print(sum(d['q1'] for d in result))
::end

::task — Tasks

A single-line directive with a checkbox. All metadata (due=, priority=, blocked-by=, status=) is preserved when you toggle it. Completed tasks get a completed= timestamp. A task with blocked-by=other-task is disabled until the blocker is done.

::task[call-finance]{due=today priority=high status=todo}

::file — Files

A reference to a file on disk. Renders a card with filename, path, and type icon. Set preview=true for a preview area.

::file[~/docs/report.pdf]{preview=true}

::chat — Chat

A slice of a chat channel from the chat backend. A projection-only channel view can render after Enter or blur without ::end; add a body only when you want to keep document-local log lines such as posted or promoted actions. If mirrored chat data is empty, the first render may generate it through the remote provider. Type a message and click Send to post to the backend and append a journal line; click promote to write a message out as prose below the embed.

::chat[#design]{limit=5}
posted to #design: "Ready for review?" — 09:15
::end

::web — Web Clips

Embeds a URL reference with reader-mode rendering. Body lines above --- are metadata (title =, summary =); lines below are your annotations, editable inline.

::web[https://example.com/article]{view=reader}
title = An Interesting Article
summary = Key findings about...
---
My notes on this article.
::end

Key Principles

  • Owned state lives in text — tasks, notes, tables, and local computation are canonical in the document.
  • Mirrored state is journaled in text — calendar, email, and chat remain canonical in their backends while the document records your context and actions.
  • Every action is a text edit — checking a task, sending a reply, filing an email all modify the document.
  • Graceful degradation — directives are readable as plain text, even without a renderer.
  • Auto-saved — every change persists to browser local storage automatically.

Security

Folio keeps a per-document trust policy. Owned-state directives always run from document text. Mirrored directives and mirrored namespaces inside ::py require both a trusted document and an explicit read grant such as email.read or cal.read. Marking a document untrusted clears its mirrored grants.

Inspection

The prototype exposes window.folioDebug.store, window.folioDebug.policy, window.folioDebug.simulation, window.folioDebug.simulationProfileStore, window.folioDebug.simulationScenarioStore, window.folioDebug.simulationSecretsStore, window.folioDebug.simulationSettingsStore, window.folioDebug.simulationLLMClient, window.folioDebug.OpenAICompatibleSimulationLLMClient, and window.folioDebug.openDocument(id) in the browser console. Use store.listDocuments(), store.getHistory(), store.getSnapshots(), simulation.exportState(), simulationScenarioStore.getLedger(), simulationScenarioStore.getSnapshot(profileId), simulationScenarioStore.listSnapshots(), and simulation.getClientStatus() to inspect persisted state, saved mirrored-backend snapshots, and the active generation provider.