Replaces the binary acl.allow/deny model with five permission verbs
(r/w/c/d/a) and first-class roles, and adds an authenticated file API
(PUT/DELETE/POST move/mkdir) so the HTML tools can edit-in-place over
HTTP. Closes the AC-3(7) and AC-6 federal-readiness gaps.
File API (zddc/internal/handler/fileapi.go)
- PUT <new> → action c
- PUT <existing> → action w
- PUT <.zddc> → action a (CanEditZddc strict-ancestor rule)
- DELETE → action d
- POST mkdir → action c (auto-writes creator-owned .zddc when the
parent is Incoming/Working/Staging)
- POST move → action w on src + c on dst, atomic via os.Rename
- Optional If-Match for optimistic concurrency, --max-write-bytes cap,
audit log emits a structured file_write event per operation.
Permission model (zddc/internal/zddc/{acl,file,roles,cascade_mode}.go)
- acl.permissions: { principal → verb-set } map; principals are email
patterns or role names. Empty verb set is an explicit deny.
- roles: { name → members } definitions, available at the level they
declare and all descendants. Closer-to-leaf shadows ancestor.
- Legacy acl.allow/deny still work; they fold into permissions at
parse time (allow → "rwcd", deny → "").
- Cascade walks leaf→root; first level with any matching entry wins;
the union of matching verb sets at that level decides.
- --cascade-mode=strict adds a root→leaf ancestor-deny pre-pass so an
ancestor explicit-deny is absolute (NIST AC-6). Default delegated
preserves the existing commercial behavior.
Special folders (zddc/internal/zddc/special.go)
- Incoming / Working / Staging: mkdir auto-writes a .zddc into the new
subdir granting created_by + that email rwcda directly. Same form
operators write by hand; creator can edit it later to add others.
- Issued / Received: server-enforced WORM split. Cascade grants
inherited from above the WORM folder are masked to r only; grants
placed at-or-below the WORM folder retain r,c. Operators grant
write-once (cr) to the doc controller via an explicit .zddc at the
Issued/Received folder. Admins exempt — only escape hatch.
Browser polyfill (shared/zddc-source.js)
- HttpDirectoryHandle + HttpFileHandle implement the FS Access API
surface (values, getFileHandle, createWritable, removeEntry,
queryPermission/requestPermission) over zddc-server's listing JSON
and file API. Existing tools written against showDirectoryPicker
work unchanged.
- detectServerRoot() returns { handle, status }: tools auto-load on
HTTP, surface a clear "no permission to list" message on 403, and
fall back to the welcome screen on 0.
- classifier renames take the atomic POST move path on HTTP-backed
handles; mdedit and transmittal route reads/writes through the
polyfill so prior FS-API code paths cover both modes.
Tests
- zddc/internal/zddc/{cascade_mode,roles,special,acl}_test.go cover
delegated vs strict, role membership / shadowing / legacy fallback,
WORM split semantics, verb-set parser round-trip.
- zddc/internal/handler/fileapi_test.go now also covers role-based
vendor scenarios, WORM blocking vendor & doc controller writes,
explicit Issued .zddc unlocking the cr drop-box, admin bypass,
auto-ownership on mkdir, and strict-mode lockouts.
Docs
- ARCHITECTURE.md + zddc/README.md document the verb model, role
syntax, special-folder behaviors, cascade-mode flag, and full file
API surface. Federal-readiness gap analysis strikes AC-3(7) and
AC-6.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|---|---|---|
| .. | ||
| css | ||
| js | ||
| vendor | ||
| build.sh | ||
| README.md | ||
| template.html | ||
ZDDC Markdown Editor
A lightweight, browser-based markdown editor with YAML front matter support.
🔗 Open Markdown Editor - Click to use online, or right-click → "Save Link As" to keep your own copy.
Reliability
This tool follows the "record player with the record" philosophy - the application and your data travel together. The single HTML file contains everything needed to edit markdown files locally in your browser.
Quick Start
- Open the editor in your browser
- Click Add Local Directory to choose a folder with markdown files
- Navigate the file tree on the left
- Click any
.mdfile to edit it - Click Save File or Save All to save changes
Features
📂 File Navigation
- Browse directories using the File System Access API
- Collapsible folder tree with file type icons
- Files sorted alphabetically with directories grouped
✏️ Markdown Editing
- Toast UI Editor with live preview
- Split view (markdown + preview)
- Full toolbar for formatting
📋 YAML Front Matter
- Separate front matter section at top of editor
- Auto-parsed and preserved on save
- Collapsible for more editing space
📑 Table of Contents
- Auto-generated from headings
- Adjustable depth (H1 only through H6)
- Click to jump to heading in preview
💾 File Operations
- Save individual files or Save All
- Reload from disk (discards unsaved changes)
- External change detection with reload prompt
- Unsaved change warnings before leaving
🖼️ File Previews
- Image preview for common formats
- HTML preview in sandboxed iframe
- Plain text editing for non-markdown files
Build
The editor is built from modular source files using a bash script:
cd mdedit
./build.sh
This concatenates CSS and JS files into dist/mdedit.html.
Project Structure
mdedit/
├── css/
│ ├── base.css # Core styles and layout
│ ├── editor.css # Toast UI Editor overrides
│ ├── toc.css # Table of Contents styles
│ └── markdown.css # Markdown rendering styles
├── js/
│ ├── app.js # Global state
│ ├── utils.js # Utility functions
│ ├── front-matter.js # YAML parsing
│ ├── file-system.js # File operations
│ ├── file-tree.js # Tree rendering
│ ├── editor.js # Toast UI setup
│ ├── toc.js # TOC generation
│ ├── resizer.js # Pane resizing
│ ├── events.js # Event listeners
│ └── main.js # Initialization
├── vendor/
│ ├── toastui-editor-all.min.js # Toast UI Editor JS (bundled)
│ └── toastui-editor.min.css # Toast UI Editor CSS (bundled)
├── template.html # HTML structure (uses CDN for local dev convenience)
├── build.sh # Build script (inlines vendor files, strips CDN refs)
└── dist/
└── mdedit.html # Built self-contained file
Technical Details
- No server required - runs entirely in browser
- File System Access API - direct local file access
- Toast UI Editor v3.2.2 - bundled from
vendor/into the built output (no CDN required) - Tailwind CSS - replaced at build time by
css/tailwind-utils.css, a hand-written static subset containing only the ~80 utility classes actually used intemplate.html(no runtime overhead, no console warnings) - Fully self-contained -
dist/mdedit.html(~850 KB) works offline with no external dependencies
Development note:
template.htmlloads Toast UI and Tailwind from CDN for a faster local development experience (opentemplate.htmldirectly in a browser). Thebuild.shscript replaces the Tailwind CDN<script>tag with nothing (utilities come fromcss/tailwind-utils.cssinstead) and replaces the Toast UI CDN tags with the locally bundledvendor/files when producingdist/mdedit.html.
Modules
CSS and JS modules live under css/ and js/. The canonical load order is in build.sh. See the root ARCHITECTURE.md for the build/module pattern and AGENTS.md for shared helpers.
mdedit-specific notes:
css/tailwind-utils.cssis a hand-curated static subset of Tailwind v3 — there is no Tailwind build step. Add a class here when adding it totemplate.html.- Toast UI Editor v3.2.2 ships pre-bundled in
vendor/.template.htmlloads it from CDN for dev convenience;build.shswaps the CDN tag for the bundled file. - File operations (create, rename, delete) live in
js/file-ops.js.
Build Process
The build script (build.sh):
- Concatenates all local CSS and JS files in dependency order
- Replaces the CDN
<script>/<link>tags for Tailwind and Toast UI with the locally bundled files fromvendor/ - Injects everything into
template.htmlto producedist/mdedit.html
The final HTML file (~850 KB) is fully self-contained and works offline.
Architecture Notes
- All local CSS/JS files are inlined into the output HTML
- Vendor dependencies (Toast UI, Tailwind) are bundled from
vendor/— no runtime CDN access template.htmlloads dependencies from CDN for convenient local development, butbuild.shreplaces these- No npm dependencies required at runtime
- File System Access API requires Chromium-based browsers