Documentation
¶
Overview ¶
Package goei pushes locally-computed Claude Code spend aggregates to a Goei dashboard's device-token ingest endpoint.
This is the "zero-key" half of Goei's two-track trust model: instead of handing Goei an Anthropic admin API key, the user runs budgetclaw locally (which only ever reads ~/.claude/projects/*.jsonl) and ships aggregated dollar-and-token rollups to their own dashboard. No key leaves the machine; the only thing transmitted is the cost summary.
The wire contract mirrors Goei's POST /api/ingest handler:
- Authorization: Bearer goei_dt_<32-hex> (exactly 40 chars)
- body: {provider, spend[], usage?} provider must be "anthropic"
- spend dedup key: (period_start, model, project, branch)
- usage dedup key: (period_start, metric_type, model, breakdown_key, breakdown_value)
Per-branch attribution (a budgetclaw differentiator) rides on the spend record's own optional branch field, so the project field always carries the bare project name. With --no-branch the branch is omitted and all branches of a project collapse server-side. Usage records break down by bare project name regardless.
Both arrays dedupe server-side via upsert, so re-running sync is idempotent: the same day re-sent overwrites, it does not double-count.
Index ¶
Constants ¶
const DefaultEndpoint = "https://goei.roninforge.org/api/ingest"
DefaultEndpoint is the production Goei ingest URL.
const Provider = "anthropic"
Provider is the only provider value Goei accepts for Claude Code data. Claude Code spend is Anthropic API spend.
Variables ¶
This section is empty.
Functions ¶
func ValidToken ¶
ValidToken reports whether a string is shaped like a Goei device token: the "goei_dt_" prefix plus a 32-char body, 40 chars total. This matches the format check Goei's ingest handler enforces.
Types ¶
type Aggregate ¶
type Aggregate struct {
Project string
GitBranch string
Model string
Day string // YYYY-MM-DD (UTC)
CostUSD float64
InputTokens int
OutputTokens int
CacheReadTokens int
CacheWrite5mTokens int
CacheWrite1hTokens int
}
Aggregate is the neutral input to BuildPayloads, decoupled from the db package so this package has no storage dependency. It mirrors db.SyncAggregate.
type Client ¶
Client posts payloads to a Goei ingest endpoint.
type Payload ¶
type Payload struct {
Provider string `json:"provider"`
Spend []SpendRecord `json:"spend"`
Usage []UsageRecord `json:"usage,omitempty"`
}
Payload is one POST body to /api/ingest.
func BuildPayloads ¶
BuildPayloads converts aggregates into one or more ingest payloads, chunked to stay under the server's per-request caps. Aggregates are grouped by day and whole days are packed into requests, so every request carries each day's spend and usage together and no request is ever spend-empty (which the endpoint rejects).
A spend record is emitted for every aggregate (including ones that round to zero cents) so that a day with only sub-cent usage still has the spend row its usage rows ride along with. Usage records are emitted only for non-zero token metrics.
type SpendRecord ¶
type SpendRecord struct {
PeriodStart string `json:"periodStart"`
PeriodEnd string `json:"periodEnd"`
AmountCents int `json:"amountCents"`
Currency string `json:"currency"`
Model string `json:"model,omitempty"`
Project string `json:"project,omitempty"`
Branch string `json:"branch,omitempty"`
}
SpendRecord is one daily per-(model, project, branch) dollar amount. Branch is optional: when empty (the --no-branch case) the server collapses every branch of a project into a single project-level row.
type UsageRecord ¶
type UsageRecord struct {
PeriodStart string `json:"periodStart"`
PeriodEnd string `json:"periodEnd"`
MetricType string `json:"metricType"`
MetricValue int `json:"metricValue"`
Model string `json:"model,omitempty"`
BreakdownKey string `json:"breakdownKey,omitempty"`
BreakdownValue string `json:"breakdownValue,omitempty"`
}
UsageRecord is one daily per-(model, project) token count for a single metric type.