Skip to content

Audit log

The audit log is skillsvault's compliance record: every time the gate evaluates a skill, it writes one event capturing who ran what, where, the decision, and why. It is append-only and immutable, so it stands up as a trustworthy record long after the fact.

What each event records

FieldExampleMeaning
Skillacme/teamleader-crm-entryThe namespaced skill the invoked name resolved to (or the name as invoked, if it isn't in your registry)
Version2.0.0The current version resolved at decision time
Decisionblockedallowed · warned · blocked
PolicyTeamleader retiredThe matched policy, if any
ReasonUse acme/hubspot-crm-entry.The explanation shown to the developer
Harnessclaude_codeThe agent the skill ran in
Machinealex-mbpThe developer's machine
Time2026-06-25T10:03:12ZWhen the control plane received the event (server clock)

The three decisions mirror the gate's outcomes: allowed (ran normally), warned (ran, with a caution surfaced — including a banned slash command, since commands are capped at warn), and blocked (stopped before it ran). skillsvault try records its demonstration decision here too, and deliveries over MCP are recorded with harness mcp.

Immutable by design

Audit events are append-only — once written, an event is never edited, and there is no API or console action that mutates one (the only way rows go away is deleting the whole organisation). Each event is also hash-chained: it stores a SHA-256 hash over its own fields and the previous event's hash, so an edited or missing row breaks the chain, and the console shows whether the chain verifies. A ban that fired six months ago reads exactly as it did the day it happened. This is what makes the log usable as evidence for compliance and incident review.

The log never changes a decision

The gate makes its decision from the local bundle and answers the harness first; only then does it ship the event, for at most 1.5 seconds. A failed upload never changes or reverses the decision. An event that doesn't get through is queued on the machine and sent by its next gate, sync or pull, once (each event carries an id the server deduplicates on). A queued event is timestamped when it arrives, not when the gate decided. (CLI 0.6.0 and later; older versions drop an event they couldn't send.)

Reading and exporting

Open Audit in the console to browse the most recent 200 events. You can narrow them by:

  • Search — matches the skill, machine, or policy name
  • Decision (allowed / warned / blocked)

The header shows whether the hash chain verifies. On plans that include audit export (Business and Enterprise), Export JSON and CSV download the log for an external SIEM or compliance archive. Each exported event also names the person whose key reported it; that field is looked up at export time and isn't covered by the hash chain. How far back the log, the dashboard and the export show events is set by your plan's retention window — 7 days on Free, 30 on Team, unlimited on Business and Enterprise. Older events are kept, not deleted, and become visible when you upgrade; the hash chain is always verified over the full log, and the JSON export states which window it covers.

Weekly digest

Every Monday, owners, admins and governance officers get one email per organisation summarising the past week: how many skill runs the gate allowed, warned on and blocked, the most-blocked skills, submissions waiting for review, members with no machine reporting, imported skills changed at their source, and how many policies changed. An organisation with no activity that week gets nothing. The email has an unsubscribe link, and turning off product emails turns it off.

Admin activity

The audit log has a second record: who changed the rules. Audit log → Admin activity lists, newest first, every change to what decides a gate call and who can make it: policies created, activated, edited, archived or deleted; skills added, banned, deprecated, reactivated, deleted, and each version published; members invited, joining, changing role or removed; namespaces, agent keys and alert destinations added or removed; skill submissions and their decisions; and plan changes.

Each entry names the person who made the change. A publish with a key minted for a person is attributed to that person; one with a headless key reads An agent key, and plan changes from Stripe read Billing. Entries are written by the database itself whenever those tables change, whichever way the change was made (console, CLI, API), and can't be edited afterwards. Every member can read them. The evidence report includes the changes in its period.

Evidence report

For an auditor, a security review or a customer questionnaire, Audit → Evidence report puts together, for the last 7, 30, 90 or 365 days:

  1. Summary — whether the hash chain verifies over the full log, and how many decisions were allowed, warned and blocked, on how many machines and by how many people.
  2. Controls in force — every active policy, its rule, action and reason.
  3. Enforcement — which policies blocked or warned and how often, and which skills were stopped.
  4. Coverage — machines reporting, and members with no machine covered.
  5. Skill registry — each skill's current version, status, check result and content hash.
  6. Access — every member, their role, and when they joined and were last active.
  7. Changes — the admin activity in the period: who changed which policy, skill, role, key or alert.

Print or save as PDF prints the report alone, on a white page, without the console around it. The period never reaches past your plan's retention window. Counts are computed in the database over every event in the period, not a sample. For independent verification, attach the JSON export: the report summarises, the export lets someone recompute the chain. The evidence report is included with audit export (Business and Enterprise).

Alerts

Settings → Alerts (owners and admins) posts to your team as things happen, so nobody has to watch the audit log. Add up to five destinations, each with the events it should receive:

  • A skill was blocked and A skill ran with a warning: one message per upload from a machine, naming each skill, the machine and the policy that decided. An event the CLI re-sends is recorded once and alerted once.
  • A member submitted a skill for review, with a link to the submission.
  • An imported skill changed at its source: found by the daily origin check; machines keep the reviewed version until someone updates it.

Paste a Slack or Microsoft Teams incoming-webhook URL, or a Discord webhook URL, and alerts arrive as chat messages; skill and machine names are neutralised so they can't mention a channel. Any other HTTPS URL receives signed JSON:

{
  "type": "decision.blocked",
  "org": { "name": "Acme", "slug": "acme" },
  "url": "https://skillsvault.io/acme/audit?decision=blocked",
  "data": { "events": [{ "skill": "acme/rm-rf", "machine": "laptop-7", "policy": "no-shell", "reason": "Runs arbitrary commands" }] },
  "sent_at": "2026-10-27T21:00:00.000Z"
}

Each request carries x-skillsvault-signature: t=<unix seconds>,v1=<hex>, where v1 is the HMAC-SHA256 of <t>.<raw body> keyed with the destination's signing secret (shown in Settings). Recompute it, compare in constant time, and reject old t values to refuse replays.

Send test posts a sample block and shows what the endpoint answered; each destination shows its last delivery. Destinations must be public HTTPS hosts on the standard port: addresses on private networks are refused, redirects aren't followed, and an endpoint gets 5 seconds to answer. A burst is capped at 30 messages a minute per destination; every decision is still in the log.

Programmatic access

Integrators can submit and consume audit events through the REST API — the same ingest endpoint the CLI uses to ship decisions. See POST /api/audit/ingest.