A small Rust CLI that bridges entries from tui-journal to a web CMS as drafts.
It reads tjournal's local SQLite, lets you pick an entry by id, and POSTs it as markdown to an admin endpoint. From there the entry lands as an editable draft — polish it in the CMS, publish, done.
Currently wired to one specific admin (a Cloudflare Workers backend behind samue.lk). The repo is open as a small example of a wider pattern: terminal tools as outbound bridges to other systems, not just personal stores. The work to adapt it to a different CMS lives in src/admin.rs and src/auth.rs. Everything else — config loading, sqlite reader, CLI — is generic.
- Rust toolchain (recent stable)
- A working tui-journal install with the SQLite backend
- An admin endpoint to POST to, or willingness to adapt the relevant modules
cp config.toml.example config.toml
# edit with your URLs and tokens
cargo build --releaseconfig.toml is gitignored.
tj-publisher list # entries tagged "📝 Post" (the default filter)
tj-publisher list --all # every entry
tj-publisher search "query" # case-insensitive substring match
tj-publisher show <id> # full content of one entry
tj-publisher publish <id> # POST as a draft (local profile)
tj-publisher --prod publish <id> # POST to production (prompts for confirmation)
tj-publisher republish <id> # idempotent update via tjournal_source_idlist and search default to entries tagged 📝 Post. Use --all to skip that filter, or --tag <name> for a different one. If you don't tag entries, --all is what you want.
When publish runs against an entry with no title, it prompts for one — empty titles are rejected by most CMS endpoints.
config.toml has two profile sections: [local] for dev, [prod] for production. The CLI defaults to local; use -l / -p to switch, or set a top-level default_profile = "prod" to flip the default once you trust it.
Environment variables override the config file:
| Variable | Purpose |
|---|---|
TJ_PUBLISHER_ADMIN_URL |
Admin/worker base URL |
TJ_PUBLISHER_BRIDGE_TOKEN |
Bearer token for service-to-service auth |
TJ_PUBLISHER_DEBUG_SECRET |
Shared secret for cookie-based auth (local only) |
TJ_PUBLISHER_CONFIG |
Path to an alternative config.toml |
TJ_PUBLISHER_DB |
Path to tjournal's SQLite DB |
Two paths:
- Bridge token (preferred).
Authorization: Bearer <token>attached to each publish request. Clean service-to-service. - Debug-login (legacy). POST to
/api/debug/admin-loginwith a shared secret, capture the session cookie, attach it to subsequent requests. Local-dev convenience only — don't expose this endpoint in production.
If both are configured, bridge token wins.
The wire contract this crate speaks:
POST /api/admin/drafts/from-tjournal
Body: { title, content_markdown, journal_date, tags, priority, idempotency_key, slug? }
Response: { id, slug, admin_url }
To wire to something different: src/admin.rs owns the payload + response shape, src/auth.rs owns the auth flow. The rest of the crate stays as-is.
MIT.