keelapps/AccessLens for Confluence/Documentation
Documentation
AccessLens reads your site's permission state and hands it back in the shape an auditor asks for: who can reach what, what is locked, what changed, and who signed for it. It writes nothing — there is no write scope in the manifest — so everything below is a read, a stored copy of a read, or a decision recorded about one.
Where AccessLens lives
- Apps → AccessLens — the main console: reverse lookup, snapshots, diffs and reviews for the whole site.
- Space settings → AccessLens — the same answers narrowed to one space, including its restriction audit, for the people who administer that space.
A daily scheduled trigger drives scheduled scans, and a queue consumer runs the scan itself in resumable shards so a large site finishes rather than timing out.
Reverse lookup
Confluence indexes permissions by space: open a space and it will tell you who has what. AccessLens inverts the index, so you can start from any of three subjects:
- By group — every space the group reaches, and at what level. The answer an access review actually needs.
- By user — every space that person reaches, with every path that grants it. If three group memberships all reach the same space, all three are listed, because removing one of them changes nothing and a reviewer needs to know that.
- By space — everyone who reaches it, with group membership expanded into people.
Expansion is the point. A grant list that says “Engineering can read this space” is not an answer; “these 148 named people can read this space” is. On sites that use role-based space permissions, the role assignments carrying the same information are read as well.
The page restriction audit
This is the piece Confluence makes hardest to self-check, and it is where the app differs most from everything else in the category.
Per space, AccessLens lists:
- Every restricted page, with the restriction type — view, edit, or both.
- Where the restriction comes from. Restrictions inherit downwards, so a page is very often hidden by an ancestor rather than by anything set on itself. The inheritance source is named per page.
- Which users and groups the restriction names.
And five classes of finding, each of which is a real mistake rather than a statistic:
| Finding | Why it matters |
|---|---|
| Restriction grants nothing | Restrictions can only narrow space permissions, never widen them. A restriction naming a group with no view permission on the space gives that group nothing — the reverse of what whoever typed it believed. |
| Only one person can read it | A page one leaver away from being unreachable. |
| Edit-only restriction | Frequently a mistake for a page that was meant to be private, not merely read-only. |
| Named individuals rather than groups | Access that no longer tracks anybody's role, and that nobody will remember to revoke. |
| Hidden by an ancestor | Removing the restriction on the page itself will change nothing; the restriction is further up. |
Reading a page's restrictions is one request per page, which is the only cost in the app that grows with the size of your content. The restriction audit therefore has three modes — off, sampled and full — and sampled is the default, with a per-space page ceiling. The interface distinguishes “checked and found nothing” from “not checked”; it never lets a sample look like a complete pass.
Snapshots
A snapshot is the whole site's permission state at a point in time: for each space its identifiers and its permission grants, for each restricted page its identity and the principals its restrictions name, and for each group referenced by a grant its name and its members.
Snapshots run manually or on a schedule. The scan is sharded and checkpointed and the cursor reaches inside a single space, page by page — a space can hold tens of thousands of pages, and a scan that can only resume at space boundaries would never finish on one of them.
If AccessLens could not read something it needed, the snapshot is marked partial and says which part is missing. It never quietly reports less than it found.
Diffs
Compare any two snapshots. The diff is reported one row per space and subject, which is the unit an administrator actually acted in: granting a contractor group read, comment and export on one space is one administrative action and reads as one row, not three permission lines.
That is a deliberate aggregation. A diff that lists every changed permission separately is technically complete and practically unreadable — the reviewer's job is to recognise the change somebody made, not to reassemble it.
Access reviews and sign-off
A review is pinned to one snapshot, so it cannot drift while it is being conducted. Space by space, a reviewer confirms or flags, with an optional note.
Each decision records:
- The reviewer's Atlassian account id.
- A server-side UTC timestamp — not the reviewer's clock.
- The decision and any note.
Changing a decision does not erase the old one: superseded decisions are kept alongside the current one. Once a review is signed off it freezes.
Risk flags, and what is not observable
AccessLens flags three site-level risks:
- Anonymous access to a space.
- “All logged-in users” grants.
- Spaces with no administrator.
The first of these depends on your tenant's permission model. On sites using role-based space permissions, anonymous access is readable as an access-class principal in the role assignments. On the older model it is not readable at all — the only endpoint that ever reported it has been removed from the API.
So AccessLens measures whether it could observe anonymous access on your site, and where it could not, the interface says “not observable here” rather than “none found”. Those are different statements, and only one of them is true. In the same spirit, when the app cannot recognise any access class on a space, that fact is itself a finding — a permission audit whose risk checks silently never fire is the most dangerous kind of clean report.
Cost estimates and the API budget
Confluence Cloud charges apps for API calls in points, and the hourly allowance is shared across every site running the app rather than granted per site. Before a scan runs, AccessLens shows what it will cost in points and what share of that hourly allowance it represents. Shard size and the restriction mode are both settings, so a large site can be scanned within a budget you chose rather than one you discovered.
CSV export
Every view exports as CSV. The file is assembled in your own browser from data delivered over the Forge bridge — it never passes through a server of ours, because there is not one. Exports carry a byte-order mark so spreadsheets open them in the right encoding, and leading formula characters are neutralised so a cell of your data cannot execute in somebody's spreadsheet.
Settings
- Scan cadence — how often a scheduled snapshot is taken.
- Shard size — how much work one shard does before checkpointing.
- Page-restriction mode and budget — off, sampled or full, with a per-space ceiling.
- Snapshot retention limit — how many snapshots to keep. At the limit AccessLens refuses a new snapshot rather than deleting an old one. Delete one yourself if you want room; the app will not make that decision for you.
Permissions the app requests
Eight scopes, and not one of them is a write:
| Scope | Why |
|---|---|
read:space:confluence | the space list, and space permission grants |
read:space.permission:confluence | space roles and role assignments, on tenants that use them |
read:page:confluence | page id, title and parent id, so a restricted page can be named and its inheritance walked — never page content |
read:content-details:confluence | a page's view and edit restrictions |
read:content.restriction:confluence | declared alongside the above so the restriction audit works across API shapes |
read:user:confluence | resolve account ids to display names |
read:group:confluence | groups and their members, so a group-level answer becomes a person-level one |
storage:app | snapshots, reviews and settings, in Atlassian-hosted storage on your own site |
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. And with
no write scope at all, it cannot change a permission, a restriction, a page or a
comment.
Not in this release
- Changing permissions or restrictions — deliberately, and permanently. This is an audit tool.
- Attachment-level or comment-level permission reporting.
- Email or Slack notifications when access changes.
- Cross-site reporting.
- Jira permissions — those are AccessLens for Jira.
Support
Questions, bug reports and feature requests: raise a support request. Security issues: report privately.