Summary
MDS currently compiles .mds templates into static .md files at build time. This feature makes MDS a runtime: when any process reads a .md file containing MDS directives, it transparently receives the compiled output. No source/target split, no build step — the file on disk contains MDS, but every reader sees rendered markdown.
Example
File on disk (CLAUDE.md):
# Project Context
@if git.branch == "main":
You are on the stable branch. Be conservative with changes.
@else:
You are on feature branch: {{git.branch}}
@end
@if system.os == "macos":
Run tests with: `cargo test --workspace`
@else:
Run tests with: `cargo test --workspace --target x86_64-unknown-linux-gnu`
@end
What Claude Code (or any process) reads:
# Project Context
You are on feature branch: feat/cool-thing
Run tests with: `cargo test --workspace`
Motivation
AI agents, LLM workflows, and developer tools all consume markdown files — CLAUDE.md, memory systems, prompt libraries, documentation. These files would benefit from being dynamic:
- Conditional sections based on git branch, OS, environment
- Context-aware instructions that adapt to who/what is reading
- Template composition without a separate build step
The compilation engine already exists in mds-core. The missing piece is a runtime trigger that intercepts file reads and compiles on the fly.
Architecture
Two platform-native implementations sharing mds-core as the compilation engine:
Linux: macOS:
┌──────────────────┐ ┌──────────────────────┐
│ mds-fuse │ │ mds-fs (Swift) │
│ (pure Rust) │ │ FSKit extension │
│ /dev/fuse proto │ │ (macOS 15+) │
└────────┬─────────┘ └──────────┬───────────┘
│ │ C FFI
▼ ▼
┌──────────────────┐ ┌──────────────────────┐
│ mds-core │ │ mds-cffi │
│ (Rust) │ │ (Rust → C API) │
└──────────────────┘ └──────────┬───────────┘
▼
┌──────────────────────┐
│ mds-core │
└──────────────────────┘
Linux — FUSE via /dev/fuse (pure Rust)
- Use the
fuse3 crate, which implements the FUSE protocol directly in Rust — no libfuse C dependency
- The kernel FUSE module is built into the Linux kernel, so there are zero external dependencies
- Passthrough filesystem: all operations delegate to the real filesystem except
read() on .md files
- On
read(): check if file contains MDS directives → compile with context → return compiled bytes
- Handle size discrepancy (compiled output differs from source) via correct
getattr reporting
macOS — FSKit (native, no kext)
- Use Apple's FSKit framework (macOS 15+), the official userspace filesystem API
- No kernel extension, no macFUSE, no third-party dependencies
- Thin Swift layer (~500-800 lines) that implements the FSKit filesystem interface
- Calls into
mds-core via C FFI through the mds-cffi crate
- Ships as a macOS system extension
C FFI bridge (mds-cffi)
- Thin C API over mds-core:
mds_compile(source, context) -> char*
- Enables the Swift FSKit extension to call into Rust
- ~200 lines — just marshaling strings and context across the FFI boundary
- Also opens the door for future integrations (Python ctypes, etc.)
Context System
Templates at read time need runtime context. A .mdscontext.toml at the mount root declares what's available:
[providers]
git = true # branch, commit, remote, dirty
env = true # all env vars (or allowlist with env.allow = ["HOME", "USER"])
system = true # os, arch, hostname, user
[static]
project = "mds"
tier = "production"
Built-in providers
| Provider |
Variables |
Refresh |
git |
git.branch, git.commit, git.remote, git.dirty |
On every read (cached, invalidated by mtime of .git/HEAD) |
env |
env.HOME, env.PATH, etc. |
On every read |
system |
system.os, system.arch, system.hostname, system.user |
On mount (static) |
Access in templates
@if git.branch == "main":
...
@end
Current user: {{system.user}}
Platform: {{system.os}}/{{system.arch}}
Directory overrides
Place another .mdscontext.toml deeper in the tree to override or extend context for a subtree. Child contexts inherit from parent and can override specific values.
CLI
mds mount <source> <mountpoint> # mount and compile on read
mds mount status # show active mounts
mds mount install # install as system service (auto-start on boot)
mds mount uninstall # remove system service
Service Integration
Users should not have to manually run mds mount after every reboot.
macOS — launchd
mds mount install generates and loads a launchd plist
- Auto-start on login, restart on crash
mds mount uninstall removes the plist and unmounts
Linux — systemd
mds mount install generates and enables a systemd user unit
- Auto-start on login, restart on crash
mds mount uninstall disables and removes the unit
Caching
- Cache compiled output keyed by
hash(source_bytes) + hash(context_state)
- Invalidate on source file mtime change or context state change (e.g., git branch switch detected via
.git/HEAD mtime)
- LRU eviction with bounded memory
- Passthrough for files with no MDS directives (detected by quick scan for
@if, @for, @define, {{)
New Components
| Component |
Language |
Location |
Estimated size |
mds-cffi |
Rust |
crates/mds-cffi/ |
~200 lines |
mds-fuse |
Rust |
crates/mds-fuse/ |
~800 lines |
mds-fs |
Swift |
darwin/mds-fs/ |
~500-800 lines |
| Context system |
Rust |
crates/mds-core/ (or new crate) |
~400 lines |
| Service integration |
Rust |
CLI extension |
~300 lines |
| Cache layer |
Rust |
Shared between fuse/cffi |
~150 lines |
Estimated Effort
| Phase |
Time |
| Context system design + providers |
1 week |
mds-cffi (C FFI bridge) |
1 day |
mds-fuse (Linux, pure Rust) |
3-4 days |
darwin/mds-fs (Swift FSKit) |
3-4 days |
| Context-aware caching |
2 days |
| CLI + service integration (launchd/systemd) |
3-4 days |
| Testing (mount/unmount cycles, concurrent reads, context changes) |
3-4 days |
| Total |
~4 weeks |
Risks
| Risk |
Impact |
Mitigation |
| FSKit API instability (macOS 15 is year one) |
API changes break macOS builds |
Pin to specific macOS SDK, abstract behind trait |
| Compiled output size ≠ source size |
Readers may truncate or over-read |
Report compiled size in getattr, not source size |
| Concurrent reads during context change |
Inconsistent output |
Atomic context snapshots per read |
| Deep/recursive MDS templates on hot path |
Latency on file reads |
mds-core already has MAX_* limits; add per-read timeout |
| FSKit requires macOS 15+ |
Excludes older macOS versions |
Document minimum version; consider macFUSE fallback as opt-in |
Non-goals (for this issue)
- Network/remote context providers (future work)
- Write interception (editing compiled output and saving back as MDS)
- Windows support (no clear userspace FS path yet)
- Custom plugin/extension providers (start with built-ins, extend later)
Summary
MDS currently compiles
.mdstemplates into static.mdfiles at build time. This feature makes MDS a runtime: when any process reads a.mdfile containing MDS directives, it transparently receives the compiled output. No source/target split, no build step — the file on disk contains MDS, but every reader sees rendered markdown.Example
File on disk (
CLAUDE.md):What Claude Code (or any process) reads:
Motivation
AI agents, LLM workflows, and developer tools all consume markdown files — CLAUDE.md, memory systems, prompt libraries, documentation. These files would benefit from being dynamic:
The compilation engine already exists in
mds-core. The missing piece is a runtime trigger that intercepts file reads and compiles on the fly.Architecture
Two platform-native implementations sharing
mds-coreas the compilation engine:Linux — FUSE via
/dev/fuse(pure Rust)fuse3crate, which implements the FUSE protocol directly in Rust — no libfuse C dependencyread()on.mdfilesread(): check if file contains MDS directives → compile with context → return compiled bytesgetattrreportingmacOS — FSKit (native, no kext)
mds-corevia C FFI through themds-cfficrateC FFI bridge (
mds-cffi)mds_compile(source, context) -> char*Context System
Templates at read time need runtime context. A
.mdscontext.tomlat the mount root declares what's available:Built-in providers
gitgit.branch,git.commit,git.remote,git.dirty.git/HEAD)envenv.HOME,env.PATH, etc.systemsystem.os,system.arch,system.hostname,system.userAccess in templates
Directory overrides
Place another
.mdscontext.tomldeeper in the tree to override or extend context for a subtree. Child contexts inherit from parent and can override specific values.CLI
Service Integration
Users should not have to manually run
mds mountafter every reboot.macOS — launchd
mds mount installgenerates and loads a launchd plistmds mount uninstallremoves the plist and unmountsLinux — systemd
mds mount installgenerates and enables a systemd user unitmds mount uninstalldisables and removes the unitCaching
hash(source_bytes) + hash(context_state).git/HEADmtime)@if,@for,@define,{{)New Components
mds-cfficrates/mds-cffi/mds-fusecrates/mds-fuse/mds-fsdarwin/mds-fs/crates/mds-core/(or new crate)Estimated Effort
mds-cffi(C FFI bridge)mds-fuse(Linux, pure Rust)darwin/mds-fs(Swift FSKit)Risks
getattr, not source sizeNon-goals (for this issue)