Evidence log and packs
The Evidence page has two tabs:
- Log: every match, fix, deploy, decision, alert, sign-in change and generated pack, as a record in an append-only, hash-chained log. Browse it, filter it by kind and date, verify the chain, and export it as CSV.
- Packs: one-click evidence packs for ISO 27001, SOC 2 and Essential Eight over a period, as a JSON file (the record) and a PDF made from it (the copy people read).
Everyone in the organisation can read the log and download packs, Viewers included. With teams, a team member reads the log entries about their teams’ projects and no packs. Owners and admins generate packs.
The log
Section titled “The log”Records are only ever added. Nothing in the log can be edited or deleted, by you or by MopokeHQ’s application.
Each record holds when it happened, its kind (such as response.not_affected, exposure.verified, scan.completed or package.released), who did it (a person, a CI job or MopokeHQ itself), what it is about, and the details. Each record’s hash covers its own fields and the hash of the record before it, so changing any record breaks every link after it.
Some examples of what lands in the log:
- an advisory matched, a fix seen on a branch, a fix seen deployed,
- a P1 alert, an escalation, an acknowledgement,
- “not affected” and “accepted risk” decisions with their reasons, and every reopen,
- external scans and their findings, ownership checks,
- packages quarantined, blocked and released,
- changes to AI settings, API keys, webhooks and teams,
- quarterly access reviews,
- each evidence pack generated, with its hash.
Verify the chain
Section titled “Verify the chain”In the app. Evidence, Log, Verify chain walks the whole chain from the first record and reports the first record that fails, if any.
Yourself. Export the log and recompute every hash. You need no MopokeHQ code to do it.
-
Export the log. Evidence, Log, Export CSV, with no kind or date filter (a filtered export leaves gaps, so its links will not match). The columns are:
tenant_id, seq, occurred_at, kind, actor, subject, payload, dedupe_key, prev_hash, hashEvery hashed field is written exactly as it was hashed.
prev_hashandhashare hex. -
Start from the genesis hash. Record 1’s
prev_hashis SHA-256 of the textmopoke-evidence-genesis:followed by thetenant_id. -
Recompute each record’s hash. SHA-256 over these fields, in this order, each written as a 4-byte big-endian length followed by its bytes:
prev_hash, as raw bytes (decode the hex first),tenant_id,seq, in decimal,occurred_at, exactly as in the export (UTC, microseconds, such as2026-10-23T01:02:03.000000Z),kind,actor,subject,payload,dedupe_key.
-
Check the links. Each record’s
hashmust equal what you computed, and each record’sprev_hashmust equal the previous record’shash.
A short Python script that does all of this:
import csv, hashlib, struct, sys
def hash_record(prev: bytes, row: dict) -> bytes: h = hashlib.sha256() fields = [prev] + [row[k].encode() for k in ("tenant_id", "seq", "occurred_at", "kind", "actor", "subject", "payload", "dedupe_key")] for f in fields: h.update(struct.pack(">I", len(f))) h.update(f) return h.digest()
with open(sys.argv[1], newline="", encoding="utf-8") as f: rows = list(csv.DictReader(f))
prev = Nonefor row in rows: if row["seq"] == "1": prev = hashlib.sha256(("mopoke-evidence-genesis:" + row["tenant_id"]).encode()).digest() elif prev is None: prev = bytes.fromhex(row["prev_hash"]) # an export that starts after record 1 if bytes.fromhex(row["prev_hash"]) != prev: sys.exit(f"Broken link at record {row['seq']}") prev = hash_record(prev, row) if prev.hex() != row["hash"]: sys.exit(f"Hash mismatch at record {row['seq']}")
print(f"{len(rows)} records verified. Head: {prev.hex() if prev else 'none'}")Run it as python3 verify.py evidence-log.csv.
The log is also on the public API (GET /evidence), in chain order with each record’s hash and previous hash, for tools that archive it.
Evidence packs
Section titled “Evidence packs”On Evidence, Packs, an owner or admin picks:
- a framework: ISO 27001, SOC 2 or Essential Eight,
- a period: last month, last quarter, or custom days (up to 400 days, the last day may be today), in your time zone,
- a client name, optional: the cover then shows the client, with “Prepared by” your organisation.
Each pack is two files:
- JSON, the record: every number in the pack, the chain head at the end of the period and its hash.
- PDF, made from the JSON, A4. The header is your organisation’s name, or the client’s. The only MopokeHQ branding is one footer line.
A pack never says a control is met. For each control it says what MopokeHQ evidences, what the records show for the period, and what MopokeHQ does not evidence. Coverage is one of evidenced for reporting repositories, partly evidenced or not evidenced by MopokeHQ, never a pass or fail. The judgement stays with you and your auditor.
MopokeHQ itself holds no ISO 27001 or SOC 2 certification. The packs help you show your own controls; they do not certify anything.
Verify a pack
Section titled “Verify a pack”- When a pack is generated, the SHA-256 of its JSON file, exactly as downloaded, is written to the log in an
evidence_pack.generatedrecord. The packs list shows the hash and the record number. - Hash the JSON you hold (
shasum -a 256 pack.jsonorsha256sum pack.json) and compare. - The pack’s
chain.period_end_seqrecord in the log must have the hashchain.period_end_hash. If the chain verifies, nothing in the period was changed after the pack was made.
What each framework covers
Section titled “What each framework covers”ISO 27001 (ISO/IEC 27001:2022 Annex A)
Section titled “ISO 27001 (ISO/IEC 27001:2022 Annex A)”| Control | Coverage | Evidence |
|---|---|---|
| 8.8, obtaining information about technical vulnerabilities | Partly | Inventory from uploaded SBOMs, advisory feeds and their freshness, daily scan records |
| 8.8, evaluating exposure | Partly | Priorities, confidence, CISA KEV handling, accepted risks and not-affected decisions |
| 8.8, taking appropriate measures | Partly | Fix times against deadlines, upgrade merge requests, deploy reports, unsupported runtimes, alerts |
| 5.7 Threat intelligence | Partly | CISA KEV and FIRST EPSS collected and applied to priority |
| 5.18 Access rights | Partly | With teams on: members see only their teams’ projects, and the quarterly access reviews completed in the period. Access to systems other than MopokeHQ is not evidenced. |
Not included: 8.9 configuration management, 5.9 asset inventory, 8.32 change management, 5.24 to 5.28 incident management.
Deadlines are MopokeHQ’s defaults (P1 48 hours, P2 14 days, P3 30 days), since the standard leaves timeframes to you. Requirement text is paraphrased (ISO text is copyright).
SOC 2 (Trust Services Criteria)
Section titled “SOC 2 (Trust Services Criteria)”| Criterion | Coverage |
|---|---|
| CC7.1, conducts vulnerability scans | Partly |
| CC7.1, monitors infrastructure and software | Partly (software only) |
| CC7.1, detects unknown or unauthorised components | Partly (known-malicious packages, not policy) |
| CC7.1, uses defined configuration standards | Not evidenced |
| CC7.1, implements change-detection mechanisms | Not evidenced |
| CC6.2 and CC6.3, authorising, reviewing and removing access | Partly, from teams and the quarterly access reviews |
CC7.2 to CC7.5 and CC8.1 are listed as not included, with reasons. Deadlines as for ISO 27001. Criteria text is paraphrased (AICPA text is copyright).
Essential Eight (patch applications, patch operating systems)
Section titled “Essential Eight (patch applications, patch operating systems)”Follows the Essential Eight Maturity Model (November 2023). Requirement text is quoted under CC BY 4.0.
| Requirement | Coverage | Evidence |
|---|---|---|
| Asset discovery (ML1 to 3) | Partly | Repositories registered from CI uploads, last SBOM for each |
| Scanner with an up-to-date vulnerability database (ML1 to 3) | Evidenced | Feed sync state, daily scan records |
| Daily scanning of online services (ML1 to 3) | Partly | Daily scan records, repositories under continuous matching, SBOM freshness |
| Patch online services within 48 hours when critical or exploited (ML1 to 3) | Partly | Fix times against the 48 hour deadline, known exploited vulnerabilities |
| Patch online services within two weeks otherwise (ML1 to 3) | Partly | Fix times against the two week deadline |
| Remove unsupported online services (ML1 to 3) | Partly | PHP and Node.js runtimes past end of life |
| Fortnightly scanning of other applications (ML2, 3) | Partly | Daily scan records, coverage |
| Patch other applications within one month (ML2, 3) | Partly | Fix times against 30 days |
| Remove unsupported applications (ML3) | Partly | Runtimes past end of life |
| Patch operating systems | Not evidenced | Listed so the gap shows |
Deadlines: online services, critical or exploited, 48 hours; online services, other, two weeks; other applications, 30 days. “Critical” is the advisory’s severity; a working exploit is taken to exist when the CVE is in CISA KEV or the package is malicious. Office suites, browsers, workstations, drivers, firmware and the other six strategies are listed as not included.
What the numbers mean
Section titled “What the numbers mean”Every metric is computed for one organisation over the period, from one consistent snapshot.
| Metric | Shows |
|---|---|
| Inventory | Repositories at the end of the period, new ones, SBOM uploads |
| Scan coverage | Repositories with a stored SBOM, re-matched as advisories land; SBOMs older than 30 days |
| Daily scans | Days in the period with a daily scan record, and the days without one |
| Feed freshness | Each advisory feed’s last sync |
| Responses | Opened, resolved, open at the end, past due, malicious packages |
| Patch timeliness | For each deadline: resolved in the period, inside the window, median time to fix, open past the window |
| CISA KEV handling | Each known-exploited advisory: listed, matched, fixed or open, hours |
| End of life | PHP and Node.js runtimes past end of life on default branches, in deployed environments and in runtime check-ins |
| Risk decisions | Risks accepted and not-affected decisions, with reasons; accepted risks still in force and past their review date |
| Fixes | Upgrade merge requests asked for and their outcome |
| Deployments | Deployments reported, and advisories resolved because the fix was seen deployed |
| Alerts | P1 alerts, escalations, digests, time to acknowledge |
| Log activity | Records added, by kind |
| Chain | Chain verified from record 1; the head at the end of the period and its hash |
| Access reviews | Whether teams limit access, and the quarterly access reviews completed: who, how many people, how many removed, quarters without one |
Notes:
- Fix times start when MopokeHQ matched the advisory to your code, or later when the CVE was added to CISA KEV or the advisory moved up to its current priority. A vendor’s release date can be earlier; every pack says so.
- An advisory resolves when its fixed version is seen deployed, or when a person confirms a fix that is only on a branch. A dismissed advisory (not affected, or risk accepted) is a decision, not open work and not a fix.
- A daily scan record is written once per day (UTC) only when every advisory feed synced on time (within 24 hours; 48 hours for EPSS). A missing day means matching ran on stale data that day. Records start from the day the feature shipped, and a pack for an earlier period says so.
- A release line missing from the end-of-life table is reported as unknown, never as supported.