All features

Feature breakdown · built with CraftHQ

PlatformSecurity

Audit log

Whole-team time at this build's pace

Estimated ~11 h

2 people incl. the product owner, requirements to merge

Estimate at this build's pace, not a measurement of this feature

Every sensitive action, a reveal, an export, a sign-off, is recorded and can be reviewed.

Size: ~14 FP · Proxy estimate; an in-house count, not independently checked. Range 10 to 18. · How we counted 14 FP

How long a feature this size usually takes

Whole-team time — this build vs a typical team

~14 FP · FP × 6.8 to 9.4 h (ISBSG 2023)

Whole-team time at this build's pace2 people incl. the product owner, requirements to merge
~11 h8 to 15 h

Estimate at this build's pace, not a measurement of this feature

A typical team, whole lifecycleRequirements to release · middle half of 512 comparable projects (Java, Agile), ISBSG 2023
~95 to 132 habout 2.5–3.5 person-weeksmedian ~111 h
grey band = a typical team, middle halfhatched = our whole-team estimate

Both figures are whole-team time over the whole lifecycle, so they compare like for like. Ours is this build's pace — about 0.8 hours per function point across the whole build (two people, including the product owner, with requirements and review time). The industry range is an ISBSG project-level figure applied to one feature, so it is indicative.

Workflow

How it moved through the workflow

Select a stage. The build shipped, so every stage is complete; a person approved the merge.

Request

Every sensitive action, a reveal, an export, a sign-off, should leave an append-only record that oversight can read later.

Questions

One design decision was recorded before building.

Approved

The product owner signs off the breakdown by moving the story into the approved state, and the build runs against that reference.

Definition of ready

The questions are answered.
The acceptance criteria are testable.
The size is estimated.
The scope is checked for splitting.
The product owner has signed off.

Building

CraftHQ's build agents write the code and the tests together, run as a loop; a failing check halts the work until it passes.

Checks

Three checks run on every pull request: one on the product owner's machine as CraftHQ builds, one on the engineer's machine before review, and a code-quality scan in CI. Merging is gated by the CI check passing and a person's approval, enforced by branch protection.

Review

An engineer reviews the pull request and merges it.

Merged

The pull request is merged to main after a person approves it. Nothing merges without that approval.

Definition of done

Every acceptance criterion is checked; where a scenario has a dedicated test it is cited, and where it was verified by review that is stated.
The three checks are green.
A person approved the merge.

Breakdown

The feature breakdown

Business first, then the engineering view. Tab through it.

Overview

The gap it closes

Without a record of sensitive actions as they happen, there is no reliable way to show who did what later.

Who uses it

Administrators and reviewers read the trail for oversight. Every role's sensitive actions are recorded.

User story

As an administrator, I want an append-only log of sensitive actions so that I can show who did what and when.

Examples

Example 1

A user exports a list. An audit row is written in the same transaction, recording who exported, what, and when.

Example 2

An administrator reveals a masked contact. The reveal is written as its own audit row, recording who revealed it and when, with no separate reason prompt.

Dependencies & risks

Depends on

An append-only, partitioned store, and the data changes and sensitive actions across the product that emit audit rows.

Risk & mitigation

A recorded action could be missed if it did not emit a row. Reduced by writing the audit row in the same transaction as the action, so a committed change cannot commit without its record. The log records data changes and sign-in outcomes.

How it works

Simplified diagrams of the parts involved, the steps of one request, and where it runs.

Architecture

People
Any userperforms an action
Administrator or reviewerreads the trail
Your app
App serverperforms the action, writes the row
Audit storeappend-only, partitioned
Activity viewper-role, per-tenant
  • 1performs a sensitive action
  • 2append the row in the same transaction as the action
  • 3filter by actor or action
  • 4read the trail, scoped by role and tenant
Simplified.

Sequence

Browsera user
App serverperforms the action
Audit storeappend-only
  • 1perform a sensitive action
  • 2build the audit row
  • 3append the row in the same transaction as the action
  • 4commit the action and the row together
  • 5action done, recorded
Simplified.

Infrastructure

Client (untrusted)
Web appthe activity view
Your app
App serverauthenticates, pins the tenant, emits audit rows
Databaseper-tenant isolation, partitioned audit store
  • 1action or read the trail (authenticate + tenant check)
  • 2append and read audit rows
Simplified.

Acceptance criteria

Each scenario is written as a Gherkin spec. The Engineering tab shows the test that covers each one, or notes where it was verified by review.

