Assets and embeds
Drop files into the editor, reference them with wiki-style embeds, and let Open Knowledge dedup duplicates and resolve paths.
Open Knowledge treats assets (images, PDFs, video, audio, fonts, archives) as first-class content. Drop a file from Finder into a note, paste a screenshot, or open an existing vault — the editor writes the binary to disk next to your markdown and inserts a reference that renders inline in WYSIWYG and round-trips byte-identically through source view.
Dropping files
Any file is accepted — there is no user-facing size cap. Uploads stream to disk with on-the-fly sha256 (memory footprint is O(1)), so a 500 MB video works the same as a 50 KB screenshot. The only rejection axis is disk fullness, which surfaces as HTTP 507 (storage-full). The editor routes files to one of three insertion shapes based on the extension:
| File type | Shape | Example |
|---|---|---|
Images — .png, .jpg, .gif, .webp, .avif, .svg | Inline image via wiki-embed | ![[photo.png]] |
| PDF, MP4, MP3, WAV, OGG, M4A, MOV, WebM | Wiki-embed (plain-link fallback today; typed viewers in a future release) | ![[draft.pdf]] |
| Opaque (ZIP, DOCX, CSV, arbitrary) | Markdown link | [archive.zip](archive.zip) |
Images render as <img> in both WYSIWYG and the rendered output. Video, audio, and PDF embeds currently render as clickable links; rich inline viewers land with the typed component-nodes rollout — the storage shape (![[file.ext]]) will not change, so you're not painting yourself into a corner.
Open Knowledge always emits ![[file.ext]] for files whose extension is in its built-in wiki-embed list (images + PDF + video + audio). Opaque file types always emit as standard markdown links. There is no configuration knob — the emit shape is determined entirely by the extension.
Where files land on disk
Assets are written next to the document that references them. Drop photo.png into docs/meeting.md and the file lands at docs/photo.png. This is the only option today; there is no config knob for a centralized /assets/ folder (see Limitations and future work below for the migration-tool story if you need one).
Deduplication
Dropping the same screenshot twice into the same directory is a silent annoyance that Obsidian has carried for years. Open Knowledge checks sha256 against sibling asset files before writing. When the bytes match:
- The server returns the existing path and the editor inserts a reference to it.
- A toast appears:
Already at docs/pasted-20260416-140523.png — reusing. - No new file is written.
Dedup is scoped to the destination directory only (different dirs that happen to contain the same bytes stay independent). There is no knob to disable it — the behavior is always on.
Moving documents with refs
When you rename a document via the UI, MCP, or drag-drop:
- Relative markdown image refs (
,) are rewritten to resolve from the new location. - Absolute-path refs (
) and URL refs (http(s):,data:) are left unchanged. - Wiki-embed refs (
![[photo.png]]) are left unchanged — they resolve dynamically through a file-basename index, so the same byte-identical string keeps working from the new location.
Assets themselves stay put during a document rename. If you want an asset to move with the doc, move it manually — the design assumption is that shared assets (one logo.png referenced by five docs) are a common case, and auto-relocating would silently break the other four.
Ambiguous embeds
Wiki-embeds use just the filename, so two files with the same basename in different directories (docs/photo.png and archive/photo.png) need a tiebreak rule. The resolver uses Foam-style shortest-path from the current doc's directory:
- Prefer a match inside the current doc's subtree; shallowest depth wins.
- Otherwise prefer the fewest path hops from the current doc.
- If still tied, pick alphabetically — deterministic across server restarts.
The lookup is case-insensitive on the basename (matching Obsidian), but the on-disk path is preserved in its original case.
Security
- Storage never sanitizes content — raw HTML and entity references pass through unchanged. XSS mitigation is a render-time concern of the publishing pipeline, not of Open Knowledge storage.
- SVG files render via
<img>(never inline DOM) to prevent script execution via SVG<script>. This routing is preserved even under the accept-all upload posture. - Filenames are sanitized to preserve Unicode code points (letters, digits, punctuation, emoji) while stripping path separators, control bytes, and escape sequences.
- Path-escape attempts (
.., absolute paths, symlinks that resolve outside the content directory) are rejected at the upload boundary.
Limitations and future work
- Non-image embeds render as plain links today. Video, audio, and PDF get dedicated inline viewers in the typed component-nodes release. Storage shape won't change.
- Cross-directory dedup is not implemented. Scope is same-directory only; if you drop the same bytes into two different directories, you get two files.
- Garbage collection of orphaned assets is manual. A future
openknowledge gcCLI command will scan refs, diff against disk, and surface orphans with confirm-delete UX. - Embed size modifiers (
![[image.png|640x480]]) aren't parsed. The base embed round-trips correctly; the|modifiersuffix passes through as alias text. - GitHub raw-file preview doesn't render wiki-embed refs. GitHub renders
and[text](url)forms natively;![[...]]shows as literal text. This is a known tradeoff of the Open Knowledge wiki-embed shape. - Editing an asset in place does not refresh the rendered embed until reload. When you overwrite an image or PDF on disk (replace-in-Finder, save-over, external tool), the file watcher fires and the file list re-invalidates — but open editor tabs keep the old bytes cached by the browser's asset URL. Reload the tab to see the updated bytes.
- No user-facing upload config. Attachment path (co-located), emit shape (
![[...]]), dedup behavior (same-dir always on with toast feedback), and the wiki-embed extension list are fixed. Re-introducing any as a knob needs a concrete user-sourced use case. Obsidian refugees whose vault uses a non-defaultattachmentFolderPathoruseMarkdownLinks: truewait for a future one-shotok migrate --from-obsidian-vaultCLI.
Next steps
Configuration
Full schema reference for every config section Open Knowledge exposes.
GitHub Sync
Asset changes sync the same way markdown does — auto-commit, push, pull.
Content filtering
What the file watcher sees. .okignore and .gitignore gate both markdown and assets; asset extensions are fixed by Open Knowledge.