// Package store is the per-tenant persistence layer. Every method // takes an orgID and resolves the per-org SQLite handle via // deps.Base.Open(ctx, orgID, "bootnode") per HIP-0302. // // TODO(bootnode-port): when deps.Base lands as a typed client // (`basezap.Client`), the `any` here becomes the typed handle and // these methods grow real CRUD bodies. package store import ( "context" "errors" bootnodezap "github.com/hanzoai/bootnode/zap/gen" ) // BaseClient is the per-tenant SQLite opener (HIP-0302). type BaseClient interface { Open(ctx context.Context, orgID, namespace string) (any, error) } // APIKeyStore persists API keys + projects. Production stores bcrypt // hashes, never plaintext (Hanzo CLAUDE.md hard rule). type APIKeyStore struct { base BaseClient } // NewAPIKeyStore builds the per-tenant API-key store. func NewAPIKeyStore(b BaseClient) *APIKeyStore { return &APIKeyStore{base: b} } // ErrNotFound is returned when a row does not exist. var ErrNotFound = errors.New("store: not found") // Create writes a new API key row, hashing the plaintext with bcrypt // before persistence. // // TODO(bootnode-port): port from bootnode/api/auth_keys.py:create_api_key — // use golang.org/x/crypto/bcrypt.GenerateFromPassword with cost 12. func (s *APIKeyStore) Create(ctx context.Context, k bootnodezap.APIKey, plaintext string) error { _ = ctx _ = k _ = plaintext return errors.New("store.APIKeyStore.Create: not implemented") } // List returns every API key for an org. // // TODO(bootnode-port): port from bootnode/api/auth_keys.py:list_api_keys func (s *APIKeyStore) List(ctx context.Context, orgID string) ([]bootnodezap.APIKey, error) { _ = ctx _ = orgID return nil, errors.New("store.APIKeyStore.List: not implemented") } // Revoke marks an API key as deleted (soft-delete to preserve audit). // // TODO(bootnode-port): port from bootnode/api/auth_keys.py:delete_api_key func (s *APIKeyStore) Revoke(ctx context.Context, orgID, id string) error { _ = ctx _ = orgID _ = id return errors.New("store.APIKeyStore.Revoke: not implemented") } // VerifyPlaintext bcrypt-compares the supplied plaintext against the // stored hash. Returns ErrNotFound if no row matches. // // TODO(bootnode-port): port from bootnode/api/auth_keys.py:verify_key func (s *APIKeyStore) VerifyPlaintext(ctx context.Context, plaintext string) (bootnodezap.APIKey, error) { _ = ctx _ = plaintext return bootnodezap.APIKey{}, ErrNotFound }