Documentation

Setup and usage guide for Shakamize Document Approval and Audit Trail for Confluence. Written for Confluence administrators and for the quality, compliance or internal audit owner responsible for controlled documents.

This documentation describes the app as it is being prepared for its first Atlassian Marketplace release. Sections that depend on behaviour not yet shipped are marked, and are updated as each part ships. Exact screen names and menu labels may differ slightly from the released build.

Overview

The app adds document control to Confluence pages. A page can be placed under control, routed through a review and approval workflow, and locked once approved so that the approved revision cannot be edited silently. Every step is recorded in a tamper-evident audit trail, and controlled pages are listed in a register showing approval state, current approved revision, approvers and review dates.

It is intended for teams operating ISO 9001 quality management systems, SOX control environments and internal audit programmes on Confluence.

The app is built on Atlassian Forge and runs inside Atlassian infrastructure. It does not transmit customer content to any external service, and it requires no separate account.

Requirements and compatibility

  • Confluence Cloud. The app is a Forge app and is not available for Confluence Data Center or Server.
  • A Confluence site administrator is required to install the app and to complete the initial configuration.
  • Space administrator rights are required to enable document control in a given space.
  • No external network access, firewall change or additional infrastructure is needed.

Installation

  1. In the Atlassian Marketplace, or from Confluence under Apps → Explore more apps, search for the app by name.
  2. Select Get app / Install and choose the Confluence site to install it into.
  3. Review the access the app requests and confirm the installation. Forge presents these as a consent screen at install time.
  4. Once installation completes, the app appears in Confluence under Apps, and a configuration entry appears in the site administration area under Apps → Manage apps.
  5. Open the app configuration and complete the initial configuration described below before enabling control on any space.

Installation does not change any existing page. Nothing is locked and no workflow applies until a space is explicitly enabled and a page is explicitly placed under control.

Permissions and data access

The scopes the app requests are shown on the Atlassian consent screen at install time and on the Marketplace listing, which is the authoritative place to review them. They are reproduced here with the reason for each, because an administrator approving an install should not have to infer why a permission is being asked for.

ScopeWhy the app needs itUsed by
read:page:confluenceRead a controlled page to establish which version is under approval and whether it has since changed.First release
read:content-details:confluenceRead the restrictions currently on a page. Also required in order to write them.First release
write:content.restriction:confluenceApply and remove the edit restriction that locks an approved page.First release
read:user:confluenceResolve an approver to a person so the register and the trail can show who acted.First release
read:confluence-content.summaryReceive Confluence page events, including the event fired when somebody changes a page’s restrictions outside the app.First release
storage:appStore the app’s own data in Forge storage on your site.First release
report:personal-dataReport personal data and act on erasure requests, as Atlassian requires of any app that stores personal data.First release
read:space:confluenceEnumerate spaces for cross-space governance reporting.Requested now, used by a later release
read:confluence-space.summaryReceive space created and updated events for the same reporting.Requested now, used by a later release

Two scopes are requested before the app uses them, and that is deliberate. Adding a scope to an already-installed Forge app strands every existing installation on the old permission set until each administrator individually re-approves it. Asking once, up front, for the two space-level scopes that cross-space reporting will need avoids putting every customer through a re-consent later. The app does not read space data before that feature exists.

Two blanket-write scopes are deliberately not requested. The classic equivalent of the restriction scope above is write access to all Confluence content, and the space-level permission events require blanket space write. The app takes the narrow restriction scope instead and detects space-level changes by other means, so neither blanket permission appears on your consent screen.

The app acts as itself, not as you, when it writes a restriction. Locking has to hold against the person being locked out, which it could not do if the lock were written with that person’s own permissions. Everything else the app shows you respects your own Confluence permissions. Because the restriction write replaces the whole restriction set on a page, the app preserves any existing view restriction rather than dropping it: approving a document never widens who can see it.

App data is held in Forge storage associated with your Atlassian site. Confluence page content itself remains in Confluence; the app records approval metadata and audit events referencing pages and versions rather than duplicating page bodies.

Existing Confluence space and page permissions continue to apply. The app does not grant any user visibility of a page they could not already see.

