Skip to content

Caveats & Limitations

Riffle converts between two fundamentally different document formats — Confluence’s rich storage format (XHTML with custom macros) and Markdown. Not everything survives the round trip. This page documents what syncs cleanly, what degrades, and what doesn’t sync at all.

These elements convert cleanly in both directions:

ElementConfluence → GitHubGitHub → Confluence
Headings (H1–H6)
Paragraphs
Bold / Italic / Strikethrough
Inline code
Code blocks (with language)
Links
Images (external URLs)
Ordered and unordered lists
Nested lists
Blockquotes
Tables
Horizontal rules
Task lists (checkboxes)
Expand/collapse sections
Status lozenges

These elements are converted but may lose some fidelity:

ElementBehavior
Status lozengesConfluence’s status macro converts to bold bracketed text like **[IN PROGRESS]** with an HTML comment <!-- status:Blue --> to preserve the color. The color comment enables lossless round-tripping. Both colour and color parameter spellings are handled. Defaults to Grey if no color is specified.
Expand/collapse sectionsConfluence’s expand macro converts to HTML <details><summary> blocks. GitHub renders these as collapsible sections. The expand title maps to <summary>. Content inside is converted as normal markdown.
Code blocks with macrosConfluence’s code macro converts to a fenced code block. Language hints are preserved. Macro parameters (line numbers, theme, collapse) are lost.
Table header rowsMarkdown tables require a header row. Confluence tables without headers get a synthetic header on conversion to Markdown.
Nested tablesMarkdown doesn’t support nested tables. Inner tables are flattened to text.
Text color and highlightsConfluence supports inline color and background color. These are stripped in Markdown (no equivalent).
Font sizeStripped — Markdown has no font size control.

These elements are not converted and will be lost or ignored:

ElementReason
Confluence macros (Jira issues, roadmaps, etc.)No Markdown equivalent. Custom macros are too varied to map generically. Expand macros, info/warning/note/tip panels, and status lozenges are supported — see above.
Embedded attachmentsConfluence attachments are stored in Confluence’s own file system. Images referenced by attachment ID (not URL) cannot be resolved from GitHub.
Page permissionsConfluence page-level restrictions don’t have a GitHub equivalent.
Page commentsComments are separate from page content and are not synced.
Inline commentsConfluence inline comments (annotations on specific text) have no Markdown representation.
LabelsConfluence labels are not included in the Markdown output.
Page history / versionsEach sync creates a new version. Historical versions are not transferred.
Confluence templatesTemplates are not pages and are not synced.
Live docs (whiteboards, databases)Only standard pages and blog posts are supported.
Emojis (Confluence-specific)Confluence has its own emoji format (:emoji: with custom IDs). Standard Unicode emoji work fine.

Riffle prevents infinite sync loops using identity-based detection. When Riffle updates Confluence, the resulting event carries Riffle’s own Atlassian account ID — the forward sync recognizes this and skips it. Similarly, commits made by Riffle include a [riffle-sync] tag that the reverse sync ignores. This is precise and instant — human edits are never dropped.

Riffle uses a last-write-wins strategy. If the same page is edited in both Confluence and GitHub between sync cycles, the most recent change overwrites the other. There is no merge or conflict detection.

Riffle tracks page-to-file mappings in a .sync-manifest.json file committed alongside your Markdown files. This manifest maps file paths to Confluence page IDs, space keys, and titles. Do not delete this file — Riffle uses it to detect renames, prevent duplicates, and track sync state.

If you previously used YAML front matter (confluence_id, space_key fields), Riffle automatically migrates to the manifest on the next sync and strips the front matter from your files.

Riffle handles renames in both directions:

  • Confluence page rename → GitHub: When a page title changes, Riffle detects the rename via the manifest, deletes the old file, and creates the new file — all in a single atomic commit. No orphaned files.
  • GitHub file rename → Confluence: When a file is renamed (appears as delete + add in a push), Riffle matches the old and new files via the manifest and updates the Confluence page title instead of trashing and recreating the page. Page history and comments are preserved.

File renames that change the H1 title will also update the Confluence page title. If the H1 title is unchanged (or absent), the page title is derived from the new filename.

Riffle only syncs with the repository’s default branch (typically main). Changes on feature branches are not synced until merged.

Confluence page hierarchy maps to folder structure:

Space Root
├── Parent Page → docs/target-dir/parent-page.md
│ ├── Child Page → docs/target-dir/parent-page/child-page.md
│ └── Another Child → docs/target-dir/parent-page/another-child.md

Page titles are slugified (lowercased, spaces replaced with hyphens, special characters removed).

  • GitHub API: 5,000 requests/hour for authenticated tokens. Each page sync uses 2–3 API calls. Large bulk operations may hit limits.
  • Confluence API: Subject to Atlassian’s standard rate limits for Forge apps.
  • Forge invocation limits: Forge functions have a 25-second execution timeout. Very large pages may time out during conversion.