Documentation
¶
Overview ¶
Package byok is the gateway's bring-your-own-key store: customer provider API keys in GCP Secret Manager, one secret per (org, user, provider), named byok--<org>--<user>--<provider>. The gateway's runtime service account is the ONLY principal with access to the byok-- namespace (IAM-conditioned at deploy). Supabase holds display metadata only — never key material — so this package is the single place raw customer keys exist at rest.
Hygiene rules enforced here: key material never appears in errors or logs, and callers get at most (key, version) — nothing else to leak.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrDisabled = fmt.Errorf("byok store not configured")
ErrDisabled is returned by a nil store — BYOK not configured in this environment.
var ErrNotFound = fmt.Errorf("no key stored")
ErrNotFound is returned when no key exists for (org, user, provider).
Functions ¶
Types ¶
type Resolver ¶
type Resolver struct {
// contains filtered or unexported fields
}
Resolver implements provider.ByokKeys over the Secret Manager store with short TTL caches, so the hot path costs zero Secret Manager calls for repeat turns:
key cache 5 min (positive; version-tagged so rotation reconstructs clients)
miss cache 60 s (presence gate said yes but the secret is gone — metadata drift)
invalid 60 s (a vendor auth-rejected the key: fail FAST with the same
actionable error instead of re-burning a vendor call per
turn; after the TTL the real key is tried again in case
the user fixed it)
Cross-instance coherence is TTL-bounded (each Cloud Run instance has its own caches); the local instance's Invalidate makes the user's own PUT/DELETE take effect immediately where they did it.
func NewResolver ¶
NewResolver wraps the store; a nil store yields a nil resolver (BYOK off).
func (*Resolver) Invalidate ¶
Invalidate clears all local cache state for (org, user, vendor) — called by the PUT/DELETE handlers so the user's own instance reflects the change immediately. Other instances converge within the TTLs.
func (*Resolver) Key ¶
Key returns the user's live key for vendor. provider.ErrKeyInvalid = the key was recently auth-rejected (fast-fail); ErrNotFound = presence-gate drift (secret missing).
func (*Resolver) MarkInvalid ¶
MarkInvalid records a vendor auth rejection: subsequent turns fail fast for invalidTTL with the same actionable message.
type SMClient ¶
type SMClient interface {
CreateSecret(ctx context.Context, req *secretmanagerpb.CreateSecretRequest, opts ...gax.CallOption) (*secretmanagerpb.Secret, error)
AddSecretVersion(ctx context.Context, req *secretmanagerpb.AddSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.SecretVersion, error)
AccessSecretVersion(ctx context.Context, req *secretmanagerpb.AccessSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.AccessSecretVersionResponse, error)
DeleteSecret(ctx context.Context, req *secretmanagerpb.DeleteSecretRequest, opts ...gax.CallOption) error
}
SMClient is the narrow slice of the Secret Manager client the store uses — an interface so tests run against a fake instead of GCP.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store reads/writes customer provider keys in Secret Manager.
func NewStore ¶
NewStore builds the store with ambient Cloud Run credentials. Returns (nil, nil) when GOOGLE_CLOUD_PROJECT is unset (local dev without GCP) — a nil *Store is "BYOK disabled" and every method fails cleanly.
func NewStoreWithClient ¶
NewStoreWithClient wires an explicit client — the test seam.
func (*Store) AccessLatest ¶
func (s *Store) AccessLatest(ctx context.Context, org, user, provider string) (key, version string, err error)
AccessLatest returns the live key + its version id for (org, user, provider). ErrNotFound when the user has no key for that provider.
func (*Store) Delete ¶
Delete removes the secret entirely. A missing secret is success (idempotent — deletes may race between www and the CLI).