Initial configuration

Site-level configuration

  1. Open Confluence administration and go to Apps → Document Approval and Audit Trail.
  2. Review the default approval settings that will apply to spaces which do not define their own.
  3. Set the default periodic review interval, if your document control procedure requires periodic re-approval.
  4. Save. Site-level settings act as defaults only; a space can override them.

Enabling a space

  1. Open the space and go to Space settings.
  2. Select the Document Approval entry.
  3. Enable document control for the space.
  4. Choose the approval workflow that applies to pages in this space, or accept the site default.
Pending first release

The precise layout of the space settings screen, and whether workflow selection is per space or also per page tree, are finalised with the first release. This section is updated when it ships.

Defining an approval workflow

A workflow defines the sequence of steps a controlled page passes through before it is considered approved, and who may act at each step.

  • Steps are ordered. A page moves to the next step only when the current step is satisfied.
  • Each step names its permitted approvers, by user or by group.
  • A step can require one approver or all named approvers, depending on how the step is configured.
  • The final step, when satisfied, marks the current revision approved and applies locking.

A single-step workflow with one approver group is a reasonable starting point for most ISO 9001 document sets, and is what we suggest configuring first.

Pending first release

The available step types, and whether workflows can branch by document type or label, depend on features still in development. Treat the description above as the shape of the model rather than an exhaustive option list.

Placing a page under document control

  1. Open the page in a space where document control is enabled.
  2. From the page actions menu, choose to place the page under document control.
  3. Confirm the workflow that applies and, if periodic review is used, the review interval for this document.
  4. The approval panel appears on the page, showing the current state as draft or awaiting approval.

Placing a page under control does not retroactively approve anything. The page starts unapproved, and its first approved revision is created the first time it completes the workflow.

The review and approval cycle

  1. An author finishes editing and submits the current revision for approval.
  2. The workflow advances to its first step, and the approvers for that step are notified.
  3. Each approver opens the page, reviews the specific revision named in the approval panel, and approves or rejects, choosing a reason from a fixed list such as Meets the requirements, Reviewed on schedule, Content is inaccurate or Evidence is missing.
  4. A rejection returns the page to draft. The author edits and resubmits, which starts a new cycle against the new revision.
  5. When the final step is satisfied, the revision becomes the approved revision and the page is locked.

Approvers always act against a named revision. If the page is edited mid-cycle, approvals already given no longer apply to the changed content and the cycle restarts, so an approval can never be silently inherited by content the approver did not see.

Locking, unlocking and revising

When a revision is approved, the app restricts editing of the page. This is the difference that matters: the approved record and the live page cannot drift apart while the page sits approved.

Revising an approved document

  1. A user with the right to revise opens the approval panel and starts a new revision.
  2. The unlock is recorded as an audit event, naming the user and the approved revision being superseded.
  3. The page becomes editable again and returns to draft state. The previously approved revision remains recorded as having been approved.
  4. The new revision passes through the workflow and, on approval, becomes the current approved revision.

There is no path that edits an approved page without producing a record. Unlocking is an explicit, attributed, recorded action.

An administrator can still override restrictions through Confluence administration, and an approved page can be edited that way. The app does not pretend otherwise. It compares the live page against the approved version and against the restrictions it wrote, and reports the difference as Page edited outside this app or Lock no longer holds, with an entry in the audit trail. See states that say the app cannot tell.

Periodic review and overdue documents

Where your procedure requires documents to be re-approved on a cycle, set a review interval on the document or inherit one from the space or site default. The app calculates the next review date from the date of the most recent approval.

  • Documents approaching their review date are surfaced in the register ahead of time.
  • Documents past their review date are marked overdue.
  • Completing a review means running the document through the approval workflow again, which resets the review date.
Pending first release

Notification delivery for upcoming and overdue reviews, including whether reminders are sent by email or surfaced in Confluence only, depends on functionality not yet shipped.

The document register

The register is the single view that answers the questions an auditor asks. It lists the controlled documents visible to you, with:

  • Document title and location
  • Current state: draft, in approval, approved, or overdue for review
  • The current approved revision
  • Who approved it and when
  • Next review date, where periodic review applies

