Skip to content

Audit event reference

Every action name cloop writes to the event_type column of audit_events, generated from the registry the emitters reference. Do not edit this file: it is rendered by tests/docs/audit_events_test.go from pkg/auditaction, and a CI check fails when it stops matching. Regenerate with make docs-audit.

For what the trail is and how it is protected see the security model; for the endpoints that read it see the HTTP API reference.

How to read this

audit_events is cloop's legal record: one append-only, hash-chained row per mutation that actually changed something. It is distinct from the events table, which is a UI journal that may be lossy and is prunable at will. A row carries an actor, an action, an entity type and id, and a JSON payload.

The action is the value of event_type. It is the field a detection rule keys on, and it is stable: a name in this list will not be renamed, because renaming it would orphan rows already sealed into a chain that cannot be rewritten. A superseded action is marked deprecated here and keeps being emitted until retention has passed over the last row carrying it.

The entity is the value of entity_type, and it says which id space entity_id is in. Filtering a subject's whole history means filtering on both.

Payload keys are conditional. An emitter omits what it does not know, so the keys listed for an action are what a consumer may expect to see, not what every row carries. New keys may be added to a stable action without notice — a consumer must tolerate that — but no listed key is removed or repurposed.

Stability is a contract with consumers, not a measure of code quality:

Marker Means
stable Safe to key a paging alert on. The name and the listed payload keys will not change.
beta The rows are real, but the name or payload may still change while the surface emitting them settles. Key a dashboard on it, not a pager.
deprecated Still emitted, replaced by something else. The note says by what.

Exporting

cloop audit export writes these rows as JSONL or CEF for a SIEM, and cloop audit verify checks the hash chain. Both are documented under commands. Note that the trail exists per project and in the control plane, with separate chains — a row emitted to one is not visible in the other.

Who may read these

Reading all 256 of the actions below requires the audit.read permission, held by admin.

The trail is one table behind one pair of admin-only endpoints, so the permission does not vary by action today. It is recorded per action anyway, because the day one family needs narrower reads than the rest, the registry has to be able to say so without a schema change — and because a reader is better served by a column that says "the same everywhere" than by prose that leaves them guessing whether it varies.

Which database an action is recorded in

audit_events is not one table. It exists in the hub's own control-plane state.db and in every project's .cloop/state.db, and each copy carries its own independent hash chain. A row does not say which database it came from, so verifying one chain proves nothing about the other: both stay internally consistent whether or not an event landed in the right one.

This matters when answering a question about one project. The plan's own life is in the project's chain; the executor that ran it, the image policy that admitted it, the workspace that was fetched for it and the credentials it held are in the hub's. Use cloop hub audit list to read both at once — it labels every row with its source — and cloop hub audit verify to verify both chains rather than whichever one happened to be opened.

Home Meaning Actions
control-plane the hub's own state.db 223
project the project's .cloop/state.db 29
either whichever chain the decision was scoped to 4

Recorded in the project's .cloop/state.db: config.set, feature.create, feature.pr_open, feature.remove, requirement.approve, requirement.check, requirement.create, requirement.delete, requirement.draft, requirement.import, requirement.prove, requirement.update, requirement.verdict, run.adopt_requested, run.cap_paused, run.cap_resumed, run.firewall_denied, run.reexecuted, state.save, step.append, task.attachment_delete, task.delete, task.dispatch, task.finish, task.gate_approve, task.quarantine, task.quarantine_release, task.status, task.upsert.

Recorded in whichever chain the decision was scoped to: audit.gap, audit.restored, authz.denied, authz.granted.

Everything else is recorded in the hub's own state.db.

Actions by family

256 actions in 53 families. Every action is listed: this section is the whole vocabulary of the event_type column.

task.* (9) · run.* (5) · feature.* (3) · requirement.* (9) · knowledge.source.* (7) · knowledge.* (2) · step.* (1) · state.* (1) · config.* (1) · executor.* (19) · forwarder.* (12) · executor_pool.* (12) · workspace.* (2) · sandbox.* (1) · sandbox.attach.* (3) · secret.* (15) · secret.lease.* (1) · lease.* (4) · github_app.* (1) · egress.* (8) · gitproxy.* (9) · kubeguard.* (6) · ci.* (1) · ci.session.* (5) · ci.exchange.* (2) · ci.relay.* (2) · ci.rule.* (3) · ci.config.* (1) · model_relay.session.* (4) · model_relay.* (5) · authz.* (2) · api_token.* (6) · static_token.* (1) · session.* (10) · device.* (3) · request.* (2) · role_binding.* (3) · quota.* (4) · resource_ceiling.* (2) · sealing_key.* (5) · hub_ca.* (8) · oidc.* (2) · telemetry.* (1) · notifications.* (1) · disk.* (1) · stt.credential.* (2) · user.* (17) · scim.* (16) · project.* (3) · project.member.* (4) · team.* (5) · audit.* (2) · audit.forward.* (2)

task.*

Action Entity Home Stability Fires when
task.attachment_delete task project stable A person deletes a file attached to a task, from the dashboard or with cloop task attachments remove.
task.delete task project stable A task disappears from the plan between two saves, or is deleted outright.
task.dispatch task project stable A task is handed to an executor, recording where it will run and what it was given.
task.finish task project stable A dispatched task reaches a terminal outcome — done, failed, skipped, timed out, or aborted.
task.gate_approve task project stable A person approves a human review gate in the task list, letting the run continue past it, or pre-approves a task that requires approval.
task.quarantine task project stable Failover marks a task as a suspected node killer: two or more distinct executors went unreachable while it was running.
task.quarantine_release task project stable An explicit reset returns a suspected node killer to pending, clearing its mark and the losses behind it.
task.status task project stable Somebody flips a task's status by hand, rather than the orchestrator moving it.
task.upsert task project stable A task is created, or a saved task's audited fields differ from the ones already stored.

Payload keys:

  • task.attachment_delete — id, attachment_id, name, size, sha256
  • task.delete — id
  • task.dispatch — task_id, title, project, run_id, executor_id, executor_kind, isolation, requested_image, pinned_image, image_digest, image_provenance, verified_digest, spec_sha256, setup_sha256, lease_ids, recorded_by
  • task.finish — task_id, outcome, title, project, run_id, executor_id, executor_kind, isolation, reason, started_at, completed_at, duration_ms, abort_class, abort_cleared, fail_count, write_back_commit
  • task.gate_approve — task_id, title, kind, waiting, via
  • task.quarantine — task_id, title, kind, nodes, reason
  • task.quarantine_release — task_id, kind, nodes, marked_at, via
  • task.status — id, old_status, new_status
  • task.upsert — id, title, status, priority, role, description

  • task.attachment_delete — Attaching is not audited: a run's files are recorded in the project's event journal, which names the run. A file deleted with its task is covered by that task's task.delete.

  • task.dispatch — This and task.finish are what make "where did this task run, and what credentials did it hold" answerable from audit_events alone, without joining the executor tables. image_digest is the digest pinned_image names. A run carried out as a seed — on a Kubernetes Pod or a device — writes its own rows into a copy of the project that never comes back, so the hub writes them when it merges the run's outcome, recorded_by "hub" (Task 20457).
  • task.finish — outcome carries the terminal status; reason carries why, when there is one.
  • task.gate_approve — kind is human_gate for a review gate and empty for a task. waiting is true when a run had stopped at the gate. via names where it was approved: the dashboard or cloop task gate approve.
  • task.quarantine — nodes lists each executor that went down under the task with when it went unreachable. A marked task runs again only after an explicit reset, recorded as task.quarantine_release.
  • task.quarantine_release — via names the reset — the dashboard, cloop task reset, cloop task bulk reset; the actor is who pressed it.
  • task.upsert — Emitted from the diff SaveState computes inside its own transaction, not from the plan — a byte-identical re-save produces no row. Before that diff existed this action was 99.7% of a 1.09M-row table.

run.*

Action Entity Home Stability Fires when
run.adopt_requested plan project stable Somebody asks a project's running host-process run to adopt the hub's build at its next task boundary.
run.cap_paused plan project stable A Claude Code subscription cap stops the run before it dispatches its next task — a configured cap, or a window used up while budget.block_extra_usage holds the run out of extra usage (cap 100).
run.cap_resumed plan project stable The hub restarts a cap-paused run after its window rolled over, without a human — or, for the extra-usage pause an older build recorded as a spent budget, once the usage API shows the window open (no resumes_at).
run.firewall_denied plan project stable A run ends whose sandbox firewall refused connections: one row per deny path that refused any, with the totals.
run.reexecuted plan project stable A run replaces its own image with a newer build at a task boundary, keeping its process.

Payload keys:

  • run.adopt_requested — project, request_id, pid, run_build, hub_build, behind
  • run.cap_paused — project, windows, utilization, cap, resumes_at, detail
  • run.cap_resumed — project, paused_detail, resumes_at, resumed_at, acted_for, acted_for_basis
  • run.firewall_denied — project, run_id, executor_id, rule, why, count, destinations, top, first_seen, last_seen, fingerprint
  • run.reexecuted — project, run_id, pid, from, to, reason, reexecs

  • run.adopt_requested — Gated like starting a run. The request names the run's process, so it is acted on by that run or by none; what the run then did is its run.reexecuted row, or a refusal in the project's journal.

  • run.cap_paused — Distinct from state.save's bare "paused" status because a cap is the one pause that ends by itself: resumes_at is the window's reset from the OAuth usage API, and it is what run.cap_resumed is later matched against.
  • run.cap_resumed — The only action recording work that a machine started on its own initiative, so "who restarted this project" stays answerable from the trail alone. On a hub with single sign-on acted_for names whom the run acts for, on their own Claude account, and acted_for_basis how that was decided: starter, the person the resumed run was started for as recorded at its dispatch (no acted_for when it was started acting for no one), owner, the project's owner, for a run older than that record, or unknown, nobody nameable, on an executor that needs no Claude home. A resume that could not act for them is not restarted, and leaves no row.
  • run.firewall_denied — Aggregated per run and rule (Task 20437), never per packet: count is the refusals the sandbox's packet log reported — rate-limited to ten a second per deny path, so a flood is at least this many — destinations how many distinct destinations, top up to eight of them as dst:port/proto. rule is the deny path's tag (default, deny:<prefix>, meta:<prefix>, block:<prefix>, xlat:<prefix>), or unitemised for what the run's capped aggregate counted without itemising; why is the hub's sentence for it. Each destination is also a network_blocked row in the run's history. Absent for a run on Kubernetes, whose NetworkPolicy has no portable log, and for one on a device whose agent predates protocol v24.
  • run.reexecuted — Written by the new image. Together with task.dispatch it answers "which build ran this task": a run's tasks before this row ran on from, after it on to.

feature.*

Action Entity Home Stability Fires when
feature.create feature project stable A feature — a git worktree of the project with its own task list — is created.
feature.pr_open feature project stable A feature's branch is pushed and a pull request for it is opened or updated.
feature.remove feature project stable A feature's worktree, and with it its task list and history, is removed.

Payload keys:

  • feature.create — slug, branch, base, path, auto_evolve, innovate, tasks
  • feature.pr_open — slug, number, url, base, existing, automatic
  • feature.remove — slug, branch, branch_deleted, force

  • feature.create — Filed in the parent project's trail, not the feature's: the feature's own trail starts with its first run, and "which features did this project have" is a question about the parent.

  • feature.pr_open — automatic is true when the hub opened it on its own because the feature completed with auto-PR set, so a pull request nobody clicked for is still attributable.

requirement.*