@platform @security
Feature: Audit log
As an administrator
I want an append-only log of sensitive actions
So that I can show who did what and when
Every data change and sensitive action writes an append-only audit row in the same transaction, and a per-role view lets oversight read the trail. The log records sign-in outcomes and data changes.
Background:
Given the audit log is append-only and partitioned
And database permissions refuse any update or delete of an audit row
Rule: A successful sensitive action and its audit row are written together
Scenario: A sensitive action records an audit row
Given a user performs a sensitive action
When the action is committed
Then an audit row is written in the same transaction
And it records who acted, what they did, and when
Rule: The log records data changes and sign-in outcomes
Scenario: A failed sign-in is recorded
Given a staff account fails a sign-in
When the outcome is recorded
Then an audit row is written for that sign-in
Scenario: A data change is recorded
Given a user makes a data change
When it is committed
Then an audit row is written in the same transaction
Rule: Rows are kept for at least seven years and never edited
Scenario: An audit row is retained, not rotated away
Given an audit row is older than a partition boundary
When partitioning runs for scale
Then the row is retained for at least seven years and is never edited
Rule: The log cannot be altered after the fact
Scenario: Updating or deleting an audit row is refused
Given an audit row has been written
When any account attempts to update or delete it
Then the database refuses the change
Rule: The audit row is the record for a sensitive reveal or export
Scenario: A reveal is recorded without a reason prompt
Given a user reveals or exports sensitive data
When they take the action
Then an audit row is written
And no separate reason prompt is required
Rule: Oversight reads the trail by role and only within its own organisation
Scenario: A per-role, per-tenant activity view
Given an administrator or a reviewer opens the activity view
When they filter by actor or action
Then they see the matching audit rows for their own organisation
And they never see another organisation's actions
@edge-cases
Scenario Outline: Sensitive actions each leave a row
Given a user performs a "<action>"
When the action is committed
Then an audit row is written
Examples:
| action |
| a reveal |
| an export |
| a sign-off |

Questions

Questions raised in planning, each with the decision taken.

Is an audit row enough on its own, or should a sensitive reveal also prompt for a reason?

A reason prompt adds friction to every reveal and export; the audit row already records who acted and when.

AThe audit row is the record. Record the action; do not add a reason prompt. · recommended
BRequire a typed reason on every sensitive reveal or export.
Decided: The audit row is the record; no reason prompt (option A).

How we sized it

A function point measures what the software does — its inputs, outputs, lookups and the data it stores — regardless of language or how many lines it took. Each counted item is weighted by complexity: a user input counts 3–6 points, an output or report 4–7, a lookup 3–6, data the feature owns 7–10, and data read from elsewhere 5–7.

  • The append-only audit store this feature owns
    Data the feature owns
    10
  • View the activity log by role
    Lookup
    4
  • Total
    14
14 function points (range 10–18). IFPUG-style count: each user action, lookup, output and stored data set, weighted by complexity. Technical plumbing is excluded, and a data store is counted once in the feature that owns it.

Not counted here

  • Writing an audit row on each action: plumbing, not a user function

Components touched

Append-only audit store

A partitioned store. Rows are only ever added; database permissions refuse any update or delete. Rows are retained for at least seven years; partitioning is for scale, not to delete old rows.

In-transaction emission

A sensitive action or data change writes its audit row in the same transaction, so a committed action is always recorded.

What is recorded

Data changes and sign-in outcomes are recorded: a sign-in, a failed sign-in, a lockout, a reveal, an export, a sign-off, a deactivation and a role change.

Activity view

A per-role, per-tenant view that filters the trail by actor or action, and never shows another organisation's actions.

Data touched

Audit storeappend

Append-only, partitioned rows: who acted, what they did, and when. Retained for at least seven years and never edited.

Audit catalogread

The map of actions that emit audit rows: a sign-in and a failed sign-in, a masked-value reveal, an unmasked export, a review sign-off, a user deactivation and a role change.

Security decisions

  • The log is append-only, enforced by database permissions: any update or delete of an audit row is refused, and a test proves it.
  • A sensitive action or data change writes its audit row in the same transaction, so a committed action is always recorded.
  • The log records data changes and sign-in outcomes: sign-ins, failed sign-ins, lockouts, reveals, exports and sign-offs.
  • Rows are retained for at least seven years and are never edited.
  • The activity view is scoped by role and by tenant: an administrator or reviewer sees only their own organisation's actions.

Subtasks

  • An append-only, partitioned audit store with database permissions refusing update and delete, retained for at least seven years.
  • In-transaction emission of audit rows from data changes and sensitive actions.
  • Recording of data changes and sign-in outcomes.
  • A per-role, per-tenant activity view with filters.

How each scenario is verified

  • A successful sensitive action records an audit row
    An integration test checks that a sensitive action writes an audit row in the same transaction.
  • Update and delete of an audit row are refused
    A test checks that the database permissions refuse any update or delete of an audit row.
  • A data change and a staff sign-in outcome are recorded
    An integration test checks that a data change and a sign-in for a staff account write an audit row.
  • The activity view is scoped by role and tenant
    A test checks the role-and-tenant-scoped activity view.

Risk tier

Core A person approves every merge. This is a Core-tier change, so it also carries the fullest set of automated tests; a regression here affects the whole product, not one page.

Start here

Want this on your backlog?

Get a free baseline report on one repository, or scope a 3-month pilot measured against what your team’s work costs today.

Measured from your own git history
Every pull request tested before review
A person approves every merge