The register respects Confluence permissions. You see the controlled documents you have permission to see, and no others.

States that say the app cannot tell

Some of what the register and the approval panel show you are not statements that something is fine or broken. They are statements that the app does not know, or that something happened that it did not do. Those states are deliberate, they are the ones that matter most in an audit, and this section exists so nobody mistakes one for a defect.

The rule the app follows is that an unknown state is reported as unknown and never rounded to a healthy one. A compliance tool that answers “no problem found” when it means “I could not look” is worse than one that says so, because the difference only surfaces during the audit it was bought for.

Lock states

What you seeWhat it means
Locked by this appThe app wrote an edit restriction and the restriction it wrote is still the one on the page.
Not locked by this appThe app holds no restriction on this page. Anyone who could edit it still can. Normal for a draft.
Lock no longer holdsThe app locked this page and the restrictions on it are no longer the ones it wrote. Somebody changed them outside the app, and the change is recorded in the trail. This is the app working: it noticed and told you.
Lock refused hereThe approval is recorded, but the app could not reach the page to lock it. The document is tracked and cannot be locked from here.
Lock state not knownThe app has not established what restrictions the page carries, so it will not claim the page is either locked or unlocked.

Whether a page can be read

What you seeWhat it means
Page can be readThe app last read this page successfully.
Page cannot be readThe app can no longer read this page, and it will not guess why. A data security policy blocking the app, a view restriction the app is not a subject of, and the page having been deleted are indistinguishable from where the app stands.
Never readThe app has not yet read this page, so it makes no claim either way. A newly controlled page shows this until the first read.

Why the app will not say which cause applies

When a Confluence data security policy blocks the app from a space, Confluence answers the app’s read with the same response it gives for a page that does not exist. A view restriction the app is not a subject of produces that same response again. The three causes are not distinguishable through the API, and any attempt to tell them apart would come down to matching on the wording of an error message, which changes without notice and would eventually report deletions that never happened.

So the app reports what it can establish and names the ambiguity rather than resolving it silently. If you need to know which of the three applies, the answer is in your own Confluence administration: check whether a data security policy covers that space, and whether the page still exists.

A data security policy also removes the app’s own panels from the blocked space, so there is no surface inside that space in which to explain the situation. That is why these states surface in the register and the audit trail rather than on the page itself.

If you are evaluating this app

On an unfamiliar site, and particularly on a test site with existing restrictions or policies, it is normal and expected to meet “Page restrictions changed outside this app” and “Never read” within minutes. Both are the app reporting accurately, not failing. The first appears whenever page restrictions are edited through Confluence rather than through the app, which includes an administrator using restriction settings directly. The second appears on any controlled page the app has not yet had occasion to read.

The audit trail and how to verify it

Every significant event is written to an append-only audit trail: submission for approval, each approval and rejection, the approval of a revision, unlocking, restrictions being changed outside the app, a page becoming unreadable or readable again, configuration changes to a workflow, and removal of a document from control. Each entry records who acted, the affected page and version, the outcome, a reason drawn from a fixed list of codes, and a timestamp.

How an entry names a person

Entries do not contain Atlassian account identifiers. Each one references a pseudonymous key, and the mapping from that key to a person is held in a separate table so that an erasure request can be honoured by deleting from the mapping. The entries themselves are untouched and the chain still verifies. An entry whose identity has been erased displays as an anonymous actor rather than a name, and verification reports identity erased as a legitimate outcome rather than as a broken record. The consequence is worth planning for: that an approval happened is permanent, but its attribution to a named person is not.

Entries also contain no free text. The reason an approver gives is a code, and the sentence you read is generated from that code when the entry is displayed.

What tamper-evident means here

Entries are hash-linked: each entry commits to the content of the entry before it. Altering or deleting an entry in the middle of the chain breaks the link, and verification reports the break and where it occurred. This is a detection guarantee, not a prevention guarantee. The audit trail is tamper-evident, not tamper-proof.