Action Entity Home Stability Fires when
requirement.approve requirement project stable A requirement becomes approved: a draft approved in the dashboard or with cloop requirements approve, a requirement edited to approved, or a spec file import that approves it.
requirement.check requirement project stable A check run ends — its tests ran on the project's executor, or it failed or was cancelled.
requirement.create requirement project stable A requirement is added to the project, from the dashboard or with cloop requirements add.
requirement.delete requirement project stable A requirement, and the history of its checks and proofs, is deleted.
requirement.draft requirement project stable The model drafts requirements from the project's goal or a specification, at a person's request — the dashboard's Draft requirements, or cloop requirements draft.
requirement.import requirement project stable The project's requirements spec file (.cloop/requirements.yaml) is merged into the hub — from the dashboard's Import, or with cloop requirements import.
requirement.prove requirement project stable A run of red-green proofs ends — the requirements' tests ran at the base and on the change, on the project's executor, or the run failed or was cancelled.
requirement.update requirement project stable A requirement's title, description, priority, tags, tests or status change.
requirement.verdict requirement project stable A person records a requirement's verdict by hand — fulfilled or unfulfilled, with a note.

Payload keys:

  • requirement.approve — id, title, previous_status, via, priority, tests, fingerprint, approved_by
  • requirement.check — run_id, state, source, requirement_ids, assess, executor_id, commit, dirty, fulfilled, unfulfilled, unverified, error, reason, task_id
  • requirement.create — id, title, priority, status, tests, fingerprint, drafted
  • requirement.delete — id, title, checks, proofs, unlinked_tasks
  • requirement.draft — source, sha256, bytes, ids, refused, executor_id, via
  • requirement.import — source, path, sha256, commit, added, restored, updated, deleted, resolved, in_sync, exports_pending
  • requirement.prove — run_id, state, source, requirement_ids, executor_id, base, commit, outcomes, reason, task_id
  • requirement.update — id, title, changed, status, tests, fingerprint, previous_fingerprint
  • requirement.verdict — id, verdict, note, fingerprint

  • requirement.approve — via is approve, edit or import. tests is each test's command as it will now run: an approval is what lets a check, and the requirements gate, run them. An edit that also changes the definition writes a requirement.update row for that beside this one.

  • requirement.check — One row per run, not per requirement: each requirement's verdict is a row of requirement_checks. assess is true when the model was asked about the requirements with no tests. A check the requirements gate ran when a task's agent reported it done has source gate and the task in task_id; it is 0 for every other check.
  • requirement.create — tests is each test's command, as it will run. drafted, present only for a requirement the model drafted, names what from — goal, text, file or stdin — and its status is then always draft.
  • requirement.delete — unlinked_tasks are the tasks that implemented it, from which the deletion took it in the same transaction; each also gets a task.upsert row under the same actor.
  • requirement.draft — One row per draft, its entity id draft, its actor the person who asked. source is goal, text (pasted), file or stdin; sha256 and bytes describe that text, not its content. ids are the requirements stored, every one with the status draft — each also a requirement.create row naming the source in drafted. refused counts the proposals that broke a bound or asked for another status.
  • requirement.import — One row per import, its entity id spec. updated lists each changed requirement with the fields that changed and its new and previous fingerprints; deleted how many recorded checks went with each, and the tasks it was taken out of (unlinked_tasks, each also a task.upsert row); resolved how each conflict was settled (file or hub). An import applies nothing unless every conflict is settled, so a row is a whole import. sha256 is the file's; a dashboard import was read from the project's checkout on its executor at commit.
  • requirement.prove — One row per run: outcomes maps each requirement (R-<n>) to proven, vacuous, broken, inconclusive or error, and each proof is a row of requirement_proofs. base is the commit the tests ran at before the change, commit the change's. The requirements gate's proof of a task's new or changed tests has source gate and the task in task_id.
  • requirement.update — changed names the fields that differ. A new fingerprint means the verdicts recorded before it are about a requirement that no longer reads that way, and the dashboard shows them stale.

knowledge.source.*

Action Entity Home Stability Fires when
knowledge.source.bind knowledge_source control-plane stable A knowledge source is made searchable from a project, or an existing binding's narrowing changes.
knowledge.source.create knowledge_source control-plane stable An administrator connects an organisation knowledge source, or a person connects a personal one.
knowledge.source.delete knowledge_source control-plane stable A knowledge source is removed, with every document, chunk and binding it had.
knowledge.source.sync knowledge_source control-plane stable A person asks the hub to sync a knowledge source now — cloop knowledge sync, or POST /api/knowledge/sources/{id}/sync — and the sync starts.
knowledge.source.test knowledge_source control-plane stable A person tries a knowledge source's connection — the dashboard's Test connection, POST /api/knowledge/test-connection — for a new source or an existing one: the hub lists one small page with the source's configuration and credential, and indexes nothing.
knowledge.source.unbind knowledge_source control-plane stable A project stops being able to search a knowledge source: the binding is removed, or the project is.
knowledge.source.update knowledge_source control-plane stable A knowledge source's name, configuration, credential reference, sync interval or enabled state changes.

Payload keys:

  • knowledge.source.bind — source_id, binding_id, project, spaces, query, created, kind, name, scope, owner
  • knowledge.source.create — id, kind, name, scope, owner, config, signs_in_with, enabled, sync_interval_minutes
  • knowledge.source.delete — id, kind, name, scope, owner, documents, chunks, bindings
  • knowledge.source.sync — id, kind, name, scope, owner, origin, member
  • knowledge.source.test — id, kind, name, scope, owner, config, signs_in_with, outcome, documents, withheld, reason, error
  • knowledge.source.unbind — source_id, binding_id, project, kind, name, scope, owner, reason
  • knowledge.source.update — id, kind, name, scope, owner, changed, config, signs_in_with, enabled, sync_interval_minutes, resync

  • knowledge.source.bind — The entity id is the source's. project is the project's path. spaces and query narrow what the project can find: only documents in those space keys, and only documents the source's connector reported as matching the query at the last sync. created is false when the row replaced an existing binding's narrowing.

  • knowledge.source.create — The entity id is the source's id, which is never reused. scope is org or personal; a personal source's owner is the person whose credential it reads with. config is the connector's configuration, which is refused if it holds anything shaped like a credential; signs_in_with is the secret store reference the hub signs in to the source with — a name, never the secret.
  • knowledge.source.delete — The counts are what goes with it. Its bindings end here too: no knowledge.source.unbind row is written for each. The source stops being searchable at this row; its documents and chunks are purged right after in bounded transactions, and what a purge cut short leaves is removed by the next one.
  • knowledge.source.sync — Written when the sync takes the source's sync lease, so a request refused because another sync of the source is running writes none. origin is manual; member is the hub process that runs it. The syncs the hub-cluster leader schedules are its own routine work and are not audited: how each went is on the source (status, counts, duration and the last error, scrubbed of credentials).
  • knowledge.source.test — The entity id is the source's for a test of an existing one, and empty for a source not yet saved. config and signs_in_with are what was tried, which a form may not have saved. outcome is ok or failed; a failure's reason is credential (the secret could not be opened) or connector (the remote refused or could not be reached), and error says why, scrubbed of the credential and of anything else shaped like one. A configuration or credential reference the hub would refuse to store is refused before anything is tried, and writes no row.
  • knowledge.source.unbind — A project removed from the hub takes its bindings with it — they are keyed by its path, and would otherwise serve whatever is registered there next — one row each, reason saying so.
  • knowledge.source.update — changed lists the fields the request changed. resync is true when a new configuration made the next sync start over from the beginning.

knowledge.*

Action Entity Home Stability Fires when
knowledge.run.get knowledge_session control-plane stable A run reads one document of its project's knowledge with its knowledge session — by its id, its URL or its issue key: the orchestrator reading a document the task names, or the agent's knowledge_get tool — POST /api/run/knowledge/get.
knowledge.run.search knowledge_session control-plane stable A run searches its project's knowledge with its knowledge session: the orchestrator assembling a task's reference material, or the agent's knowledge_search tool or cloop knowledge search in its sandbox — POST /api/run/knowledge/search.

Payload keys:

  • knowledge.run.get — session_id, run_id, project, executor_id, task, purpose, lookup, documents, outcome, reason
  • knowledge.run.search — session_id, run_id, project, executor_id, task, purpose, query, k, hits, documents, mode, outcome, reason

  • knowledge.run.get — lookup says how the document was named (id, url or key) with the value, scrubbed and cut at 512 bytes. documents are the ids returned: none when the project's knowledge holds nothing by that name, which is also the answer for another project's document.

  • knowledge.run.search — The entity id is the knowledge session's. query is what the run searched for, scrubbed of anything shaped like a credential and cut at 512 bytes; documents are the ids of the documents the answer held. purpose is inject for the orchestrator's own retrieval, whose documents the task cites as injected; otherwise they are recorded as the agent's reads. outcome is ok, or refused with reason for a request the session's rate limit or a malformed query refused. A token that is not a live session's is refused before anything is read, and writes no row.

step.*

Action Entity Home Stability Fires when
step.append step project stable One execution step finishes and its transcript is appended to the run.

Payload keys, on every action above: step, task, exit_code, duration, time, input_tokens, output_tokens

state.*

Action Entity Home Stability Fires when
state.save plan project stable The project's plan-level state is written: goal, run status, counters, and the mode flags in force.

Payload keys, on every action above: goal, status, current_step, evolve_step, plan_version, task_count, total_input_tokens, total_output_tokens, auto_evolve, innovate_mode, parallel, max_parallel, pause_code, pause_detail, pause_resumes_at

  • state.save — The pause_* keys appear only when status is paused, and are what make an approval gate distinguishable from a spent budget or an exhausted usage window — before them all ~26 pause conditions recorded the same word.

config.*

Action Entity Home Stability Fires when
config.set config project stable A configuration write lands, from the Settings panel, the CLI, or config validation repair.

Payload keys, on every action above: yaml

  • config.set — yaml is the post-write document with secret-shaped values redacted, not a diff.

executor.*

Action Entity Home Stability Fires when
executor.audience executor control-plane stable An admin admits a user or group to an executor, or withdraws one.
executor.autoupdate executor control-plane stable An administrator changes whether the control plane upgrades executors unattended, which release they converge on, or how many move at once.
executor.bind executor control-plane stable A project is pinned to one executor, at creation time or from the Executors panel.
executor.cordon executor control-plane stable An executor is marked to refuse new work while finishing what it holds.
executor.drain executor control-plane stable An executor is set to shed in-flight work as well as refuse new work.
executor.enroll executor control-plane stable A remote agent completes outbound enrolment and joins the fleet.
executor.failover executor_session control-plane stable A session is moved off an executor that stopped answering, or fails to be placed anywhere.
executor.failover_exhausted executor_session control-plane stable An executor goes unreachable holding a session that was already re-dispatched as often as executors.failover.max_attempts allows, so nothing re-dispatches it.
executor.firewall executor control-plane stable An admin sets or clears a device's firewall rule set, the superset every sandbox on it must fit inside.
executor.isolation_verdict executor control-plane stable The isolation probe, run under an executor's runtime at reconcile, at a device's hello, after a runtime change, daily or on request, reaches a verdict that differs from the last one recorded for that executor and runtime.
executor.labels executor control-plane stable An admin replaces an enrolled device's labels, the ones label grants are matched against.
executor.limits executor control-plane stable An admin sets the most CPU, memory, disk and processes any one workload on an executor may be given.
executor.revoke executor control-plane stable An enrolled agent's credential is revoked and it is removed from the fleet.
executor.sandbox executor control-plane stable An admin sets where an executor's payloads run: the device's host, or a container on it.
executor.state_change executor control-plane stable Liveness tracking moves an executor between health states without an operator asking.
executor.unbind executor control-plane stable A project's executor pin is cleared and it falls back to registry placement.
executor.uncordon executor control-plane stable A cordoned executor is returned to normal scheduling.
executor.upgrade executor control-plane stable The control plane asks an enrolled device to replace its own binary with a published release and restart.
executor.virtual executor control-plane stable An admin creates, edits or deletes a virtual executor on an enrolled device.

