Documentation
¶
Overview ¶
Package ledger is the agent session ledger (ADR-0031): one normalized, identity-linked record per agent session, built from several evidence sources (halo-proxy spans, Claude Code OTel events, the Anthropic Compliance API, the GitHub enterprise audit log, a JSONL import).
A record holds identifiers, counts and timestamps only. Every string field must match a token pattern with no whitespace, so prose (a prompt, a tool input, an error message) cannot be stored in any field; file paths and repositories are dropped or salted-hashed by the redaction policy before a record leaves Normalize. Validate re-checks all of it on the server, which refuses the whole batch on the first violation (fail closed).
Index ¶
- Constants
- func IDFor(harness, sessionID string) string
- func Insert(ctx context.Context, ch *promote.ClickHouse, recs []Record, storedAt time.Time) error
- func Join(recs []Record, lookup func(vendorUser string) (user string, ok bool))
- func ParseTime(s string, now time.Time) (time.Time, error)
- type Anthropic
- type ClaudeOTel
- type Filter
- type Gateway
- type GitHubAudit
- type JSONL
- type Record
- type Redaction
- type Source
- type Stored
- type Who
- type Window
Constants ¶
const ( SourceGateway = "gateway" // halo-proxy GenAI spans (authenticated collector receiver) SourceOTel = "otel" // Claude Code OTel log events (client-reachable receiver) SourceAnthropic = "anthropic" // Anthropic Compliance API activities SourceGitHub = "github" // GitHub enterprise audit log // SourceCloudAgents is cloud agent tasks halo-server saw (ADR-0034): // HMAC-verified GitHub webhooks and vendor task APIs. SourceCloudAgents = "cloudagents" SourceJSONL = "jsonl" // operator-supplied import )
Evidence sources.
const ( High = "high" // server-side or vendor-authenticated evidence Medium = "medium" // client-reported telemetry Low = "low" // operator import of unknown provenance )
Confidence is the trust of the best evidence behind a record.
const ( OutcomeCompleted = "completed" OutcomeError = "error" OutcomeCancelled = "cancelled" OutcomeUnknown = "unknown" )
Outcomes.
const ( PathsDrop = "drop" PathsHash = "hash" )
Redaction modes.
const AnthropicBaseURL = "https://api.anthropic.com"
AnthropicBaseURL is the Compliance API's host.
const AnthropicVersion = "2023-06-01"
AnthropicVersion is the anthropic-version header the Compliance API examples send (checked 2026-10-27).
const GitHubAPIVersion = "2026-03-10"
GitHubAPIVersion is the X-GitHub-Api-Version sent to the audit log API.
const Table = "halo_session_ledger"
Table is the ClickHouse table stored records are copied to for analytics (deploy/observability/clickhouse/schema.sql). The audit chain stays the record of truth; the table is a query copy, one row per stored version (ReplacingMergeTree on stored_at keeps the latest). record_sha256 is the SHA-256 of the record JSON halo-server put in the audit entry's details.record, so a row can be checked against the chain.
Variables ¶
This section is empty.
Functions ¶
func IDFor ¶
IDFor is the ledger id of a harness session: stable across sources and imports, opaque and URL-safe.
func Insert ¶
Insert copies records halo-server stored (POST /api/v1/sessions answers them) into the ClickHouse table. A record that does not validate is refused.
Types ¶
type Anthropic ¶
type Anthropic struct{ Client *vendoradmin.Client }
Anthropic reads Claude Code cloud sessions from the Compliance API activity feed (GET /v1/compliance/activities, type ccr_session_created), one record per activity. The activity names the actor and the agent but carries no session id, model, tool or usage field, so the record's session id is the activity id and nothing else is filled in: it proves who started a cloud session when, not what it did. The client is vendoradmin's, so the key is only ever in the Authorization header and scrubbed from errors.
type ClaudeOTel ¶
type ClaudeOTel struct{ CH *promote.ClickHouse }
ClaudeOTel reads Claude Code's log events (claude_code.*) from the collector's otel_logs table, one record per session.id. It selects named attributes only: never prompt, tool_input, error text or the raw tool_parameters (which holds Bash commands); the MCP server name is extracted from tool_parameters inside the query. Repositories come from vcs.repository.url.full (OTEL_METRICS_INCLUDE_REPOSITORY=1). These events arrive on the client-reachable receiver, so the confidence is medium.
func (ClaudeOTel) Name ¶
func (ClaudeOTel) Name() string
type Filter ¶
type Filter struct {
User, Harness, Ring, Tool string
Since, Until time.Time // on Start: Since inclusive, Until exclusive
}
Filter selects records for list and export. Zero fields match everything.
type Gateway ¶
type Gateway struct{ CH *promote.ClickHouse }
Gateway reads halo-proxy's GenAI spans (telemetry.genai.traces) from the collector's otel_traces table, one record per trace. Only spans stamped halo.source=gateway count: the collector sets that on the authenticated gateway receiver, never from a client. halo-proxy exports no user or session id, so the session is the W3C trace (the caller's traceparent, or one call when it sent none) and Who carries the halo.unit pseudonym only.
type GitHubAudit ¶
type GitHubAudit struct {
Client *vendoradmin.Client
Enterprise string
Phrase string // "" = "action:agent_session"
}
GitHubAudit reads cloud agent sessions from GET /enterprises/{enterprise}/audit-log (read:audit_log). Events that carry agent_session_id (documented on pull_request.create, checked 2026-10-27) are grouped by that id; every other event is ignored. Phrase narrows the search server-side; the default asks for agent_session.* events in the window.
func (GitHubAudit) Name ¶
func (GitHubAudit) Name() string
type JSONL ¶
JSONL reads one Record per line. Decoding is strict: a field the ledger does not define (a "prompt", say) fails the import rather than being silently dropped, and Normalize then rejects any value that is not a token. Sources, confidence and id are set by the ledger, not the file.
type Record ¶
type Record struct {
ID string `json:"id"` // IDFor(Harness, SessionID)
SessionID string `json:"sessionId"`
Who Who `json:"who"`
Harness string `json:"harness"`
HarnessVersion string `json:"harnessVersion,omitempty"`
Release string `json:"release,omitempty"`
Ring string `json:"ring,omitempty"`
Experiment string `json:"experiment,omitempty"`
Variant string `json:"variant,omitempty"`
Models []string `json:"models,omitempty"`
MCPServers []string `json:"mcpServers,omitempty"`
Tools []string `json:"tools,omitempty"`
Repos []string `json:"repos,omitempty"` // "h:<hex>" once redacted
Files []string `json:"files,omitempty"` // "h:<hex>" once redacted
FilesTouched int `json:"filesTouched,omitempty"`
// DLPSecrets and DLPInjections count the gateway's DLP findings in the
// session (ADR-0033): secrets and prompt-injection signals, never content.
DLPSecrets int64 `json:"dlpSecrets,omitempty"`
DLPInjections int64 `json:"dlpInjections,omitempty"`
InputTokens int64 `json:"inputTokens,omitempty"`
OutputTokens int64 `json:"outputTokens,omitempty"`
CostUSD float64 `json:"costUsd,omitempty"`
Start time.Time `json:"start"`
End time.Time `json:"end,omitzero"`
Outcome string `json:"outcome"`
Sources []string `json:"sources"`
Confidence string `json:"confidence"`
// Redacted counts values the redaction policy withheld (paths dropped,
// identifiers that were not tokens).
Redacted int `json:"redacted,omitempty"`
}
Record is one agent session.
type Redaction ¶
type Redaction struct {
// Paths is "drop" (the default: files and repos are counted, not kept) or
// "hash" (HMAC-SHA256 with Salt, truncated; equal paths stay comparable
// within one salt).
Paths string
Salt []byte
}
Redaction is the path policy. Prompt text has no policy: no field can hold it.
type Source ¶
Source is one evidence adapter. Fetch returns raw records (unredacted, unvalidated): pass them through Normalize before anything else sees them.
type Stored ¶
Stored is a record as halo-server keeps it: the audit chain entry holding it is the tamper evidence (ADR-0016's chain, exported to SIEM).
type Who ¶
type Who struct {
// User is the Halos identity (the SCIM userName). Only halo-server sets it,
// from its directory; a value sent by a client is discarded (Join).
User string `json:"user,omitempty"`
// VendorUser is the identity the source reported: an email, a GitHub login,
// an Anthropic account id.
VendorUser string `json:"vendorUser,omitempty"`
// Unit is halo-proxy's halo.unit pseudonym (a salted hash of the verified
// subject) when that is all the evidence carries.
Unit string `json:"unit,omitempty"`
// Agents is the delegated agent chain, root side first (ADR-0024).
Agents []string `json:"agents,omitempty"`
}
Who is the session's principal.