# Hyper Admin & Developer Guide

This guide is the complete operational and architectural reference for **Hyper**,
the extensible terminal built on Electron, React, Redux, and `xterm.js`. It is
written for administrators packaging or deploying Hyper, plugin authors building
on its extension API, and developers who need to understand or reproduce its
internals. Every fact, default, file path, configuration key, and IPC channel
documented here is derived directly from Hyper's source code.

> [!NOTE]
> Reference version: Hyper **4.0.0-canary.5**. Hyper 4 changed the configuration
> format from Hyper 3's JavaScript `~/.hyper.js` to a JSON `hyper.json` file in a
> platform-specific config directory; the migration path is documented in
> [Configuration Migration](#configuration-migration).

---

## 1. Executive Summary

Hyper is a cross-platform desktop terminal emulator. It is a thin, plugin-driven
**Electron** application: a Node-powered **main process** owns the OS-level
concerns (windows, pseudo-terminals, config, plugin installation, auto-update,
native menus), and a **React + Redux renderer** draws the terminal using
`xterm.js`. The two halves communicate over a per-window RPC bridge built on
Electron IPC.

| Attribute | Value |
|---|---|
| Product | Hyper ("A terminal built on web technologies") |
| Version | `4.0.0-canary.5` |
| Author / owner | ZEIT, Inc. / Vercel |
| License | MIT |
| App ID | `co.zeit.hyper` |
| Platforms | macOS, Windows, Linux (`x64` + `arm64`) |
| Runtime | Electron 22.3.25 (Chromium + Node) |
| UI stack | React 18.2, Redux 4.2 (+ react-redux, redux-thunk, reselect, seamless-immutable) |
| Terminal engine | `xterm.js` 5.3 + addons (fit, search, web-links, webgl, unicode11, image, ligatures, canvas) |
| PTY | `node-pty` 1.0 |
| Language | TypeScript 5.4 |
| Package manager | pnpm 10.33 |
| Config file | `hyper.json` (JSON) |
| Extensibility | npm-published plugins/themes via a ~40-method extension API |

> [!IMPORTANT]
> Hyper deliberately runs its renderer with `nodeIntegration: true` and
> `contextIsolation: false`, and enables `@electron/remote`. This is the opposite
> of the modern hardened-Electron posture, and it exists so that plugins (arbitrary
> npm packages) can `require` Node modules directly in the renderer. The security
> consequences of this design are covered in [Security Model](#7-security-model).

---

## 2. Architecture

### 2.1 Two-process design

```
                          ┌───────────────────────────────────────────┐
                          │                Hyper (Electron)            │
                          │                                            │
  ~/.config/Hyper/        │   ┌──────────────────┐   IPC/RPC   ┌─────┐ │
  hyper.json  ───watch───▶│   │   MAIN PROCESS   │◀──(uuid)───▶│ R   │ │
                          │   │   (app/, Node)   │   channel   │ E   │ │
  node-pty ──spawn──────▶ │   │                  │             │ N   │ │
   │  shell (zsh/bash/…)  │   │  • windows       │             │ D   │ │
   │                      │   │  • sessions(PTY) │             │ E   │ │
   ▼                      │   │  • config loader │             │ R   │ │
  PTY data ──batched────▶ │   │  • plugin loader │             │ E   │ │
   (16ms / 200KB)         │   │  • auto-updater  │             │ R   │ │
                          │   │  • native menus  │             │  (lib/, React+Redux+xterm.js)
                          │   └──────────────────┘             └─────┘ │
                          └───────────────────────────────────────────┘
```

| Layer | Source dir | Responsibilities |
|---|---|---|
| **Main process** | `app/` | App lifecycle, `BrowserWindow` management, `node-pty` sessions, config load/watch, plugin install + loading, auto-update, native menus, OS integration (CLI install, ssh:// protocol, context menu) |
| **Renderer** | `lib/` | React UI, Redux store, `xterm.js` terminal, tab/pane (term-group) layout, dispatching user intents back to main via RPC |
| **CLI** | `cli/` | Standalone `hyper` command for plugin management and launching |
| **Type contracts** | `typings/` | Shared `.d.ts` definitions: `config`, `hyper`, `common`, action constants |

### 2.2 Repository structure

```
hyper/
├── app/                      # Electron MAIN process (TypeScript)
│   ├── index.ts              # Main entry: app lifecycle, window creation
│   ├── config.ts             # Config facade: profiles, watch/reload, decorate
│   ├── config/               # Config subsystem
│   │   ├── config-default.json   # Default settings (ground truth for defaults)
│   │   ├── schema.json           # Generated JSON Schema for hyper.json
│   │   ├── import.ts / init.ts    # Load + merge user/default config
│   │   ├── migrate.ts            # Hyper 3 → Hyper 4 migration
│   │   ├── paths.ts              # All config/plugin/CLI paths per platform
│   │   ├── open.ts               # Open hyper.json in editor
│   │   └── windows.ts            # Window position/size persistence (electron-store)
│   ├── session.ts            # PTY session (node-pty) + DataBatcher
│   ├── rpc.ts                # Per-window RPC Server (IPC bridge)
│   ├── plugins.ts            # Plugin loading, npm install, decorate hooks
│   ├── plugins/              # extensions.ts (API surface) + install.ts (yarn)
│   ├── commands.ts           # Command map (keymap → action) for the main process
│   ├── menus/                # Native menu builders (per platform + per section)
│   ├── keymaps/              # Default key bindings: darwin/linux/win32 JSON
│   ├── ui/                   # window.ts (BrowserWindow), contextmenu.ts
│   ├── updater.ts            # Auto-update feed + channel logic
│   ├── auto-updater-linux.ts # Custom Linux update-check implementation
│   ├── notifications.ts      # Remote "news" notifications
│   ├── notify.ts             # Native desktop notifications
│   └── utils/                # cli-install, colors, map-keys, shell-fallback, …
├── lib/                      # RENDERER (React + Redux + xterm.js)
│   ├── index.tsx             # Renderer entry: store + RPC wiring
│   ├── command-registry.ts   # Renderer-side command handlers
│   ├── actions/              # Redux action creators (sessions, term-groups, ui, …)
│   ├── reducers/             # Redux reducers (sessions, term-groups, ui)
│   ├── containers/           # Connected React containers (hyper, terms, header, …)
│   ├── components/           # React components (term, tabs, split-pane, searchBox, …)
│   ├── store/                # Store config + write-middleware
│   ├── selectors.ts          # reselect selectors
│   └── utils/                # config, plugins, ipc, rpc, paste, term-groups, …
├── cli/                      # hyper CLI (api.ts + index.ts)
├── typings/                  # Shared TypeScript definitions
├── electron-builder.json     # Packaging/distribution config
├── webpack.config.ts         # Renderer bundling
└── package.json              # Root (build) + app/package.json (runtime deps)
```

### 2.3 The RPC bridge (the API surface)

Communication between renderer and main is a custom event emitter layered over
Electron IPC. Each window gets a `Server` instance (`app/rpc.ts`) keyed by a
freshly generated UUID channel. On `did-finish-load`, the main process sends an
`init` message carrying the channel id and profile name; thereafter both sides
`emit`/`on` typed events.

Because Hyper has no HTTP API and no class-based route handlers, **the RPC event
table is the authoritative API surface** — AST extraction reports zero endpoints,
which is expected for this architecture. The principal events:

**Renderer → Main** (the user's intents):

| Event | Effect in main process |
|---|---|
| `new` | Create a session (spawn PTY), optional split direction/profile |
| `exit` | Terminate a session by `uid` |
| `data` (`{uid, data, escaped}`) | Write bytes to a session's PTY |
| `resize` (`{uid, cols, rows}`) | Resize the PTY |
| `maximize` / `unmaximize` / `minimize` / `close` | Window state |
| `open external` (`{url}`) | Open a URL in the default browser |
| `open context menu` (selection) | Pop up the terminal context menu |
| `open hamburger menu` (`{x, y}`) | Pop up the application menu (Win/Linux) |
| `command` (name) | Execute a named command in the main process |
| `quit and install` | Trigger an in-place app update |

**Main → Renderer** (results and OS events): `init`, `session add`,
`session data`, `session exit`, `termgroup add req`, `split request
horizontal`/`vertical`, `session clear req`, `session search`, `reset/increase/
decrease fontSize req`, `move left/right req`, `move jump req`, `next/prev pane
req`, `open file`, `open ssh`, `update available`, `add notification`, `enter/
leave full screen`, `windowGeometry change`, plus a family of `session move/del
… req` events that send line-editing escape sequences.

In addition, the main process registers `ipcMain.handle` request/response
endpoints used by the renderer and plugins:

| IPC handle | Returns |
|---|---|
| `child_process.exec` | Result of running a command (for plugins) |
| `child_process.execFile` | Result of running a file |
| `getDecoratedConfig` (profile) | Final config after plugin decoration |
| `getDecoratedKeymaps` | Final keymaps after plugin decoration |
| `getLoadedPluginVersions` | Loaded plugin name/version list |
| `getPaths` / `getBasePaths` | Plugin directory paths |
| `getDeprecatedConfig` | Plugins using deprecated config CSS |

### 2.4 Terminal session data pipeline

```
shell (PTY) ──onData──▶ DataBatcher ──flush (16ms or 200KB)──▶ Session 'data'
   ▲                      (prefixes each batch with the                │
   │                       session uid; utf8 StringDecoder)            ▼
   │                                                          rpc.emit('session data')
keystrokes ◀── pty.write ◀── rpc 'data' ◀──── renderer (xterm.js onData) ◀┘
```

- Each `Session` (`app/session.ts`) wraps a `node-pty` PTY.
- A **`DataBatcher`** accumulates PTY output and flushes it to the renderer at most
  every **16 ms** or when the buffer reaches **200 KB**, prefixing each batch with
  the 36-character session UUID. This batching minimizes IPC calls and GC
  pressure. The renderer strips the UUID prefix and feeds the rest to `xterm.js`.
- On the renderer side, tabs are **root term-groups** and panes are nested
  term-groups (`lib/actions/term-groups.ts`, `lib/reducers/term-groups.ts`).

> [!NOTE]
> Line-editing commands (move/delete by word or line, break, stop, quit, tmux
> prefix) are implemented in `lib/index.tsx` by writing raw escape/control
> sequences to the active session — e.g. `\x1bb` (move word left), `\x03`
> (`Ctrl+C` / break), `\x02` (tmux prefix). They are not shell-specific
> keybindings.

---

## 3. System Requirements

### Runtime (end users)

| Requirement | Detail |
|---|---|
| OS | macOS, Windows 10+, or a mainstream Linux distribution |
| Architecture | `x64` or `arm64` |
| Disk | Electron app bundle (~150–250 MB depending on platform) |
| Network | Optional — used for plugin install, update checks, and remote notifications |

### Build / development

| Requirement | Detail |
|---|---|
| Node.js | Compatible with `@types/node` 18 (Node 18.x toolchain) |
| pnpm | 10.33+ (enable via `corepack enable pnpm`) |
| Native build tools | Required to compile `node-pty` |
| Windows | `pnpm add --global windows-build-tools` from an elevated prompt |
| macOS | Xcode CLT (agree to license: `sudo xcodebuild`) |
| Linux (RPM-based) | `GraphicsMagick`, `libicns-utils`, `xz` |
| Linux (Debian-based) | `graphicsmagick`, `icnsutils`, `xz-utils` |

---

## 4. Configuration Reference

Hyper's entire configuration is the `hyper.json` file. This section documents the
file's structure and **every** key under `config`, with type, default, and meaning,
as defined by `typings/config.d.ts`, `app/config/schema.json`, and
`app/config/config-default.json`.

### 4.1 File location and load order

`app/config/paths.ts` resolves the config directory as follows:

| Condition | Config directory |
|---|---|
| `XDG_CONFIG_HOME` is set | `$XDG_CONFIG_HOME/Hyper` |
| Windows | Electron `userData` directory |
| macOS / Linux | `~/.config/Hyper` |

The config file is `hyper.json` within that directory. In development
(`NODE_ENV !== production`), if a `hyper.json` exists at the repo root, that file
is used instead — so developers can keep their personal config separate.

**Load sequence** (`app/config/import.ts` → `init.ts`):

1. Ensure plugin directories exist (`plugins/`, `plugins/local/`).
2. Run Hyper 3 → 4 migration if needed.
3. Read `config-default.json` (the baseline).
4. Overlay the platform's default keymap JSON.
5. Read the user's `hyper.json`; on parse failure, notify and fall back to defaults.
6. Deep-merge user config over defaults; normalize profiles; merge keymaps; filter
   out empty plugin entries.

Hyper **watches** `hyper.json` with `chokidar` and reloads ~100 ms after any
change, notifying subscribers and re-running plugin installation if the plugin
list changed (`app/config.ts`).

### 4.2 Top-level keys

| Key | Type | Purpose |
|---|---|---|
| `config` | object | All terminal/appearance/behavior settings |
| `plugins` | string[] | npm packages to install + load (`[@org/]project[#version]`) |
| `localPlugins` | string[] | Local plugin folder names under `plugins/local/` |
| `keymaps` | object | Command → shortcut(s) overrides |

### 4.3 Root-level config options

These are application-wide (not per-profile) settings.

| Key | Type | Default | Description |
|---|---|---|---|
| `updateChannel` | `'stable' \| 'canary'` | `stable` | Release channel: polished vs. frequent |
| `disableAutoUpdates` | boolean | `false` | If `true`, never check for app updates |
| `autoUpdatePlugins` | boolean \| string | `true` | Auto-update plugins every 5 h (or a custom interval like `1d`, `2h`) |
| `defaultSSHApp` | boolean | `true` | Register Hyper as the default `ssh://` protocol client |
| `useConpty` | boolean | _(unset)_ | Force ConPTY on/off on Windows; if unset, `node-pty` decides |

### 4.4 Profile (terminal) config options

These can be set at the root **and** overridden per profile (see
[Profiles](#45-profiles)). Defaults below come from `config-default.json`.

| Key | Type | Default | Description |
|---|---|---|---|
| `fontSize` | number | `12` | Font size in px |
| `fontFamily` | string | `Menlo, "DejaVu Sans Mono", Consolas, "Lucida Console", monospace` | Font stack |
| `fontWeight` | FontWeight | `normal` | Normal-text weight (`100`–`900`, `normal`, `bold`) |
| `fontWeightBold` | FontWeight | `bold` | Bold-text weight |
| `lineHeight` | number | `1` | Relative line height |
| `letterSpacing` | number | `0` | Relative letter spacing |
| `uiFontFamily` | string | _(unset)_ | Font for Hyper's own UI chrome |
| `cursorColor` | string | `rgba(248,28,229,0.8)` | Cursor color/opacity |
| `cursorAccentColor` | string | `#000` | Text color under a BLOCK cursor |
| `cursorShape` | `'BEAM'\|'UNDERLINE'\|'BLOCK'` | `BLOCK` | Cursor shape |
| `cursorBlink` | boolean | `false` | Blinking cursor |
| `foregroundColor` | string | `#fff` | Default text color |
| `backgroundColor` | string | `#000` | Terminal background (opacity macOS-only) |
| `selectionColor` | string | `rgba(248,28,229,0.3)` | Selection highlight |
| `borderColor` | string | `#333` | Window/tab border |
| `colors` | ColorMap | _16-color palette_ | ANSI 16-color map (see default in User Guide) |
| `css` | string | `""` | Custom CSS for the main window |
| `termCSS` | string | `""` | Custom CSS for the terminal area |
| `padding` | string | `12px 14px` | CSS padding around the terminal |
| `workingDirectory` | string | `""` | Absolute startup directory |
| `showHamburgerMenu` | boolean \| `''` | `""` | Show hamburger menu (Win/Linux); `''`→default true |
| `showWindowControls` | boolean \| `'left'` \| `''` | `""` | Window controls visibility/side |
| `scrollback` | number | `1000` | Lines of scrollback buffer |
| `shell` | string | `""` | Shell binary path (empty = login shell) |
| `shellArgs` | string[] | `["--login"]` | Shell arguments |
| `env` | object | `{}` | Extra environment variables |
| `bell` | `'SOUND' \| false` | `SOUND` | Audible bell or off |
| `bellSound` | string \| null | `null` | Base64-encoded bell sound |
| `bellSoundURL` | string \| null | `null` | Absolute path to a bell sound file |
| `copyOnSelect` | boolean | `false` | Copy selection to clipboard automatically |
| `defaultSSHApp` | boolean | `true` | (root) see above |
| `quickEdit` | boolean | `false` | Right-click copy/paste (default `true` on Windows; disables context menu) |
| `macOptionSelectionMode` | string | `vertical` | `vertical` column mode or `force` |
| `webGLRenderer` | boolean | `false` | WebGL vs. canvas (canvas supports transparency) |
| `webLinksActivationKey` | `'ctrl'\|'alt'\|'meta'\|'shift'\|''` | `""` | Modifier required to click links |
| `disableLigatures` | boolean | `true` | `false` enables font ligatures |
| `screenReaderMode` | boolean | `false` | Enable screen-reader access |
| `imageSupport` | boolean | `true` | Sixel + iTerm2 inline image protocol |
| `preserveCWD` | boolean | `true` | New tabs/splits inherit the active pane's CWD |
| `modifierKeys` | object | _(unset)_ | `{ altIsMeta, cmdIsMeta }` |
| `windowSize` | `[number, number]` | _(unset)_ | Initial window size in px |

> [!IMPORTANT]
> Negative knowledge (prevents fabrication): Hyper does **not** read any system-wide
> config file, has **no** HTTP/REST API, **no** webhooks, and accepts **no**
> behavior-changing command-line arguments (`hyper --help`/`--version` only print
> diagnostics). All configuration flows through `hyper.json`. There is no telemetry
> endpoint; the only outbound calls are plugin-registry queries, the update feed,
> and the remote "news" notification fetch.

### 4.5 Profiles

`defaultProfile` (string) names the profile used for new sessions; `profiles` is an
array of `{ name, config }` where `config` is a partial set of the profile options
above. `app/config.ts:getProfileConfig()` merges a profile's overrides onto the
base config (objects are shallow-merged; scalars replaced). If `defaultProfile`
doesn't match any profile, the first profile's name is used; an empty profile list
is normalized to a single `default` profile.

### 4.6 Keymaps

`keymaps` maps a command name to one shortcut (string) or several (array). User
keymaps are merged over the platform defaults. `app/utils/map-keys.ts` normalizes
every binding to an array, rewrites deprecated `cmd` → `command`, and expands any
command ending in `:prefix` into nine generated bindings (`…:1`…`…:8`, `…:last`)
by appending digits `1`–`9` to the prefix shortcut. The complete default keymaps
for each platform are listed in the User Guide; the full command catalog is in
[Command Reference](#9-command-reference).

### 4.7 Window state persistence

Window position and size are persisted separately from `hyper.json` via
`electron-store` (`app/config/windows.ts`), defaulting to position `[50, 50]` and
size `[540, 380]`. They are recorded on window close and restored on next launch
(validated against connected displays in `app/utils/window-utils.ts`).

---

## 5. Plugin Extension API

Plugins are npm packages (or local folders) that export one or more recognized
**extension methods**. The complete set of recognized methods is defined in
`app/plugins/extensions.ts` as `availableExtensions`. A package that exports none
of these is rejected with *"does not expose any Hyper extension API methods."*

### 5.1 Extension method catalog

| Category | Methods |
|---|---|
| **Main-process lifecycle** | `onApp`, `onWindow`, `onWindowClass`, `onUnload`, `onRendererWindow` |
| **Config / environment** | `decorateConfig`, `decorateKeymaps`, `decorateEnv`, `decorateMenu`, `decorateBrowserOptions` |
| **Session / window classes** | `decorateSessionClass`, `decorateSessionOptions`, `decorateWindowClass` |
| **Redux store** | `middleware`, `reduceUI`, `reduceSessions`, `reduceTermGroups` |
| **React component HOCs** | `decorateTerm`, `decorateHyper`, `decorateHyperTerm` (compat), `decorateHeader`, `decorateTerms`, `decorateTab`, `decorateTabs`, `decorateNotification`, `decorateNotifications`, `decorateTermGroup`, `decorateSplitPane` |
| **Component prop getters** | `getTermProps`, `getTabProps`, `getTabsProps`, `getTermGroupProps` |
| **State / dispatch mappers** | `mapHyperTermState`, `mapTermsState`, `mapHeaderState`, `mapNotificationsState`, `mapHyperTermDispatch`, `mapTermsDispatch`, `mapHeaderDispatch`, `mapNotificationsDispatch` |

### 5.2 How decoration works

`app/plugins.ts` loads each plugin module, records its `_name` and `_version`, and
chains decorators: for a given hook key, every plugin that exports it is called in
order, each receiving the previous result. Object-returning hooks (`decorateConfig`,
`decorateMenu`, `decorateEnv`, etc.) must return an object; class hooks
(`decorateSessionClass`) must return a function/class. Invalid returns produce a
*"invalid return type"* notification and are skipped. The decorated results feed
the IPC handles `getDecoratedConfig` / `getDecoratedKeymaps` and the window/session
constructors.

For backward compatibility, the main process patches Node's `Module._load` so
plugins can `require('react')`, `require('react-dom')`, `require('hyper/component')`,
`require('hyper/decorate')`, etc. (these specific requires are **deprecated**).

### 5.3 Plugin installation

`plugins` entries are synced into an auto-generated `package.json` inside the
plugin base directory and installed with a **bundled standalone Yarn**
(`bin/yarn-standalone.js`) run through Electron-as-Node
(`ELECTRON_RUN_AS_NODE=true`), serialized through a single-concurrency queue with a
5-minute timeout (`app/plugins/install.ts`). Installation is triggered on launch
(when the plugin set changed), on config change, on the periodic
`autoUpdatePlugins` interval, and on demand via the `plugins:update` command.

### 5.4 Plugin directories

`app/config/paths.ts` defines (relative to the config dir):

| Path | Purpose |
|---|---|
| `plugins/` | Base: auto-generated `package.json` + npm-installed `node_modules` |
| `plugins/local/` | Local (unpublished) plugins listed in `localPlugins` |
| `plugins/cache/` | Yarn cache folder used during install |

> [!TIP]
> Plugin development workflow (`PLUGINS.md`): run Hyper in dev mode on the `canary`
> branch, copy your `hyper.json` to the repo root, drop or symlink your plugin into
> `plugins/local/`, and add its folder name to `localPlugins`. A successful load
> logs `Plugin <name> (<version>) loaded.` Renderer-side `console.log` appears in
> the Electron DevTools; main-process logging appears in the launching terminal.

---

## 6. Operations

### 6.1 Running and lifecycle

The main process (`app/index.ts`) initializes config, enables `@electron/remote`,
and on the `ready` event creates the first window. It installs React/Redux DevTools
in dev mode, wires the `activate`/`window-all-closed`/`open-file`/`open-url`
handlers, builds the native menu, loads plugins (`onApp`), and (in production)
registers/deregisters the `ssh://` protocol client and installs the CLI.

| OS event | Behavior |
|---|---|
| `activate` (macOS dock click, no windows) | Create a new window |
| `window-all-closed` | Quit, except on macOS |
| `open-file` | Send the path to a window (`open file`) |
| `open-url` | Parse an `ssh://` URL and open a session (`open ssh`) |

### 6.2 Auto-update

`app/updater.ts` builds an update feed URL and polls it 10 s after launch and every
30 minutes thereafter (unless `disableAutoUpdates` is set):

```
https://{releases|releases-canary}.hyper.is/update/{platform}{_arm64?}/{version}
```

- `releases-canary` is used when `updateChannel` is `canary`; switching channels at
  runtime re-points the feed and re-checks.
- `_arm64` is appended on Apple Silicon / ARM64 (including Rosetta-translated runs).
- On Linux, the platform segment is `deb` and a **custom** `AutoUpdater`
  (`app/auto-updater-linux.ts`) fetches release metadata over HTTP. Linux cannot
  install in place (`quitAndInstall` is unimplemented), so `canInstall` is `false`
  and users are only notified.
- On macOS/Windows, Electron's native `autoUpdater` downloads and installs; the
  renderer's `quit and install` RPC triggers `quitAndInstall()`.

### 6.3 Remote notifications

`app/notifications.ts` fetches `https://hyper-news.now.sh` on launch and every
30 minutes, sending `X-Hyper-Version` and `X-Hyper-Platform` headers. A returned
message is surfaced in the UI; malformed responses raise `Bad response`.

### 6.4 CLI installation into PATH

`app/utils/cli-install.ts` installs the `hyper` command on first run:

| Platform | Mechanism |
|---|---|
| macOS / Linux | Symlink `bin/hyper` → `/usr/local/bin/hyper`; on `EACCES`, offers an elevated `sudo ln -sf …` via `sudo-prompt` or copies the command to the clipboard |
| Linux AppImage | Skipped (AppImage mounts at a temporary path) |
| Windows | Adds the `bin` directory to the user `PATH` via the `HKCU\Environment` registry key, removing stale Squirrel-era paths |

### 6.5 Windows system context menu

`app/utils/system-context-menu.ts` adds/removes **"Open Hyper here"** entries under
`HKCU\Software\Classes\…\shell\Hyper` for directory background, directory, and
drive, via **Tools → Add/Remove Hyper to system context menu**.

### 6.6 Logs and diagnostics

- Open DevTools: `Cmd+Alt+I` (macOS) / `Ctrl+Shift+I` (Win/Linux), or
  **View → Developer Tools**.
- Enable verbose Electron logging by launching via the CLI with `--verbose`
  (sets `ELECTRON_ENABLE_LOGGING=1`).
- **Help → Report Issue** generates a GitHub issue pre-filled with version, OS,
  arch, Electron version, `LANG`, `SHELL`, `TERM`, and the contents of `hyper.json`
  and the plugin list (large payloads are copied to the clipboard instead).

---

## 7. Security Model

> [!CAUTION]
> Hyper's renderer runs with **`nodeIntegration: true`**, **`contextIsolation:
> false`**, and **`@electron/remote` enabled** (`app/ui/window.ts`). This is an
> intentional trade-off that grants renderer code — including every installed
> plugin — full Node.js and OS access. It is **not** the hardened contextBridge
> model recommended for Electron apps that load untrusted remote content.

### 7.1 Trust boundary

Hyper loads only local content (`file://…/index.html`) — it never navigates to
remote web pages — so the classic remote-code-execution risk of `nodeIntegration`
is mitigated by *what* is loaded. The real trust surface is **plugins**: a plugin
is arbitrary npm code that executes with full privileges in both processes.
Administrators deploying Hyper in sensitive environments should treat the
`plugins`/`localPlugins` lists as a code-allowlist and review plugins as
dependencies.

### 7.2 Navigation and external content

`app/ui/window.ts` constrains navigation:

- `will-navigate` and `setWindowOpenHandler` intercept drag-dropped/clicked URLs:
  `file://` paths are inserted as escaped shell input; `http(s)://` URLs are passed
  through; other navigations are denied. This prevents the terminal window from
  navigating away from the app.
- `open external` routes URLs through `shell.openExternal` (the OS browser), not an
  in-app webview.

### 7.3 Shell input escaping

When the renderer sends `data` with `escaped: true` (e.g. dropped file paths), the
main process quotes it for the target shell — wrapping in double quotes for
`cmd.exe`, single-quotes (with `'\\''` escaping) otherwise (`app/ui/window.ts`).

### 7.4 Environment hygiene

`app/session.ts` strips Electron's injected `GOOGLE_API_KEY` from the shell
environment (so it isn't leaked to child processes) and, on AppImage installs,
fixes `PATH` so the bundled CLI isn't shadowed by the AppImage mount point.

### 7.5 Packaging hardening (macOS)

The macOS build is **notarized** (`afterSign: ./bin/notarize.js`) and ships an
entitlements file (`build/mac/entitlements.plist`). The app declares a full set of
`NS…UsageDescription` strings because plugins may invoke OS capabilities (AppleScript,
camera, contacts, etc.). Linux `snap` uses **classic** confinement.

---

## 8. Build & Distribution

### 8.1 Build pipeline

| Script | Action |
|---|---|
| `pnpm run dev` | Concurrent `webpack -w` (renderer) + `tsc --build --watch` (types/main) |
| `pnpm run app` | Launch the built app via `electronmon` (auto-restart on change) |
| `pnpm run build` | Production webpack + `tsc -b` + Babel-minify the renderer bundle |
| `pnpm run dist` | `build` then `electron-builder` (produces installers) |
| `pnpm run rebuild-node-pty` | Rebuild the native `node-pty` for Electron |
| `pnpm run v8-snapshot` | Build V8 startup snapshots (x64 + arm64) for faster boot |
| `pnpm run generate-schema` | Regenerate `app/config/schema.json` from `typings/config.d.ts` |
| `pnpm run lint` / `test:unit` / `test:e2e` | ESLint / AVA unit tests / AVA + Playwright e2e |

The renderer is bundled by `webpack.config.ts` (entry `lib/index.tsx`); the main
process is type-checked and emitted by the TypeScript project references. Babel
(with `@babel/preset-react` + `@babel/preset-typescript`) handles the renderer's
JSX/TS. `tsconfig.base.json` targets **ES2022**, `strict: true`, `jsx: react`,
CommonJS modules.

### 8.2 Distribution matrix (`electron-builder.json`)

| Platform | Targets | Arch | Signing / notes |
|---|---|---|---|
| **macOS** | `default` (dmg + zip) | `x64`, `arm64` | Notarized (`afterSign`), entitlements, dark-mode support, registers folder & Unix-executable document types |
| **Windows** | `nsis` | `x64`, `arm64` | RFC-3161 timestamp; installer is non-one-click, per-user, allows changing the install dir; custom `installer.nsh` |
| **Linux** | `deb`, `AppImage`, `rpm`, `snap`, `pacman` | — | Category `TerminalEmulator`; deb uses bzip2 + after-install hook; snap uses **classic** confinement (published to GitHub) |

Other packaging facts:

- `appId`: `co.zeit.hyper`; artifact name `${productName}-${version}-${arch}.${ext}`.
- The packaged app directory is `target/`; extra resources include
  `bin/yarn-standalone.js`, `bin/cli.js`, and the per-OS `hyper*` CLI binaries.
- `afterPack` copies the V8 snapshot (`bin/cp-snapshot.js`).
- A dedicated `electron-builder-linux-ci.json` extends the base config but disables
  `afterSign` and `npmRebuild` for CI Linux builds.
- The `ssh` URL scheme is registered as an app protocol (`protocols`).
- `release.js` is an `electron-builder` release-notes hook that prompts for a
  one-line summary and prepends it to the generated changelog.

---

## 9. Command Reference

Commands are named actions bound to keyboard shortcuts and menu items. Main-process
commands are defined in `app/commands.ts` (each emits an RPC event or performs an
OS action); some are handled in the renderer (`lib/command-registry.ts`).

### 9.1 Window & application

| Command | Action |
|---|---|
| `window:new` | Create a new window |
| `window:minimize` / `window:zoom` / `window:close` | Window state (Electron roles) |
| `window:toggleFullScreen` | Toggle full screen |
| `window:reload` | Hot-reload the renderer/plugins (RPC `reload`) |
| `window:reloadFull` | Full `webContents.reload()` |
| `window:devtools` | Toggle detached DevTools |
| `window:preferences` | Open `hyper.json` |
| `window:hamburgerMenu` | Pop up the app menu (Win/Linux) |
| `window:toggleKeepOnTop` | Toggle always-on-top |

### 9.2 Tabs & panes

| Command | Action |
|---|---|
| `tab:new` | New tab (or new window if none focused) |
| `tab:next` / `tab:prev` | Cycle tabs |
| `tab:jump:1`…`8`, `tab:jump:last` | Jump to a tab (generated from `tab:jump:prefix`) |
| `pane:splitRight` / `pane:splitDown` | Split vertically / horizontally |
| `pane:close` | Close the active pane |
| `pane:next` / `pane:prev` | Cycle pane focus |

### 9.3 Editor (terminal input)

| Command | Action / sequence |
|---|---|
| `editor:clearBuffer` | Clear the scrollback |
| `editor:selectAll` | Select all text |
| `editor:search` / `editor:search-close` | Open / close the find bar |
| `editor:movePreviousWord` / `moveNextWord` | `ESC b` / `ESC f` |
| `editor:moveBeginningLine` / `moveEndLine` | `ESC O H` / `ESC O F` |
| `editor:deletePreviousWord` / `deleteNextWord` | `ESC DEL` / `ESC d` |
| `editor:deleteBeginningLine` / `deleteEndLine` | `ESC w` / `\x10B` |
| `editor:break` / `editor:stop` / `editor:quit` | `Ctrl+C` / `Ctrl+Z` / `Ctrl+\` |
| `editor:tmux` | tmux prefix `Ctrl+B` |
| `editor:undo`/`redo`/`cut`/`copy`/`paste` | Native Electron roles |

### 9.4 Zoom, plugins, CLI, OS

| Command | Action |
|---|---|
| `zoom:in` / `zoom:out` / `zoom:reset` | Adjust renderer font size |
| `plugins:update` | Force a plugin update |
| `cli:install` | Install the `hyper` CLI into PATH |
| `systemContextMenu:add` / `remove` | Windows Explorer context-menu entries |
| `window:new:<profile>`, `tab:new:<profile>`, `pane:splitRight:<profile>`, `pane:splitDown:<profile>` | Per-profile variants (generated per profile) |

> [!NOTE]
> Some commands (`window:close`, `editor:undo/redo/cut/copy/paste/selectAll`,
> `window:minimize/zoom/toggleFullScreen`) are executed directly by Electron menu
> roles. `lib/command-registry.ts` marks these as `roleCommands` and does **not**
> prevent their default, so the OS handles them.

### 9.5 Native menu structure

`app/menus/menu.ts` assembles the menu from per-section builders, injecting the
first shortcut for each command as the accelerator:

| Menu | Items |
|---|---|
| **Hyper** (macOS only) | About, Preferences…, services, hide/quit |
| **Shell** (macOS) / **File** (Win/Linux) | New Tab, New Window, Split Down/Right, per-profile submenus, Close, Close Window/Quit |
| **Edit** | Undo/Redo/Cut/Copy/Paste, Select All, Move to…, Delete…, Clear Buffer, Search (+ Preferences… off-mac) |
| **View** | Reload, Full Reload, Developer Tools, Zoom Reset/In/Out |
| **Tools** | Update plugins, Install Hyper CLI in PATH, (Windows) context-menu add/remove |
| **Window** | Minimize, Zoom, Select Tab (incl. tab-jump 1–9), Select Pane, Front, Toggle Always on Top, Full Screen |
| **Help** | Hyper Website, Report Issue, (off-mac) About |

The same Edit + Shell builders also produce the in-terminal right-click context
menu (`app/ui/contextmenu.ts`), filtering Cut/Copy to appear only when there's a
selection.

---

## 10. Dependency Reference

### 10.1 Renderer / shared (root `package.json`)

| Dependency | Version | Role |
|---|---|---|
| `react`, `react-dom` | 18.2.0 | UI framework |
| `redux`, `react-redux`, `redux-thunk`, `reselect` | 4.2.1 / 8.1.3 / 2.4.2 / 4.1.8 | State management |
| `seamless-immutable` | 7.1.4 | Immutable term-group state |
| `xterm` + addons | 5.3.0 | Terminal engine (canvas, fit, image, ligatures, search, unicode11, web-links, webgl) |
| `@electron/remote` | 2.1.2 | Renderer→main object bridge |
| `mousetrap` | (fork) | Keyboard shortcut handling |
| `styled-jsx`, `stylis`, `clsx`, `color` | — | Styling |
| `args`, `chalk`, `columnify`, `got`, `open`, `ora` | — | CLI tooling |

### 10.2 Main process (`app/package.json`)

| Dependency | Version | Role |
|---|---|---|
| `node-pty` | 1.0.0 | Pseudo-terminal spawning |
| `electron-store` | 8.2.0 | Window-state persistence |
| `chokidar` | ^3.6.0 | Config file watching |
| `default-shell`, `shell-env`, `os-locale` | — | Shell/env resolution |
| `electron-is-dev`, `electron-fetch`, `git-describe` | — | Runtime helpers |
| `recast`, `@babel/parser`, `ast-types` | — | Hyper 3 → 4 config migration (AST transform) |
| `native-process-working-directory` | ^1.0.2 | CWD inheritance for splits |
| `native-reg` (optional), `sudo-prompt` | — | Windows registry / elevated CLI install |
| `async-retry`, `queue`, `fs-extra`, `ms`, `uuid`, `semver` | — | Utilities |

### 10.3 Build toolchain (devDependencies)

Electron 22.3.25, electron-builder, electron-rebuild, electron-mksnapshot,
electron-link, electronmon, webpack 5.91 + webpack-cli, Babel 7.24, TypeScript
5.4.5, ESLint 8.57 (+ TypeScript/React/import/prettier plugins), AVA 6.1 +
Playwright 1.43 (tests), Husky, Prettier, Terser.

---

## 11. Code Quality & Patterns

- **Strict TypeScript** end to end (`strict: true`, ES2022). Shared contracts in
  `typings/` keep the IPC event payloads, config shape, and Redux actions
  type-safe across the process boundary (`MainEvents`/`RendererEvents` typed
  emitters in `rpc.ts`).
- **Redux with thunks + effects.** Action creators return objects carrying an
  `effect()` callback executed by custom store middleware (`lib/store/`), used to
  bridge dispatched actions to RPC emits — a clean separation of state change from
  side effect.
- **Immutable term-group tree.** Tabs/panes form a tree of term-groups kept in
  `seamless-immutable` state; pane navigation and closing recompute the active
  session by walking the tree (`lib/actions/term-groups.ts`).
- **Decorator chains for extensibility.** Every customizable surface — config,
  keymaps, menus, env, sessions, windows, React components — runs through the same
  `decorateEntity` chain, giving plugins uniform, composable hooks.
- **Performance.** PTY output batching (16 ms / 200 KB), V8 startup snapshots, and
  an imperative `terms` registry for hot-path term access (`lib/terms.ts`) target
  responsiveness under heavy output.

### Known tech-debt signals (from source)

- Several plugin `require` aliases (`react`, `react-dom`, `hyper/component`,
  `hyper/decorate`) are explicitly marked **DEPRECATED** but retained for
  compatibility (`app/plugins.ts`).
- The `extendKeymaps` plugin hook is deprecated; plugins using it are warned at
  load time.
- A `webLinksActivationKey` `TODO` notes that link-activation changes don't apply
  without restarting the terminal (`typings/config.d.ts`).
- Pre-`xterm.js` CSS selectors are detected and flagged as deprecated rather than
  removed (`app/config.ts:getDeprecatedCSS`).

---

## 12. Potential Improvements

These are observations for maintainers and downstream packagers, grounded in the
current source:

- [ ] **Revisit the renderer security posture.** With `nodeIntegration: true` /
  `contextIsolation: false`, consider whether a contextBridge-based plugin API
  could preserve extensibility while shrinking the renderer's privilege — at minimum,
  document the plugin trust model prominently for enterprise deployers.
- [ ] **Plugin provenance.** There is no signature/integrity check on installed
  plugins beyond npm resolution; an allowlist or lockfile-pinning story would help
  managed environments.
- [ ] **Linux in-place updates.** `auto-updater-linux` only notifies; a packaged
  updater (or clearer per-format guidance) would close the gap with macOS/Windows.
- [ ] **Deprecation cleanup.** The deprecated `require` aliases and `extendKeymaps`
  hook could be removed in a future major, with a migration note for plugin authors.

---

## 13. Quick Reference

| Item | Value |
|---|---|
| Config file | `hyper.json` |
| Config dir (mac/Linux) | `~/.config/Hyper/` |
| Config dir (Windows) | Electron `userData` dir |
| Config dir (XDG) | `$XDG_CONFIG_HOME/Hyper/` |
| Plugin dirs | `plugins/`, `plugins/local/`, `plugins/cache/` |
| Default shell args | `["--login"]` |
| Default window size | `540 × 380` (min `370 × 190`) |
| Scrollback default | `1000` lines |
| PTY batch flush | 16 ms or 200 KB |
| Update feed | `https://releases[-canary].hyper.is/update/{platform}{_arm64}/{version}` |
| App ID | `co.zeit.hyper` |
| CLI binary | `hyper` (symlink `/usr/local/bin/hyper` on Unix; PATH entry on Windows) |
| Open config | `Cmd+,` / `Ctrl+,` |
| DevTools | `Cmd+Alt+I` / `Ctrl+Shift+I` |
| Plugin update | `Cmd+Shift+U` / `Ctrl+Shift+U` |
| Build installers | `pnpm run dist` |
| Regenerate schema | `pnpm run generate-schema` |
| Related repos | hyper-site, hyperpower (sample ext), hyperyellow (sample theme), Awesome Hyper |

---

_Generated June 2026 from vercel/hyper at da0c401. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
