Skip to content
subcloakdocs

Team Cloud

Audit log

Since 0.7.0 Requires Cloak Proxy 0.7.0 or later.

What Team Cloud's audit log records, how it's chained and checked by admins' Macs, how to filter and export it, and how to verify an export offline with cloakctl.

Team Cloud keeps a log of who did what in the team for 365 days. It holds metadata only: who acted, what kind of action, when, from which country, and on which item by id. It never holds names, comment text, secrets or any other content, which stay end-to-end encrypted.

Area Events
Team team.created, team.renamed, console.team_delete (the owner asked to delete the team, with when it happens), console.team_delete_cancelled
Members member.invited (also when an invitation is withdrawn), member.joined, member.removed, member.role_changed, member.removal_needs_kit (a removal that only the Team Recovery Kit can finish)
Macs device.requested, device.approved (with how: code comparison, automatic after SSO, or the kit), device.confirmed, device.denied, device.removed
Keys roster.epoch, roster.options, kit.stored (an admin confirmed the current Team Recovery Kit is stored), kit.used, kit.rotated
Spaces space.created, space.deleted, space.acl_changed, space.write (grouped per Mac per space per 10 minutes, with a count), vault.read (per Mac per folder per hour)
Reviews proposal.created, proposal.approved
CI ci.created, ci.revoked, ci.run
Pairing pair.started, pair.joined, pair.ended
Monitors* monitor.created, monitor.updated, monitor.paused, monitor.resumed, monitor.deleted, monitor.failing, monitor.recovered
Hosted mocks* mock.published, mock.republished, mock.unpublished, mock.taken_down, mock.restored
Alert routes integration.created, integration.updated, integration.deleted, integration.tested
Settings policy.changed, sso.changed, sso.tested, scim.token_rotated
SCIM scim.user_created, scim.user_updated, scim.user_deactivated, scim.user_deleted
Console console.signin, console.stepup, console.user_added, console.user_removed (a billing manager added or removed)
The log itself audit.budget_exceeded: a limit below was reached; it names whose entries were left out (a member’s, or the whole team’s for the shared limit) and which kind

So one member can’t flood the log, some entries have daily limits (counted in UTC days). Past a limit, one audit.budget_exceeded entry says so and the rest of that day’s entries of that kind are left out:

  • Each Mac: 200 space.acl_changed, 200 proposal.created, 200 proposal.approved and 200 pair.started a day. space.write entries past 300 ten-minute groups a day are merged into one entry for the rest of that day.
  • Each member: 1,000 a day of space.write, proposal.created, space.created and pair.joined together. Only that member’s entries stop, and the marker names them. vault.read is hourly per Mac and folder for a member’s first 2,000 entries a day; past that it’s recorded once per member, folder and day, so every folder read still shows.
  • The whole team: those kinds plus pair.ended, 5,000 a day or 150 for each member, whichever is more.

vault.read is never cut by another member’s activity or the team limit. space.acl_changed, proposal.approved, and everything about members, Macs, keys, CI, settings, SCIM, the console and billing are never left out by the member or team limits.

Billing changes (seats, the billing interval, payments and cancellation) aren’t in the audit log; Polar’s receipts and customer portal show them.

Each entry names its actor: a member’s Mac, a console user (by email), a CI token (by label), the Team Recovery Kit, SCIM or Subcloak’s server. The country comes from the request’s network.

vault.read means a Mac downloaded a vault folder’s items, not that anyone used a value. Use can’t be seen, because the vault is end-to-end encrypted.

  • Each entry includes a hash of the one before, so the log is a chain.
  • Admins’ Macs check the chain as they sync and sign a checkpoint at least once a day.
  • If entries before a checkpoint were changed or removed, admins’ Macs and cloakctl team audit verify on an export detect it. The console’s line at the top of the page, for example “Chain verified to entry 18,204, signed by Priya’s Mac 3 hours ago”, is Subcloak’s server checking its own log: the independent check is an admin’s Mac or an export.
  • After 365 days entries are deleted, and the log keeps an anchor so what remains still verifies.

Subcloak’s server writes the entries. The checkpoints mean it can’t quietly rewrite the history afterwards; they don’t stop it from leaving out an event when it happens.

In the console (owners and admins): Audit log, with filters for actor, kind and date. Columns: Time, Actor (an email, or a Mac as its platform and a short id), Action, Target (an id) and Country.

In the app (admins): Settings › Team › Activity. Your Mac replaces ids with names where it can decrypt them.

Export… on the console’s Audit log page asks you to confirm with a passkey or a fresh email code, then downloads:

  • JSONL, with the hashes, the checkpoints and the team’s signed member list, for offline verification;
  • CSV, for spreadsheets. Values that a spreadsheet would treat as formulas are quoted.

Verify a JSONL export offline with cloakctl:

Terminal
cloakctl team audit verify export.jsonl --security-code "1234 5678 9012 3456 7890" --at-change 42

Take the Team Security code and the change number beside it from Settings › Team › Security on an admin’s Mac. The export carries its own copy of the team’s member list, so the code is what ties it to your real team: cloakctl checks that the export’s member list gives the same code at that change, then checks the chain and the checkpoints admins’ Macs signed against that list.

Without a code, cloakctl stops and shows how to give one. With --unpinned it checks only that the export agrees with itself, and says so: that doesn’t show the export is your team’s. The check an admin’s Mac runs in the app, against the member list it has verified all along, is the authoritative one.