Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ body:
id: version
attributes:
label: T4 Code version
placeholder: "0.1.8"
placeholder: "0.1.9"
validations:
required: true
- type: dropdown
Expand Down
132 changes: 122 additions & 10 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Release desktop builds
name: Release app builds

on:
push:
Expand Down Expand Up @@ -159,6 +159,115 @@ jobs:
if-no-files-found: error
retention-days: 7

build-android:
needs: verify
runs-on: ubuntu-24.04
timeout-minutes: 35
steps:
- name: Check out verified release source
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: ${{ needs.verify.outputs.source_sha }}
persist-credentials: false

- name: Install pnpm
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
with:
version: 11.10.0

- name: Install Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 24.13.1
cache: pnpm

- name: Install Java
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4
with:
distribution: temurin
java-version: "21"
cache: gradle

- name: Install Android SDK
uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3

- name: Install pinned Android platform and build tools
run: sdkmanager --install "platforms;android-36" "build-tools;36.0.0"

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Restore and validate Android release signing key
shell: bash
env:
T4_ANDROID_KEYSTORE_BASE64: ${{ secrets.T4_ANDROID_KEYSTORE_BASE64 }}
T4_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.T4_ANDROID_KEYSTORE_PASSWORD }}
T4_ANDROID_KEY_ALIAS: ${{ secrets.T4_ANDROID_KEY_ALIAS }}
T4_ANDROID_KEY_PASSWORD: ${{ secrets.T4_ANDROID_KEY_PASSWORD }}
run: |
set -euo pipefail
for name in \
T4_ANDROID_KEYSTORE_BASE64 \
T4_ANDROID_KEYSTORE_PASSWORD \
T4_ANDROID_KEY_ALIAS \
T4_ANDROID_KEY_PASSWORD
do
if [[ -z "${!name:-}" ]]; then
echo "required GitHub Actions secret ${name} is not configured" >&2
exit 1
fi
done
keystore_path="$RUNNER_TEMP/t4-code-release.jks"
printf '%s' "$T4_ANDROID_KEYSTORE_BASE64" | base64 --decode > "$keystore_path"
chmod 600 "$keystore_path"
keytool -list \
-keystore "$keystore_path" \
-storepass "$T4_ANDROID_KEYSTORE_PASSWORD" \
-alias "$T4_ANDROID_KEY_ALIAS" >/dev/null

- name: Build signed Android APK
env:
T4_ANDROID_KEYSTORE_PATH: ${{ runner.temp }}/t4-code-release.jks
T4_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.T4_ANDROID_KEYSTORE_PASSWORD }}
T4_ANDROID_KEY_ALIAS: ${{ secrets.T4_ANDROID_KEY_ALIAS }}
T4_ANDROID_KEY_PASSWORD: ${{ secrets.T4_ANDROID_KEY_PASSWORD }}
run: |
pnpm build:web
pnpm --filter @t4-code/mobile build:android:release

- name: Inspect signed Android APK
shell: bash
env:
VERSION: ${{ needs.verify.outputs.version }}
run: |
set -euo pipefail
apk="apps/mobile/android/app/build/outputs/apk/release/app-release.apk"
test -f "$apk"
build_tools="${ANDROID_SDK_ROOT:-$ANDROID_HOME}/build-tools/36.0.0"
export PATH="$build_tools:$PATH"
apksigner verify --verbose "$apk"
aapt dump badging "$apk" | grep -F "package: name='com.lycaonsolutions.t4code'"
aapt dump badging "$apk" | grep -F "versionName='${VERSION}'"

- name: Stage Android artifact
shell: bash
env:
VERSION: ${{ needs.verify.outputs.version }}
run: |
mkdir -p artifacts
cp \
apps/mobile/android/app/build/outputs/apk/release/app-release.apk \
"artifacts/T4-Code-${VERSION}-android.apk"
(cd artifacts && sha256sum T4-Code-* > SHA256SUMS-android.txt)