One limit is worth stating plainly, because it affects how you use the trail as evidence: chain verification detects modification or removal of entries within the chain, but it cannot by itself detect a chain that has been truncated at its most recent end. If the last entries were removed, the remaining chain still verifies. Exporting and retaining verification output periodically gives you an external reference point that makes truncation detectable against your own records.

Running verification

Verification is something you run against an export you hold, not something the app asserts about itself. That distinction is the whole point: an app that grades its own record gives you no more assurance than the vendor’s word, which is exactly what a tamper-evident trail is supposed to replace.

The check is a single script, verify-trail.mjs, that you download and run yourself. It is published as source, with a release for every version, so that you can read it before you run it. See the verifier, and reading it first.

  1. Export the audit trail for a document, or for the site, and keep the export.
  2. Download verify-trail.mjs from its release page, read it, and check the SHA-256 of what you received against the digest named on that same release page.
  3. Run node verify-trail.mjs my-export.json. Once you hold more than one export, pass them all on the same command line, oldest first.
  4. Keep the output alongside the exports as audit evidence. Two exports taken at different times are what make a removed tail detectable, as described above.

What verification can answer

OutcomeWhat it means
verifiedEvery row hashes to the value stored with it, every row follows the one before it, and the sequence numbers run without a gap. The row count and head hash are printed per document; those two values are what you keep in order to compare against a later export.
BROKEN at sequence NA row was altered, or a row was removed from the middle of a chain. The document and the sequence number are named. That number is where the chain stops agreeing with itself, which is at or before the change.
identity erasedSomebody exercised their right to erasure and the app erased the account behind their approver key. The approval stands, the row is untouched and the chain still verifies. This is a lawful erasure, not a defect, and it is reported separately from any verdict about the chain.
Cannot tellThe file could not be read, or it is in a format or a hash construction that version of the script does not implement. This is explicitly not a report that the record is intact, and it is not a report that it is broken.

The exit code carries the same verdict: 0 verified, 1 broken, 2 cannot tell.

Making the truncation limit detectable

The limit above is not only a caveat; it is actionable. If you keep every export and pass them all to the script, oldest first, each earlier file is checked against the later ones, and a document whose rows have receded, meaning fewer rows than an earlier export held or a row that is not the row the earlier export held, is reported as broken. Keeping every export is what turns a limit you have to live with into something you can detect. A single file cannot do this, and the script does not claim otherwise.

Exporting records

The document register and the audit trail can both be exported so that the record can be retained outside Confluence, handed to an auditor, or kept if you stop using the app.

  • Register export: the list of controlled documents with state, approved revision, approvers and review dates.
  • Audit trail export: the event entries, with the values needed to re-verify the chain independently.

The audit trail export format

The format is settled. Its identifier is dc-audit-export-v3 and an export is a single JSON file. Every envelope carries:

FieldWhat it holds
formatThe format identifier, dc-audit-export-v3.
algorithmThe hash the rows were chained with, dc-audit-sha256-v1.
constructionHow the hashes in this file are built: digest, encoding, separator, genesis hash and the ordered list of hashed fields. An export describes its own construction so that the check does not depend on this page being accurate.
limitsWhat the export covers, so that a partial export is not mistaken for a complete one.
attributionThe standing of each approver key: whether the app still holds an account behind it, whether it was erased, or whether it could not be looked up.
documentsThe controlled documents, each with its chained rows.

An export never contains an Atlassian account identifier. Rows carry the pseudonymous approver key described above, and the mapping from that key to an account stays inside the app, which is what allows an erasure request to be honoured without rewriting a single hashed row.

The verifier, and reading it first

Being handed a script by the vendor whose record you are checking is a reasonable thing to be suspicious of. So the checker is published as source, in its own public repository, rather than only as a download from us, and it is short enough and plain enough to read in a sitting.

Where it is published

The checker is published as source at github.com/shakamize/document-control-verifier under the Apache 2.0 licence. Every version is a release, and each release page names the SHA-256 of the exact file it attaches, so you can confirm you have the file we published before you run it. It needs Node 18 or later and nothing else. There is nothing to install and it reads no network.

