Browse Ship docs
Ship docs
Ship docsThe audit log

The audit log

What you came for

There is a question every product owner eventually gets asked, sometimes by a customer, sometimes by an auditor, sometimes by their own engineering lead at 8 a.m. "Who changed that, and when?" The audit log is the answer. It is the workspace-wide ledger of every privileged action — every member added, every token minted, every integration connected, every routine reassigned, every repo onboarded — with the actor, the time, the target, and the payload of what changed.

The audit log is queryable through the audit_search MCP tool (your agent: "show me every membership change last week") and the /v1/audit API. It is owner- and admin-scoped. Ship's Console does not render a dedicated Audit page today — the log lives behind the API and the operator's agent.

What gets recorded

The log captures the things that matter for compliance and the things that matter for debugging "why is the system doing that this morning?" Both audiences read the same page.

The log shows a steady stream of actions. Some are routine — a routine claiming a schedule window, a pipeline run completing. Others are human decisions you would want a record of — someone promoting a member, someone connecting a tracker, someone rotating an API token. Action names are lowercase and dot-namespaced (e.g. member.invite, member.role_change, integration.connect, auth.token_mint, pipeline_run.claim, repo.activate, process.update). You can filter by a prefix family (member, integration, auth, repo, pipeline, pipeline_run, invite, improvement, clarification, agent, agent_dispatch, process, workspace, artifact_repo) or a fully-qualified value. The names are not glamorous on purpose — they are easy to search for in a year, when nobody remembers what the system looked like.

How the filters work

The audit_search MCP tool (and the /v1/audit API) takes a small set of filters that let you carve fifty rows out of fifty thousand. They are query parameters, not on-screen chips — you pass them as tool arguments or API query params.

  • action. A prefix family or a fully-qualified value. Pass member to get every membership action, or member.invite for just invites. The families match the way your security team will ask questions — "show me everything that happened to members last week" — so the answer is one argument away.
  • actor. A substring search over the email of the person, or the name of the token, that did the action. Pass alex@ and you get every move by Alex. Pass ci-bot and you get every move by the named automation.
  • target_kind. What was acted on (API param, not a UI dropdown): workspace, user, api_token, integration, artifact_repo, pipeline, pipeline_run, workspace_repo, workspace_invite, github_installation, improvement, clarification, process. "Show me everything that ever touched this integration" — set the target kind, read the result.
  • since / until. Plain ISO dates. "Between the 1st and the 10th."

The filters AND together. You can ask "every action by Alex against integrations in the last seven days" and the log will give it to you, paginated, in order, with a stable cursor for the next page.

How to read a row

Each row tells one self-contained story. The actor, the action, the target, the time. Every row carries a structured payload with the before / after of what changed — the old role next to the new role, the old token name next to the new one, the integration ID and its scopes.

The payload matters. It is the difference between "a token was minted" (which a log would tell you) and "a token was minted by alex@example.com on 2026-05-12 with scope read-workspace, named ci-prod-deploy" (which is what you actually need when something later goes wrong). Audit answers questions that future tense will ask.

Paging through results does not widen your filter. Every active filter — action, actor, target_kind, dates — is carried into the next page via a stable cursor. You can walk backwards through six months of one filtered view without ever losing the question you started with. This sounds like a small thing. It is the difference between using audit as a real investigative tool and treating it as an unsearchable wall of text.

Pulling rows for a regulator

For longer questions and for handing data to a regulator, pull the filtered rows via the /v1/audit API. The same filters apply. You get one record per action with the payload as a structured field your auditor can parse — the same data the audit_search MCP tool returns to your agent.

When to read the log

Three habits cover most use of audit:

  • Weekly sanity check. Once a week, query with since set to seven days ago and read through. Familiarise yourself with normal. Next time something feels off, you will notice.
  • After an incident. The first place to look is not the dashboards — it is the audit log. Set since to before the incident, read what changed. Most "mysterious" incidents stop being mysterious when you see the configuration change someone made forty minutes earlier.
  • When a question lands. "Did we rotate that token?" "Who added this integration?" Ask your agent — it runs audit_search and answers in seconds, always faster than asking around.

Audit logs are dull in the same way fire alarms are dull. The promise is that on the day a question lands you cannot afford to be wrong about, the answer is already there.

The audit log — Ship docs — Harbor Gang