- name: Upload Android artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: android-release
path: artifacts/*
if-no-files-found: error
retention-days: 7

build-macos:
needs: verify
runs-on: macos-15
Expand Down Expand Up @@ -208,7 +317,7 @@ jobs:
retention-days: 7

publish:
needs: [verify, build-linux, build-macos]
needs: [verify, build-android, build-linux, build-macos]
runs-on: ubuntu-24.04
timeout-minutes: 10
permissions:
Expand Down Expand Up @@ -239,7 +348,7 @@ jobs:

- name: Assemble checksums
shell: bash
run: cat artifacts/SHA256SUMS-linux.txt artifacts/SHA256SUMS-macos.txt | sort -k2 > artifacts/SHA256SUMS.txt
run: cat artifacts/SHA256SUMS-android.txt artifacts/SHA256SUMS-linux.txt artifacts/SHA256SUMS-macos.txt | sort -k2 > artifacts/SHA256SUMS.txt

- name: Publish GitHub release
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
Expand All @@ -248,21 +357,23 @@ jobs:
generate_release_notes: true
fail_on_unmatched_files: true
body: |
## Tailnet web app
## Android app

This release includes a signed Android APK for direct installation. It is a thin client: the T4 Code interface is bundled in the APK, while OMP sessions and runtime state remain on the host. On first run, enter the host's HTTPS Tailscale address and complete the normal T4 pairing flow.

The source-hosted Tailnet client is now installable from its titlebar on Android and iPhone. It opens in a standalone window with the T4 Code icon; after installation, the same titlebar action reloads the live client. This remains an online thin client and does not cache host configuration, transcripts, or session state for offline use.
Android device credentials are encrypted with an app-specific Android Keystore key. The app does not contain an OMP appserver and does not require a public route; the phone and host should use the same tailnet.

## Session controls
## Browser and iOS

Queue and Steer no longer attach a volatile active-turn revision that OMP can reject as stale. Normal prompts keep their revision guard, and prompt leases remain supported when negotiated. Fast mode copy now accurately describes a request for provider priority processing without implying a change in reasoning effort.
The installable Tailnet web app remains available. iOS TestFlight is coming soon; there is no iOS binary in this release.

## Development guardrails
## Connection security

The arbitrary physical-line ceiling has been removed. File boundaries are judged by cohesion and reviewability rather than a hard line count.
The Tailnet gateway accepts only the configured HTTPS web origin and the exact native app origins. It rejects wildcard, opaque, and untrusted origins. Keep the route on Tailscale Serve and do not enable Funnel.

## Install notes

Linux packages are built for x86_64. macOS packages are built for Apple Silicon.
The Android APK is signed and supports Android 7.0 or later. Linux packages are built for x86_64. macOS packages are built for Apple Silicon.

## Runtime compatibility

Expand All @@ -276,6 +387,7 @@ jobs:

Verify downloads with `SHA256SUMS.txt`.
files: |
artifacts/T4-Code-*.apk
artifacts/T4-Code-*.deb
artifacts/T4-Code-*.AppImage
artifacts/T4-Code-*.dmg
Expand Down
47 changes: 28 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,56 +4,65 @@ T4 Code is a free, open-source (MIT) desktop app for [Oh My Pi](https://github.c

![T4 Code main window](docs/assets/t4-code-main.png)

[**Download v0.1.8**](https://github.com/LycaonLLC/t4-code/releases/tag/v0.1.8) · [**Docs**](https://t4code.net/docs) · [**Get the source**](#build-from-source)
[**Download v0.1.9**](https://github.com/LycaonLLC/t4-code/releases/tag/v0.1.9) · [**Docs**](https://t4code.net/docs) · [**Get the source**](#build-from-source)

## Requirements

T4 Code needs an OMP build with desktop appserver support. For v0.1.8, use the public integration build below.
T4 Code needs an OMP build with desktop appserver support. For v0.1.9, use the public integration build below.

T4 Code v0.1.8 was verified with OMP 16.5.0 built from [`d4a0b934`](https://github.com/lyc-aon/oh-my-pi/commit/d4a0b9344e1796c0e56041cfeea3431a8a728e61), tagged [`t4code-16.5.0-appserver-3`](https://github.com/lyc-aon/oh-my-pi/tree/t4code-16.5.0-appserver-3). That public integration is based on the official upstream [`v16.5.0`](https://github.com/can1357/oh-my-pi/tree/v16.5.0) tag at [`3047c27c`](https://github.com/can1357/oh-my-pi/commit/3047c27c332c5629c8e063283d349384c10c9a56). It adds bounded large-session replay, complete desktop runtime events, catalog-backed session management, ordered remote delivery, cross-client control-state convergence, terminal streaming-state settlement, and restart-safe session teardown. The official upstream v16.5.0 tag has no `appserver` command, so it cannot host T4 Code. The verified runtime is a normal build from the public `lyc-aon/oh-my-pi` source; T4 Code does not depend on private home-directory files, an auth broker, or a custom Codex CLI fork. T4 Code vendors `@oh-my-pi/app-wire` 0.5.2 from integration commit [`5d4315ee`](https://github.com/lyc-aon/oh-my-pi/commit/5d4315eea317260fec030e2b4726f10fed0cd5f6), source tree `713688e8099d4553a0a30b1bf415a7cffb5963f4`.
T4 Code v0.1.9 was verified with OMP 16.5.0 built from [`d4a0b934`](https://github.com/lyc-aon/oh-my-pi/commit/d4a0b9344e1796c0e56041cfeea3431a8a728e61), tagged [`t4code-16.5.0-appserver-3`](https://github.com/lyc-aon/oh-my-pi/tree/t4code-16.5.0-appserver-3). That public integration is based on the official upstream [`v16.5.0`](https://github.com/can1357/oh-my-pi/tree/v16.5.0) tag at [`3047c27c`](https://github.com/can1357/oh-my-pi/commit/3047c27c332c5629c8e063283d349384c10c9a56). It adds bounded large-session replay, complete desktop runtime events, catalog-backed session management, ordered remote delivery, cross-client control-state convergence, terminal streaming-state settlement, and restart-safe session teardown. The official upstream v16.5.0 tag has no `appserver` command, so it cannot host T4 Code. The verified runtime is a normal build from the public `lyc-aon/oh-my-pi` source; T4 Code does not depend on private home-directory files, an auth broker, or a custom Codex CLI fork. T4 Code vendors `@oh-my-pi/app-wire` 0.5.2 from integration commit [`5d4315ee`](https://github.com/lyc-aon/oh-my-pi/commit/5d4315eea317260fec030e2b4726f10fed0cd5f6), source tree `713688e8099d4553a0a30b1bf415a7cffb5963f4`.

| Platform | Arch | Package |
| --- | --- | --- |
| Android | arm64, armv7, x86_64 | `.apk` (**signed**) |
| Linux | x86_64 | `.deb`, AppImage |
| macOS | Apple Silicon (arm64) | `.dmg`, `.zip` (**unsigned, see below**) |

No Windows build and no Intel Mac build in v0.1.8.
No Windows build and no Intel Mac build in v0.1.9. The iOS TestFlight build is coming soon.

## What changed in v0.1.8
## What changed in v0.1.9

- The Tailnet browser client can now be installed to an Android or iPhone home screen. The titlebar download button uses the browser's native install prompt when available and otherwise shows the Add to Home Screen path.
- Installed web apps open in a standalone window with the T4 Code icon. The same titlebar slot becomes a Reload T4 Code button, so a phone user does not need browser chrome to refresh the client.
- Queue and Steer now work during an active turn without sending a stale session revision. Normal prompts keep their revision guard, and negotiated prompt leases remain supported.
- Fast mode now explains what the host actually requests: provider priority processing when supported. It does not imply a lower thinking effort.
- The installed client remains online-only. It fetches the current gateway UI on launch and does not cache host configuration, transcripts, or session state for offline use.
- The arbitrary physical-line ceiling is gone. File boundaries are now judged by cohesion and reviewability instead of a hard line count.
- The manifest, phone icons, gateway content type, install states, active-turn commands, and 320 × 568 titlebar layout are covered by unit and built-app browser tests. The verified OMP 16.5.0 and app-wire 0.5.2 runtime contract is unchanged from v0.1.7.
- Android now has a signed, installable APK. It is a thin client: the UI ships inside the app, while sessions and runtime state stay on your T4 Code host.
- First run accepts an HTTPS Tailscale hostname, checks the secure WebSocket route, and then uses the normal T4 pairing flow.
- Android device credentials are encrypted with an app-specific Android Keystore key. Only the non-secret Tailnet hostname is kept in web storage.
- The Tailnet gateway accepts the exact Capacitor Android and iOS origins while continuing to reject wildcard, opaque, and untrusted origins.
- The connection screen and titlebar controls are sized for small phone displays. A user can clear the saved host and pair again without reinstalling.
- The browser-installable PWA remains available, and the iOS download slot now reports that TestFlight is coming soon without advertising a build that is not published.

## Install

### Android

1. On the Android phone, sign in to Tailscale with an account that can reach the T4 Code host.
2. Download [`T4-Code-0.1.9-android.apk`](https://github.com/LycaonLLC/t4-code/releases/download/v0.1.9/T4-Code-0.1.9-android.apk).
3. If Android asks, allow your browser or file manager to install unknown apps, then install the APK.
4. Open T4 Code and enter the host's HTTPS Tailscale address, including its port.

The APK does not contain an appserver or expose one to the public internet. It connects to the separately running Tailnet gateway on your OMP host.

### Linux (Debian/Ubuntu)

```sh
wget https://github.com/LycaonLLC/t4-code/releases/download/v0.1.8/T4-Code-0.1.8-linux-amd64.deb
sudo apt install ./T4-Code-0.1.8-linux-amd64.deb
wget https://github.com/LycaonLLC/t4-code/releases/download/v0.1.9/T4-Code-0.1.9-linux-amd64.deb
sudo apt install ./T4-Code-0.1.9-linux-amd64.deb
```

Use `apt install` rather than `dpkg -i` so system dependencies resolve automatically.

### Linux (AppImage)

```sh
wget https://github.com/LycaonLLC/t4-code/releases/download/v0.1.8/T4-Code-0.1.8-linux-x86_64.AppImage
chmod +x T4-Code-0.1.8-linux-x86_64.AppImage
./T4-Code-0.1.8-linux-x86_64.AppImage
wget https://github.com/LycaonLLC/t4-code/releases/download/v0.1.9/T4-Code-0.1.9-linux-x86_64.AppImage
chmod +x T4-Code-0.1.9-linux-x86_64.AppImage
./T4-Code-0.1.9-linux-x86_64.AppImage
```

### macOS (Apple Silicon)

> [!WARNING]
> **The macOS v0.1.8 build is unsigned and unnotarized.** Apple has not signed or notarized it, so Gatekeeper can report a "damaged" app or an unidentified developer. Only continue if you trust the release from this repository. You can always build from source instead.
> **The macOS v0.1.9 build is unsigned and unnotarized.** Apple has not signed or notarized it, so Gatekeeper can report a "damaged" app or an unidentified developer. Only continue if you trust the release from this repository. You can always build from source instead.

1. Download [`T4-Code-0.1.8-mac-arm64.dmg`](https://github.com/LycaonLLC/t4-code/releases/download/v0.1.8/T4-Code-0.1.8-mac-arm64.dmg) (or [`T4-Code-0.1.8-mac-arm64.zip`](https://github.com/LycaonLLC/t4-code/releases/download/v0.1.8/T4-Code-0.1.8-mac-arm64.zip)).
1. Download [`T4-Code-0.1.9-mac-arm64.dmg`](https://github.com/LycaonLLC/t4-code/releases/download/v0.1.9/T4-Code-0.1.9-mac-arm64.dmg) (or [`T4-Code-0.1.9-mac-arm64.zip`](https://github.com/LycaonLLC/t4-code/releases/download/v0.1.9/T4-Code-0.1.9-mac-arm64.zip)).
2. Drag `T4 Code.app` into `/Applications`.
3. If Gatekeeper blocks the app and you choose to proceed, remove the quarantine attributes from the copied app bundle:

Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ We read every report and will reply to tell you what happens next. This is a sma

- T4 Code is a desktop client. The OMP runtime is a separate project; runtime vulnerabilities belong at <https://github.com/can1357/oh-my-pi>.
- Pairing credentials are encrypted with the OS keychain via Electron `safeStorage`. Reports about credential handling, the pairing flow, or the `t4-code://` deep-link handler are especially welcome.
- The macOS v0.1.8 build is unsigned and unnotarized; that is a known, disclosed limitation, not a vulnerability report. Removing `com.apple.quarantine` changes Gatekeeper handling but does not sign, notarize, or verify the app.
- The macOS v0.1.9 build is unsigned and unnotarized; that is a known, disclosed limitation, not a vulnerability report. Removing `com.apple.quarantine` changes Gatekeeper handling but does not sign, notarize, or verify the app.
2 changes: 1 addition & 1 deletion apps/desktop/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@t4-code/desktop",
"version": "0.1.8",
"version": "0.1.9",
"private": true,
"type": "module",
"main": "dist-electron/main.cjs",
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/target-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ export class DesktopTargetManager {
cursorStore: this.cursorStoreFactory(targetId),
capabilities: requestedCapabilities,
requestedFeatures: ADDITIVE_FEATURES,
client: { name: "T4 Code", version: "0.1.8", build: "desktop", platform: process.platform },
client: { name: "T4 Code", version: "0.1.9", build: "desktop", platform: process.platform },
reconnect: { attemptCap: 12, baseMs: 250, maxMs: 10_000 },
};
const client = createOmpClient(clientOptions);
Expand Down
23 changes: 23 additions & 0 deletions apps/mobile/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# T4 Code Mobile

Android thin-client shell for the bundled T4 Code web application. The APK contains the UI assets and connects over secure WebSocket to a separately running T4 Tailnet gateway; it never embeds or starts an OMP appserver.

## Build

```bash
pnpm build:web
pnpm --filter @t4-code/mobile build:android:debug
```

The installable debug APK is written to `android/app/build/outputs/apk/debug/app-debug.apk`.

A release build can be signed without committing credentials by setting all four variables before `pnpm --filter @t4-code/mobile build:android:release`:

```text
T4_ANDROID_KEYSTORE_PATH
T4_ANDROID_KEYSTORE_PASSWORD
T4_ANDROID_KEY_ALIAS
T4_ANDROID_KEY_PASSWORD
```

Without those variables Gradle still creates an unsigned release APK for CI inspection. The mobile shell deliberately has no Capacitor `server.url`; `cap sync` copies `apps/web/dist` into the native application.
Loading
Loading