Payload keys:

  • executor.audience — action, executor_id, principal_kind, principal_value, restricted, members
  • executor.autoupdate — action, enabled, target_version, max_in_flight
  • executor.bind — action, executor_id, project, project_path, kind, via
  • executor.cordon — action, executor_id, reason, state
  • executor.drain — action, executor_id, reason, state
  • executor.enroll — action, executor_id, name, expires_at, workdir_root, labels
  • executor.failover — session_id, from, to, attempt, max_attempts, project_path, task_id, running_tasks, placed, acted_for, error
  • executor.failover_exhausted — session_id, from, attempt, max_attempts, project_path, running_tasks, nodes, error
  • executor.firewall — action, executor_id, from, to, fingerprint, cleared, constrained
  • executor.isolation_verdict — runtime, claim, verdict, status, previous_verdict, previous_status, point, detail
  • executor.labels — action, executor_id, from, to, leases, sessions, taken_back
  • executor.limits — action, executor_id, from, to, cleared
  • executor.revoke — action, executor_id, name, kind
  • executor.sandbox — action, executor_id, from, to, mode, cleared
  • executor.state_change — from, to, reason
  • executor.unbind — action, project, project_path
  • executor.uncordon — action, executor_id, state
  • executor.upgrade — action, executor_id, requested_version, force, accepted, from_version, target_version, reason
  • executor.virtual — action, executor_id, parent_id, name, from, to, reason

  • executor.audience — action is "admit" or "withdraw"; restricted is whether the executor is access-controlled after the change, and members how many principals remain. Withdrawing the last entry sets restricted=false, which widens the executor to the whole fleet — the one edit here that grants rather than revokes.

  • executor.bind — Where a project's code runs is the most consequential setting on the hub, which is why the creation-dialog path emits this too.
  • executor.enroll — action repeats the verb without the family prefix — enroll, not executor.enroll.
  • executor.failover — placed distinguishes a successful move from one with nowhere to go; then to is empty and error says why. On a hub with single sign-on acted_for names whom the replacement acts for — the person who started the run it continues, whose Claude account it runs on — and is absent when it acts for no one. A session past executors.failover.max_attempts is recorded as executor.failover_exhausted instead.
  • executor.failover_exhausted — nodes names every executor the run was lost on, oldest first, each with when it went unreachable. A workload that takes down every node it lands on ends here instead of on the next node.
  • executor.firewall — from and to describe the whole rule set — allowlist, denylist, ports, resolvers and the public-Internet switch — so a widened device is legible from this row alone. cleared is true when the rule set was removed, which leaves the device bounded only by its configuration. constrained counts the virtual executors and project rule sets the save narrowed to fit; each of those has its own row (executor.virtual with action "constrain", project.firewall with action "constrain").
  • executor.isolation_verdict — claim is what the runtime's name claims (vm, userspace-kernel, host-kernel) and verdict what the probe found; status is contradicted when the evidence shows less isolation than the claim, which refuses projects that require kernel isolation or virtualization. previous_* are empty for an executor's first verdict. A probe that could not run is recorded on the evidence row, not here.
  • executor.labels — from and to are the device's labels before and after. leases and sessions count the live secret leases and egress sessions — the device's and its virtual executors' — that were re-judged by the new labels, and taken_back how many label grants they no longer matched; each of those has its own lease.revoke_sent or egress.close row naming the grant.
  • executor.limits — Records its previous ceiling as well as the new one. A raised cap is the change worth reviewing, and it is invisible in the new value alone. cleared is true when the ceiling was removed, which makes the executor uncapped rather than capped at zero.
  • executor.sandbox — One of two executor actions that record their previous value: it changes a containment boundary, so from is what makes "when did this device stop isolating its workloads" answerable from the trail alone. cleared is true when the configuration was removed rather than replaced.
  • executor.virtual — action is "create", "update", "delete" or "constrain". from and to describe the whole configuration — sandbox, firewall allow and deny lists, devices — so a widened firewall or an added device is visible from this row alone. reason is set when the change was not asked for directly: a device's virtual executors are deleted with it when it is revoked, and narrowed ("constrain") when its firewall is tightened below what they allowed.

forwarder.*

Action Entity Home Stability Fires when
forwarder.attach forwarder control-plane beta A run starts with a forwarded device: the hub relays the forwarder's stream to the executor that attaches it and gives it to the run's sandbox.
forwarder.bind forwarder control-plane beta A forwarder's device is made available to a project's runs, optionally only on one virtual executor, by an operator or by approving a request.
forwarder.bind_request forwarder control-plane beta Someone without executor.manage asks for a forwarder binding through the grant-request flow.
forwarder.bind_request_approve forwarder control-plane beta An administrator approves a forwarder binding request, minting the binding.
forwarder.bind_request_deny forwarder control-plane beta An administrator denies a forwarder binding request.
forwarder.bind_request_expire forwarder control-plane beta A forwarder binding request lapses undecided.
forwarder.bind_request_withdraw forwarder control-plane beta The requester withdraws a pending forwarder binding request.
forwarder.detach forwarder control-plane beta A forwarded device is taken back from a run: the run ended, its binding or forwarder was revoked, or a side disconnected.
forwarder.enroll forwarder control-plane stable An operator mints an enrollment token for a device forwarder.
forwarder.expose forwarder control-plane beta An operator probes a forwarded device through the hub: a stream opened to the forwarder and, for USB, its device list read, handing the device to nothing.
forwarder.revoke forwarder control-plane stable A device forwarder's enrollment token or long-lived credential is revoked.
forwarder.unbind forwarder control-plane beta A forwarder binding is revoked by an operator, by the forwarder's own revocation, or by its TTL lapsing; a run holding the device has it detached.

Payload keys:

  • forwarder.attach — action, forwarder_id, binding_id, project, executor_id, handle_id, run_id
  • forwarder.bind — action, forwarder_id, binding_id, project, virtual_executor, expires_at, request_id
  • forwarder.bind_request — action, forwarder_id, request_id, project, virtual_executor, ttl_seconds
  • forwarder.bind_request_approve — action, forwarder_id, request_id, binding_id, expires_at
  • forwarder.bind_request_deny — action, forwarder_id, request_id, note
  • forwarder.bind_request_expire — action, forwarder_id, request_id
  • forwarder.bind_request_withdraw — action, forwarder_id, request_id
  • forwarder.detach — action, forwarder_id, binding_id, project, executor_id, handle_id, reason
  • forwarder.enroll — action, forwarder_id, name, device, expires_at
  • forwarder.expose — action, forwarder_id, device, reachable, member
  • forwarder.revoke — action, forwarder_id, kind
  • forwarder.unbind — action, forwarder_id, binding_id, project, reason

  • forwarder.bind — Gated on executor.manage, like enrollment. A run of the project gets the device in its sandbox while the binding is in force.

  • forwarder.enroll — A forwarder bridges one on-prem USB or layer-2 device to the hub and runs no workload; it is not an executor.

executor_pool.*

Action Entity Home Stability Fires when
executor_pool.audience executor_pool control-plane beta An administrator admits a principal to a pool's access list, or withdraws one.
executor_pool.controller_status executor_pool control-plane beta A pool's controller reports that something keeps it from starting runners — a runner volume stuck Pending, a full one — or that it no longer does.
executor_pool.cordon executor_pool control-plane beta An administrator cordons a pool — its runners are handed no queued run and its controller is minted no runner, while busy runners finish — or uncordons it.
executor_pool.create executor_pool control-plane beta An administrator creates an executor pool.
executor_pool.credential_mint executor_pool control-plane beta An administrator mints a controller credential for a pool.
executor_pool.credential_revoke executor_pool control-plane beta An administrator revokes one of a pool's controller credentials.
executor_pool.revoke executor_pool control-plane beta An administrator revokes a pool: its controller credentials, unredeemed runner tokens, live runners and queued runs go with it.
executor_pool.run_queue executor_pool control-plane beta A project's run is queued for a pool, handed to a runner, or leaves the queue cancelled or failed.
executor_pool.runner_enroll executor_pool control-plane beta A runner redeems its runner token and joins its pool as an ephemeral executor.
executor_pool.runner_retire executor_pool control-plane beta A runner is retired: its one run settled, it left before it was given a run, or its pool was revoked.
executor_pool.token_mint executor_pool control-plane beta A pool's controller is issued runner tokens: single-use, bound to the pool, valid for ten minutes at most.
executor_pool.update executor_pool control-plane beta An administrator changes a pool's labels, capabilities, max_runners, owner or workdir root.

Payload keys:

  • executor_pool.audience — pool_id, action, kind, value, restricted
  • executor_pool.controller_status — pool_id, healthy, reason, message
  • executor_pool.cordon — pool_id, name, cordoned, reason
  • executor_pool.create — pool_id, name, labels, capabilities, max_runners, owner, workdir_root
  • executor_pool.credential_mint — pool_id, credential_id
  • executor_pool.credential_revoke — pool_id, credential_id
  • executor_pool.revoke — pool_id, name, credentials, tokens, runners, cancelled_runs
  • executor_pool.run_queue — pool_id, queue_id, action, project_path, starter, executor_id, reason
  • executor_pool.runner_enroll — pool_id, enrollment_id, executor_id, name, member
  • executor_pool.runner_retire — pool_id, executor_id, reason, queue_id, project_path
  • executor_pool.token_mint — pool_id, credential_id, count, enrollment_ids, holding, expires_at, runners, max_runners
  • executor_pool.update — pool_id, name, changed, labels, capabilities, max_runners, owner, workdir_root

  • executor_pool.controller_status — Recorded when the report changes, not every time the controller repeats it. The runs waiting in the pool's queue say the same while the report is under ten minutes old.

  • executor_pool.cordon — Recorded only when the pool's state changes: cordoning a cordoned pool records nothing.
  • executor_pool.credential_mint — The credential itself is shown once to the administrator and never recorded; the row names its public ID.
  • executor_pool.revoke — runners lists the executor IDs whose credentials were revoked; each is also recorded as executor_pool.runner_retire when its hub member ends its connection.
  • executor_pool.run_queue — action is queued, assigned, dispatched, cancelled or failed.
  • executor_pool.token_mint — A refused request — the pool at max_runners, or revoked — is counted in cloop_executor_pool_mint_refusals_total rather than recorded here.

workspace.*

Action Entity Home Stability Fires when
workspace.provision_end project control-plane stable Workspace provisioning finishes, successfully or not.
workspace.provision_start project control-plane stable An executor begins materialising a project's source tree, before the harness starts.

Payload keys:

  • workspace.provision_end — phase, project, executor_id, executor_kind, handle_id, kind, repo, ref, depth, grant_id, lease_id, duration_ms, error
  • workspace.provision_start — phase, project, executor_id, executor_kind, handle_id, kind, repo, ref, depth, grant_id, lease_id

  • workspace.provision_end — A row with no error is the success case; there is no separate failure action.

  • workspace.provision_start — depth is the shallow-fetch depth, or the string full when the spec asked for whole history.

sandbox.*

Action Entity Home Stability Fires when
sandbox.image_denied project control-plane stable Image trust policy refuses a container image a project asked to run.

Payload keys, on every action above: image, rule, reason, registry, repository, project

  • sandbox.image_denied — The one sandbox placement decision worth a permanent row: the others record a project asking for less than it could have, this one records it asking to execute code from somewhere the operator does not trust.

sandbox.attach.*

