The shared contract between go-aiquota/tray (the host) and every AI-quota
provider plugin (go-aiquota/plugin-claude, and others to come): a
hashicorp/go-plugin gRPC service,
plus the credential-safety types every side of that contract is built on.
What's here
quotapb — the generated QuotaProvider gRPC service
(quota.proto): Describe (provider identity + where the host's embedded
onboarding browser should log an account in) and FetchQuota (one
account's current session/weekly quota).
plugin — the go-plugin wiring both the host and every provider
register through: the handshake, the GRPCPlugin adapter, and the
ClientConfig the host launches a provider subprocess with (gRPC only,
AutoMTLS so a credential never crosses the loopback socket in the clear).
secret — a Secret type wrapping a credential value so that %v,
%+v, a wrapped error, or an accidental JSON-encode can never print it.
The only way to read it back is Secret.Reveal(func(string)).
redact — shape-based text scrubbing, a second line of defense over
raw text (subprocess stderr, an upstream error message) that isn't already
Secret-wrapped.
Why a credential is map<string,string>, not a single opaque string
A FetchQuotaRequest.credential is a cookie-jar snapshot (cookie name to
value, for whatever domain the provider declared in Describe), not one
string. That's what lets go-aiquota/tray's onboarding window stay
provider-agnostic: it drives an isolated embedded browser to
ProviderInfo.login_url, captures whatever the jar holds for
ProviderInfo.cookie_domain once login completes, and hands the whole map
to the plugin. Adding a new provider is a new plugin declaring its own
login URL and cookie domain — the host's onboarding flow doesn't change.
Serve it with hcplugin.Serve(&hcplugin.ServeConfig{HandshakeConfig: plugin.Handshake, Plugins: plugin.Map(yourImpl), GRPCServer: hcplugin.DefaultGRPCServer}).
Name the built binary go-aiquota-plugin-<name> (e.g.
go-aiquota-plugin-chatgpt) and put it on PATH. That's the whole
registration step: go-aiquota/tray discovers every plugin matching
that naming convention at startup (quota.DiscoverProviders) and calls
its Describe to learn its login URL and cookie domain — there's no
manifest to edit and no host code to change. <name> is also what ends
up in Account.Provider and the "Add account…" menu (capitalized for
display), so keep it short and lowercase.
Never format a raw credential value into a log line or error string —
wrap it in secret.Secret the moment you have it, and reveal it only at
the single call site that needs the raw value (e.g. building an HTTP
request). See go-aiquota/plugin-claude for the reference
implementation.
Package plugin is the hashicorp/go-plugin wiring shared by go-aiquota/tray (the host) and every provider plugin: the handshake, the plugin map, and the GRPCPlugin adapter for quotapb.QuotaProvider.
Package plugin is the hashicorp/go-plugin wiring shared by go-aiquota/tray (the host) and every provider plugin: the handshake, the plugin map, and the GRPCPlugin adapter for quotapb.QuotaProvider.
Package secret wraps a credential value so that the ordinary ways a Go program accidentally prints something — fmt's %v/%+v, an error wrapped with %w, a JSON-encoded struct, a debugger's variable dump reaching a log — cannot show it.
Package secret wraps a credential value so that the ordinary ways a Go program accidentally prints something — fmt's %v/%+v, an error wrapped with %w, a JSON-encoded struct, a debugger's variable dump reaching a log — cannot show it.
Click to show internal directories.
Click to hide internal directories.
go.dev uses cookies from Google to deliver and enhance the quality of its services and to
analyze traffic. Learn more.