Attachments
Contents
Media files referenced with ![[filename]] are served directly from your vault via the /attachments/<path> route. OnyxFolio searches three locations in order when resolving a filename.
Resolution order
When OnyxFolio sees ![[photo.jpg]] in a note at blog/My Post.md, it checks:
<section>/_attachments/photo.jpg— e.g.blog/_attachments/photo.jpg_attachments/photo.jpg— vault root attachments folder<ATTACHMENTS_PATH>/photo.jpg— custom path from.env
The first match wins. If no match is found, the embed renders as <em>Missing media: photo.jpg</em>.
Recommended structure
Place media files in an _attachments/ subfolder relative to the note's folder:
vault/
blog/
My Post.md
_attachments/
photo.jpg
video.mp4
gallery/
Photo Gallery.md
_attachments/
image1.jpg
image2.jpg
_attachments/
shared-logo.png ← available to all notes as fallback
Vault root fallback
Files in the vault root _attachments/ are available to all notes regardless of section. Useful for shared assets like logos, icons, or common diagrams.
Custom ATTACHMENTS_PATH
Set ATTACHMENTS_PATH in .env to point to a directory outside the vault:
ATTACHMENTS_PATH=/home/user/shared-media
This is the third and last fallback. Useful when media files live in a separate folder from the vault (e.g. a shared photos directory).
Security
OnyxFolio validates that all resolved file paths stay within the vault directory using os.path.realpath(). Symlinks or path-traversal sequences that escape the vault are rejected and render as missing media. The ATTACHMENTS_PATH fallback is checked separately and not subject to this constraint.
Serving attachments
Attachments are served by Flask's send_from_directory at /attachments/<path>. The path is relative to VAULT_PATH. Direct URL access: /attachments/blog/_attachments/photo.jpg.
See also
- Media Embeds —
![[file]]syntax for images, video, audio - Getting Started — setting
VAULT_PATHin.env