Skip to content
HaneDOCS日本語

Hane Platform

Audit log and usage

How the tamper-evident audit log works and how to search it, how usage, budgets, and model calls are counted, and how undelivered events are handled.

This page explains how Hane records operations and counts usage. It shows how to read the audit log and how to check budgets and model usage.

Hane records operations in your organization in the audit log. This includes changes to members and roles, issuing and revoking API keys, activating definitions, approval decisions, executing outbound actions, and revealing contact values.

  • The audit log is append-only. There is no way to rewrite or delete records.
  • Each record is chained to the hash of the previous one, and a signed checkpoint is written at each boundary. Replacing or removing a record in the middle breaks the chain.
  • If the same event arrives twice, it is recorded once.
  • Business processing and audit storage are separate; the business side has no permission to rewrite the audit log.

Each record has the following fields.

Field Meaning
type What happened (an event type such as platform.api_key.created)
principal The principal: who asked for the operation
actor The actor: what actually made the call (such as your app’s service account)
engagement_id, operator_tenant_id For operations made through an operator engagement, the engagement and the operator’s organization
subject What the operation acted on
project_id, environment, request_id Where it happened and which request
hash The hash chained to the previous record

Because principal and actor are kept apart, you can trace which user was behind an operation even when your app called with a service account.

GET /v1/audit/events searches records, newest first. It requires audit:read.

Parameter Filter
type Prefix of the event type (for example data.)
actor Actor
engagement Only operations made through an operator engagement
scope project for the current project only, organization for the whole organization
since, until Time range
cursor, limit Paging
const page = await unwrap(
client.GET("/v1/audit/events", {
params: { query: { type: "platform.api_key", scope: "organization", limit: 50 } },
}),
);

GET /v1/audit/events/{seq} returns one record together with the stored body (chained to the previous record’s hash). The console’s “Audit log” page shows the same data.

If too many records cannot reach audit storage, Hane stops high-impact operations. Issuing API keys, activating definitions, deleting documents, executing approved outbound actions, and similar operations are refused with 503 (AUDIT_UNAVAILABLE). Reads and safe operations, such as revoking API keys or the emergency stop, keep working. Once storage is back, Hane resends the pending records and resumes.

API Contents Permission
GET /v1/usage/daily Daily counts (public API requests, API Gateway calls, and so on) usage:read
GET /v1/usage/overview Dashboard numbers (active release, records per type, cases and tasks per status, number of schedules) usage:read
GET /v1/agents/budget Today’s (UTC) budget, with used and reserved amounts agents:read
GET /v1/agents/model-usage Model calls through the Model Gateway, totaled per day, agent, and model agents:read

The console’s “Usage and budgets” page shows the last 14 days of usage, today’s budget, and a table of model calls. When the share of a budget used passes alertAtPercent (80% by default), the page shows a warning and a usage.budget.alert event is recorded.

Budgets are set per project and environment with a Budget in platform/v1alpha1.

Field When the limit is reached
agentRunsPerDay No more agent runs start
externalActionsPerDay No more email, SMS, or call proposals are accepted
modelTokensPerDay Agents that use a model do not start, and running ones stop at the limit

Days are counted in UTC. See Agents and AgentRuns for an example.

Business events reach the audit and notification processing through queues. Events that still fail after repeated delivery are listed in GET /v1/audit/dead-letters. After fixing the cause, requeue one with POST /v1/audit/dead-letters/{id}/retry (which requires audit:dead-letters.retry). Handlers drop duplicates by event ID, so requeuing twice gives the same result.