Skip to content

How to set up the JIRA integration

Full knob table (base-URL overrides, secrecy guarantees, resolution order) is in configuration.md. The design rationale is in externals.md. This guide is the end-to-end setup recipe.

With the connector on and credentialed, a pasted JIRA link is drawn down through JIRA's authenticated REST API into a structured snapshot. Without it, the same link falls through to the generic web connector — which hits JIRA's login wall and tombstones. The degrade is quiet: an unconfigured connector is never an error, just inactive.

Cloud only. JIRA Data Center / Server is explicitly out of scope.

What you need first

1. Provide credentials (env-var primary)

Credentials resolve env var first, config.toml second. The recommended path keeps secrets out of the config file, in your environment:

export LODE_JIRA_TOKEN=<api-token>
export LODE_JIRA_EMAIL=<account-email>
Alternative: put them in config.toml (plaintext on disk) No secret is *required* to live on disk, but it *may* — in plaintext; there is no keyring integration (a deliberate deferral, see [`decisions.md`](https://github.com/bildzeitung/lode/blob/trunk/docs/decisions.md)). Add to `$LODE_HOME/config.toml`:
jira_email = "you@example.com"
jira_token = "your-atlassian-api-token"
An env var, if also set, wins over the `config.toml` value.

2. Turn the connector on

The flag is off by default. In $LODE_HOME/config.toml (flat table, no [section] headers — see config-change.md):

jira_enabled = true

Optional: base-URL override

Leave jira_base_url empty (the default) and lode infers the API base from the *.atlassian.net host of the link you paste. Set it only if your Cloud site's API host differs from the link itself:

jira_base_url = "https://acme.atlassian.net"

A non-empty value must be a well-formed http(s) URL, or Settings() construction fails at load with a clear message.

3. Confirm the connector is active

A connector is active only when its flag is on and credentials resolve. Either missing → inactive (the link quietly uses the web path instead).

lode config shows jira_enabled = true in the knob table, and — since lode-dx4r — also shows a presence indicator for all four credential keys (jira_email, jira_token, confluence_email, confluence_token): [REDACTED] if a value resolves from either the env var or config.toml, [unset] if neither does. That confirms lode found something, but not whether it's the right something, or whether the tenant is actually reachable. lode verify (read-only, writes nothing) checks both in one shot:

lode verify --jira

It prints whether the flag is on, whether credentials resolved (and from which source — env var or config.toml; the token is never shown), and whether the base URL is configured or will be inferred — then, if active, makes one authenticated GET to confirm the credentials actually reach the tenant, printing the authenticated account's display name on success. Pass a real issue key to also dry-run fetching real content:

lode verify --jira PROJ-123

Exit code 0 means verified reachable; non-zero names exactly what's wrong (inactive flag/credentials, unresolved base URL, bad credentials, bad base URL, or an unreachable tenant) — fix that before moving on to step 4.

4. Try it end to end

A JIRA permalink has the shape /browse/{KEY}. Pasting one into a note enqueues an async refresh job; lode work drains the queue and fetches the snapshot.

lode add "Debugging https://<site>.atlassian.net/browse/PROJ-123"
lode work                       # drain the async job queue → fetch the snapshot
lode dump-html <note-id>        # print the fetched snapshot's raw JSON payload

lode jobs shows the queue state if you want to watch the refresh land.

Confluence

Confluence Cloud is the same shape, a separate flag — confluence_enabled = true plus LODE_CONFLUENCE_TOKEN / LODE_CONFLUENCE_EMAIL (or the confluence_token / confluence_email config fallbacks), with an optional confluence_base_url. A Confluence link is the id-bearing page URL: https://<site>.atlassian.net/wiki/spaces/<SPACE>/pages/<id>/<slug>.

A link pasted while the connector was inactive (flag off, or credentials not resolving in the shell that ran lode add) is stamped source_type='web' at paste time — and stays there. It keeps refreshing through the generic web leg forever, which for JIRA/Confluence hits the product's login wall and returns the SPA app shell, not ticket/page text (lode dump-html prints JS, no real content). Fixing the config afterwards only changes routing for new pastes — it does nothing for a link you already pasted.

To repair an already-pasted link, re-run the connector's migration for its already-processed links under current (now-fixed) routing — the lode backfill command (lode.backfill framework, lode.jira_backfill for JIRA):

lode verify --jira                 # confirm the connector is actually active now
lode backfill jira --dry-run       # preview: what would migrate, nothing written
lode backfill jira                 # mint the JIRA identity, repoint the edge, enqueue a refresh
lode work                          # drain the queue -- fetches the real snapshot via the REST API

Confluence is the same shape: lode backfill confluence --dry-run / lode backfill confluence.

When you need --retry-tombstoned. A first backfill pass always mints a brand-new, never-tombstoned identity, so --retry-tombstoned is never needed the first time. It only matters on a re-run: if that fresh identity's own head snapshot then tombstoned (e.g. the token was still wrong, or expired mid-run — a 401), a plain re-run treats that as a permanent failure and skips it, same as the periodic refresh sweep would. Once you've actually fixed the cause (rotated the token, fixed the base URL), lode backfill jira --retry-tombstoned is the explicit opt-in to retry that specific target now instead of waiting on a schedule.

Idempotent. Re-running lode backfill jira after a link already migrated is a no-op for that link — reclassification runs from the edge's original pasted URL every time, so an edge already repointed onto its JIRA identity is simply revisited and its current head snapshot re-checked, not re-migrated from scratch. Safe to run even when nothing needs it.

Full design: externals.md.

Troubleshooting

Symptom Likely cause
JIRA link tombstones / looks like a login page Connector inactive — flag off, or token/email not resolving from either source. Re-check jira_enabled and that the env vars are exported.
A JIRA/Confluence link I pasted earlier still shows the web/JS shell after I fixed my config Edge frozen as source_type='web' at paste time — fixing config only changes routing for future pastes. Recover it with lode backfill jira (or confluence), then lode work — see Recovering a link pasted before the connector was active above.
Settings() fails on load with a base-URL error jira_base_url / confluence_base_url is non-empty but malformed — must be a well-formed http(s) URL, or leave it empty to infer.
lode config won't show my token or email value Working as designed — all four credential fields are secret=True and never echoed; the row shows [REDACTED] (resolved) or [unset] (not resolved) instead.
Two URL forms of the same issue made two nodes They shouldn't — a browser permalink and an API URL of the same issue parse to the same key and dedup onto one row. If they didn't, the link shape may not carry the issue key/page id; check the URL includes /browse/{KEY} (JIRA) or /pages/{id}/ (Confluence).

Confluence-specific gate: the Confluence dispatch leg needs lode-mfts landed on trunk to route through refresh_external's dispatcher; JIRA's dispatch has been wired since lode-gpzn.3. If Confluence links aren't drawing down even when configured, verify that ticket's state (bd show lode-mfts). See externals.md.