confluence-cli
Markdown-sync-first Confluence CLI in Rust.
confluence-cli is built around a safe local workflow:
pullConfluence content into Markdown plus sidecar metadata.planchanges against the pulled sidecar state without contacting Confluence.applyonly when the diff is correct; it rechecks remote versions before writing.
It also exposes direct page, blog, search, attachment, label, comment, and property commands for non-sync use cases.
Status
Early release, but already live-verified against both Confluence Cloud and Confluence Data Center.
| Area | Cloud | Data Center | Notes |
|---|---|---|---|
| Auth, spaces, search, page/blog CRUD | Verified | Verified | Live e2e |
| Attachments, labels, properties, comments | Verified | Verified | Live e2e |
pull -> plan -> apply sync flow |
Verified | Verified | Includes drift refusal and noop checks |
doctor environment/profile validation |
Verified | Verified | Live checked |
| Markdown round-trip for common built-in macros | Verified | Verified | Unsupported cases preserve storage safely |
Installation
From PyPI with uv:
The PyPI distribution is named confluence-cli-rs; it installs the
confluence executable.
From crates.io:
From Homebrew:
Prebuilt macOS and Linux archives for Intel and ARM are published on the GitHub releases page.
Quick Start
For guided setup, run confluence auth login (or confluence init) in a
terminal. The wizard opens Atlassian's token page when useful, discovers the
Cloud ID required by scoped tokens, hides credential entry, verifies access,
and stores the token in your operating-system keychain. Existing profiles are
offered as defaults. If no OS credential service is available, setup offers an
explicit protected-file fallback rather than silently weakening storage.
For Confluence Data Center, setup can create a dedicated PAT through the
official API using a one-time password or existing PAT; the bootstrap secret is
never saved. If automatic creation is unavailable, it opens
https://<your-host>/plugins/personalaccesstokens/usertokens.action (also
available under Avatar → Settings → Personal access tokens). Onboarding
never consumes accidental piped input as answers.
init is the short name for the guided auth login flow. Use
auth login --profile NAME ... for explicit scripted login, and use
profile add --name NAME ... as the explicit profile-management equivalent.
Cloud profile:
CONFLUENCE_API_TOKEN=""
Data Center profile:
|
Environment-driven mode also works without a stored profile:
Non-interactive login stores credentials in the OS keychain by default. For a
headless machine without a credential service, prefer environment-driven mode;
if persistent storage is necessary, explicitly accept the protected config-file
fallback with --insecure-storage. Existing inline-token profiles remain
readable and can be moved transactionally with confluence auth migrate.
Markdown Sync Workflow
Pull a page tree:
Pulls are staged beside the destination and installed as one snapshot. A pull
refuses to replace local Markdown changes or unmanaged files; inspect and apply
or preserve those files first. Use --force only when the remote snapshot
should replace the entire destination. Attachment names are confined to their
page's attachments/ directory. Filtered pull space --since ... exports must
use a new or empty destination, because partial results cannot safely replace a
complete local snapshot.
Inspect the planned changes:
plan validates the complete local tree and compares it with the sidecar state
captured by pull; it does not contact Confluence. apply validates every local
document and preflights every remote version before its first write. Versioned
updates still reject drift that occurs during the apply unless --force is
explicitly supplied. Partial remote mutations are reported in structured error
details so automation can reconcile them safely.
Apply the diff:
Local content is stored as:
<slug>/index.md<slug>/.confluence.json<slug>/attachments/*
The frontmatter carries editable metadata such as title, type, labels, status, and properties. parent is informational: move a page directory beneath its desired local parent to reparent it. The sidecar stores remote ids, versions, hashes, and attachment mappings used for safe sync and drift detection.
doctor
Use doctor before a first sync, in CI, or when a profile behaves unexpectedly.
It checks:
- config loading and profile resolution
- base URL and auth shape
- provider reachability
- optional space access
- optional local sync path planning
doctor exits non-zero on failures and supports --json for machine-readable checks.
Commands
Top-level command groups:
auth login|status|logout|migrateprofile add|list|use|removespace list|getsearchpage list|get|tree|move|create|update|deleteblog list|get|create|update|deletepull page|tree|spaceplanapplyattachment list|download|upload|deletelabel list|add|removecomment list|add|update|deleteproperty list|get|set|deletedoctorcompletionsschema
All data commands accept the suite-wide --output auto|text|json, --quiet, and
--no-color flags. Auto output is readable text on a terminal and JSON when
piped; --json remains a hidden compatibility alias. Successful JSON-mode
commands always emit one JSON document. completions intentionally emits an
opaque shell script, while schema always emits JSON.
List commands consistently support --limit, --offset, and JSON-only
--fields. Search reports an exact total when Confluence supplies one and
null otherwise. Errors use the shared exit-code contract (input 2, auth 3,
not found 4, API/network 5, rate limit 6, conflict 7), and
confluence schema --command 'page get' returns one token-efficient command
contract. The response contract is versioned independently in the schema.
Where a command accepts REFERENCE, use a numeric content ID, a Confluence URL,
or SPACE:Title. Destructive operations require an interactive confirmation or
--yes; conditional overwrites additionally require --force or --replace.
For sensitive content, prefer --body-file or standard input over --body,
because command-line arguments can be visible to other local processes.
Auth And Environment Overrides
Stored profiles live under the local config directory used by directories::ProjectDirs.
Supported environment overrides:
CONFLUENCE_PROFILECONFLUENCE_DOMAINCONFLUENCE_PROVIDERCONFLUENCE_API_PATHCONFLUENCE_AUTH_TYPECONFLUENCE_EMAILorCONFLUENCE_USERNAMECONFLUENCE_API_TOKEN,CONFLUENCE_TOKEN,CONFLUENCE_PASSWORD, orCONFLUENCE_BEARER_TOKENCONFLUENCE_TOKEN_KIND(classicorscoped)CONFLUENCE_CLOUD_ID(required for scoped Cloud tokens)CONFLUENCE_READ_ONLY
CONFLUENCE_PROVIDER must be cloud or data-center. CONFLUENCE_AUTH_TYPE must be basic or bearer.
Shell Completions
Testing
make test runs unit tests and stateful process-level simulator tests. The simulator drives the compiled CLI through the same complete lifecycle for Cloud and Data Center: pages, blogs, hierarchy, labels, properties, comments, attachments, Markdown pull/plan/apply, macros, cleanup, and authentication failures. It binds only to localhost and never needs credentials.
Live provider checks remain separate:
e2e_cli_canaryis a small create/get/pull/plan/apply/delete smoke test.e2e_cli_lifecycleis the complete live provider suite..github/workflows/cloud-e2e.ymlruns the canary Monday through Saturday, the complete suite on Sunday, and either suite on demand through a protected GitHub Environment.
See docs/testing.md for the confidence model and Cloud automation setup.
Local Data Center
The repo includes a local Confluence Data Center stack for integration testing.
The default e2e path targets the local local-dc profile and the TEST space.
Available helpers:
make confluence-backupmake confluence-restoremake confluence-resetmake confluence-logs
Backups are written to:
docker/backup/confluence-data.tar.gzdocker/backup/postgres-data.tar.gz
The first boot after make confluence-restore can take several minutes before HTTP responds.
To point the e2e suite at another instance:
CONFLUENCE_E2E_PROFILE=other-profile CONFLUENCE_E2E_SPACE=SPACE
Or run fully env-driven:
CONFLUENCE_E2E_PROFILE= \
CONFLUENCE_E2E_BASE_URL=http://localhost:8090 \
CONFLUENCE_E2E_TOKEN="" \
CONFLUENCE_E2E_PROVIDER=data-center \
CONFLUENCE_E2E_SPACE=TEST \
Env-driven Cloud tests also accept CONFLUENCE_E2E_TOKEN_KIND and, for scoped tokens, CONFLUENCE_E2E_CLOUD_ID.
Release And CI
Local release gate:
That runs formatting, clippy, tests, CLI smoke checks, and cargo package.
Local versioned releases use vership, matching the other CLI projects:
vership uses vership.toml in this repo so vership preflight runs the stricter make release-check gate rather than the default Rust-only lint/test commands.
GitHub Actions is set up to:
- run CI on pushes and pull requests
- publish tagged releases to crates.io
- publish native macOS and Linux wheels to PyPI as
confluence-cli-rs - build tagged macOS and Linux release archives
- attach release archives and checksum files to GitHub releases
- update
rvben/tapautomatically on tagged releases whenHOMEBREW_TAP_TOKENis configured
Markdown Fidelity
Remote canonical content stays in Confluence storage format. Markdown is the editable local representation.
The converter already handles a large set of common Confluence constructs directly, including:
- headings, lists, tables, code blocks, task lists, links, and attachments
- page links and typed page/user/space resource parameters
- layouts, panels, expand blocks, status, TOC-family macros, and search/navigation macros
- excerpt, excerpt-include, include-page, page-tree, page-tree-search, and page-index
- label/reporting/content-property/report-table/task-report families
- attachment preview and other common built-in macros
When a construct is unsupported or would be lossy, confluence-cli preserves the Confluence storage fragment instead of flattening the whole page.
Known Limits
- Storage fidelity is strongest for supported built-in macros and generic resource-aware macro fallback. Unknown provider-specific macro behavior can still vary between Cloud and Data Center.
- Pull requests use the stateful simulator; live tenant checks run on the protected schedule and can also be dispatched manually.
applyrefuses remote version drift unless--forceis used.
License
MIT
Releasing
Vership owns versioning, changelog generation, release commits, and tags. See the release runbook for the verified workflow and recovery policy.