# State Machine

## Service state machine

The finite state machine is implemented in the `IPProtectionService` class
and the states are defined in the `IPProtectionStates` object.

### Service states

The service transitions across the following states:

- `UNINITIALIZED`: Service not initialized or feature disabled.
- `UNAVAILABLE`: User not eligible (Nimbus) or signed out with no eligibility; UI hidden.
- `UNAUTHENTICATED`: User signed out but eligible; UI shows login.
- `READY`: Ready to activate the proxy.

### High‑level transitions

- Feature disabled → `UNINITIALIZED`.
- During startup, if initialization isn’t complete, use cached state from `IPPStartupCache`.
- Not signed in → `UNAVAILABLE` if not eligible, otherwise `UNAUTHENTICATED`.
- If an entitlement is cached/valid → `READY`.
- Otherwise, check enrollment with Guardian (via `IPPFxaActivateAuthProvider.updateEntitlement()`):
  \- Not enrolled → `UNAVAILABLE` (not eligible).
  \- Enrolled → fetch entitlement; if successful → `READY`, else `UNAVAILABLE` when not eligible.

### Events and integration points

- `IPProtectionService:StateChanged` is dispatched on state changes with
  `detail.state` and `detail.prevState`.
- Helpers can call `IPProtectionService.updateState()` to recompute the state immediately; update any helper-owned data first because the call is synchronous.
- Public actions: `start(userAction)`, `stop(userAction)`.

## Proxy manager state machine

The `IPPProxyManager` layers a proxy‑specific finite state machine on top of
`IPProtectionService`. It mirrors eligibility changes from the service and
drives the lifecycle of the proxy connection.

### Proxy states

- `NOT_READY`: Service is not `READY`. Channel filters are torn down and UI should not offer activation.
- `READY`: Service is `READY` and the proxy can be activated.
- `ACTIVATING`: `start()` is creating a channel filter, fetching a proxy pass, and selecting an endpoint.
- `ACTIVE`: Proxy connected. Usage and network observers are reporting metrics.
- `ERROR`: An unrecoverable error occurred while the proxy is connected such as failing when rotating credentials. Stop must be called to change states.
- `PAUSED`: Everything is working but the bandwidth limit has been reached so we can't connect to the VPN. The bandwidth will reset next month.

### Proxy transitions

- `IPProtectionService:StateChanged` → `IPPProxyManager.updateState()`:
  \- Service `READY` → proxy `READY` (resets connection/error history).
  \- Any other service state → proxy `NOT_READY` (stops active connections).
- `start(userAction)` from `READY` moves to `ACTIVATING`.
  \- Successful activation → `ACTIVE` and telemetry `ipprotection.started`.
  \- Failures during activation (missing entitlement, server list, proxy pass…) call `updateState()` to demote the proxy back to its previous state.
  \- Errors after the proxy is connected → `ERROR` via `#setErrorState`.
- `stop(userAction)` from `ACTIVE` → `READY` after closing the channel filter and observers.
- `reset()` or helper‑driven recomputations call `updateState()` which demotes the proxy back to `READY`/`NOT_READY` and clears the credential cache.
- Network errors (`proxy-http-error` with 401) trigger Proxy Pass rotation while staying `ACTIVE`; repeated failures bubble up through `#setErrorState`.

### Proxy events and hooks

- `IPPProxyManager:StateChanged` is dispatched with `detail.state` whenever the proxy state machine moves.
- `IPPProxyManager` listens to `IPProtectionService:StateChanged` and to `proxy-http-error` from `IPPNetworkErrorObserver`.
- Consumers can observe `IPPProxyManager.state` (or listen for events) to synchronize UI/telemetry with the proxy lifecycle.
