Open KnowledgeOpen Knowledge
Guides

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 typeShapeExample
Images — .png, .jpg, .gif, .webp, .avif, .svgInline image via wiki-embed![[photo.png]]
PDF, MP4, MP3, WAV, OGG, M4A, MOV, WebMWiki-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 (![alt](./photo.png), ![alt](subdir/photo.png)) are rewritten to resolve from the new location.
  • Absolute-path refs (![alt](/docs/photo.png)) 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:

  1. Prefer a match inside the current doc's subtree; shallowest depth wins.
  2. Otherwise prefer the fewest path hops from the current doc.
  3. 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 gc CLI 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 |modifier suffix passes through as alias text.
  • GitHub raw-file preview doesn't render wiki-embed refs. GitHub renders ![](url) 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-default attachmentFolderPath or useMarkdownLinks: true wait for a future one-shot ok migrate --from-obsidian-vault CLI.

Next steps