auditaction

package
v0.0.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 16, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package auditaction is the registry of every action name the hub can write into the `event_type` column of `audit_events`.

Before this package, every one of those names was an inline string literal at its emission site, spread across pkg/statedb, pkg/secretbroker, pkg/gitproxy, pkg/kubeguard, pkg/claudeproxy, pkg/oidcauth, pkg/offboard and pkg/ui. Two things followed from that, and both of them were real:

  • A typo produced an action name that no consumer would ever match. There was nothing to compare a literal against, so `gitproxy.push_deneid` would have appended rows forever, passed chain verification, exported cleanly to a SIEM, and matched no detection rule. The failure is silent in the one direction that matters — a control that looks like it is working.

  • Nothing told a reader which names exist. An operator wiring the SIEM export into a detection rule had to read emission code in eight packages to learn what they could key on, and docs/security/model.md cited `sandbox.image_denied` and `gitproxy.push_denied` as evidence that a control works with nothing guaranteeing those strings were still in the code it described.

So the names live here, once, each with the trigger that fires it, the entity it is recorded against, the payload keys it carries, a stability marker, and the permission that gates reading it. docs/reference/audit-events.md is generated from this registry, and three gates keep the arrangement from rotting:

  • tests/arch/auditaction_test.go fails when an emission site names an action this registry does not know, and when a family's source enum grows a member with no registry entry.
  • tests/docs/audit_events_test.go fails when the generated page drifts from the registry, and when a prose page cites an action name that no longer exists.
  • The registry's own tests below fail on a duplicate, a malformed name, or an entry missing the fields the page renders.

What is not enforced

The registry does not filter. An emitter that somehow produces an unregistered name still writes its row: dropping an audit record to punish a bookkeeping mistake would turn a documentation defect into an evidence one. Enforcement happens at CI time, where a missing entry is a failed build rather than a missing row.

The reverse direction is deliberately not gated either. A registry entry whose emission site is unreachable on a given build — a family member that only fires on an offline-agent path, say — is not an error. Requiring every entry to be provably emitted would mean deleting the documentation for the rarest events, which are the ones most in need of it.

Index

Constants

View Source
const (
	// FamilyExecutor is the fleet-lifecycle family: enrol, revoke, cordon,
	// and the rest of the operations an operator performs on an executor.
	FamilyExecutor = "executor"
	// FamilyWorkspace is the source-provisioning family.
	FamilyWorkspace = "workspace"
	// FamilyGitProxy is the git interception proxy's family.
	FamilyGitProxy = "gitproxy"
	// FamilyKubeGuard is the Kubernetes interception proxy's family.
	FamilyKubeGuard = "kubeguard"
	// FamilyAuthz is the authorization-decision family.
	FamilyAuthz = "authz"
)

Variables

This section is empty.

Functions

func Families

func Families() []string

Families returns every family name that has at least one registered action, in the order the generated page presents them — which is the order the registry declares, not alphabetical, so related families stay adjacent.

func Markdown

func Markdown(intro string) string

Markdown renders the whole registry as docs/reference/audit-events.md.

The page is generated rather than written for the same reason docs/reference/http-api.md is: a hand-maintained list of 100-odd strings diverges from the code within one release, and the divergence is invisible — a stale page still reads as authoritative. tests/docs/audit_events_test.go compares this output against the checked-in file and fails when they differ.

intro is the hand-written lead. It is passed in rather than embedded here so the prose that explains the trail lives next to the test that owns the page, where a writer will find it, rather than inside a package whose job is the data.

func Registered

func Registered(a Action) bool

Registered reports whether a is a known action.

Types

type Action

type Action string

Action is one value of the `event_type` column in `audit_events`.

Values are stable wire strings. They appear in exported SIEM records, in customer detection rules, and in rows already sealed into a hash chain that cannot be rewritten. Never rename one: add a new constant, mark the old one StabilityDeprecated, and let both exist until no stored row carries the old name — which, for an append-only table with a retention policy, means until retention has passed over it.

