PHPackages                             sugarcraft/candy-wish - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. sugarcraft/candy-wish

ActiveLibrary[Framework](/categories/framework)

sugarcraft/candy-wish
=====================

SSH server middleware framework — port of charmbracelet/wish. Build TUIs that anyone can `ssh user@host` to run, with composable middleware (auth, logging, rate-limiting, ext-ssh2 client wrappers, mount a SugarCraft Program).

1311PHP

Since Jun 29Pushed 1mo agoCompare

[ Source](https://github.com/sugarcraft/candy-wish)[ Packagist](https://packagist.org/packages/sugarcraft/candy-wish)[ RSS](/packages/sugarcraft-candy-wish/feed)WikiDiscussions master Synced 3w ago

READMEChangelogDependenciesVersions (1)Used By (0)

[![candy-wish](.assets/icon.png)](.assets/icon.png)

CandyWish
=========

[](#candywish)

[![CI](https://github.com/detain/sugarcraft/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/detain/sugarcraft/actions/workflows/ci.yml)[![codecov](https://camo.githubusercontent.com/7b818f8ae2ff49690fe9b26fcf0fd8e75a5c801c2489b34ab00aa7411476be1a/68747470733a2f2f636f6465636f762e696f2f67682f64657461696e2f737567617263726166742f6272616e63682f6d61737465722f67726170682f62616467652e7376673f666c61673d63616e64792d77697368)](https://app.codecov.io/gh/detain/sugarcraft?flags%5B0%5D=candy-wish)[![Packagist Version](https://camo.githubusercontent.com/e750a7e5f45f5d62de45faed4e7426b6fdbd9ec145109a9a9e374dd2addd505b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f737567617263726166742f63616e64792d776973683f6c6162656c3d7061636b6167697374)](https://packagist.org/packages/sugarcraft/candy-wish)[![License](https://camo.githubusercontent.com/7013272bd27ece47364536a221edb554cd69683b68a46fc0ee96881174c4214c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667)](LICENSE)[![PHP](https://camo.githubusercontent.com/e78ffc83837c0d12647811a7fd1910c3cbeae04988de94bb4fd5b67e0874696a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d254532253839254135382e312d3838393262662e737667)](https://www.php.net/)

PHP port of [`charmbracelet/wish`](https://github.com/charmbracelet/wish) — an SSH server middleware framework that lets you build TUIs anyone can `ssh user@host` to run.

```
composer require sugarcraft/candy-wish
```

Shared foundations
------------------

[](#shared-foundations)

CandyWish uses **candy-palette** for terminal capability probing. Call `\SugarCraft\Palette\Probe\TerminalProbe::run()` to detect color support, Sixel, HalfBlock, and other terminal capabilities — do not call `getenv()`or read terminfo directly. The probe is used by UI components that need to adapt rendering to the client's feature set.

Architecture
------------

[](#architecture)

CandyWish leans on the host's OpenSSH daemon rather than implementing the SSH wire protocol from scratch. Each SSH connection forks a fresh PHP process under `sshd` (via `ForceCommand`). What that PHP process does internally depends on the active **transport**:

### `InProcessTransport` (default)

[](#inprocesstransport-default)

```
[client] ─ssh─▶ [sshd] ─ForceCommand──▶ [php supervisor] ──▶ [middleware stack]
                                              │                    │
                                              └─pump bytes──┐      └─Spawn middleware
                                                            │              │
                                                            ▼              ▼
                                           [candy-pty master ◀──── slave / inner cmd]
                                                            (bash, vim, custom binary)

```

The supervisor allocates a `candy-pty` master/slave pair, spawns the user's cmd as a subprocess with full controlling-terminal semantics (Ctrl+C → SIGINT, SIGWINCH-driven resize, job control), and pumps bytes between the supervisor's STDIN/STDOUT (= sshd's PTY slave) and the candy-pty master. The terminal middleware is `Spawn`, which produces the cmd from the Session.

### `HostSshdTransport` (legacy, opt-in)

[](#hostsshdtransport-legacy-opt-in)

```
[client] ─ssh─▶ [sshd] ─ForceCommand──▶ [php supervisor] ──▶ [middleware stack] ──▶ [SugarCraft Program reading STDIN, writing STDOUT]

```

The pre-PTY-upgrade architecture: middleware run inline in the supervisor, and the terminal middleware (`BubbleTea`) mounts a SugarCraft `Program` directly on the supervisor's STDIN/STDOUT. Pin via `Server::new()->withTransport(new HostSshdTransport())`. Use this if your existing entry script reads STDIN/echoes STDOUT directly without a subprocess.

### Picking a transport

[](#picking-a-transport)

- **`InProcessTransport`** when you want to spawn arbitrary shells (`bash -i`, `zsh`, `fish`), editors (`vim`, `less`), or compiled TUI binaries — anything that needs a controlling terminal. Subprocess overhead per connection (~50-200ms PHP cold start), but full PTY semantics.
- **`HostSshdTransport`** when your TUI is a SugarCraft `Program` and you want zero subprocess overhead, or when you have an inline-STDIN-reading middleware (banner-style). No subprocess, but no controlling-terminal isolation.

Quickstart
----------

[](#quickstart)

### 1. Configure sshd

[](#1-configure-sshd)

Add to `/etc/ssh/sshd_config.d/wish.conf`:

```
Match User wishuser
    ForceCommand /usr/bin/php /opt/wish/server.php
    AllowTcpForwarding no
    PermitTTY yes
    X11Forwarding no

```

Then `systemctl reload sshd`.

### 2. Write the entry script

[](#2-write-the-entry-script)

**InProcessTransport (default) — spawn an interactive shell:**

```
