# firebase Firebase App component with launcher lifecycle and health check integration. ## Purpose Manages the lifecycle of a `firebase.google.com/go/v4` App: validates config at init time, exposes the native `*fb.App` to consumers, and performs health checks by probing the Auth service. Does not wrap or abstract any Firebase service beyond App construction. ## Tier & Dependencies **Tier 3 (infrastructure)** — depends on: - `code.nochebuena.dev/go/health` (Tier 2) - `code.nochebuena.dev/go/launcher` (Tier 2) - `code.nochebuena.dev/go/logz` (Tier 1) - `firebase.google.com/go/v4` (Firebase Admin SDK, wraps gRPC/HTTP internally) ## Key Design Decisions - **Health check via known-nonexistent UID**: `HealthCheck` calls `GetUser` with the UID `"health-probe-non-existent"` and uses `auth.IsUserNotFound(err)` to distinguish a healthy "not found" response from a real connectivity failure. Avoids string matching on error messages. See ADR-001. - **SDK lifecycle management**: `OnInit` calls `fb.NewApp`. `OnStop` is a no-op log because the SDK has no `Close` method. See ADR-002. - **App exposed directly**: `App() *fb.App` gives consumers the full SDK entry point. They call `app.Auth(ctx)` or `app.Firestore(ctx)` themselves. The module does not cache or pre-construct service-specific clients. - **Health priority is Critical**: `Priority()` returns `health.LevelCritical`. A Firebase Auth outage is treated as a critical failure. - **Duck-typed Logger**: The internal `logger` field is typed as `logz.Logger`, the shared interface from the `logz` module (ADR-001 global pattern). ## Patterns **Lifecycle registration:** ```go fb := firebase.New(logger, cfg) lc.Append(fb) // registers OnInit / OnStart / OnStop ``` **Obtaining service clients from consumers:** ```go type authService struct { provider firebase.Provider } func (s *authService) VerifyToken(ctx context.Context, token string) (*auth.Token, error) { authClient, err := s.provider.App().Auth(ctx) if err != nil { return nil, err } return authClient.VerifyIDToken(ctx, token) } ``` **Health check registration:** ```go health.Register(fbComponent) // satisfies health.Checkable via Name()/Priority()/HealthCheck() ``` ## What to Avoid - Do not call `App()` before `OnInit` has run — it returns `nil`. Guard against this in tests or when constructing components manually outside a lifecycle manager. - Do not add service-specific methods (e.g. `AuthClient()`, `FirestoreClient()`) to this module. Callers obtain them from `App()` directly, which keeps the module free of service proliferation. - Do not increase the health check polling frequency significantly. Each `HealthCheck` call makes a live API call to Firebase Auth. Poll at most every 30 seconds. - Do not rely on `OnStop` for graceful in-flight request drain. The SDK has no explicit close; in-flight Firebase calls at shutdown must be handled by request context deadlines in the caller. ## Testing Notes - Unit tests do not require a real Firebase project. They test structural invariants: `Name()`, `Priority()`, nil-safety of `OnStop`, and error on empty `ProjectID`. - `TestComponent_OnInit_MissingProjectID` verifies that `OnInit` returns an error before attempting SDK initialisation when `ProjectID` is empty. - `TestComponent_App_ReturnsNilBeforeInit` confirms `App()` is nil before `OnInit` is called. - Integration tests (real token verification, real health check) require Firebase credentials and belong outside this module. - `compliance_test.go` (package `firebase_test`) asserts `New(...)` satisfies `Component` at compile time.