const (

	// ActionTaskUpsert records a task whose audited fields changed.
	ActionTaskUpsert Action = "task.upsert"
	// ActionTaskDelete records a task leaving the plan.
	ActionTaskDelete Action = "task.delete"
	// ActionTaskStatus records a hand-made status change.
	ActionTaskStatus Action = "task.status"
	// ActionTaskDispatch records a task being handed to an executor.
	ActionTaskDispatch Action = "task.dispatch"
	// ActionTaskFinish records a dispatched task reaching a terminal outcome.
	ActionTaskFinish Action = "task.finish"
	// ActionTaskQuarantine records a task marked as a suspected node killer
	// (Task 20391).
	ActionTaskQuarantine Action = "task.quarantine"
	// ActionTaskQuarantineRelease records an explicit reset releasing such a
	// task.
	ActionTaskQuarantineRelease Action = "task.quarantine_release"
	// ActionTaskAttachmentDelete records a person deleting a file attached to
	// a task (Task 20428).
	ActionTaskAttachmentDelete Action = "task.attachment_delete"

	// ActionRunCapPaused records a run parked by a Claude Code subscription
	// cap, together with when the window reopens.
	ActionRunCapPaused Action = "run.cap_paused"
	// ActionRunCapResumed records the hub restarting such a run once the
	// window rolled over, with no human involved.
	ActionRunCapResumed Action = "run.cap_resumed"
	// ActionRunAdoptRequested records somebody asking a running project's run
	// to adopt the hub's build at its next task boundary (Task 20389).
	ActionRunAdoptRequested Action = "run.adopt_requested"
	// ActionRunReexecuted records a run replacing its own image with a newer
	// build at a task boundary, keeping its process (Task 20389).
	ActionRunReexecuted Action = "run.reexecuted"

	// ActionFeatureCreate records a feature worktree being created.
	ActionFeatureCreate Action = "feature.create"
	// ActionFeatureRemove records a feature worktree being removed.
	ActionFeatureRemove Action = "feature.remove"
	// ActionFeaturePROpen records a feature's pull request being opened or
	// updated.
	ActionFeaturePROpen Action = "feature.pr_open"

	// ActionStepAppend records one execution step's transcript.
	ActionStepAppend Action = "step.append"

	// ActionStateSave records a plan-level state write.
	ActionStateSave Action = "state.save"
	// ActionConfigSet records a configuration write.
	ActionConfigSet Action = "config.set"

	// ActionExecutorEnroll records an agent joining the fleet.
	ActionExecutorEnroll Action = "executor.enroll"
	// ActionExecutorRevoke records an agent's credential being revoked.
	ActionExecutorRevoke Action = "executor.revoke"
	// ActionExecutorCordon records an executor being closed to new work.
	ActionExecutorCordon Action = "executor.cordon"
	// ActionExecutorUncordon records a cordoned executor returning to service.
	ActionExecutorUncordon Action = "executor.uncordon"
	// ActionExecutorDrain records an executor being told to shed work.
	ActionExecutorDrain Action = "executor.drain"
	// ActionExecutorBind records a project being pinned to one executor.
	ActionExecutorBind Action = "executor.bind"
	// ActionExecutorUnbind records a project's executor pin being cleared.
	ActionExecutorUnbind Action = "executor.unbind"
	// ActionExecutorSandbox records an executor's sandbox configuration being
	// set: whether its payloads run on the device's host or in a container on
	// it, and under which engine, runtime and image (Task 20307).
	//
	// Auditable because it is a change to a containment boundary, which makes it
	// the one executor setting whose *previous* value a reviewer needs. The
	// payload carries both, so "when did this device stop isolating its
	// workloads, and who decided that" is answerable from the trail alone.
	ActionExecutorSandbox Action = "executor.sandbox"
	// ActionExecutorLimits records an executor's resource ceiling being set:
	// the most CPU, memory, disk and processes any one workload on that device
	// may be given, whatever it asks for (Task 20310).
	//
	// Auditable for the reason the sandbox action is: it bounds what code
	// running on that machine can consume, so raising it is a change an
	// incident reviewer needs attributed. The payload carries the previous
	// ceiling as well as the new one, because "who raised the memory cap on
	// the build fleet the day it fell over" is not answerable from the new
	// value alone.
	ActionExecutorLimits Action = "executor.limits"
	// ActionExecutorAudience records a change to who may run work on one
	// executor (Task 20310).
	//
	// The one executor action that is an access-control decision rather than a
	// configuration change. It is emitted for the add and the removal alike,
	// and carries whether the list became restricted or unrestricted as a
	// result — an executor's last audience entry being withdrawn widens access
	// to the whole fleet, which reads as a small edit and is not one.
	ActionExecutorAudience Action = "executor.audience"
	// ActionExecutorVirtual records a virtual executor being created, changed
	// or deleted (Task 20345): a sub-executor of a device with its own engine,
	// runtime, IP firewall and host devices.
	//
	// One action for the three verbs, carried in the payload, because a
	// reviewer's question is the same for all of them — "who changed what this
	// sandbox can reach, and which hardware it holds" — and the payload keeps
	// the configuration before and after, so a widened firewall or an added
	// device is legible from the trail alone.
	ActionExecutorVirtual Action = "executor.virtual"
	// ActionExecutorFirewall records a device's firewall rule set being set or
	// cleared (Task 20363): the admin's superset of what any sandbox on that
	// device — on the device itself or on any of its virtual executors — may
	// reach.
	//
	// Auditable for the reason the sandbox action is, and one more: tightening
	// a device rewrites the rule sets saved underneath it, and each of those
	// rewrites is recorded too (executor.virtual with action "constrain",
	// project.firewall with action "constrain"), so this row carries how many
	// there were and a reviewer can find all of them from it.
	ActionExecutorFirewall Action = "executor.firewall"
	// ActionExecutorUpgrade records the control plane asking a device to
	// replace its own binary and restart (Task 20331).
	//
	// Its own name rather than a flavour of state_change, because of the three
	// things this vocabulary says about an executor — a configuration changed,
	// a health state moved, a binary was replaced — only this one is remote
	// code execution. An auditor building a detection rule for "who can make
	// our edge fleet run new code" needs a string to key on, and a name they
	// have to find inside another event's payload is one they will miss.
	ActionExecutorUpgrade Action = "executor.upgrade"
	// ActionExecutorAutoUpdate records a change to the fleet's standing
	// auto-update policy: whether the hub rolls devices forward unattended,
	// onto which release, and how many at a time.
	//
	// Separate from ActionExecutorUpgrade because enabling the policy is not an
	// upgrade — it is a grant of the authority to perform them indefinitely
	// without another human decision. That is the rarer event, and the more
	// interesting one to review.
	ActionExecutorAutoUpdate Action = "executor.autoupdate"
	// ActionExecutorStateChange records a health-state transition nobody asked for.
	ActionExecutorStateChange Action = "executor.state_change"
	// ActionExecutorFailover records a session moving off a failed executor.
	ActionExecutorFailover Action = "executor.failover"
	// ActionExecutorFailoverExhausted records a session lost after it had
	// already been re-dispatched as often as executors.failover.max_attempts
	// allows, so nothing re-dispatches it (Task 20391).
	ActionExecutorFailoverExhausted Action = "executor.failover_exhausted"

	// ActionForwarderEnroll records a device forwarder's enrollment token being
	// minted (Task 20429). A forwarder is a minimal on-prem binary that bridges
	// one USB or layer-2 device to the hub and runs no workload; it is not an
	// executor, which is why it has its own action family.
	ActionForwarderEnroll Action = "forwarder.enroll"
	// ActionForwarderRevoke records a forwarder's enrollment token or
	// credential being revoked.
	ActionForwarderRevoke Action = "forwarder.revoke"
	// ActionForwarderExpose records an operator probing a forwarded device
	// through the hub (Task 20430): a stream opened to the forwarder and, for
	// USB, a device list read through it — proof the device is reachable,
	// which hands it to nothing. (Before Task 20430 it opened a loopback
	// import endpoint, which is gone.)
	ActionForwarderExpose Action = "forwarder.expose"
	// ActionForwarderBind records a binding that makes a forwarder's device
	// available to a project's runs, optionally only on one virtual executor
	// (Task 20430) — minted by an operator or by approving a request.
	ActionForwarderBind Action = "forwarder.bind"
	// ActionForwarderUnbind records a binding revoked: by an operator, by the
	// forwarder's own revocation, or by its TTL lapsing. A run holding the
	// device has it detached.
	ActionForwarderUnbind Action = "forwarder.unbind"
	// ActionForwarderBindRequest records a request for a binding, filed
	// through the same request flow as secret grant requests.
	ActionForwarderBindRequest Action = "forwarder.bind_request"
	// ActionForwarderBindRequestApprove, -Deny, -Withdraw and -Expire record
	// a binding request's decision.
	ActionForwarderBindRequestApprove  Action = "forwarder.bind_request_approve"
	ActionForwarderBindRequestDeny     Action = "forwarder.bind_request_deny"
	ActionForwarderBindRequestWithdraw Action = "forwarder.bind_request_withdraw"
	ActionForwarderBindRequestExpire   Action = "forwarder.bind_request_expire"
	// ActionForwarderAttach records a forwarded device attached for a run:
	// the hub relaying the forwarder's stream to the executor that gives it
	// to the run's sandbox.
	ActionForwarderAttach Action = "forwarder.attach"
	// ActionForwarderDetach records a forwarded device taken back from a run,
	// and why: the run ended, the binding or forwarder was revoked, a side
	// disconnected.
	ActionForwarderDetach Action = "forwarder.detach"

	// ActionWorkspaceProvisionStart records the beginning of source provisioning.
	ActionWorkspaceProvisionStart Action = "workspace.provision_start"
	// ActionWorkspaceProvisionEnd records provisioning finishing, with or without an error.
	ActionWorkspaceProvisionEnd Action = "workspace.provision_end"

	// ActionSandboxImageDenied records an image refused by trust policy.
	ActionSandboxImageDenied Action = "sandbox.image_denied"

	// ActionSandboxAttachOpen records a shell being opened inside a running sandbox.
	ActionSandboxAttachOpen Action = "sandbox.attach.open"
	// ActionSandboxAttachClose records an attached session ending.
	ActionSandboxAttachClose Action = "sandbox.attach.close"
	// ActionSandboxAttachDenied records an attach request being refused.
	ActionSandboxAttachDenied Action = "sandbox.attach.denied"

	// ActionSecretMint records a credential being sealed into a new secret.
	ActionSecretMint Action = "secret.mint"
	// ActionSecretDelete records a secret being destroyed.
	ActionSecretDelete Action = "secret.delete"
	// ActionSecretGrant records authority to lease a secret being written.
	ActionSecretGrant Action = "secret.grant"
	// ActionSecretRevoke records a grant being marked unusable.
	ActionSecretRevoke Action = "secret.revoke"
	// ActionSecretLease records a lease being issued or refused.
	ActionSecretLease Action = "secret.lease"
	// ActionSecretRenew records a live lease being re-issued.
	ActionSecretRenew Action = "secret.renew"
	// ActionSecretRelease records a lease ending in the ordinary way.
	ActionSecretRelease Action = "secret.release"
	// ActionSecretAccessCheck records an access decision against a grant's constraints.
	ActionSecretAccessCheck Action = "secret.access_check"
	// ActionSecretRequest records a self-service access request being filed.
	ActionSecretRequest Action = "secret.request"
	// ActionSecretRequestApprove records a request being approved.
	ActionSecretRequestApprove Action = "secret.request_approve"
	// ActionSecretRequestDeny records a request being refused.
	ActionSecretRequestDeny Action = "secret.request_deny"
	// ActionSecretRequestWithdraw records a requester withdrawing their request.
	ActionSecretRequestWithdraw Action = "secret.request_withdraw"
	// ActionSecretRequestExpire records a request lapsing undecided.
	ActionSecretRequestExpire Action = "secret.request_expire"
	// ActionSecretLeaseSweep records one run of the expired-lease sweeper.
	ActionSecretLeaseSweep Action = "secret.lease.sweep"
	// ActionSecretUnguardedAck records an administrator acknowledging that a
	// kubeconfig grant may be delivered without the Kubernetes access monitor.
	ActionSecretUnguardedAck Action = "secret.unguarded_ack"
	// ActionSecretUnguardedWithdraw records that acknowledgement being taken back.
	ActionSecretUnguardedWithdraw Action = "secret.unguarded_withdraw"

	// ActionLeaseRevokeSent records a revocation being queued for delivery.
	ActionLeaseRevokeSent Action = "lease.revoke_sent"
	// ActionLeaseRevokeAcked records an executor confirming it destroyed the material.
	ActionLeaseRevokeAcked Action = "lease.revoke_acked"
	// ActionLeaseRevokeFailed records a revocation that did not land.
	ActionLeaseRevokeFailed Action = "lease.revoke_failed"
	// ActionLeaseRefresh records a refreshed GitHub App token file reaching —
	// or failing to reach — an executor holding the lease (Task 20375).
	ActionLeaseRefresh Action = "lease.refresh"

	// ActionGitHubAppTokenDestroy records a GitHub App installation token deleted at GitHub.
	ActionGitHubAppTokenDestroy Action = "github_app.token_destroy"

	// ActionEgressGrant records authority to reach the network being written.
	ActionEgressGrant Action = "egress.grant"
	// ActionEgressRevoke records an egress authorisation being withdrawn.
	ActionEgressRevoke Action = "egress.revoke"
	// ActionEgressRedeem records a proxy session being minted against a grant.
	ActionEgressRedeem Action = "egress.redeem"
	// ActionEgressConnect records a connection attempt being evaluated.
	ActionEgressConnect Action = "egress.connect"
	// ActionEgressRequest records an HTTP request passing through the proxy.
	ActionEgressRequest Action = "egress.request"
	// ActionEgressClose records a proxy session closing with its byte counts.
	ActionEgressClose Action = "egress.close"
	// ActionEgressRenew records a live proxy session being renewed for its run.
	ActionEgressRenew Action = "egress.renew"
	// ActionEgressRestore records a proxy session restored with its adopted run.
	ActionEgressRestore Action = "egress.restore"

	// ActionGitProxySessionMinted records a git proxy session being created.
	ActionGitProxySessionMinted Action = "gitproxy.session_minted"
	// ActionGitProxySessionClosed records a git proxy session ending.
	ActionGitProxySessionClosed Action = "gitproxy.session_closed"
	// ActionGitProxySessionRestored records a git proxy session restored with its adopted run.
	ActionGitProxySessionRestored Action = "gitproxy.session_restored"
	// ActionGitProxySessionRenewed records a workspace's git proxy session
	// renewed while its run lives (Task 20422).
	ActionGitProxySessionRenewed Action = "gitproxy.session_renewed"
	// ActionGitProxyPushAllowed records a push admitted by branch policy.
	ActionGitProxyPushAllowed Action = "gitproxy.push_allowed"
	// ActionGitProxyPushDenied records a push refused by branch policy.
	ActionGitProxyPushDenied Action = "gitproxy.push_denied"
	// ActionGitProxyFetch records a read passing through the proxy.
	ActionGitProxyFetch Action = "gitproxy.fetch"
	// ActionGitProxyRejected records a request refused before policy evaluation.
	ActionGitProxyRejected Action = "gitproxy.rejected"

	// ActionKubeGuardSessionMinted records a Kubernetes proxy session being created.
	ActionKubeGuardSessionMinted Action = "kubeguard.session_minted"
	// ActionKubeGuardSessionClosed records a Kubernetes proxy session ending.
	ActionKubeGuardSessionClosed Action = "kubeguard.session_closed"
	// ActionKubeGuardSessionRestored records a Kubernetes proxy session restored with its adopted run.
	ActionKubeGuardSessionRestored Action = "kubeguard.session_restored"
	// ActionKubeGuardRequestDenied records a Kubernetes request refused by policy.
	ActionKubeGuardRequestDenied Action = "kubeguard.request_denied"
	// ActionKubeGuardRequestAllowed records a Kubernetes request admitted by policy.
	ActionKubeGuardRequestAllowed Action = "kubeguard.request_allowed"
	// ActionKubeGuardRejected records a request refused before its session was identified.
	ActionKubeGuardRejected Action = "kubeguard.rejected"

	// ActionCIRejected records a relay request refused before a session existed.
	ActionCIRejected Action = "ci.rejected"
	// ActionCISessionMinted records a relay session being issued to a pipeline.
	ActionCISessionMinted Action = "ci.session.minted"
	// ActionCISessionClosed records a relay session ending.
	ActionCISessionClosed Action = "ci.session.closed"
	// ActionCISessionRevoked records an operator revoking a live relay session.
	ActionCISessionRevoked Action = "ci.session.revoked"
	// ActionCISessionSuspended records a hub process that stops serving a recorded relay session without ending it there: it is stopping gracefully, or another process closed or took over the record.
	ActionCISessionSuspended Action = "ci.session.suspended"
	// ActionCISessionRestored records a recorded relay session restored by the hub process that received its next call.
	ActionCISessionRestored Action = "ci.session.restored"
	// ActionCIExchangeAccepted records a pipeline's OIDC token being exchanged.
	ActionCIExchangeAccepted Action = "ci.exchange.accepted"
	// ActionCIExchangeRejected records a token exchange being refused.
	ActionCIExchangeRejected Action = "ci.exchange.rejected"
	// ActionCIRelayAllowed records a relayed API call being forwarded.
	ActionCIRelayAllowed Action = "ci.relay.allowed"
	// ActionCIRelayDenied records a relayed API call being refused.
	ActionCIRelayDenied Action = "ci.relay.denied"
	// ActionCIRuleCreated records an allowlist rule being added.
	ActionCIRuleCreated Action = "ci.rule.created"
	// ActionCIRuleUpdated records an allowlist rule being edited.
	ActionCIRuleUpdated Action = "ci.rule.updated"
	// ActionCIRuleDeleted records an allowlist rule being removed.
	ActionCIRuleDeleted Action = "ci.rule.deleted"
	// ActionCIConfigUpdated records the relay's own settings changing.
	ActionCIConfigUpdated Action = "ci.config.updated"

	// ActionAuthzGranted records a privileged permission being exercised.
	ActionAuthzGranted Action = "authz.granted"
	// ActionAuthzDenied records a permission check refusing a caller.
	ActionAuthzDenied Action = "authz.denied"

	// ActionAPITokenCreated records a token or glasses link being minted.
	ActionAPITokenCreated Action = "api_token.created"
	// ActionAPITokenRevoked records a token or glasses link being revoked.
	ActionAPITokenRevoked Action = "api_token.revoked"
	// ActionAPITokenAuthFailed records a presented token failing to authenticate.
	ActionAPITokenAuthFailed Action = "api_token.auth_failed"
	// ActionAPITokenCreateDenied records a mint refused by the anti-escalation check.
	ActionAPITokenCreateDenied Action = "api_token.create_denied"
	// ActionAPITokenApproved records an administrator approving a token minted
	// with no role policy in force (Task 20422).
	ActionAPITokenApproved Action = "api_token.approved"
	// ActionAPITokenApproveDenied records such an approval refused.
	ActionAPITokenApproveDenied Action = "api_token.approve_denied"

	// ActionStaticTokenRetired records the static admin token being retired:
	// every hub member refuses it from then on, without a restart (Task 20406).
	ActionStaticTokenRetired Action = "static_token.retired"

	// ActionSessionCreated records a sign-in completing.
	ActionSessionCreated Action = "session.created"
	// ActionSessionExpired records the janitor removing a deadline-expired session.
	ActionSessionExpired Action = "session.expired"
	// ActionSessionRevoked records a logout or an operator termination.
	ActionSessionRevoked Action = "session.revoked"
	// ActionSessionIdPRevoked records the IdP reporting the authorisation is gone.
	ActionSessionIdPRevoked Action = "session.idp_revoked"
	// ActionSessionRoleNarrowed records a re-assertion demoting a live session.
	ActionSessionRoleNarrowed Action = "session.role_narrowed"
	// ActionSessionClaimsUnverified records the hub failing to reach the IdP.
	ActionSessionClaimsUnverified Action = "session.claims_unverified"
	// ActionSessionClaimsRejected records the IdP refusing a re-assertion.
	ActionSessionClaimsRejected Action = "session.claims_rejected"
	// ActionSessionClaimsStale records a privileged operation blocked on claim age.
	ActionSessionClaimsStale Action = "session.claims_stale"
	// ActionSessionRenewalMismatch records a silent renewal answered for a
	// different subject than the session's, which leaves the session as it was.
	ActionSessionRenewalMismatch Action = "session.renewal_mismatch"

	// ActionDeviceCodeIssued records a terminal asking for a sign-in code
	// (`cloop login`, Task 20412).
	ActionDeviceCodeIssued Action = "device.code_issued"
	// ActionDeviceApproved records a signed-in browser approving a terminal's
	// code, and the CLI session it became.
	ActionDeviceApproved Action = "device.approved"
	// ActionDeviceDenied records a terminal's code denied — in the browser, or
	// by the hub on the approver's behalf.
	ActionDeviceDenied Action = "device.denied"

	// ActionRequestOriginRefused records a state-changing request or a
	// WebSocket handshake refused because a page on another origin made it
	// (Task 20394).
	ActionRequestOriginRefused Action = "request.origin_refused"
	// ActionRequestHostRefused records a hub without sign-in refusing a Host
	// it does not answer to — the shape DNS rebinding takes (Task 20394).
	ActionRequestHostRefused Action = "request.host_refused"

	// ActionRoleBindingGranted records a runtime binding granting a role.
	ActionRoleBindingGranted Action = "role_binding.granted"
	// ActionRoleBindingDenied records a runtime binding taking a role away.
	ActionRoleBindingDenied Action = "role_binding.denied"
	// ActionRoleBindingDeleted records a runtime binding being removed.
	ActionRoleBindingDeleted Action = "role_binding.deleted"

	// ActionQuotaDenied records admission control refusing an operation.
	ActionQuotaDenied Action = "quota.denied"
	// ActionQuotaOverrideSet records a per-identity quota override being written.
	ActionQuotaOverrideSet Action = "quota.override_set"
	// ActionQuotaOverrideCleared records an override being removed.
	ActionQuotaOverrideCleared Action = "quota.override_cleared"
	// ActionQuotaSpendRefused records a run stopped for exceeding a spend limit.
	ActionQuotaSpendRefused Action = "quota.spend_refused"

	// ActionResourceCeilingSet records a per-project resource ceiling being
	// written (Task 20301).
	//
	// Beside the quota overrides above, and for the same reason they are on the
	// trail: the authority to edit one is the authority to decide how much of
	// the machine a tenant gets. The fleet ceiling has no action of its own
	// because it is not editable at runtime — it comes from config.yaml, which
	// is reviewed and deployed rather than written through an API.
	ActionResourceCeilingSet Action = "resource_ceiling.set"
	// ActionResourceCeilingCleared records a per-project ceiling being removed,
	// after which the project is bounded by the fleet ceiling alone.
	ActionResourceCeilingCleared Action = "resource_ceiling.cleared"

	// ActionSealingKeyRotated records the secret store's sealing key being rotated.
	ActionSealingKeyRotated Action = "sealing_key.rotated"
	// ActionSealingKeyRetired records a superseded sealing key being retired.
	ActionSealingKeyRetired Action = "sealing_key.retired"
	// ActionSealingKeyEscrowed records CLOOP_SECRET_KEY being escrowed under
	// an operator's passphrase, or an escrow being recorded again after a
	// restore (Task 20410).
	ActionSealingKeyEscrowed Action = "sealing_key.escrowed"
	// ActionSealingKeyRootChanged records a change of CLOOP_SECRET_KEY itself
	// being finished: every sealed row moved onto a key of the new root, and
	// every key of the previous root retired (Task 20425).
	ActionSealingKeyRootChanged Action = "sealing_key.root_changed"

	// ActionHubCAMinted records the hub minting its own certificate authority
	// (tls: auto, Task 20409).
	ActionHubCAMinted Action = "hub_ca.minted"
	// ActionHubCALeafIssued records the hub issuing a credential monitor's
	// serving certificate from its CA.
	ActionHubCALeafIssued Action = "hub_ca.leaf_issued"

	// ActionOIDCConfigUpdated records the hub's single sign-on configuration
	// being changed.
	ActionOIDCConfigUpdated Action = "oidc.config.updated"
	// ActionOIDCRBACEnforced records an admin enforcing deny-by-default on a
	// hub whose single sign-on ran without a role policy.
	ActionOIDCRBACEnforced Action = "oidc.rbac.enforced"

	// ActionTelemetryConfigUpdated records front-end telemetry collection
	// being switched on or off, or narrowed to particular front ends.
	ActionTelemetryConfigUpdated Action = "telemetry.config.updated"

	// ActionNotificationsConfigUpdated records the webhooks approval notices
	// are posted to (Task 20427) being changed from Settings.
	ActionNotificationsConfigUpdated Action = "notifications.config.updated"

	// ActionDiskFloorUpdated records the hub's free-space floor
	// (orchestrator.min_free_disk_mb, Task 20381) being changed from Settings.
	ActionDiskFloorUpdated Action = "disk.floor.updated"

	// ActionSTTCredentialSet records the speech-to-text key being configured.
	ActionSTTCredentialSet Action = "stt.credential.set"
	// ActionSTTCredentialCleared records the speech-to-text key being removed.
	ActionSTTCredentialCleared Action = "stt.credential.cleared"

	// ActionUserOffboard is the offboarding summary row.
	ActionUserOffboard Action = "user.offboard"
	// ActionUserOffboardSession records sessions revoked during offboarding.
	ActionUserOffboardSession Action = "user.offboard_session"
	// ActionUserOffboardToken records API tokens revoked during offboarding.
	ActionUserOffboardToken Action = "user.offboard_token"
	// ActionUserOffboardGlasses records glasses links revoked during offboarding.
	ActionUserOffboardGlasses Action = "user.offboard_glasses"
	// ActionUserOffboardDeny records deny bindings written during offboarding.
	ActionUserOffboardDeny Action = "user.offboard_deny"
	// ActionUserOffboardMembership records project memberships dropped during offboarding.
	ActionUserOffboardMembership Action = "user.offboard_membership"
	// ActionUserOffboardLease records secret leases released during offboarding.
	ActionUserOffboardLease Action = "user.offboard_lease"
	// ActionUserOffboardTask records tasks stopped during offboarding.
	ActionUserOffboardTask Action = "user.offboard_task"
	// ActionUserOffboardProject records projects reported as needing a new owner.
	ActionUserOffboardProject Action = "user.offboard_project"
	// ActionUserOffboardGrant records grants over the identity's personal
	// secrets revoked during offboarding (Task 20400).
	ActionUserOffboardGrant Action = "user.offboard_grant"
	// ActionUserOffboardSecret records the identity's personal secrets
	// destroyed during offboarding.
	ActionUserOffboardSecret Action = "user.offboard_secret"
	// ActionUserOffboardRequest records the identity's pending grant requests
	// withdrawn during offboarding.
	ActionUserOffboardRequest Action = "user.offboard_request"
	// ActionUserOffboardClaude records the identity's Claude Code logins
	// cancelled, logged out and removed — or kept — on every hub member.
	ActionUserOffboardClaude Action = "user.offboard_claude"
	// ActionUserOffboardHold records an offboarding run under a legal hold,
	// and what it kept.
	ActionUserOffboardHold Action = "user.offboard_hold"
	// ActionUserClaudeHomeSwept records a hub member removing, from its own
	// tree, the Claude Code home of an identity the control plane denies —
	// a copy no offboarding reached (Task 20422).
	ActionUserClaudeHomeSwept Action = "user.claude_home_swept"
	// ActionUserHoldPlace records a legal hold placed on an identity, which
	// every later offboarding of it honours (Task 20415).
	ActionUserHoldPlace Action = "user.hold_place"
	// ActionUserHoldRelease records a legal hold released.
	ActionUserHoldRelease Action = "user.hold_release"

	// ActionSCIMCredentialCreate records a SCIM bearer credential minted for
	// an identity provider (Task 20415).
	ActionSCIMCredentialCreate Action = "scim.credential_create"
	// ActionSCIMCredentialRotate records a SCIM credential's secret replaced.
	ActionSCIMCredentialRotate Action = "scim.credential_rotate"
	// ActionSCIMCredentialRevoke records a SCIM credential revoked.
	ActionSCIMCredentialRevoke Action = "scim.credential_revoke"
	// ActionSCIMRequestRefused records a request to /scim/v2 refused before it
	// reached the directory: no credential, a wrong or revoked one, the rate
	// limit, or a hub without single sign-on.
	ActionSCIMRequestRefused Action = "scim.request_refused"
	// ActionSCIMUserCreate records a user provisioned.
	ActionSCIMUserCreate Action = "scim.user_create"
	// ActionSCIMUserUpdate records a provisioned user's attributes changed.
	ActionSCIMUserUpdate Action = "scim.user_update"
	// ActionSCIMUserSuspend records a user deactivated at the identity
	// provider, and everything the hub severed in the same transaction.
	ActionSCIMUserSuspend Action = "scim.user_suspend"
	// ActionSCIMUserSuspendIncomplete records a part of a suspension that
	// could not join its transaction and failed: a lease, a task.
	ActionSCIMUserSuspendIncomplete Action = "scim.user_suspend_incomplete"
	// ActionSCIMUserReactivate records a user reactivated at the identity
	// provider, and what the lift restored.
	ActionSCIMUserReactivate Action = "scim.user_reactivate"
	// ActionSCIMUserDelete records a user deleted at the identity provider.
	ActionSCIMUserDelete Action = "scim.user_delete"
	// ActionSCIMUserOffboardExpired records the full offboarding of a user
	// inactive for ui.oidc.scim.offboard_after_days.
	ActionSCIMUserOffboardExpired Action = "scim.user_offboard_expired"
	// ActionSCIMMatchAmbiguous records a provisioned user who matches more
	// than one hub identity, and that the hub refused to choose.
	ActionSCIMMatchAmbiguous Action = "scim.match_ambiguous"
	// ActionSCIMGroupCreate records a group provisioned.
	ActionSCIMGroupCreate Action = "scim.group_create"
	// ActionSCIMGroupUpdate records a provisioned group renamed or otherwise
	// changed.
	ActionSCIMGroupUpdate Action = "scim.group_update"
	// ActionSCIMGroupMembers records members added to or removed from a
	// provisioned group.
	ActionSCIMGroupMembers Action = "scim.group_members"
	// ActionSCIMGroupDelete records a provisioned group deleted.
	ActionSCIMGroupDelete Action = "scim.group_delete"

	// ActionProjectCreate records a project created through the hub — the
	// dashboard's New Project, or `cloop remote init` — or a creation refused
	// because of where it would have put the project (Task 20418).
	ActionProjectCreate Action = "project.create"
	// ActionProjectDelete records a project removed from the hub, whether its
	// directory was deleted with it, and a ?delete_root=true the hub refused.
	ActionProjectDelete Action = "project.delete"
	// ActionProjectFirewall records a project's firewall rule set being set,
	// cleared, or narrowed to fit a tightened executor (Task 20363) — the
	// innermost level, which a project's maintainers edit and which may only
	// narrow the executor's.
	ActionProjectFirewall Action = "project.firewall"

	// ActionProjectMemberGrant records an identity joining a project's roster.
	ActionProjectMemberGrant Action = "project.member.grant"
	// ActionProjectMemberChange records a member's role on a project changing.
	ActionProjectMemberChange Action = "project.member.change"
	// ActionProjectMemberRevoke records a maintainer removing a member.
	ActionProjectMemberRevoke Action = "project.member.revoke"
	// ActionProjectMemberLeave records a member removing themselves.
	ActionProjectMemberLeave Action = "project.member.leave"

	// ActionAuditGap records audit events that could not be appended to the
	// chain it is written in: how many, of which actions, when and why
	// (Task 20404).
	ActionAuditGap Action = "audit.gap"
	// ActionAuditRestored records a database restored from a backup, in the
	// chain that was restored: the history it gave up, who and why
	// (Task 20410).
	ActionAuditRestored Action = "audit.restored"

	// ActionAuditForwardConfigured records the hub's audit forwarding
	// configuration changing — sinks added, removed or altered — as the
	// cluster leader first runs under it (Task 20426).
	ActionAuditForwardConfigured Action = "audit.forward.configured"
	// ActionAuditForwardDiscontinuity records a sink's cursor that the chain
	// it tails no longer continues: rows pruned before the sink acknowledged
	// them, a tail truncated or rewritten after it did, and where forwarding
	// resumed (Task 20426).
	ActionAuditForwardDiscontinuity Action = "audit.forward.discontinuity"
)

