# How the Update Notice System Detects New Browser Versions in Ego Lite

> Discover how Ego Lite's update notice system detects new browser versions. Learn about the injected bridge, version validation, and update notifications.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: internals
- Published: 2026-08-25

---

**The Ego Lite update notice system detects new browser versions by calling `ego.getBrowserVersion()` through an injected bridge, validating the returned `BrowserVersionInfo`, and emitting a formatted hint when `updateAvailable` equals `true`.**

The update notice mechanism in `citrolabs/ego-lite` provides lightweight, stateless version detection that runs during every SDK command execution. Located in [`package/ego-browser/src/update-notice.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/update-notice.ts), this system surfaces upgrade hints without persisting data or blocking command completion. This article breaks down the detection flow, suppression controls, and validation logic that powers the feature.

## Entry Point: emitUpdateNotice and the Bridge Interface

The detection process begins at **`emitUpdateNotice`** in [`package/ego-browser/src/update-notice.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/update-notice.ts) (lines 36-39). This function receives two critical inputs:

- The `ego` bridge object, which optionally exposes `getBrowserVersion()`
- An `emit` callback that appends the notice to command output

When invoked, `emitUpdateNotice` passes a version source to `updateNoticeLine`. This source calls `ego.getBrowserVersion()` if the bridge method exists, otherwise resolving to `null` (lines 41-43).

```typescript
// Simplified SDK usage pattern
import { emitUpdateNotice } from "./update-notice.js";

function runCommand(egoBridge, outputEmit) {
  // Command execution logic...

  // Attach update notice to output stream
  emitUpdateNotice(egoBridge, (line) => outputEmit(line));
}

```

## Suppressing the Notice with Environment Variables

Before any bridge interaction, the system checks **`noticeSuppressed`** (lines 50-53). When either condition below is true, the entire notice pipeline exits silently:

- `EGO_BROWSER_NO_UPDATE_NOTIFIER` is set
- `CI` environment variable is present

This prevents update hints from appearing in automated pipelines or when explicitly disabled by the user.

## Fetching the Version with Timeout Protection

The **`updateNoticeLine`** function executes the `VersionSource` within **`withTimeout`** (lines 44-45, 94-100). This races the bridge call against a **2-second timeout** defined by `NOTICE_PROBE_TIMEOUT_MS` (2000 ms).

```typescript
// Timeout-protected version probe
const NOTICE_PROBE_TIMEOUT_MS = 2000;

async function withTimeout<T>(
  promise: Promise<T>,
  ms: number
): Promise<T | null> {
  // Races bridge call against timeout
}

```

This guarantee prevents a stalled `getBrowserVersion()` from degrading command performance.

## Validating and Formatting the Notice

Once the bridge returns data, **`composeNotice`** (lines 70-85) applies strict validation rules:

| Field | Requirement |
|-------|-------------|
| `updateAvailable` | Must be literal `true` |
| `currentVersion` | Must be non-empty string |
| `latestVersion` | Optional inclusion in output |
| `mandatory` | Added to message when true |

Valid notices receive the **`[ego-browser:notice]`** prefix from `NOTICE_PREFIX` (lines 40-41) before emission.

```typescript
// Example formatted notice output
[ego-browser:notice] ego lite 1.3.0 is available (current 1.2.3) — run: ego-browser upgrade …

```

## Emitting the Final Line

After `updateNoticeLine` resolves, `emitUpdateNotice` invokes the provided `emit` callback with the formatted line (lines 45-47). Errors at this stage are intentionally swallowed—wrapped in a catch block—to prevent unhandled rejections from disrupting command execution (lines 48-49).

## Mocking for Testing

The modular design enables straightforward unit testing. Supply a fake `VersionSource` to `updateNoticeLine` to verify formatting logic without bridge dependencies:

```typescript
import { updateNoticeLine } from "./update-notice.js";

const fakeSource = async () => ({
  currentVersion: "1.2.3",
  updateAvailable: true,
  latestVersion: "1.3.0",
  mandatory: false,
});

const line = await updateNoticeLine({ source: fakeSource });
console.log(line);
// → [ego-browser:notice] ego lite 1.3.0 is available (current 1.2.3) — run: ego-browser upgrade …

```

## Supporting Files in the Detection Pipeline

| File | Responsibility |
|------|----------------|
| [`package/ego-browser/src/update-notice.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/update-notice.ts) | Core detection, validation, and formatting |
| [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts) | Registers `emitUpdateNotice` as output-sink trailer during SDK init |
| [`package/ego-browser/src/env.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/env.ts) | Resolves suppression environment variables |
| [`package/ego-browser/src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts) | Defines native `ego` bridge with `getBrowserVersion` |

## Summary

- **Bridge-dependent detection**: The system relies on `ego.getBrowserVersion()` injected at runtime, falling back to null if unavailable
- **Zero persistence**: No caching or state storage; checks run fresh on every command
- **2-second timeout**: `NOTICE_PROBE_TIMEOUT_MS` prevents blocking
- **Strict validation**: Only `updateAvailable: true` with valid `currentVersion` triggers output
- **Environment controls**: `EGO_BROWSER_NO_UPDATE_NOTIFIER` and `CI` provide opt-out mechanisms
- **Fail-silent emission**: Errors in notice delivery never propagate to callers

## Frequently Asked Questions

### How does the update notice system detect new browser versions without network calls?

The system delegates detection to the native `ego` bridge via `getBrowserVersion()`. This local call returns `BrowserVersionInfo` without HTTP requests, keeping the check lightweight and private. The bridge implementation in [`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts) handles actual version comparison against available releases.

### What happens if the bridge call hangs or responds slowly?

`withTimeout` enforces a 2000 ms ceiling on the `VersionSource` promise. If `getBrowserVersion()` exceeds this limit, the promise races to `null` resolution, and no notice appears. Commands continue normally regardless of bridge responsiveness.

### Why does the notice appear on every command instead of rate-limited?

By design, `emitUpdateNotice` executes statelessly. No timestamp files, registry entries, or cache entries track last-check time. This eliminates filesystem dependencies and ensures users always see current availability status, accepting the minimal overhead of a 2-second-capped local call.

### Can I disable update notices in CI environments?

Yes. Setting `CI` to any value automatically suppresses detection via `noticeSuppressed`. Alternatively, set `EGO_BROWSER_NO_UPDATE_NOTIFIER` for explicit opt-out in any environment. These variables are resolved in [`package/ego-browser/src/env.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/env.ts) before any bridge interaction occurs.