Download verify-trail.mjs from release v1.1.0, check its digest against the one on that release page, and run node verify-trail.mjs your-export.json. Keep every export you take: passing several to the same command, oldest first, is the only way to detect rows removed from the end of a chain.

The digest is deliberately not repeated on this page. A second copy of a digest is a second thing that can drift out of step with the file it describes, and a digest that disagrees with an intact file is worse than no digest at all, because it reports tampering that did not happen. The release page that carries the file is the one place that carries its digest.

To check what you received: on macOS or Linux, shasum -a 256 verify-trail.mjs; on Windows, certutil -hashfile verify-trail.mjs SHA256.

The script is licensed under the Apache 2.0 licence. You may read it, run it, modify it, redistribute it and use it as the basis of your own checker, including in a commercial setting, without asking us.

What it does and does not do

  • It needs Node 18 or later and nothing else.
  • It has no dependencies. There is nothing to install.
  • It reads only the files you name on the command line.
  • It writes nothing. Its whole output is what it prints to the terminal.
  • It makes no network calls. Nothing about your export leaves your machine, and the app is never told that you ran it.
  • It recomputes hashes and compares them. It has no way of repairing anything, and no way of asserting that a record is good other than by the arithmetic agreeing.

The hash construction

An auditor who will not run anyone else’s code does not have to. Every export describes this under construction, and it is small enough to reimplement in about twenty lines:

  • The digest is SHA-256, rendered as lower case hex.
  • Ten parts are hashed, in the order the export lists them: the literal algorithm identifier dc-audit-sha256-v1, then nine fields taken from the row, which are previousHash, documentId, sequenceNumber, spaceKey, recordedAt, eventType, actorKey, outcome and detailCanonical.
  • Each part is prefixed with its length in UTF-8 bytes and a colon.
  • The parts are joined with a vertical bar.
  • The first row of every document chains from a genesis hash of sixty four zeros.

Verify all of that against the published source rather than trusting this description of it. The script refuses to answer at all if an export describes a construction other than the one it implements, rather than checking rows against the wrong rule and reporting a break that is really its own mistake.

Roles and who can do what

  • Confluence site administrator: installs the app, sets site defaults, and is the only role that can remove app data wholesale by uninstalling.
  • Space administrator: enables document control for a space and selects the workflow that applies.
  • Author: places pages under control, edits drafts, and submits revisions for approval.
  • Approver: approves or rejects a named revision at a workflow step they are assigned to.
  • Reader: sees the approval state and the approved revision on any page they already have permission to view.

All of these are layered on top of Confluence permissions. A user who cannot view a space cannot see its controlled documents in the register.

Uninstalling and removing data

Before uninstalling, export the register and the audit trail if you need to retain the record. Uninstalling removes the app’s stored data.

  1. Export the register and the audit trail and store them with your quality or audit records.
  2. Remove pages from document control if you want their edit restrictions lifted in a recorded way rather than as a side effect of uninstall.
  3. Uninstall the app from Confluence administration under Apps → Manage apps.

Confluence page content is never removed by the app. Only the app’s own approval metadata and audit records are affected.

For data subject requests, including erasure, see the privacy policy.

Troubleshooting

The approval panel does not appear on a page

Check that document control is enabled for the space, and that the page has been explicitly placed under control. The panel only appears on controlled pages.

A user cannot approve a step

Confirm that the user is named as an approver on that step, either directly or through a group, and that they have permission to view the page in Confluence.

An approved page can still be edited

Confluence site administrators can override page restrictions through administration. Check the register: the document shows Lock no longer holds when the restrictions on the page are no longer the ones the app wrote, and the audit trail carries the corresponding entry. See states that say the app cannot tell.

A document shows a state that looks like a fault

“Page restrictions changed outside this app”, “Lock refused here”, “Page cannot be read” and “Never read” are reported states rather than errors. Each is explained in states that say the app cannot tell, with what to check in Confluence for each one.

Verification reports a broken chain

The report names the entry at which the link fails. Retain the verification output and contact support before making further changes, so the state can be investigated as it stands.

Support

Support for this app is handled directly, by the people who build it. See the support page for how to reach us and what to include in a report.