One constant per registered action.

These are what emission sites reference. Writing the literal again at an emission site is what tests/arch/auditaction_test.go exists to catch, so a new action means a new constant here and a new entry in registry.go — adding one without the other fails the build rather than shipping a name no consumer can match.

The constants are grouped and ordered to mirror registry.go. Keeping the two files in the same order is not cosmetic: it is how a reviewer reads a diff that adds an action and sees both halves of it at once.

func AuthzOutcome

func AuthzOutcome(outcome string) Action

AuthzOutcome returns the action for an authorization outcome.

func ExecutorLifecycle

func ExecutorLifecycle(verb string) Action

ExecutorLifecycle returns the action for an executor-fleet verb: ExecutorLifecycle("cordon") is ActionExecutorCordon.

func GitProxyEvent

func GitProxyEvent(kind string) Action

GitProxyEvent returns the action for a pkg/gitproxy EventKind.

func KubeGuardEvent

func KubeGuardEvent(kind string) Action

KubeGuardEvent returns the action for a pkg/kubeguard EventKind.

func WorkspacePhase

func WorkspacePhase(phase string) Action

WorkspacePhase returns the action for a workspace-provisioning phase.

func (Action) Family

func (a Action) Family() string

Family returns the dotted prefix an action belongs to: "task.upsert" is in the "task" family, "sandbox.attach.open" in "sandbox.attach".

