goei

package
v1.1.4 Latest Latest
Warning

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

Go to latest
Published: Jul 10, 2026 License: MIT Imports: 8 Imported by: 0

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)

Each spend record also carries an optional inline "tokens" object (input, output, cache_read, cache_write_5m, cache_write_1h) at the same per-(day, project, branch, model) grain as its dollar amount. The current server ignores it and keys off amountCents; a future server prefers tokens so it can re-price at its own point-in-time rate. Both are always sent, so the change is backward compatible.

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

View Source
const DefaultEndpoint = "https://goei.roninforge.org/api/ingest"

DefaultEndpoint is the production Goei ingest URL.

View Source
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

func ValidToken(t string) bool

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

type Client struct {
	Endpoint string
	Token    string
	HTTP     *http.Client
}

Client posts payloads to a Goei ingest endpoint.

func New

func New(endpoint, token string) *Client

New returns a Client with sane defaults. An empty endpoint falls back to DefaultEndpoint.

func (*Client) Push

func (c *Client) Push(ctx context.Context, p Payload) (int, error)

Push sends a single payload and returns the number of records Goei reports it stored. Errors include the HTTP status and a snippet of the response body for diagnosis.

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

func BuildPayloads(aggregates []Aggregate, includeBranch bool, machine string) []Payload

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.

machine is stamped on every spend record so the server can attribute rollups to the machine they came from. An empty machine is fine: the omitempty field is dropped and the server treats it as legacy.

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"`
	Machine     string       `json:"machine,omitempty"`
	Tokens      *TokenCounts `json:"tokens,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.

Machine is an optional per-machine identity (typically the OS hostname). The Goei server uses it to keep two machines' rollups from colliding: the same (day, project, branch, model) synced from a laptop and a desktop stay separate instead of overwriting each other. When empty the server treats it as legacy/unknown, so the field is additive and backward compatible.

Tokens is an optional per-(day, project, branch, model) token rollup at the same grain as AmountCents. The current Goei server ignores it and keys off AmountCents; a future server prefers Tokens so it can re-price at its own point-in-time rate. Both are always sent.

type TokenCounts added in v1.0.0

type TokenCounts struct {
	Input        int `json:"input"`
	Output       int `json:"output"`
	CacheRead    int `json:"cache_read"`
	CacheWrite5m int `json:"cache_write_5m"`
	CacheWrite1h int `json:"cache_write_1h"`
}

TokenCounts is the per-(day, project, branch, model) token rollup carried inline on a spend record. It is the same grain as the spend dollar amount, so a future Goei server can re-price the tokens at its own point-in-time rate instead of trusting amountCents. The current server ignores this field; sending it is forward-compatible.

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.

Jump to

Keyboard shortcuts

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