Action Entity Home Stability Fires when
sandbox.attach.close sandbox_session control-plane stable An attached session ends, by the caller leaving or the sandbox going away.
sandbox.attach.denied sandbox_session control-plane stable An attach request is refused, by permission, by scope, or because the target is not running.
sandbox.attach.open sandbox_session control-plane stable A caller is admitted to a shell inside a running task's sandbox.

Payload keys:

  • sandbox.attach.close — session_id, writable, executor_id, handle_id, task_id, command, detail
  • sandbox.attach.denied — session_id, writable, executor_id, handle_id, task_id, command, detail
  • sandbox.attach.open — session_id, writable, executor_id, handle_id, task_id, command

  • sandbox.attach.open — writable records whether the second, narrower sandbox.attach.write permission was exercised.

secret.*

Action Entity Home Stability Fires when
secret.access_check secret control-plane stable A repository or cluster access decision is evaluated against a grant's constraints.
secret.delete secret control-plane stable A secret is destroyed and every grant depending on it is revoked with it.
secret.grant secret control-plane stable A grant is written authorising a subject to lease a secret.
secret.lease secret control-plane stable A lease is issued — or refused — against the grants matching a request.
secret.mint secret control-plane stable A credential is sealed and stored as a new secret.
secret.release secret control-plane stable A workload finishes with a lease and it is dropped from the server-side record — or a recorded lease nobody holds any more is retired: its run ended while no hub process held it, or it lapsed after its holder stopped.
secret.renew secret control-plane stable A live lease is renewed before it expires: re-issued to the same holder, extended in place while its run is live, or taken over by the hub process that adopted its run after the one holding it stopped. Also a credential renewed in place: a GitHub App installation token re-minted, or a Kubernetes share's ServiceAccount token replaced — by the hub at half its life, or by its owner from the terminal.
secret.request secret control-plane stable A developer files a self-service request for access they do not have.
secret.request_approve secret control-plane stable A reviewer approves a pending request and the grant it asked for is minted.
secret.request_deny secret control-plane stable A reviewer refuses a pending request.
secret.request_expire secret control-plane stable A pending request lapses with nobody having decided it.
secret.request_withdraw secret control-plane stable A requester withdraws their own pending request.
secret.revoke secret control-plane stable An operator marks a grant unusable.
secret.unguarded_ack secret control-plane stable An administrator records that a kubeconfig grant whose namespaces or verbs only the Kubernetes access monitor enforces may be delivered on a hub that runs no monitor — or the attempt is refused.
secret.unguarded_withdraw secret control-plane stable An unguarded acknowledgement on a kubeconfig grant is withdrawn, so the grant is refused again on a hub with no Kubernetes access monitor.

Payload keys, on every action above: decision, subject, secret_id, secret_name, kind, grant_id, request_id, lease_id, executor_id, project_id, run_id, constraints, reason, task_id, host, port, bytes_up, bytes_down, expires_at

  • secret.grant — This is the row that answers "what authority exists"; the request family answers "who asked for it".
  • secret.lease — decision is allow or deny on every action in this family; a denial is a row, not a missing one.
  • secret.release — An ordinary end of life. Compare the lease.revoke_* family, which is somebody deciding mid-run that a holder may no longer hold it.
  • secret.renew — A Kubernetes share's renewal (Task 20416) is attributed to hub when the hub renewed it and to the owner when they pushed a token with cloop remote kube renew; reason names the ServiceAccount and both expiries, expires_at the new one. A renewal that failed is a deny row, written once per distinct cause.
  • secret.request — The gap between this row and its approval is the number that says whether the request path is used or routed around.
  • secret.request_expire — Its own action rather than a flavour of deny: denied is an answer, expired is the absence of one, and conflating them hides a queue nobody is working.
  • secret.unguarded_ack — Without it such a grant is refused at every lease (Task 20420). reason names what goes unenforced and the administrator's own justification; grant_id the grant, which carries the acknowledgement until it is withdrawn.

secret.lease.*

Action Entity Home Stability Fires when
secret.lease.sweep secret control-plane stable The periodic sweeper reaps leases whose TTL has passed.

Payload keys, on every action above: decision, wiped, vanished, skipped, failed, expired

  • secret.lease.sweep — One row per sweep, not per lease — the counts are the point.

lease.*

Action Entity Home Stability Fires when
lease.refresh lease control-plane stable A GitHub App token re-minted before its hour ran out is delivered to an executor holding the lease's token file, or cannot be.
lease.revoke_acked lease control-plane stable An executor confirms it destroyed the material for a revoked lease.
lease.revoke_failed lease control-plane stable A revocation fails to land on an executor that still holds the credential.
lease.revoke_sent lease control-plane stable A revocation is queued for delivery to the executors holding a lease.

Payload keys:

  • lease.refresh — decision, lease_id, grant_id, executor_id, project_id, files, handles, eventual, error, reason
  • lease.revoke_acked — decision, lease_id, grant_id, executor_id, project_id, action, state, env_scrubbed, files_removed, reason
  • lease.revoke_failed — decision, lease_id, grant_id, executor_id, project_id, action, state, error, reason
  • lease.revoke_sent — decision, lease_id, grant_id, executor_id, project_id, action, secrets, holders, reason

  • lease.refresh — The re-mint itself is a secret.renew row; this one says whether the workload got the new token. A deny means it did not — an agent too old for the refresh frame, a device offline, a Role without patch on secrets — and the run's GitHub access ends when the token it holds expires.

  • lease.revoke_failed — The row an incident response cares about: the credential is still out there.

github_app.*

Action Entity Home Stability Fires when
github_app.token_destroy secret control-plane stable A GitHub App installation token is deleted at GitHub.

Payload keys, on every action above: decision, subject, secret_id, secret_name, kind, grant_id, request_id, lease_id, executor_id, project_id, run_id, constraints, reason, task_id, host, port, bytes_up, bytes_down, expires_at

  • github_app.token_destroy — Deliberately not secret.revoke: a github_app token is destroyed on every ordinary lease release, so folding these in would make "how many grants did an operator withdraw" a number dominated by routine teardown.

egress.*

Action Entity Home Stability Fires when
egress.close secret control-plane stable An egress proxy session closes, carrying the bytes it moved in each direction.
egress.connect secret control-plane stable A sandbox's connection attempt is evaluated against the session's host allowlist.
egress.grant secret control-plane stable A subject is authorised to reach the network through the hub's egress proxy.
egress.redeem secret control-plane stable A proxy session is minted against a matching egress grant.
egress.renew secret control-plane stable A hub renews a live proxy session for a run that is still going, after re-reading its grant.
egress.request secret control-plane stable An HTTP request passes through the egress proxy.
egress.restore secret control-plane stable The hub process that adopted a run after the one serving it stopped restores the run's proxy session, after re-reading its grant.
egress.revoke secret control-plane stable An egress authorisation is marked unusable.

Payload keys, on every action above: decision, subject, secret_id, secret_name, kind, grant_id, request_id, lease_id, executor_id, project_id, run_id, constraints, reason, task_id, host, port, bytes_up, bytes_down, expires_at

  • egress.connect — host and port name the attempted destination; decision says whether it was reached.
  • egress.renew — Allowed rows carry the new expires_at. A refused renewal is a deny row; the session it concerned then closes with an egress.close row saying why.
  • egress.restore — Allowed rows carry the counters the session resumes from (bytes_up, bytes_down), which keep counting against the grant's quota, and the reason names the process that held it. A deny row is a session that will not come back: its grant was revoked or expired, or it lapsed, while no hub process held it (Task 20383).

gitproxy.*

Action Entity Home Stability Fires when
gitproxy.fetch gitproxy control-plane stable A read passes through the proxy.
gitproxy.push_allowed gitproxy control-plane stable A push is checked against the session's branch allowlist, scanned for the credentials its run was given (unless executors.git_proxy.push_scan is off), and forwarded.
gitproxy.push_denied gitproxy control-plane stable A push is refused because it names a ref the session may not write — or because its pack could not be scanned for the run's credentials: past a push_scan limit, or unreadable.
gitproxy.push_secret_refused gitproxy control-plane stable A push the branch policy allowed is refused because it carries a credential the session's run was given: an env value, a kubeconfig token or client key, registry auth, an egress proxy credential, a GitHub token — as delivered, base64-encoded or percent-encoded — found in a commit, tree, blob, tag, delta insert, ref name or push option.
gitproxy.rejected gitproxy control-plane stable A request is refused before any policy could be evaluated — no session, bad credential, unknown repository.
gitproxy.session_closed gitproxy control-plane stable A proxy session ends and its credential stops working.
gitproxy.session_minted gitproxy control-plane stable A proxy session is created for a task, scoping which repository and refs it may touch.
gitproxy.session_renewed gitproxy control-plane stable A workspace's pinned proxy session nears its end while its run is still alive, and the hub renews it for another session_minutes.
gitproxy.session_restored gitproxy control-plane stable The hub process that adopted a run restores the run's proxy session under its original id and token, with an upstream credential re-derived from the run's lease.

Payload keys:

  • gitproxy.fetch — kind, session_id, repo, project_id, task_id, refs, detail
  • gitproxy.push_allowed — kind, session_id, repo, project_id, task_id, refs, detail
  • gitproxy.push_denied — kind, session_id, repo, project_id, task_id, refs, detail, scan, run_id, findings
  • gitproxy.push_secret_refused — kind, session_id, repo, project_id, task_id, refs, detail, scan, run_id, findings
  • gitproxy.rejected — kind, session_id, repo, project_id, task_id, refs, detail
  • gitproxy.session_closed — kind, session_id, repo, project_id, task_id, refs, detail
  • gitproxy.session_minted — kind, session_id, repo, project_id, task_id, refs, detail
  • gitproxy.session_renewed — kind, session_id, repo, project_id, task_id, refs, detail
  • gitproxy.session_restored — kind, session_id, repo, project_id, task_id, refs, detail

  • gitproxy.push_denied — The row that matters in this family: it is the only place a sandbox's attempt to write outside its lane is recorded. refs names what it tried to push. A push the scan could not finish carries scan (limit or unreadable) and run_id, and detail names the limit and its setting (Task 20436).

  • gitproxy.push_secret_refused — findings name each object, its path and commit where the pack says, and the grant and secret the value came from; nothing records the value. The forge got no complete pack and no ref moved. A push refused because it could not be scanned — past a push_scan_* limit, or a pack the proxy cannot read — is a gitproxy.push_denied row with scan set (Task 20436).
  • gitproxy.session_closed — Also written for a session that ended while no hub process served it — not restored with its run, or retired by the leader's janitor once it lapsed — with the reason in detail.
  • gitproxy.session_renewed — detail gives the new deadline. Renewed only while the workspace lease it stands on is kept alive and the run's executor still holds the workload; the session is closed once the run is done with it (Task 20422).
  • gitproxy.session_restored — detail names the hub process that held the session until then. A session that could not be restored gets a gitproxy.session_closed row instead (Task 20383).

kubeguard.*

Action Entity Home Stability Fires when
kubeguard.rejected kubeguard control-plane stable A request is refused before its session could be identified.
kubeguard.request_allowed kubeguard control-plane stable A Kubernetes request is admitted by policy.
kubeguard.request_denied kubeguard control-plane stable A Kubernetes request is refused by the session's verb and resource policy, or because it would hand the workload a credential of its own.
kubeguard.session_closed kubeguard control-plane stable A Kubernetes proxy session ends.
kubeguard.session_minted kubeguard control-plane stable A Kubernetes proxy session is created for a task against one cluster and context.
kubeguard.session_restored kubeguard control-plane stable The hub process that adopted a run restores the run's Kubernetes proxy session under its original id and token, with the cluster credential re-derived from the run's lease and its policy narrowed to what the grant and the hub allow now.

Payload keys, on every action above: kind, session_id, cluster, context, verb, resource, namespace, object, reason, project_id, task_id, executor_id, grant_id, lease_id, detail

  • kubeguard.request_allowed — Sampled rather than exhaustive — a watch against a busy cluster would otherwise be the whole table.
  • kubeguard.request_denied — This is what enforces a read-only grant on a cluster from outside the sandbox: verb, resource and namespace say exactly what was attempted. reason classifies it; service_account_token_secret, workload_service_account, host_access, rbac_binding, rbac_role, certificate_signing_request and unreadable_body are a write grant's attempts to obtain a credential that would bypass the monitor (Task 20420).
  • kubeguard.session_closed — Also written for a session that ended while no hub process served it, with the reason in detail.
  • kubeguard.session_restored — detail names the hub process that held the session until then (Task 20383).

ci.*

Action Entity Home Stability Fires when
ci.rejected ci_session control-plane beta A relay request is refused before a session could be established.

Payload keys, on every action above: kind, session_id, rule_id, rule_name, project, subject, repository, ref, workflow, actor, run_id, method, path, model, status, reason, input_tokens, output_tokens, detail, at

ci.session.*

Action Entity Home Stability Fires when
ci.session.closed ci_session control-plane beta A relay session ends, by expiry or by revocation.
ci.session.minted ci_session control-plane beta A pipeline's OIDC token matches an allowlist rule and a relay session is issued.
ci.session.restored ci_session control-plane beta A hub process receives a call presenting the token of a recorded relay session whose holder stopped, takes the record over, holds the session to its rule as the rule stands now, and serves it under its original id and token.
ci.session.revoked ci_rule control-plane beta An operator revokes a live relay session from the CI panel.
ci.session.suspended ci_session control-plane beta A hub process stops serving a recorded relay session without ending it here: it is stopping gracefully, and the session's spend is written and its record stays open; or the record was closed or taken over by another hub process, whose row says why.

Payload keys:

  • ci.session.closed — kind, session_id, rule_id, rule_name, project, subject, repository, ref, workflow, actor, run_id, method, path, model, status, reason, input_tokens, output_tokens, detail, at
  • ci.session.minted — kind, session_id, rule_id, rule_name, project, subject, repository, ref, workflow, actor, run_id, method, path, model, status, reason, input_tokens, output_tokens, detail, at
  • ci.session.restored — kind, session_id, rule_id, rule_name, project, subject, repository, ref, workflow, actor, run_id, method, path, model, status, reason, input_tokens, output_tokens, detail, at
  • ci.session.revoked — rule_id, rule_name, repository, ref, condition, models, detail, error
  • ci.session.suspended — kind, session_id, rule_id, rule_name, project, subject, repository, ref, workflow, actor, run_id, method, path, model, status, reason, input_tokens, output_tokens, detail, at

  • ci.session.restored — detail names the hub process that held it until then and anything its rule narrowed. A session whose rule was deleted, disabled or no longer admits the pipeline gets a ci.session.closed row instead, and its call a 401 (Task 20390).

  • ci.session.revoked — Also written for a suspended session — one no hub process serves — whose record the revocation ends, so its job's next call is refused rather than restored (Task 20390).
  • ci.session.suspended — After a graceful stop the session is restored by the hub process that receives its job's next call (Task 20390); a session with no record is closed instead, with a ci.session.closed row. One whose record another process closed or took over is ended, or served, there.

ci.exchange.*

Action Entity Home Stability Fires when
ci.exchange.accepted ci_session control-plane beta A pipeline's OIDC token is verified and exchanged for relay credentials.
ci.exchange.rejected ci_session control-plane beta A token exchange fails: bad signature, wrong issuer, or no rule admits the claims.

Payload keys, on every action above: kind, session_id, rule_id, rule_name, project, subject, repository, ref, workflow, actor, run_id, method, path, model, status, reason, input_tokens, output_tokens, detail, at

  • ci.exchange.rejected — The row to alert on. A pipeline that should be admitted and is not looks identical here to one that should never have asked.

ci.relay.*

Action Entity Home Stability Fires when
ci.relay.allowed ci_session control-plane beta A relayed API call is forwarded upstream on a live session.
ci.relay.denied ci_session control-plane beta A relayed API call is refused — expired session, unsupported path, or a quota.

Payload keys, on every action above: kind, session_id, rule_id, rule_name, project, subject, repository, ref, workflow, actor, run_id, method, path, model, status, reason, input_tokens, output_tokens, detail, at

ci.rule.*

Action Entity Home Stability Fires when
ci.rule.created ci_rule control-plane beta An allowlist rule is added, widening which pipelines may authenticate.
ci.rule.deleted ci_rule control-plane beta An allowlist rule is removed.
ci.rule.updated ci_rule control-plane beta An allowlist rule is edited; live sessions it no longer admits are revoked in the same operation.

Payload keys, on every action above: rule_id, rule_name, repository, ref, condition, models, detail, error

  • ci.rule.created — match is the CEL expression the rule admits on. Rules are the CI surface's whole access-control policy, so these three rows are its change log.

ci.config.*

Action Entity Home Stability Fires when
ci.config.updated ci_rule control-plane beta The CI relay's own settings change — whether it is enabled, its issuer, its session ceiling.

Payload keys, on every action above: rule_id, rule_name, repository, ref, condition, models, detail, error

model_relay.session.*

Action Entity Home Stability Fires when
model_relay.session.closed model_relay_session control-plane beta A run's relay session ends: its run ended or did not start, its lease was released, revoked or lapsed, its grant was revoked, or it expired.
model_relay.session.minted model_relay_session control-plane beta A run on an isolating executor is dispatched with a model relay session in place of its Claude credential.
model_relay.session.restored model_relay_session control-plane beta The hub process adopting a run takes its relay session over with the run's lease, re-deriving the credential from the grant and holding the session to today's model allowlist.
model_relay.session.suspended model_relay_session control-plane beta A hub process stops serving a recorded relay session without ending it: it is stopping gracefully, or another process took the session's record over with the run's lease.

Payload keys, on every action above: kind, session_id, project, run_id, lease_id, grant_id, executor_id, payer, upstream_kind, method, path, model, status, reason, input_tokens, output_tokens, detail, at

  • model_relay.session.closed — input_tokens and output_tokens are what the session spent in all, as the API counted it; input_tokens includes cache reads and writes.

model_relay.*

Action Entity Home Stability Fires when
model_relay.denied model_relay_session control-plane beta A relayed model call is refused before it reaches the API: its model is not allowed, the identity paying for the run has spent a daily budget, or the request is not one a run may make.
model_relay.models_set project control-plane beta A project's maintainer narrows, or clears, the models its relayed runs may use.
model_relay.not_relayed project control-plane beta The model relay is on, and a run on an isolating executor is dispatched with its Claude credential delivered to the sandbox as before.
model_relay.rejected model_relay_session control-plane beta A model call presents the token of a relay session that has ended, and is refused with the reason it ended.
model_relay.usage_gap project control-plane beta A relayed run ends and the usage it reported to its project's ledger differs from what the relay counted from the API's own responses.

Payload keys:

  • model_relay.denied — kind, session_id, project, run_id, lease_id, grant_id, executor_id, payer, upstream_kind, method, path, model, status, reason, input_tokens, output_tokens, detail, at
  • model_relay.models_set — project, models
  • model_relay.not_relayed — project, run_id, executor_id, credential_kind, reason
  • model_relay.rejected — kind, session_id, project, run_id, lease_id, grant_id, executor_id, payer, upstream_kind, method, path, model, status, reason, input_tokens, output_tokens, detail, at
  • model_relay.usage_gap — project, run_id, session_ids, payer, relay_tokens, relay_usd, reported_tokens, reported_usd, gap_tokens, gap_usd

  • model_relay.denied — reason is the relay's refusal code: budget_exhausted, model_not_allowed, path_not_allowed, method_not_allowed, body_too_large or body_malformed.

  • model_relay.models_set — An empty models clears the project's list, and the hub's allowlist applies to its runs again.
  • model_relay.not_relayed — reason says why: the credential's kind has not been proven through the relay against the real API, or the device's agent predates handing the hub's CA to the harness.
  • model_relay.rejected — A call with no usable token at all is counted in cloop_model_relay_requests_total and not written here.
  • model_relay.usage_gap — The relay's count is what the payer was charged; the run's ledger gains a row bringing its total to it. A claudecode run reports no tokens at all, so its whole relayed spend shows as the gap.

authz.*

Action Entity Home Stability Fires when
authz.denied permission either stable Any permission check refuses a caller.
authz.granted permission either stable A privileged permission is exercised successfully.

Payload keys, on every action above: outcome, permission, role, source, scope, subject, method, path, binding

  • authz.denied — Every denial, unlike the grant side. scope says whether the check was fleet-wide or against one project; binding names the runtime rule when one decided it. Neither action in this family is emitted when RBAC is not in force for the caller — with no identity provider configured every request is granted everything and there is no decision to record, so an absence of rows here means "nobody was refused" only on a hub that has SSO or API tokens. Project-scoped decisions land in that project's trail rather than the control plane's.
  • authz.granted — Not every allow: ordinary reads would drown the table, so only privileged permissions are recorded on this side. Home varies with scope — a check against a project lands in that project's chain, a fleet-wide one in the hub's — so answering "what was this subject allowed to do" needs both.

api_token.*

Action Entity Home Stability Fires when
api_token.approve_denied api_token control-plane stable An approval is refused: no role policy is in force, or the approver does not hold the token's roles.
api_token.approved api_token control-plane stable An administrator approves a token minted with no role policy in force, which a hub enforcing one refuses until then (Task 20422).
api_token.auth_failed api_token control-plane stable A request presents a token that does not authenticate: unknown, revoked, or expired.
api_token.create_denied api_token control-plane stable A token mint is refused because it would grant more than the caller holds.
api_token.created api_token control-plane stable A scoped API token or a display-glasses link is minted, from the UI or the CLI.
api_token.revoked api_token control-plane stable A token or glasses link is revoked, or rotated — a rotation revokes the old one.

Payload keys:

  • api_token.approve_denied — reason, roles, project_scope
  • api_token.approved — name, roles, project_scope, minted_under, created_by, created_at, via
  • api_token.auth_failed — reason, ip, method, path
  • api_token.create_denied — reason, roles, project_scope
  • api_token.created — name, roles, project_scope, expires_at, kind, owner, minted_under
  • api_token.revoked — name, roles, reason

  • api_token.approved — From the dashboard the approver must hold every role the token carries, under the policy now in force; via: cli is an operator at the control plane.

  • api_token.auth_failed — Filed against the token's public id (cloop_pat_<id>), never the secret half, and against (unparseable) when the credential is not even shaped like a token — so a garbage value is not echoed back into the trail. Rate of this action by ip is the credential-stuffing signal. awaiting_approval is a token minted with no role policy in force, refused on a hub that enforces one until an administrator approves it.
  • api_token.create_denied — The anti-escalation check. A maintainer trying to mint an admin token lands here.
  • api_token.created — kind distinguishes a service-account token from a glasses link, which is a token with one narrow role and a URL as its only delivery mechanism. minted_under is policy when the hub's role policy was in force, no_policy when it was not, operator from the CLI (Task 20422).

static_token.*

Action Entity Home Stability Fires when
static_token.retired static_token control-plane stable The static admin token is retired, from the Settings card or cloop hub token static retire.

Payload keys, on every action above: fingerprint, reason, via, forced, self, os_user

  • static_token.retired — Filed against the token's fingerprint — a SHA-256 of the value, never the value. self is true when the static token retired itself; forced when the retirement overrode the refusal to leave a hub without single sign-on or an admin API token with no way in. Recorded in the same commit as the retirement, so a retirement with no row cannot happen.

session.*

Action Entity Home Stability Fires when
session.claims_rejected session control-plane stable The IdP answers the re-assertion by refusing the claims outright.
session.claims_stale session control-plane stable A privileged operation is blocked because the session's claims are older than the freshness bound allows.
session.claims_unverified session control-plane stable The hub cannot reach the IdP to re-assert a session's claims.
session.created session control-plane stable A sign-in completes and a durable session is written.
session.expired session control-plane stable The session janitor removes a session past its absolute or idle deadline.
session.idp_revoked session control-plane stable The identity provider reports the authorisation behind a session is gone.
session.reauth session control-plane beta A privileged request reaches the hub on a session whose last interactive sign-in is older than ui.oidc.sudo_max_age_minutes (sudo mode), or the confirm-it's-you sign-in that answers one completes.
session.renewal_mismatch session control-plane stable A silent renewal from the user's browser is answered by the identity provider for a different subject than the session belongs to.
session.revoked session control-plane stable A user logs out, or an operator terminates a session from the UI or the CLI.
session.role_narrowed session control-plane stable A re-assertion finds the session's claims now map to a lower role than it held.

Payload keys:

  • session.claims_rejected — event, session_id, subject, email, actor, reason, ip, user_agent, prior_role, role, dropped_claims, issued_at, selector, selected, via, os_user, kind, client
  • session.claims_stale — event, session_id, subject, email, actor, reason, ip, user_agent, prior_role, role, dropped_claims, issued_at, selector, selected, via, os_user, kind, client
  • session.claims_unverified — event, session_id, subject, email, actor, reason, ip, user_agent, prior_role, role, dropped_claims, issued_at, selector, selected, via, os_user, kind, client
  • session.created — event, session_id, subject, email, actor, reason, ip, user_agent, prior_role, role, dropped_claims, issued_at, selector, selected, via, os_user, kind, client
  • session.expired — event, session_id, subject, email, actor, reason, ip, user_agent, prior_role, role, dropped_claims, issued_at, selector, selected, via, os_user, kind, client
  • session.idp_revoked — event, session_id, subject, email, actor, reason, ip, user_agent, prior_role, role, dropped_claims, issued_at, selector, selected, via, os_user, kind, client
  • session.reauth — session_id, subject, email, ip, user_agent, kind, client, outcome, reason, max_age_seconds, authenticated_at, method, path, replaces, suppressed
  • session.renewal_mismatch — event, session_id, subject, email, actor, reason, ip, user_agent, prior_role, role, dropped_claims, issued_at, selector, selected, via, os_user, kind, client
  • session.revoked — event, session_id, subject, email, actor, reason, ip, user_agent, prior_role, role, dropped_claims, issued_at, selector, selected, via, os_user, kind, client
  • session.role_narrowed — event, session_id, subject, email, actor, reason, ip, user_agent, prior_role, role, dropped_claims, issued_at, selector, selected, via, os_user, kind, client

  • session.claims_stale — A block, not a revocation: the session stays, and ordinary reads keep working.

  • session.reauth — outcome is stale-refused for the refused request — method and path name it, authenticated_at is the session's last interactive sign-in (absent: none on record) and max_age_seconds the window it missed; at most thirty such rows a minute are written, the next one carrying suppressed, while cloop_session_reauth_total counts every one. granted is a sign-in the hub started with prompt=login&max_age=0 that the provider answered for the same person: session_id is the fresh session it created and replaces the one that asked. refused is one that did not count — reason subject_mismatch (another account signed in), not_reauthenticated (the provider's auth_time is older than the request, so it did not ask), or idp_error: <code>; nothing is created then. API tokens and the static token are not sessions and never produce this row.
  • session.renewal_mismatch — The session is left exactly as it was: nothing of the other identity is applied or recorded. The benign reading is a user who switched accounts at the provider in another tab; the other is a browser signed into somebody else's account at the provider in the hope that the renewal adopts it. ip and user_agent are the browser that performed the renewal.
  • session.role_narrowed — prior_role and role bracket the demotion; dropped_claims names what the IdP stopped asserting. via is browser_renewal when the narrowed claims arrived through a silent renewal from the user's browser, and absent when the hub's own refresh found them.

device.*

Action Entity Home Stability Fires when
device.approved device_authorization control-plane beta A signed-in browser approves a terminal's code at /device and completes the identity provider round trip that writes the CLI session.
device.code_issued device_authorization control-plane beta A terminal asks the hub for a sign-in code (cloop login): POST /api/device/code.
device.denied device_authorization control-plane beta A terminal's code is denied at /device, or the hub refuses the approval on the approver's behalf.

Payload keys, on every action above: device_id, client_ip, client_user_agent, client_hostname, expires_at, session_id, subject, email, ip, user_agent, reason, reauth, kind, client

  • device.approved — session_id is the CLI session's id, whose own session.created row (kind cli, via device_grant) follows; ip and user_agent are the approving browser's, the client_* keys the terminal's. reauth is true when the browser session was older than ui.oidc.device_approval_max_age_minutes and the provider was asked for a fresh sign-in.
  • device.code_issued — Unauthenticated by design — the terminal has no credential yet — and rate-limited per address. device_id is a prefix of the device code's digest, never the code; client_hostname is what the terminal reported and proves nothing.
  • device.denied — reason is denied_at_approval_page for a person pressing Deny; identity_refused when a runtime deny binding (an offboarding) matched the approving identity; session_error when the session could not be written. The actor is hub for the last two.

request.*

Action Entity Home Stability Fires when
request.host_refused request control-plane beta A hub without sign-in refuses a request addressed to a host name it does not answer to — the shape DNS rebinding takes.
request.origin_refused request control-plane beta The hub refuses a state-changing request or a WebSocket handshake because a page on another origin made it, or because its body is form data or text/plain.

Payload keys, on every action above: reason, method, path, origin, sec_fetch_site, host, content_type, ip, user_agent, session, open_hub, own_origin, suppressed

  • request.host_refused — Only a hub with neither SSO nor a static token checks the Host: rebinding cannot carry a credential to a hub that has one. A burst after a new DNS name was pointed at the hub is that name missing from ui.allowed_hosts. Rate-limited like request.origin_refused.
  • request.origin_refused — The browser that sent it belongs to whoever was using it: the row records the page that asked (origin, sec_fetch_site), not an identity, and session says only whether that browser carried a sign-in cookie. reason is a cloop_cross_origin_refusals_total label. Rows are written at most 30 a minute; suppressed counts the refusals since the previous row that were not.

role_binding.*

Action Entity Home Stability Fires when
role_binding.deleted role_binding control-plane stable A runtime binding is removed and the identity falls back to its configured role.
role_binding.denied role_binding control-plane stable A runtime deny binding is written, demoting an identity ahead of the IdP catching up.
role_binding.granted role_binding control-plane stable A runtime binding is written mapping a claim to a role, without an IdP or config change.

Payload keys, on every action above: id, effect, claim, value, role, project, executor, binding_reason, created_at, created_by, reason, via, os_user

  • role_binding.denied — The incident-response verb: it takes authority away now, from a CLI, without a browser.

quota.*

Action Entity Home Stability Fires when
quota.denied quota control-plane stable Admission control refuses an operation because the caller is at a resource ceiling.
quota.override_cleared quota control-plane stable A per-identity override is removed and the identity returns to the default ceiling.
quota.override_set quota control-plane stable A per-identity quota override is written, from the UI or the CLI.
quota.spend_refused project control-plane stable A run is stopped between tasks because the identity paying for it is over its spend limit.

Payload keys:

  • quota.denied — limit, used, requested, source, transient, method, path
  • quota.override_cleared — target, identity, cleared_limits, reason, via, os_user
  • quota.override_set — target, identity, limits, unset, reason, via, os_user
  • quota.spend_refused — identity, resource, limit, used, source, project, outcome

  • quota.denied — The identity is the row's actor and the resource is its entity id, so neither repeats in the payload.

  • quota.override_set — Two emitters with different shapes: the UI writes target plus one key per resource, the CLI writes identity with the limits nested under limits.
  • quota.spend_refused — Enforced between tasks, not mid-task: a task already running is allowed to finish.

resource_ceiling.*

Action Entity Home Stability Fires when
resource_ceiling.cleared project_resource_limit control-plane stable A per-project ceiling is removed and the project returns to the fleet ceiling alone.
resource_ceiling.set project_resource_limit control-plane stable A per-project resource ceiling is written with cloop hub limits set.

Payload keys:

  • resource_ceiling.cleared — target, project, reason, via, os_user
  • resource_ceiling.set — target, project, cpu_millis, memory_mb, disk_mb, pids, reason, via, os_user

  • resource_ceiling.set — The values are the merged ceiling that was stored, not the flags that were passed: set merges onto the existing row, so the flags alone would not say what the project ended up capped at.

sealing_key.*

Action Entity Home Stability Fires when
sealing_key.escrowed sealing_key control-plane beta cloop hub key escrow export wraps CLOOP_SECRET_KEY under an operator's passphrase, or cloop hub key escrow verify --record records a verified escrow again after a restore.
sealing_key.kms_rewrapped sealing_key control-plane beta A key-encryption key held by a KMS is wrapped again under the key the KMS now encrypts under — by the hub's cluster leader when the KMS reports a rotation of its key, or by cloop hub key rewrap. Nothing sealed under the key changes.
sealing_key.retired sealing_key control-plane stable A superseded sealing key is retired once nothing is wrapped under it.
sealing_key.root_changed sealing_key control-plane stable cloop hub key rotate --retire-previous finishes a change of root — of CLOOP_SECRET_KEY, onto a KMS or off one: every hub member held the new root (and, for a passphrase root, an escrow of it was on record), every sealed row was rewrapped onto a key of it, and every key of the previous root was retired. Written when the run ends, finished or not.
sealing_key.rotated sealing_key control-plane stable The secret store's sealing key is rotated and stored payloads are re-wrapped under the new one.

Payload keys:

  • sealing_key.escrowed — escrow_id, key_fingerprint, kdf, created_at, recorded, via
  • sealing_key.kms_rewrapped — key_id, kms, from_kms_key, to_kms_key, via
  • sealing_key.retired — key_id, via
  • sealing_key.root_changed — root, previous_root, minted, to_key, rewrapped, skipped, failed, retired, remaining, members, escrow_id, complete, via
  • sealing_key.rotated — from_primary, to_key, rewrapped, skipped, failed, complete, via

  • sealing_key.escrowed — Carries the key's fingerprint, never the key or the passphrase. cloop hub doctor compares the escrow on record against the key in use and fails when they differ.

  • sealing_key.kms_rewrapped — Carries KMS key ids, never a ciphertext. Once no live key names the KMS's old key version in cloop hub key list, that version holds nothing of the hub's and can be disabled in the KMS.
  • sealing_key.root_changed — Roots are named by fingerprint, the name every checkpoint and escrow carries, never by value, and a KMS root by its name (Task 20433), so moving onto a KMS or off one is recorded here too. The rotation and each retirement are recorded as sealing_key.rotated and sealing_key.retired as well.
  • sealing_key.rotated — Envelope encryption means rotation re-wraps data keys rather than re-encrypting payloads, so this row is cheap even on a large store.

hub_ca.*

Action Entity Home Stability Fires when
hub_ca.emergency_rotated hub_ca control-plane beta cloop hub ca rotate --now replaces every hub CA at once with a fresh one, for a key believed compromised.
hub_ca.leaf_issued hub_ca control-plane beta A hub process issues a credential monitor's serving certificate from the hub CA: at its first start, when the cluster leader renews one, when a member needs a name the stored one lacks, or when a rotation activated another CA.
hub_ca.minted hub_ca control-plane beta A hub process mints the control plane's certificate authority, the first time any starts a credential monitor under tls: auto.
hub_ca.previous_retired hub_ca control-plane beta cloop hub ca rotate --retire-previous deletes the replaced CA with secure_delete on, so its sealed key is gone from the database file, and drops it from every bundle.
hub_ca.rotation_abandoned hub_ca control-plane beta cloop hub ca rotate --abort destroys a staged CA that was never activated, or an emergency rotation abandons one.
hub_ca.rotation_activated hub_ca control-plane beta cloop hub ca rotate --activate makes the staged CA the one leaves are issued from; the cluster leader then reissues every leaf.
hub_ca.rotation_staged hub_ca control-plane beta cloop hub ca rotate --stage (or Settings → Hub health) mints a CA to replace the hub's, sealed under the KEK registry like the first (Task 20438).
hub_ca.sessions_ended hub_ca control-plane beta A hub member ends the git proxy, Kubernetes monitor and model relay sessions of the runs an emergency rotation named, within a minute of it.

Payload keys:

  • hub_ca.emergency_rotated — rotation, replaced, active, names, cut_runs, via
  • hub_ca.leaf_issued — monitor, serial, names, not_after, replaced, issuer, reason
  • hub_ca.minted — serial, not_after, fingerprint
  • hub_ca.previous_retired — rotation, retired, active, cut_runs, emergency, via
  • hub_ca.rotation_abandoned — rotation, abandoned, active, detail, emergency, via
  • hub_ca.rotation_activated — rotation, from, to, forced, emergency, via
  • hub_ca.rotation_staged — rotation, from, to, fingerprint, not_after, names, emergency, via
  • hub_ca.sessions_ended — rotation, member, runs, git_proxy, kube_guard, model_relay

  • hub_ca.emergency_rotated — Alert on it. cut_runs are the runs that trusted only the replaced CAs; each hub member ends their proxy sessions and records hub_ca.sessions_ended.

  • hub_ca.leaf_issued — reason is first, renewal, names, activation or unreadable; issuer is the serial of the CA that signed it.
  • hub_ca.minted — Every sandbox is told to trust this CA for the monitors' URLs, so a second hub_ca.minted row for one control plane — its hub_tls table lost or restored without it — means sandboxes holding the previous CA will refuse the next certificate the monitors serve.
  • hub_ca.previous_retired — A backup taken before this row still holds the retired CA's sealed key.
  • hub_ca.rotation_activated — forced names the runs dispatched before the stage that an operator activated past with --force: they stop verifying the credential monitors.
  • hub_ca.rotation_staged — From this row on every dispatch trusts both CAs; leaves still come from from. names are the new CA's X.509 name constraints, empty when it was minted without them.
  • hub_ca.sessions_ended — Counts per monitor are what this member held; a run's sessions on another member are in that member's row.

oidc.*

Action Entity Home Stability Fires when
oidc.config.updated config control-plane stable The hub's single sign-on configuration is changed, from the Settings panel or cloop config set ui.oidc.*.
oidc.rbac.enforced config control-plane stable An admin enforces deny-by-default from Settings → Single sign-on on a hub whose single sign-on ran without a role policy: default_role: none, plus a hub-wide admin mapping for them unless they already hold admin.

Payload keys:

  • oidc.config.updated — changed, enabled, was_enabled, issuer, default_role, admin_emails, role_mappings, require_idp, require_rbac, rbac_enforced, was_rbac_enforced, restart_required
  • oidc.rbac.enforced — issuer, default_role, bound, admin_emails, role_mappings, restart_required

  • oidc.config.updated — changed lists the field names that moved, so the row answers what was touched without storing two copies of the block. The client secret appears in changed when it moves and nowhere else — not its value, not its length. admin_emails and role_mappings are counts, not contents. rbac_enforced and was_rbac_enforced say whether the saved block puts a role policy in force after and before the change, so the rows where RBAC switched on or off are a filter, not a reconstruction; the Settings panel makes either switch only when the request confirms it. restart_required records whether the change was live yet, because the authenticator is built at startup: a row with it set means the hub was still running the previous configuration when the change landed.

  • oidc.rbac.enforced — It changes ui.oidc as oidc.config.updated does, and is written instead of it, so a query for who last changed single sign-on reads both. bound is the mapping added for the acting admin as sub=<subject> — the subject, because an email can be renamed at some providers — and empty when they were already an admin. admin_emails and role_mappings are counts. Not live until the hub restarts, which restart_required records.

telemetry.*

Action Entity Home Stability Fires when
telemetry.config.updated config control-plane stable Front-end telemetry collection is switched on or off, or narrowed to particular front ends, from Settings → Telemetry.

Payload keys, on every action above: enabled, was_enabled, sources, was_sources

  • telemetry.config.updated — Collection is off unless an operator turned it on, and this row is when they did. It exists so that "was this hub recording its users at the time, and who decided that" is answerable afterwards — including by the people who were recorded. sources is empty when every front end is collected. A save that changed nothing emits no row.

notifications.*

Action Entity Home Stability Fires when
notifications.config.updated config control-plane beta The webhooks the hub tells approvers through — when a grant request or an API token waits on them — are added, changed or removed, or the dashboard link they carry is, from Settings → Notifications.

Payload keys, on every action above: webhooks, was_webhooks, url_changed, dashboard_url, was_dashboard_url

  • notifications.config.updated — A webhook decides where a stream about who asked for which credential leaves the hub, so this row is when that destination was chosen, and by whom. Each webhook is described as name (type) scheme://host, or secret:<name> when its URL is kept in the secret store, with its events and allowed addresses — never the URL itself, which is the channel's credential. url_changed names the webhooks whose URL was replaced. A save that changed nothing emits no row.

disk.*

Action Entity Home Stability Fires when
disk.floor.updated config control-plane stable The hub's free-space floor (orchestrator.min_free_disk_mb) is changed from Settings → Disk & Retention.

Payload keys, on every action above: min_free_disk_mb, was_min_free_disk_mb, file

  • disk.floor.updated — 0 means the check is off: runs then start on any disk, and a task's outcome written onto a full one has no reserve to fall back on. file names where the value was written — the hub's per-instance overlay or config.yaml. A save that changed nothing emits no row.

stt.credential.*

Action Entity Home Stability Fires when
stt.credential.cleared config control-plane stable The speech-to-text API key is removed.
stt.credential.set config control-plane stable The speech-to-text API key behind the Dictate button is configured.

Payload keys, on every action above: scope

  • stt.credential.set — The key itself never reaches the payload. The row is filed against entity id stt.groq_api_key, so what changed is in the entity rather than the payload.

user.*

Action Entity Home Stability Fires when
user.claude_home_swept user control-plane stable A hub member, as it starts and then hourly, removes from its own tree the Claude Code home of an identity the control plane denies hub-wide — a copy an offboarding did not reach.
user.hold_place user control-plane stable A legal hold is placed on an identity, or its reason restated (cloop hub user hold), or an offboarding run with --keep-credentials records the hold it was given.
user.hold_release user control-plane stable A legal hold is released.
user.offboard user control-plane stable An identity is offboarded, summarising every surface the operation touched.
user.offboard_claude user control-plane stable Offboarding ends the identity's Claude Code logins on every hub member: cancels one in flight, logs each home out and removes it.
user.offboard_deny user control-plane stable Offboarding writes deny bindings so a stale IdP mapping cannot re-admit the identity.
user.offboard_glasses user control-plane stable Offboarding revokes the identity's display-glasses links.
user.offboard_grant user control-plane stable Offboarding revokes every live grant over the identity's personal secrets, under a legal hold too.
user.offboard_hold user control-plane stable An offboarding runs under a legal hold (--keep-credentials): access is severed and the stored credentials are kept.
user.offboard_lease user control-plane stable Offboarding releases secret leases held on the identity's behalf.
user.offboard_membership user control-plane stable Offboarding drops the identity's project memberships, and the teams it was named on.
user.offboard_project user control-plane stable Offboarding reports projects that need a new owner; it does not reassign them.
user.offboard_request user control-plane stable Offboarding withdraws the self-service grant requests the identity left pending.
user.offboard_secret user control-plane stable Offboarding destroys the identity's personal secrets through the broker.
user.offboard_session user control-plane stable Offboarding revokes the identity's live sessions.
user.offboard_task user control-plane stable Offboarding stops tasks the identity had running.
user.offboard_token user control-plane stable Offboarding revokes the identity's API tokens.

Payload keys:

  • user.claude_home_swept — identity, member, host, dir, denies, had_login, login_cancelled, logged_out, logout, removed, error, trigger
  • user.hold_place — identity, hold_reason, placed_at, reason, via, os_user, identity_input, subjects, emails
  • user.hold_release — identity, hold_reason, placed_at, placed_by, reason, via, os_user
  • user.offboard — sessions, tokens, glasses, denies, leases, tasks, projects, memberships, teams, personal_secrets, personal_grants, grant_requests, claude_homes, legal_hold, warnings, identity, identity_input, subjects, emails, reason, via
  • user.offboard_claude — copies, removed, logged_out, logins_cancelled, unreached, kept, identity, identity_input, subjects, emails, reason, via
  • user.offboard_deny — count, bindings, claims, identity, identity_input, subjects, emails, reason, via
  • user.offboard_glasses — count, links, identity, identity_input, subjects, emails, reason, via
  • user.offboard_grant — count, grants, identity, identity_input, subjects, emails, reason, via
  • user.offboard_hold — secrets, claude_homes, grants_revoked, requests_withdrawn, identity, identity_input, subjects, emails, reason, via
  • user.offboard_lease — count, leases, identity, identity_input, subjects, emails, reason, via
  • user.offboard_membership — count, projects, teams, identity, identity_input, subjects, emails, reason, via
  • user.offboard_project — count, projects, action, identity, identity_input, subjects, emails, reason, via
  • user.offboard_request — count, requests, identity, identity_input, subjects, emails, reason, via
  • user.offboard_secret — count, secrets, action, identity, identity_input, subjects, emails, reason, via
  • user.offboard_session — count, sessions, identity, identity_input, subjects, emails, reason, via
  • user.offboard_task — count, tasks, identity, identity_input, subjects, emails, reason, via
  • user.offboard_token — count, tokens, identity, identity_input, subjects, emails, reason, via

  • user.claude_home_swept — One row per home, trigger startup or timer. logout says why it was not logged out at Anthropic first — strict no-host-execution, a symbolic link — and error why it could not be removed, which the next sweep tries again. An identity under a legal hold, or denied only by a SCIM suspension still in force, keeps its home; cloop hub doctor reports the homes left on its host (offboard.claude_homes).

  • user.hold_place — Every offboarding of the identity from then on — an operator's, the identity provider deleting the account, or ui.oidc.scim.offboard_after_days — keeps its personal secrets and Claude Code homes, as --keep-credentials does, and writes user.offboard_hold; so does every hub member's sweep of denied identities' Claude homes (Task 20422). An offboarding asked to keep credentials records its hold here before it denies anything, carrying the run's reason as hold_reason and the identity's resolved spellings.
  • user.offboard_claude — copies names each home by member and directory with what became of it. unreached lists hub members that could not be asked — a copy there may still hold a refresh token.
  • user.offboard_grant — Each grant also has its own secret.revoke row from the broker, naming the reason. grants names each one with its secret and subject, which is where a shared project that loses the credential shows up.
  • user.offboard_hold — Written whether or not anything was found to keep, so whether a hold was in force does not depend on what the person had stored.
  • user.offboard_project — Deliberately a report rather than a mutation — picking a project's next owner is not a decision an offboarding script should make.
  • user.offboard_request — Each request also has a secret.request_withdraw row whose reason says it was withdrawn at offboarding rather than by its requester.
  • user.offboard_secret — Each secret also has its own secret.delete row. The sealed columns are overwritten before the row is deleted, and a tombstone keeps the name so a later lease can say which credential a project lost. Not written under a legal hold — user.offboard_hold says what was kept instead.

scim.*

Action Entity Home Stability Fires when
scim.credential_create scim_credential control-plane stable A SCIM bearer credential is minted for an identity provider.
scim.credential_revoke scim_credential control-plane stable A SCIM credential is revoked, both its secrets at once.
scim.credential_rotate scim_credential control-plane stable A SCIM credential's secret is replaced.
scim.group_create scim_group control-plane stable The identity provider provisions a group.
scim.group_delete scim_group control-plane stable The identity provider deletes a group.
scim.group_members scim_group control-plane stable Members are added to or removed from a provisioned group.
scim.group_update scim_group control-plane stable A provisioned group is renamed or its attributes change.
scim.match_ambiguous scim_user control-plane stable A provisioned user's match value addresses more than one hub identity.
scim.request_refused scim control-plane stable A request to /scim/v2 is refused before it reaches the directory.
scim.user_create scim_user control-plane stable The identity provider provisions a user.
scim.user_delete scim_user control-plane stable The identity provider deletes a user; the full offboarding runs first.
scim.user_offboard_expired scim_user control-plane stable A user inactive for ui.oidc.scim.offboard_after_days is offboarded in full.
scim.user_reactivate scim_user control-plane stable The identity provider reactivates a user (active: true).
scim.user_suspend scim_user control-plane stable The identity provider deactivates a user (active: false).
scim.user_suspend_incomplete scim_user control-plane stable A lease or task of a suspended user could not be ended after the suspension committed.
scim.user_update scim_user control-plane stable The identity provider changes a provisioned user's attributes with PUT or PATCH.

Payload keys:

  • scim.credential_create — id, name, reason, via, os_user
  • scim.credential_revoke — id, name, reason, via, os_user
  • scim.credential_rotate — id, name, grace_seconds, previous_valid_until, reason, via, os_user
  • scim.group_create — id, display_name, external_id, members, unknown_members
  • scim.group_delete — id, display_name, external_id, members
  • scim.group_members — id, display_name, added, removed, unknown_members, method
  • scim.group_update — id, display_name, previous_display_name, external_id, method
  • scim.match_ambiguous — id, user_name, match_claim, match_value, subjects, emails, operation
  • scim.request_refused — reason, method, path, ip, user_agent, client, suppressed
  • scim.user_create — id, user_name, external_id, email, active, match_claim, match_value
  • scim.user_delete — id, user_name, identity, legal_hold, failures
  • scim.user_offboard_expired — id, user_name, identity, deactivated_at, legal_hold, failures
  • scim.user_reactivate — id, user_name, denies_lifted, denies_kept, grants_resumed, offboarded_at, note
  • scim.user_suspend — id, user_name, identity, subjects, emails, usernames, oids, ambiguous, denies_written, denies_in_force, sessions, api_tokens, glasses, grants_suspended, leases, tasks, warnings
  • scim.user_suspend_incomplete — id, user_name, failures
  • scim.user_update — id, user_name, external_id, email, active, method, changed, match_claim, match_value

  • scim.credential_create — The secret is shown once and stored only as an HMAC; no row carries it.

  • scim.group_members — A removal takes effect at the member's next authorization decision: provisioned groups are read at every decision, not at the next claim refresh.
  • scim.group_update — A rename changes what a scim_group role binding naming the old name grants.
  • scim.match_ambiguous — The hub does not choose among them: it acts on the matched claim alone — a deny binding on it, and what carries it — and grants the user's provisioned groups to nobody.
  • scim.request_refused — reason is unauthenticated, unknown, revoked, locked_out (a wrong credential past SCIM's own lockout), rate_limited, unavailable (the credential store could not be read) or sso_disabled. At most 30 rows a minute; suppressed counts the ones the limit did not write.
  • scim.user_create — A user provisioned inactive is suspended at once, and gets a scim.user_suspend row too.
  • scim.user_delete — The offboarding writes its own user.offboard* rows, with via scim.
  • scim.user_offboard_expired — The offboarding writes its own user.offboard* rows, with via scim-expiry.
  • scim.user_reactivate — Lifts only what the suspension wrote: a deny an administrator wrote for the same identity stays (denies_kept). Sessions, tokens, leases and tasks the suspension ended are not restored, and after an offboarding nothing is lifted.
  • scim.user_suspend — Written in the transaction that writes the deny bindings, deletes the sessions, revokes the API tokens and glasses links and suspends the grants over the person's personal secrets. Leases and tasks are listed as planned; they end after the commit, and a failure there is scim.user_suspend_incomplete. Memberships and stored credentials are kept.

project.*

Action Entity Home Stability Fires when
project.create project control-plane beta A project is created through the hub, or a creation is refused because of the directory it named.
project.delete project control-plane beta A project is removed from the hub, or the hub refuses to delete its directory.
project.firewall project control-plane stable A project's maintainer sets or clears its firewall rule set, or a tightened executor narrows it.

Payload keys:

  • project.create — outcome, path, requested, name, owner, hub_made, projects_root, executor_id, rule, reason
  • project.delete — outcome, path, name, owner, delete_root, root_deleted, hub_made, reason
  • project.firewall — action, project_path, executor_id, from, to, fingerprint, cleared, reason

  • project.create — outcome is "created" or "refused". path is the canonical directory and requested what the caller sent. hub_made says this hub made the directory itself under projects_root — the only directories ?delete_root=true may remove. A refusal carries the rule it broke (pkg/projectconfine: control_plane, hub, project, projects_root, outside_projects_root, relocated, unsafe, unverifiable, unresolvable, or taken for a path another creation holds) and the full reason, which the caller may have been answered more vaguely than this: what exists at a path they cannot see is not theirs to learn.

  • project.delete — outcome is "removed" or "refused". delete_root is what the caller asked for and root_deleted what happened; a refused one names its reason — a directory the hub did not make, or one that now overlaps something it must not touch — and the project stays registered.
  • project.firewall — action is "set", "clear" or "constrain". A constrain row is written by the save that tightened a device or a virtual executor, against the identity that saved it, and reason names the executor and what had to be taken away. Stored in the control plane, where the rule set is.

project.member.*

Action Entity Home Stability Fires when
project.member.change project_member control-plane beta A member's role on a project is changed.
project.member.grant project_member control-plane beta An identity is added to a project's members at a role.
project.member.leave project_member control-plane beta A member removes themselves from a project.
project.member.revoke project_member control-plane beta A member is removed from a project, or the project itself is removed from the hub.

Payload keys, on every action above: project, project_path, identity, team, role, previous_role, reason, via, left

  • project.member.change — previous_role is the role replaced; a change in either direction is recorded.
  • project.member.grant — via is "api" or "cli". A membership only adds access: the hub unions its role with whatever the identity already holds.
  • project.member.leave — Separate from project.member.revoke so "was this person removed or did they leave" stays answerable.
  • project.member.revoke — role is the role the member held. A project removed from the hub drops its whole roster, one row per member, with reason saying so.

team.*

Action Entity Home Stability Fires when
team.create team control-plane beta An administrator creates a team.
team.delete team control-plane beta An administrator deletes a team.
team.member.add team control-plane beta An administrator adds a person to a team by name.
team.member.remove team control-plane beta An administrator takes a person off a team.
team.update team control-plane beta An administrator renames a team, or changes the groups it follows.

Payload keys, on every action above: team, name, idp_group, scim_group, previous, identity, members, shares, via, reason, os_user

  • team.create — via is "api" or "cli". idp_group and scim_group name the groups whose members are on the team without being added by name.
  • team.delete — members lists the people it named and shares counts the projects it was shared with. Each of those shares is removed in the same commit and recorded as its own project.member.revoke, with reason saying the team was deleted.
  • team.member.remove — Removes the person's named membership only: somebody on the team through its identity-provider or SCIM group stays on it until the group changes.
  • team.update — previous holds the name and links replaced. Relinking a team changes who is on it, and so who reaches every project shared with it.

audit.*

Action Entity Home Stability Fires when
audit.gap audit either stable A process could not append audit events to this chain, and records what was lost: with the next append that succeeds, at a periodic flush, at shutdown, or when a later process adopts the losses a crashed one left behind.
audit.restored audit either beta cloop db restore puts a backup in place of a database, recorded in the chain it restored as that chain's next row.

Payload keys:

  • audit.gap — lost_events, failed_appends, first_failed_at, last_failed_at, first_error, last_error, actions, reasons, recorded, host, pids, adopted_from
  • audit.restored — backup, backup_sha256, backup_time, sidecar, restored_id, restored_row_hash, restored_time, superseded_id, superseded_row_hash, superseded_time, lost_events, reason, accepted_loss, lost_keys, key_fingerprint, checkpoint_record

  • audit.gap — Home is whichever chain lost the events: a failed write to the hub's control-plane database is recorded there, a failed write to a project's database in that project's chain — the gap has to sit in the chain it is a gap in. actions counts the lost events by action name and reasons by cause (locked, readonly, full, io, closed, other), each a list of {name, events} so redaction cannot withhold a count. recorded says what wrote the row (recovery, flush, shutdown, exit) and adopted_from names processes that exited owing it. cloop hub audit verify reports a chain holding gap rows as intact with gaps and exits 3.

  • audit.restored — Home is whichever chain was restored: the control plane's, or a project's restored with the same command — the record has to sit in the history it interrupts. The row takes the id after the restored head, an id the superseded checkpoints saw with another hash; the restore record cloop db restore seals to the checkpoint sinks pins it, and is what lets cloop hub audit verify --checkpoints report the restore as acknowledged rather than as a truncation. accepted_loss is true when the restore went ahead under --accept-secret-loss, and lost_keys lists the sealing keys whose material it gave up, each with the rows sealed under it.

audit.forward.*

Action Entity Home Stability Fires when
audit.forward.configured config control-plane beta The cluster leader starts forwarding under an audit.forward configuration that differs from the one it last recorded: a sink added, removed, or changed in where or how it sends.
audit.forward.discontinuity audit control-plane beta A sink's cursor no longer lines up with the chain it tails — its row was pruned past, truncated away or rewritten — and the forwarder resumes from the last point that does.

Payload keys:

  • audit.forward.configured — added, removed, changed, sinks, refused, previous_fingerprint, fingerprint, file
  • audit.forward.discontinuity — sink, source, dir, reason, cursor_id, cursor_row_hash, resumed_from_id, resume_prev_hash, lost_rows

  • audit.forward.configured — Detected rather than reported by an editor: the leader compares a fingerprint of the sinks it runs under — config.yaml merged with the hub's per-port overlay — against the one stored in the control plane, so an edit made with any tool, while the hub was down or up, is recorded by the next leader to run under it. sinks describes every sink in force (name, type, format, endpoint, the secret-store references it reads — names, never values — and whether it sends plaintext to loopback); added, removed and changed are sink names; refused names sinks the loader switched off. The row is forwarded like any other, so the sinks that remain see what changed about the ones that left.

  • audit.forward.discontinuity — reason is pruned (rows were pruned before the sink acknowledged them, lost_rows of them — a prune forced past the sink's cursor; they are in the prune's sealed archive, not at the sink), truncated (the chain now ends before the cursor), rewritten (the cursor's row has another hash), or broken (the cursor's row is intact and what follows it does not continue it: lost_rows rows are missing inside the chain, or a row was relinked; the rows are forwarded as they stand). For truncated and rewritten the forwarder resends from the newest point it acknowledged that the chain still holds, so the sink receives the chain as it now stands beside what it already has: two rows with one id and different hashes are the evidence, and cloop hub audit verify --checkpoints names the truncation. Recorded in the control plane's chain whichever chain broke, because the cursor is the control plane's.