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.
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.
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.
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.
Morning planning
Follow up with the venue after rehearsal numbers are confirmed.
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.
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.
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.
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.
If no API key is saved, generation will stop and ask you to configure the remote provider first.
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.
.folio.txt file.For implementation details, architecture mapping, and the main JS modules in the prototype, use these companion docs:
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.
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.
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}
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
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.
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
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
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:
::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
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
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}
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}
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
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
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.
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.