Skip to content

PiChamber v1.0.5

Latest

Choose a tag to compare

@github-actions github-actions released this 19 Oct 00:19

[1.0.5] - 2026-10-19

Tailscale remote access, needs-input alerts on every device, push notification removal, and composer, diff, and config fixes since 1.0.4.

  • Tailscale remote access (#190, #193, #194). Settings → Remote Access can make PiChamber reachable through Tailscale. Private uses tailscale serve, so only devices on your tailnet can reach it. Public uses tailscale funnel and is reachable from the internet after a confirmation. Both are off by default and need a UI password. PiChamber applies the mapping after the server starts listening, removes it on quit, and only removes mappings it can confirm are its own. The ts.net URL stays the same for a machine. The CLI adds serve --tailscale [--public] [--https-port N] and pair --tailscale, and status shows the Tailscale state (lib/tailscale, TailscaleRouteRow).
  • Tailscale probe works in containers (#193). When the host cannot reach its own ts.net name, for example tailscaled with --tun=userspace-networking or a resolver without MagicDNS, the probe checks /health through tailscale nc over the tailnet. A probe_failed error says to check that MagicDNS and HTTPS certificates are enabled (tailscale/nc-probe, tailscale/service).
  • Public Tailscale links report DNS readiness (#194). Public (Funnel) mode is active as soon as the server answers over the tailnet, instead of the URL looking ready while it still fails on the internet. Tailscale can take 10–15 minutes to publish the public DNS record for a new Funnel link. Until it appears, Remote Access says the public link is waiting for DNS and that tailnet devices can connect now. It switches to ready on its own. The server checks with public resolvers (1.1.1.1 and 8.8.8.8), sending only the ts.net hostname. pichamber status shows public DNS pending, and --quiet/--json report publicDns (tailscale/public-dns, TailscaleRouteRow).
  • Remote Access and Servers settings (#190). Remote Access covers incoming connections: ways to connect, devices with access, and security. Each address shows as one field that copies on click, with the Tailscale open-link button inside it. When Tailscale needs a UI password, one callout says so. Adding a device no longer asks for a transport. The QR code carries every reachable route and shows an expiry countdown and a New code button. Servers covers outgoing connections. The desktop switcher, the Servers page, the mobile Servers sheet, and first-run share one server list and one set of status labels. Each server shows its route as text after its name (my-server · Local network), and a server with several routes picks one from a dropdown. Re-pairing a server over a different route updates its existing entry instead of adding a second one (RouteRow, ServerRow).
  • connect-url is now pair (#190). The old name still works as a hidden alias and prints a deprecation notice. With --quiet, stdout carries only the link, and a Tailscale approval or failure goes to stderr.
  • Cloudflare quick tunnels removed (#190). Quick tunnel requests fail with quick_tunnel_removed. Managed Cloudflare tunnels are unchanged.
  • One stable server identity (#190, #193). /health, /api/version, pairing links, and the Tailscale probe report the same serverId. Its signing key moved from settings.json to a host-local relay-identity.json (mode 0600), so saving UI settings can no longer change the id and orphan paired devices. Existing installs keep their id: the key is copied from settings.json on first read. The server logs a warning if it cannot save the key (relay/identity-store, relay/server-id).
  • Per-client login rate limits (#190). A successful login clears only the requester's own rate-limit bucket. Before, it reset the bucket shared by all Tailscale Serve and Funnel traffic, which arrives on loopback.
  • Sessions that need input show on every device (#191). When an extension dialog waits for an answer, the session shows a "Needs input" marker in the sidebar, the session switchers, and folder and project rollups. A strip above the composer lists the other waiting sessions. A device that connects later still sees the prompt, and answering on any device clears it everywhere. A device looking at another session gets a toast with an Open session action. Desktop and browser clients in the background also get an OS notification, controlled by the new Input needed setting (on by default). The Electron dock badge, the browser app badge, and the tab title show how many sessions are waiting. Idle disposal no longer cancels a dialog that is still waiting, and a daemon restart dismisses alerts for requests it ended. The daemon adds GET /api/pi/sessions/pending-input and a session.input event (pending-input, NeedsInputAlerts).
  • Consistent session names (#191). An empty session reads "Awaiting first prompt" in the header, sidebar, switchers, toasts, and strip on every device. The generic fallback is "Untitled session".
  • Push notifications removed (#192). No push service ever existed behind them. The /api/push/* routes, the delivery runtime, the web-push dependency, and the background push settings section are gone. The mobile app drops @capacitor/push-notifications, the iOS APNs entitlement and notification service extension, and the Android Firebase wiring and POST_NOTIFICATIONS permission. Desktop OS notifications, browser notifications, in-app toasts, and needs-input alerts are unchanged. An existing notifications.json is no longer read.
  • No default relay endpoint (#192). Pairing links include a relay route only when PICHAMBER_RELAY_URL or settings.privateRelay.relayUrl is set, and relay identity keys are not generated otherwise. pair --relay without a relay URL fails with a usage error.
  • Large pastes become attachments (#183). Pasted text over 16,000 characters is attached as pasted-text.txt instead of being inserted into the composer, where it could be truncated. Smaller pastes, images, and mixed image and text pastes work as before (useComposerPaste).
  • Symlinked config files stay symlinks (#185). Saving settings.json, snippets.json, the STT config, and other server-written files through a symlink, for example one managed by GNU Stow, now writes the link's target. Before, each save replaced the link with a regular file. A failed write leaves the previous content in place (fs/atomic-write).
  • Wrap long lines in diffs and files (#189). Diff headers and the Files view have a per-file Wrap lines toggle. A new Wrap long lines by default setting under Appearance is on by default. With wrapping off, a horizontal scrollbar stays pinned to the bottom of the visible pane.
  • Extension message renderers show inline (#188). Custom messages from an extension that calls pi.registerMessageRenderer now render the way the Pi terminal shows them instead of as raw text. For example, @tintinweb/pi-subagents completion notices show the plugin's summary instead of their <task-notification> XML. The daemon runs the renderer collapsed and expanded with the extension theme, on live messages and in history, and redacts attachment paths in its output. The chat shows the collapsed lines with a Show more toggle, and the original text and details move into a collapsed Raw message section. A renderer that throws or returns nothing leaves the message as a plain note. The route caps each render at 200 lines of 2000 characters (extension-message-render, routes, ExtensionNoteRow).
  • Sign in with ChatGPT for the openai provider (#182). Pi 1.0's ChatGPT subscription login for the openai provider failed immediately in PiChamber. The daemon now passes Pi's installation device ID to provider logins the same way the Pi CLI does. The Providers page guides browser sign-in in two steps: open the sign-in page (with a copy-link button for another device), then paste the redirect URL. It rejects a pasted URL that isn't the redirect address before sending it. A failed login now shows Pi's reason, with URL query strings stripped so an authorization code never reaches the response. If the browser callback already settled the login, a later paste reports that state instead of a false failure (session-daemon, routes, ProviderLoginFlow).
  • Session engine routing (#187). The session daemon can route sessions to registered engines. PiChamber registers none, so behavior is unchanged except that GET /api/pi/engines returns an empty list and POST /api/pi/sessions with an unknown engine value fails with INVALID_ARGUMENT.
  • Device setup docs (#178). The Quick start and Connect devices pages explain loopback, LAN access, tunnels, browser links, and app pairing links, plus guided setup, generated passwords, startup integration, and shutting down.