Confluence Cleanup Assistant
Confluence v1.0.0 · Pre-release
Continuously analyses every Confluence space, scores knowledge health, and remediates fourteen classes of problem in bulk — with a preview before anything is written and a full audit trail after.
Overview
Cleanup Assistant scans every accessible space, produces a Knowledge Health Score, detects fourteen classes of problem, recommends fixes, and lets administrators remediate them in bulk.
The first full scan builds the index every health check reads. It runs entirely in the background across queued events, and the dashboard shows live progress. Each phase does as much as its invocation budget allows, writes a checkpoint, queues the next event and returns — no handler assumes it can finish in one go, which is what lets a 100,000-page scan complete on a platform built around short-lived functions.
Every check is individually switchable and its severity is overridable from the Settings tab.
Highlights
Fourteen health checks
Broken links, empty and placeholder pages, duplicate and near-duplicate content, orphans, stale pages, large attachments and version bloat, missing labels, missing or deactivated owners, invalid macros, unused attachments, restricted pages, long-archived content, inactive spaces and oversized spaces.
A score that survives scrutiny
The health score, trends over time and storage analytics are derived from a real index of the site — not sampled — and each finding carries its evidence, a recommendation, and a confidence value.
Preview before anything is written
Bulk actions produce a preview first. Remediation then runs as the calling user, so Confluence independently enforces that they may perform it.
Findings survive a re-scan
Each finding is keyed by a fingerprint of rule + target + discriminator, never by time. Re-scanning re-derives the same key, which is what lets an administrator’s “ignore” decision stick.
Reports written from the format up
CSV, XLSX, PDF and ZIP writers are implemented directly against their file formats rather than pulled from libraries — a Forge bundle pays for its size on every cold start. The tests check structural validity, not just that bytes were produced: CRC-32 against the standard check value, xref offsets that resolve to real objects, stream lengths that match their contents.
Zero runtime dependencies beyond the Forge SDK
Nothing else ships in the deployed bundle.
The scan lifecycle
An hourly scheduled tick decides what is due. Work then moves through a queue: START → SPACE_PAGE → ATTACHMENT → ANALYZE → FINALIZE — enumerating spaces, indexing pages while running streaming rules, indexing files, then building the link graph, detecting duplicates and orphans, and finally scoring, summarising and recording trends.
Results land in custom entities. The Dashboard API reads them, and every read passes through the per-user permission validator before anything reaches a browser.
The workers budget themselves to 50 seconds and then checkpoint, which leaves almost no headroom against the default 55-second ceiling — so the scan and action workers run with a raised ceiling as a safety margin, not as licence to run longer. One slow Confluence call would otherwise be killed before the checkpoint was written, stranding the scan.
User guide
Who this is forConfluence administrators run scans and change settings. Space administrators and content owners can read findings and remediate what they already have permission to touch.
The app installs as two dashboards — one site-wide, one scoped to a single space — plus background machinery. Everything else on the dashboard reads the index a scan builds, so nothing is meaningful until the first scan has run.
| Surface | Where to find it |
|---|---|
| Site-wide dashboard | Apps → Cleanup Assistant |
| Per-space dashboard | The same dashboard from within a space, scoped to that space |
| Settings | The Settings tab, which renders only for Confluence administrators |
-
Run the first full scan
Open Apps → Cleanup Assistant. Before any scan the screen is an empty state inviting an administrator to run one. Use Full scan for the first run — Quick scan is incremental and only covers content changed since the last scan.
The scan runs entirely in the background across queued events, checkpointing as it goes, so a site of any size completes regardless of per-invocation limits. A progress banner replaces the buttons and the page polls every eight seconds until it finishes. A scan already in flight blocks any new one.
-
Read the Overview
A health score out of 100 with a grade band (healthy / warning / critical), headline totals — open findings, pages indexed, attachments, attachment storage, spaces — a findings-by-severity bar, and a clickable tile per category.
-
Work the Findings tab
One category at a time, worst first, with filters for space, severity, owner, label, file type and update window, plus free-text search over titles and detail. Each finding carries what was found, why it matters, a recommendation, and the actions that apply to it.
Anything you lack permission to see is removed before the list is returned, and the response reports how many were withheld rather than silently returning a short page.
-
Remediate — select, preview, confirm, execute
Nothing mutates without a preview. Select findings or targets, then Preview: the server resolves the exact target set, removes anything you cannot act on (with a reason for each), describes the effects, and returns a signed token tied to that specific set.
For permanent actions you then type
DELETEto confirm. The token is redeemed once. Small batches run inline; larger ones move to a queue and the UI polls for progress.Partial failure is normal and reported. Deleting 200 pages where three fail deletes 197 and tells you which three failed and why — aborting the batch would leave the site in a state nobody chose.
-
Tune thresholds in Settings
What counts as stale, how similar is a duplicate, what size is a large attachment, scan cadence and hour, notification rules, report schedule and formats, action safety switches, retention windows, and standing ignore rules.
Validation is strict and server-side: a bad threshold is rejected at save time with every problem listed at once, rather than surfacing hours later inside a background scan.
-
Set a cadence and leave it running
Defaults are a monthly full scan and a daily incremental. An hourly trigger decides what is actually due — a single tick rather than several schedules, because Forge offers only hour/day/week intervals at a time of its choosing. Deciding in code buys an administrator-chosen hour, monthly cadence, and a catch-up run if the platform skipped a tick.
Good to know
- The seven tabs: Overview (score and totals), Findings (the working surface), Spaces (per-space rollup — page and attachment counts, storage, last activity, health score), Analytics (trend over time), Reports (exports), Activity (the audit log), Settings (administrators only).
- Analytics needs at least two scans on different days before it shows a trend. One data point is recorded per UTC day; re-scanning the same day overwrites it.
- Reports are point-in-time. Re-downloading returns the stored file rather than regenerating — deliberately, so a figure quoted from a report stays quotable. Four formats: PDF (executive summary), CSV (raw rows), XLSX (multi-sheet workbook) and ZIP (bundle), capped at 20,000 rows so the output stays openable in Excel.
- Reversibility, by action: archiving pages, deleting pages (to trash), adding labels, changing owner, moving pages and ignoring/resolving a finding are all reversible. Deleting attachments is permanent and is the action that requires the typed confirmation.
- Every mutation runs as you, so Confluence enforces authorisation independently of the app’s own checks. The bulk cap is enforced server-side at preview time, so a scripted caller cannot exceed the configured maximum.
- Notifications are an in-app digest feed, read from the Alerts bell in the dashboard masthead, because Forge apps cannot send arbitrary email. A quiet week produces nothing rather than an empty report.
- An “ignore” decision sticks. A finding’s identity is derived from what is wrong — rule + target + discriminator — never from when it was found, so re-scanning re-derives the same key instead of resurfacing the finding every night.
If something looks wrong
The dashboard is empty and offers only a scan button.
No scan has completed yet. The index is what every other screen reads, so nothing else is meaningful until it exists — run a full scan.
Analytics shows no trend.
It needs at least two scans on different days. One point is stored per UTC day.
A findings list looks shorter than expected.
Findings you lack permission to see are removed before the list is returned. The response reports how many were withheld — it does not silently return a short page.
A bulk action reports some targets skipped.
The preview removes anything the calling user cannot act on and gives a reason for each. That list is the app telling you what it will not touch, before the token is redeemed.
What it does not do
- Mutate anything during a scan. Scans are read-only; every write happens through a previewed, confirmed remediation.
- Act with more permission than you have. Remediation runs as the calling user, never as the app.
- Send email. Notifications are an in-app digest feed.
- Recover a deleted attachment. That action is permanent, which is why it requires typed confirmation.
Permissions & scopes
| Scope | Why it's needed |
|---|---|
read:page:confluence, read:space:confluence, read:attachment:confluence, read:comment:confluence, read:label:confluence | Building the content index every health check reads |
read:content-details:confluence, read:content.metadata:confluence, read:hierarchical-content:confluence | Page metadata, hierarchy and the link graph |
read:analytics.content:confluence | View counts, used by the stale-page and orphan checks |
read:configuration:confluence, read:confluence-user, read:user:confluence, read:group:confluence, read:space.permission:confluence | Owner resolution, deactivated-owner detection and the restricted-pages check |
write:page:confluence, delete:page:confluence, write:attachment:confluence, delete:attachment:confluence, write:label:confluence, write:space:confluence | Bulk remediation — applied only to targets an administrator explicitly selected, and executed as that administrator |
storage:app | Custom entities holding the scan index, findings, trends, audit log and generated reports |
The write and delete scopes exist for remediation only. Nothing is written during a scan, and every mutation runs as the calling user rather than as the app, so Confluence independently enforces whether they may perform it.
External access
Data stored
| Record | Key | Contents | Retention |
|---|---|---|---|
| space | one row per space | Key, name, type, status, page and attachment counts, storage bytes, last activity, health score, homepage | Refreshed on every scan |
| content | one row per page or blog post | Title, space, owner and author ids, timestamps, body length, label and link counts, a simhash band, version, view and comment counts, restriction flag, macro names | Refreshed on every scan |
| attachment | one row per file | File name, media type, family, size and total size across versions, version count, timestamps, what references it | Refreshed on every scan |
| issue | one row per finding | Category, severity, status, target, title, detail, recommendation, suggested actions, evidence, confidence, and ignore/resolve attribution | Keyed by fingerprint, so it survives re-scans |
| scan / trend / audit / report | operational | Scan runs and their stats, daily health trend points, the action audit log, and generated report metadata | While the app is installed |
Page bodies are never stored — only measurements derived from them. Note that the index deliberately contains content most users cannot see, including page titles, which are themselves sensitive on a restricted page. That is why nothing from the index reaches a browser unfiltered.
Security notes
Two facts drive the whole design
- Scanning runs as the app. A background job has no user, and a health score computed from a partial index is meaningless. So the index contains content most users cannot see.
- Nothing from that index reaches a browser unfiltered. Every read path runs findings through the permission validator, which asks Confluence what the calling user can retrieve and drops everything else.
The permission validator
- It does not reimplement Confluence’s permission model. It asks Confluence and believes the answer: ids that come back are visible, ids that do not are filtered out.
- It fails closed — a check that errors withholds the content.
- Reports are filtered the same way, because otherwise “export to CSV” would be a way to read restricted content the dashboard correctly hides.
Remediation
- All remediation runs as the calling user, so Confluence independently enforces that they may perform it. A mistake in the app’s own filtering cannot escalate into data loss.
- Bulk actions produce a preview before execution, and the audit log records the actor, the action, the target count, the targets and the outcome.
Storage schema honesty
- Forge’s entity store types
integeras 32-bit signed, which caps at 2,147,483,647 — so a space holding more than ~2.1 GB of attachments failed its whole row. Attribute types cannot be changed after deployment and attributes cannot be deleted, so float replacements sit alongside the originals and the superseded attributes are no longer written. The same applies to the indexes that ranged on them. - This is recorded in the manifest and the data model docs rather than quietly worked around, because it is a landmine for the next schema change.
Verification
- 96 automated tests, all passing, covering parsing, scoring, report file structure and storage semantics against an in-memory KVS.
- A manifest consistency check runs offline and fails the build if an index references an attribute that was never declared, or if a queue or handler name drifts away from the code.
forge lintpasses, which confirms the manifest — including the custom-entity and index schema — is valid to Atlassian.- A mock bridge lets the whole dashboard be driven locally against representative data. The mock deliberately supplies unflattering data — a mid-70s score, long page titles, a category with no findings, spaces of wildly different sizes — because a happy-path mock hides exactly the layout problems worth catching.
Known limitations
- Not yet validated against a live Confluence tenant. Everything verifiable offline has been verified; behaviour against real Confluence API responses is the remaining unproven area, and the operations guide lists what to watch on the first run, ordered by what is most likely to break.
- Registering the app creates
development,stagingandproductionenvironments. Storage is not shared between them — each needs its own first scan. - The first full scan is the expensive one; it builds the index that every subsequent health check reads.
Release notes
Version history for Confluence Cleanup Assistant. The most recent release is listed first.
-
v1.0.0 Current
Initial release.
What it does
- Fourteen health checks across broken links, empty and duplicate content, orphans, stale pages, attachment bloat, missing labels and owners, invalid macros, unused attachments, restricted pages, archived content, and inactive or oversized spaces.
- A Knowledge Health Score with daily trend points and storage analytics.
- Bulk remediation with a preview step, executed as the calling user, with a full audit trail.
- Label and owner recommendation engines.
- Report export to CSV, XLSX, PDF and ZIP, filtered through the same per-user permission validator as the dashboard.
- A global page and a space page, both backed by the same Dashboard API.
Architecture
- A queue-driven, checkpointed scan that scales to 100,000+ pages by never assuming a handler can finish in one invocation.
- Findings keyed by fingerprint rather than by time, so an administrator’s “ignore” decision survives a re-scan.
- Custom entities with secondary indexes chosen from the actual query patterns — every index declared is one the dashboard or rule engine really queries, enforced by an offline manifest check in both directions.
- No runtime dependencies beyond the Forge SDK; the XLSX, PDF and ZIP writers are implemented directly against their file formats.
Get it & contact
Vendor
AryonForge
Support
Security
App ID
ari:cloud:ecosystem::app/73df44d6-0678-45da-9a42-447abd95c99b