The two-segment case is not a special rule bolted on for one family; it is what the names already say. `sandbox.image_denied` and `sandbox.attach.open` are not siblings — the first is a placement decision, the second is a human opening a shell — and a reader scanning the generated page for "everything about interactive access" is served by them grouping separately.

func (Action) String

func (a Action) String() string

String returns the action as it appears in the database column.

func (Action) Verb

func (a Action) Verb() string

Verb returns the action with its family prefix removed: "executor.cordon" yields "cordon".

This exists because several emitters put the bare verb in the payload as well as the qualified name in the column — `{"action":"cordon"}` alongside `event_type='executor.cordon'`. Those payloads are already in sealed rows, so converting an emission site to carry a fully-qualified Action must not change what lands in the payload. Verb is how a converted site keeps the stored bytes identical.

type Entry

type Entry struct {
	// Action is the exact string written to the event_type column.
	Action Action

	// Entity is the value of the entity_type column for rows of this action.
	// It is what the EntityID is an identifier *of*, and a reader filtering
	// the trail by subject needs it to know which id space they are in.
	Entity string

	// Home is which of the two `audit_events` chains this action is written
	// to: the hub's control plane, the project's own database, or — for
	// authorization decisions alone — whichever the decision was scoped to.
	//
	// Declared rather than inferred because there is nothing to infer it
	// from. The home is decided by which *statedb.DB handle an emission site
	// holds, which is a fact about a call stack and not about the row, so a
	// reader of the trail cannot recover it and a mis-routed row cannot be
	// distinguished from a correctly-routed one. See home.go.
	Home HomeDB

	// Trigger is one sentence, present tense, naming the moment the row is
	// written — not what the feature is for. "A push is refused by branch
	// policy", not "guards pushes".
	Trigger string

	// Payload lists the JSON keys the emitter can set, in the order the
	// emitter sets them. Keys are frequently conditional: an emitter omits
	// what it does not know. The list is what a consumer may *expect to see*,
	// not what every row carries.
	Payload []string

	// Stability marks how much a consumer may rely on this.
	Stability Stability

	// Read is the permission a caller must hold to read rows of this action
	// back through the hub's audit API.
	//
	// It is per-entry rather than a package constant because the trail is one
	// table serving several audiences, and the day a family needs narrower
	// reads than authz.PermAuditRead the registry must be able to say so
	// without a schema change. Today every entry names the same permission,
	// and the generated page says that plainly rather than implying a
	// distinction that does not exist.
	Read authz.Permission

	// Note is optional: a second sentence for an action whose name or payload
	// would otherwise mislead. Most entries do not need one, and an entry that
	// has one usually earned it by being confusable with a neighbour.
	Note string
}

