Skip to content
psxvoidPublic

About

Extend Obsidian bases with custom and global formulas, virtual files, and more.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

ExBase

ExBase is a proof of concept that exposes objects in a note's frontmatter array or in a .base file itself to Obsidian's built-in Bases views. It does not register a custom Bases view or create proxy files.

Obsidian Bases indexes files, not arbitrary objects. ExBase bridges that boundary with an in-memory emulation layer over Obsidian's Vault and MetadataCache read APIs. Native Bases receives virtual TFile entries whose complete state comes from the source note's frontmatter or the base file's own entries.

Donate

If you like what exbase does, you can support its development with a donation:

Currency Address
BTC 1GdViZLJpCFJJ9PVfFkHsK4LsGdbVghtz2
BCH qz4hp4fved5jq0pwnv69d4hez5f9m7dh0v4hh8pfgz
ETH 0x49e826930ee9819Cc92c339a7DCBa9f43A516332

AI tokens are expensive nowadays.

Testing

Vaults for testing all features:

  1. 0.1.0-obsidian-1.13.0

Usage

Add an exbase-entries array to a note:

---
exbase-version: 1
exbase-path: Virtual tasks
exbase-entries:
  - id: first
    xb-content: "# First item\n\nThis Markdown body is stored in frontmatter."
    xb-ctime: 2026-08-01T09:00:00Z
    xb-mtime: 2026-08-09T12:00:00Z
    name: First item
    status: active
    score: 10
  - id: second
    xb-basename: second
    xb-content: "# Second item"
    name: Second item
    status: paused
    score: 4
---

Enable the plugin. ExBase publishes the array items as in-memory files and refreshes them when source metadata changes. The array property can be changed under Settings → ExBase.

By default, Expose entries as loaded files makes entry files visible at their virtual paths in Obsidian's file manager, loaded-file collection, and other plugins. Source notes remain regular Markdown files. Disable this patch under Settings → ExBase → Patches to keep entries out of those APIs; Bases and ExBase's virtual read APIs continue to work.

Frontmatter entries are hidden from the file explorer by default. Enable Show frontmatter entries in the file explorer under Settings → ExBase to surface them under virtual folders named after their source notes. This also makes them available to other plugins through the explorer-facing vault APIs. The option requires Expose entries as loaded files and is disabled while that patch is off.

File operations in ExBase-managed folders follow the virtual entry model:

  • Create a Markdown file to append an entry to the source note.
  • Rename, copy, or delete an entry to update its source array item. Entries cannot be dragged to another folder, and managed folders cannot contain nested folders.
  • Rename, copy, or delete the managed folder to perform the same operation on its physical source note.

By default, the source note remains in file lists exposed to Bases and its configured entries property remains available in metadata. Enable Exclude notes containing entries under Settings → ExBase to show only virtual entries in those file lists.

Entry storage

ExBase caches scanned entries in IndexedDB so startup does not re-derive every entry from source frontmatter. Sources whose content is unchanged since the last scan are served from the cache; everything else is re-scanned and the cache is refreshed.

The cache is validated per source file (modification time and size) and never overrides the vault, which remains the source of truth. Writes are batched and flushed asynchronously; a crash before a flush simply causes the affected sources to be rescanned on the next startup. The cache is per vault and is wiped automatically when the cache schema or the configured array property changes.

Create entries from a Base

Without Choose source for new base entries, Obsidian creates each new Base item as a regular Markdown file. Enable the setting under Settings → ExBase to add a Source control to built-in Base toolbars. The selection is stored per Base view:

  • Default keeps Obsidian's regular Markdown file creation.
  • This Base stores entries in the .base file itself (see below).
  • ExBase note appends entries to the configured entries array of a chosen Markdown note.

With ExBase note selected, creating an item from the Base appends a new virtual note to the selected note's frontmatter array instead of creating a regular file. ExBase applies the Base's initial properties and adds the managed xb-ctime, xb-mtime, and xb-size metadata.

Positive file.hasTag(...) filters on the Base are also applied to new entries as their tags property, so the entry remains visible in that Base.

The Source control is not available in embedded ```base code blocks: they have no backing file to store entries in, so their New button keeps Obsidian's default behavior.

Store entries in the Base itself

Add an exbase-entries array (the configured array property) at the top level of a .base file:

filters: ...
views:
  - type: table
    name: Table
exbase-entries:
  - id: first
    xb-content: "# First item"
    name: First item
    status: active

ExBase publishes these items as entries of that base. The key survives Obsidian's own rewrites of the .base file (column reorders, filter edits, view changes). With This Base selected in Source, the New button appends entries to this array; reading, renaming, copying, deleting, and editing properties in the base view all write back to the .base file.

Base-stored entries are private to their base:

  • They appear in that base's views, but not in the file explorer, global search, graph, or other plugins.
  • Other bases do not list them as results; they remain reachable from other bases through relation properties. Disable Scope base entries to their base under Settings → ExBase → Patches to let every base list them.
  • Entries already stored in a base are always shown in that base, even if Source is set to Default or ExBase note — the source option only steers where new entries go.
  • Enable Show base entries in the file explorer under Settings → ExBase to surface them under a virtual folder named after the base. This also makes them available to other plugins and requires Expose entries as loaded files.

Clicking an entry in a base opens it in the Markdown editor (the rendered virtual note), not the base view; this is the Open base entries in the editor patch, enabled by default.

Filter entries stored in a base

Base-stored entries live at virtual paths beneath a folder named after the base file, so the standard Bases filter works on file.path. For a base at Projects/Apollo.base the entries are at Projects/Apollo/...; to show only its entries (or only note entries elsewhere), filter on the path prefix:

  • Only this base's entries: file.path.startsWith("Projects/Apollo/")
  • Only note (frontmatter) entries, excluding all base-stored entries: keep them out by excluding every base folder you use, for example !file.path.startsWith("Projects/Apollo/").

Because the virtual folder name derives from the base file name, renaming the base moves the folder and requires updating the filter.

The same special properties (exbase-path, exbase-version, xb-*) apply as for note-stored entries.

To delete a custom property or formula from a .base file, enable Remove custom base properties under Settings → ExBase, right-click its column header, and select Remove column.

Remove column is available for:

  • Formulas declared in the .base file's formulas section.
  • Columns with a property configuration explicitly saved in the .base file's properties section, such as a custom display name.

It is not available for:

  • Note properties that Bases inferred from note frontmatter but that have no saved configuration in the .base file.
  • Built-in file properties, such as file.name or file.mtime, unless the .base file contains an explicit property configuration for that column.
  • Any column that is only referenced by a view's order, sort, summary, or grouping configuration.

Removing an eligible column deletes its definition and its references from the Base; it does not delete property values from notes.

Note relations in Bases

Enable Edit relations in bases under Settings → ExBase → Patches to add searchable note selection to configured Base properties. The patch is disabled by default and currently supports Obsidian 1.12.x.

Open a Base, select Properties, and open a note property. Set its Base-local type to Relation, then configure Target and Multiple. A column's context menu also provides Relation in this view for quickly enabling or disabling the relation. The Properties list, column header, edit form, and cells reflect the active view's setting.

ExBase stores this assignment in that view's relations list in the .base file, not in .obsidian/types.json. The same property can therefore be a Relation in one Base view and use its normal note property type, such as List, in another:

relations:
  - property: participants
    target: exbase-people.base#People
    multiple: true

Its target must be a vault folder, a .base file, or a .base file plus view fragment, such as People, People.base, or People.base#Directory. A Base target uses its first view unless a view fragment is supplied. Targets without a leading slash are resolved relative to the Base containing the relation, with the vault root as a fallback. Prefix a target with / to resolve it from the vault root, for example /Directories/People.base#Directory or /People. These are vault paths; filesystem paths such as C:\Notes\People.base are not supported. The note Properties editor shows an inline error when a Base file or view cannot be resolved. It continues to manage the property's global fallback type independently.

Add the configured property as a column in a built-in Base table, then select its cell. ExBase displays existing values as native-style link pills and opens Obsidian's suggestion menu as you type. Enter or mouse selection adds the highlighted note, Escape closes the editor, and Backspace in an empty input removes the last relation. Multiple relations remain open after selection so more notes can be added. Select a relation pill to open its note or virtual entry; Ctrl-click or Cmd-click opens it in a new tab.

Relation values remain ordinary Obsidian Markdown links in frontmatter for both physical notes and virtual ExBase entries:

project: "[[Projects/Apollo|Apollo]]"
participants:
  - "[[People/Ada Lovelace|Ada Lovelace]]"
  - "[[People/Grace Hopper|Grace Hopper]]"
  - "[[ExBase demo/People/Contacts/Alan Turing|Alan Turing]]"

Relation values rendered in Base cells use normal Obsidian internal file links, including links to ExBase virtual entries. Newly selected relations use the note name as a link alias, so pills show the name instead of the full path.

Links use the vault's configured link format. Existing unresolved links remain visible and removable. Relation changes use the built-in Bases change callback, so they participate in native Base updates and undo instead of writing frontmatter independently.

Virtual ExBase entries are selectable from folder, vault-wide, and Base targets. Legacy encoded ExBase relation references remain readable and are rendered as normal internal file links.

Two-way relations, inverse columns, note creation from suggestions, and rollups are not yet implemented.

Each virtual file exposes the source's parsed exbase-version and exbase-path properties. Version 1 is supported; a missing, invalid, or unsupported version is exposed as unknown. Set exbase-path to place entries in a custom vault folder. These special properties are hidden from the source file's exposed metadata and are read-only on virtual files; change their stored values in the source note or .base file.

Edit an ordinary property such as name, status, or score directly in a built-in Base view. ExBase intercepts Obsidian's frontmatter write, updates the corresponding array item in the source note, and refreshes the virtual entry. Adding and deleting properties through a Base are supported as well.

Create a normal Base and filter it to the virtual paths, for example:

filters:
  and:
    - file.inFolder("Virtual tasks")
views:
  - type: table
    name: Array entries

Global formulas

Define formulas once under Settings → ExBase → Global formulas. Select the plus button, enter a property name and a Bases formula, and the definition is saved in the plugin's data.json. Use the remove button beside a definition to delete it.

Global formulas are enabled by default. Turn off Enable global formulas to stop exposing the definitions in Bases without deleting them. Custom formula functions remain available while global formulas are disabled.

Each complete definition is available in every Base as a formula property. For example, a global formula named total is exposed as formula.total and can be added to a view like a formula declared in that Base's formulas section. Changes refresh open Bases immediately. An incomplete row is saved but is not exposed until both fields have values. If a Base declares a formula with the same name, the Base-local formula takes precedence.

Custom formula functions

ExBase adds custom functions to built-in Bases formulas.

Each custom formula function is enabled by default and has its own toggle under Settings → ExBase → Custom formula functions. If a function fails at runtime, ExBase disables only that function for the current session and marks its toggle with an alert icon. Fix the formula, then enable the function again to clear the failure quarantine and retry it without reloading Obsidian. Other custom functions and global formulas remain available.

getFilesByPathRegex

getFilesByPathRegex(pattern) accepts one regular expression string and returns file links for matching vault paths, in the same list-like form as file.backlinks.

formulas:
  projectFiles: 'getFilesByPathRegex("^Projects/.*\\.md$")'

noteContent

noteContent(pathOrLink, maxCharacters?) accepts a vault path or internal link and an optional non-negative character limit. It returns the Markdown body without YAML frontmatter. Note content is cached and refreshed when files change.

formulas:
  preview: 'noteContent(file.path, 160)'
  linkedContent: 'formula["sameNameNote2"].map(noteContent(value))'

taskCount

taskCount(path, mode) counts Markdown tasks in a vault file or ExBase entry. The mode must be "total", "completed", "cancelled", "todo", or "inprogress". Completed tasks use [x], cancelled tasks use [-], todo tasks use [ ], and in-progress tasks use [/]. Other custom task statuses count only toward the total.

formulas:
  openTasks: 'taskCount(file.path, "todo") + taskCount(file.path, "inprogress")'

Custom functions are registered in src/formulas/custom-functions, where more function definitions can be added without changing the evaluator patch.

The following xb- array keys describe the virtual file and are not exposed as note properties:

  • xb-basename: virtual filename without the .md extension.
  • xb-content: Markdown body returned by Vault.read and Vault.cachedRead.
  • xb-ctime: creation time as epoch milliseconds or a date string.
  • xb-mtime: modification time as epoch milliseconds or a date string.
  • xb-size: optional reported byte size. It is calculated from generated content by default.

All other keys become the virtual file's frontmatter properties, so names such as mtime remain available for user data. ExBase combines exbase-path with xb-basename, the item's id, or its array position. When exbase-path is omitted or invalid, ExBase derives the folder from the source note. Renaming a virtual file updates xb-basename on its source entry.

In Base formulas, access these values by their stored frontmatter key, for example file.properties["namePrefix"], note.namePrefix, or namePrefix. Enable Property aliases in formulas under Settings → ExBase to also access properties by Base display names, such as file.properties["notePrefix"]. Alias support updates immediately when the setting or a Base configuration changes. Aliases shared by different stored properties across Base files are ignored to avoid ambiguous results.

Limitations

Virtual note properties are writable from built-in Base views. File metadata fields with the xb- prefix must still be edited in the source frontmatter array, except xb-basename, which is updated when the virtual file is renamed.

Large source notes are indexed incrementally: ExBase derives entries and publishes file events in small batches that yield to the app between batches, so creating or editing a source note with thousands of entries stays responsive. During bulk work, a status bar item shows progress as ExBase: indexing entries processed / total, mirroring Obsidian's own indexing indicator; it appears only when a reconcile processes at least 500 units of work.

Obsidian operations that require a physical adapter-backed file, including opening the virtual Markdown body in an editor, deleting the virtual file, attachments, and resource URLs, are not emulated.

The implementation patches public read and file-enumeration methods at runtime because Obsidian does not expose an official virtual-file provider API. The optional relation editor also patches Obsidian's internal metadata-widget dispatch because the public API does not expose custom Base cell editors. Compatibility can therefore vary between Obsidian releases. Failed patches are disabled automatically and marked in settings. Automatic patch retries under Settings → ExBase → Patches controls how many times ExBase retries each failed patch or global-formula refresh with base-2 delays of 1, 2, 4, 8, 16, and 32 seconds; set it to 0 to disable automatic retries. Formula refresh failures escalate to the custom-functions patch failure flow only after their retries are exhausted. ExBase then groups patch failures that occur together into one notice; select Try Enable to restore and retry every listed patch, or Dismiss to leave them disabled.

Developer and troubleshooting

Developer settings are hidden by default. To reveal the Developer Settings section, select the Troubleshooting heading under Settings → ExBase three times within five seconds.

Developer commands are disabled by default. After revealing the settings, enable Enable developer commands to expose Generate test note, Generate test base, and Generate test bases with relations in the command palette. Generate test base creates a .base file with entries stored in its own exbase-entries array, exercising the base entry source. Generate test bases with relations creates two .base files whose entries cross-link each other through relation properties (team and project), with each view's relations config targeting the other base. All generated entries include xb-ctime, xb-mtime, and xb-size metadata; xb-size is the UTF-8 byte size of the generated xb-content.

Index virtual entries is also available in Developer Settings. It is enabled by default and caches entries in Obsidian so other plugins can discover them.

The Troubleshooting section remains visible. Troubleshooting logs are disabled by default; enable Enable troubleshooting logs there to write ExBase warnings and errors to the developer console. The setting takes effect immediately and does not require reloading the plugin.

The command asks how many entries to create and defaults to 1000. Press Enter to accept the selected value. It creates and opens a new ExBase note containing random properties, Markdown content, and tasks for every entry. Existing test notes are preserved by adding a numeric suffix to the generated filename.

Development

npm install
npm run dev
npm run build
npm run lint
npm test
npm run test:watch

About

Extend Obsidian bases with custom and global formulas, virtual files, and more.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages