keelapps

keelapps/Satchel for Jira/Documentation

Documentation

Satchel does two things: it gets attachments out of Jira in bulk, and it tells you what is in there. Everything below is one of those two, plus the parts where Jira's own limits decide what is possible.

Jira's issue view does not show app panels automatically. On an issue, open the View app actions button under the issue title — the round icon, not the “+” — and choose Attachments. The panel then stays visible on that issue.

This is the single most common “is the app broken?” moment after installing, and it is Jira's behaviour rather than the app's.

Finding the panel

Satchel has three surfaces:

A panel stuck permanently on “Loading Satchel…” is a bug worth reporting, not slowness: every call the interface makes is timed out and turns into a visible error.

Downloading one issue

From the issue panel:

Two things the archive guarantees:

A long download shows progress and can be cancelled.

A JQL result, or a project

The project page's browser lists the project's issues and their attachments, filterable and searchable, with JQL for anything more specific. Select rows — or take the whole result — and download one archive foldered by ISSUE-KEY/, so the files arrive already sorted by the issue they belong to.

The scan behind a project-wide result runs as a background job with a checkpoint, so a large project finishes rather than timing out, and closing the tab does not lose the work.

How the bytes travel

This is the part worth being precise about, because the app's compliance story rests on it.

The archive is assembled in your browser tab. The compression library is part of the app's own bundle rather than fetched from a content delivery network — a CDN would need an external network permission, and that permission is exactly what the app does not have. Bytes go Jira → your tab → your disk. Nothing is written to app storage, and nothing reaches any server we operate, because we operate none.

There are two routes for getting those bytes into the tab, and which one runs is measured rather than assumed:

RouteHowTrade-off
Bridge the browser calls Jira directly, as you fastest, and no backend function runs at all — but the platform bridge can mangle non-text payloads on some sites
Relay the backend reads the file in slices and hands them to the tab correct for every file type, slower, and subject to a per-file ceiling

On the first download Satchel probes which route returns bytes intact on your site, then uses it and says on screen which one is in use and why. Both routes are zero-egress; the relay is slower, not less private.

The project inventory

A scan records, for every attachment in the project: filename, byte size, media type, upload date, uploader, and the issue and issue type it belongs to. Metadata only — never file content. That inventory is what the duplicate and storage reports are computed from, and what the CSV export writes out.

The ten most recent scans per project are kept; creating an eleventh deletes the oldest. You can delete a scan yourself from the project page.

The scan itself runs as the app rather than as you, because a queued background job has no signed-in user. Its output is metadata for a governance report — and anything you then ask to download is re-fetched as you, so Jira's permissions still decide what you can actually have.

Duplicate candidates

Satchel groups files by filename and exact byte size and reports the bytes each group would reclaim if all but one copy went away.

They are called candidates on purpose. Jira exposes no content hash for an attachment, so proving two files byte-identical would mean downloading both — across a project, every byte in it. What Satchel can honestly say is “same name, same size”, and that qualification travels with the number in the data itself, so no screen can render a reclaimable figure without the sentence that limits it.

Two deliberate exclusions follow from the same honesty:

Storage reports

Jira gives you one site-wide storage figure. Satchel breaks the project's share of it down by:

CSV export

The whole inventory exports as CSV: one row per attachment with its metadata. Useful when the person who has to approve a deletion is not the person running the app.

Bulk deletion

Jira has no recycle bin for attachments. An attachment deleted through Satchel is gone from Jira, and the only recovery is an Atlassian support restore of the entire site. Satchel cannot undo a deletion, and does not pretend it can.

So deletion is fenced in four ways:

Audit records are capped at 500 per project, oldest dropped first, and they are deliberately not deletable from the interface — an audit trail a user can erase is not an audit trail. To clear one, contact support.

Download what you are deleting first. That is what the zip is for.

What runs as you

Every attachment read and every deletion runs as you, using your own Atlassian identity. If you cannot open a file in Jira, Satchel cannot hand it to you — including attachments hidden by issue security levels or added inside restricted comments, which Jira withholds at the API level.

The one exception is the background project scan described above, which runs as the app because a queued job has no signed-in user.

Permissions the app requests

ScopeWhy
read:jira-workread issues, run JQL, read attachment metadata and attachment content — how the app finds and downloads files
read:jira-userturn an uploader's account id into a display name, so a storage report names people
write:jira-workone API call only: deleting an attachment
storage:appscan records, audit records and settings, in Atlassian-hosted storage on your own site

write:jira-work is broader than what Satchel does with it, and that deserves stating plainly. Atlassian's consent text for it mentions creating and editing issues, posting comments, creating worklogs and deleting issues. Satchel does none of those things. It is the narrowest scope Atlassian currently offers that includes deleting an attachment — the granular scope that would say exactly that is still in Beta — and a test in the build fails if any code path reaches a mutating call other than the single attachment delete. That test exists to stop the gap between the consent prompt and the behaviour from widening silently.

There is no permissions.external block: the app cannot make an outbound call even if it tried to, which Atlassian enforces at the platform level.

Licensing and the free tier

Sites with 10 users or fewer pay nothing. Above that, two things stay free forever on every install: the single-issue zip and the direct single-file download. The fix for the seven-year-old missing download button is never behind a licence check.

A licence buys scale and governance: cross-issue and project-wide archives, the project inventory, the duplicate and storage reports, CSV export, and deletion.

Not in this release

Support

Questions, bug reports and feature requests: raise a support request. Security issues: report privately.