Entry is one registered action: everything the generated reference page says about it, and everything the gates check.

func All

func All() []Entry

All returns every registered entry, sorted by action name. The slice is a fresh copy: callers may sort or filter it without disturbing the registry.

func InFamily

func InFamily(family string) []Entry

InFamily returns the registered actions of one family, sorted by name.

func InHome

func InHome(h HomeDB) []Entry

InHome returns every registered action whose home is h, sorted by name.

Used by the generated page and by the merged reader, which needs to know which actions it should expect to find in which chain in order to tell "this project has no executor events because none happened" apart from "because I was reading the wrong database".

func Lookup

func Lookup(a Action) (Entry, bool)

Lookup returns the entry for a, and whether it is registered.

type HomeDB

type HomeDB string

HomeDB names the chain an action is recorded in.

const (
	// HomeControlPlane: the hub's own state.db. Facts about the fleet and the
	// hub as a whole — which executors exist, who holds a credential, who
	// logged in, what an operator administered. These outlive any one project
	// and several of them belong to no project at all, so a project's chain is
	// the wrong place for them even when a project is what triggered them.
	HomeControlPlane HomeDB = "control-plane"

	// HomeProject: the project's own .cloop/state.db. The plan's life — tasks
	// created, dispatched and finished, steps appended, the project's config
	// written. These travel with the project directory, which is what makes a
	// project's trail readable after it has been moved off the hub that ran it.
	HomeProject HomeDB = "project"

	// HomeEither: genuinely both, depending on the scope of the thing being
	// recorded — not "we did not decide".
	//
	// One family needs this honestly. An authorization decision is recorded
	// against whatever it was scoped to: a permission check on a project lands
	// in that project's chain, and a fleet-wide one lands in the hub's. Forcing
	// either answer would make the declaration a lie, and a lie here is worse
	// than the ambiguity it papers over, because the runtime assertion would
	// then fire on correct code and the next person would delete the assertion
	// rather than the lie.
	//
	// The gate keeps this from becoming a dumping ground: an entry declaring
	// HomeEither must carry a Note explaining what varies, or tests/arch fails.
	// An escape hatch that costs a sentence stays rare.
	HomeEither HomeDB = "either"
)

func HomeOf

func HomeOf(a Action) (HomeDB, bool)

HomeOf returns where a registered action is recorded, and whether the action is registered at all. An unregistered action has no declared home — callers deciding whether to assert must not treat that as a mismatch, because an unknown name is tests/arch's problem, not the emit path's.

func (HomeDB) Describe

func (h HomeDB) Describe() string

Describe is the one-line gloss the generated page uses. Kept next to the constants so a reader changing what a home means changes its description in the same edit.

func (HomeDB) String

func (h HomeDB) String() string

String renders the home as it appears in the generated docs and in the --source column of a merged read.

func (HomeDB) Valid

func (h HomeDB) Valid() bool

Valid reports whether h is one of the declared homes. The zero value is not valid, which is the point: an entry that forgot to say fails the gate rather than defaulting into somebody's chain.

type Stability

type Stability string

Stability says how much a consumer may rely on an action's name and payload.

const (
	// StabilityStable: the name and the payload keys listed for it will not
	// change. Safe to key a detection rule on. New payload keys may still be
	// added — a consumer must tolerate that — but no listed key will be
	// removed or repurposed.
	StabilityStable Stability = "stable"

	// StabilityBeta: the action fires, and its rows are real, but its name or
	// payload may still change while the surface that emits it settles. Key a
	// dashboard on it; do not key a paging alert on it.
	StabilityBeta Stability = "beta"

	// StabilityDeprecated: still emitted for compatibility, replaced by
	// something else. The Note says by what. Kept in the registry rather than
	// deleted because stored rows still carry the name and a reader looking it
	// up deserves an answer better than silence.
	StabilityDeprecated Stability = "deprecated"
)

func (Stability) Valid

func (s Stability) Valid() bool

Valid reports whether s is a known stability marker.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL