2007 · Phone · Apple

iPhone (first generation)

A UIKit host with no Objective-C. Zero shaders on the MBX Lite. 59.99 fps.

Hardware-tested hostGuest · QuickJS + native corehosts/iphone2g/

The original iPhone ran iPhone OS on a Samsung S5L8900: an ARM11 clocked down to 412 MHz, a PowerVR MBX Lite with no programmable shaders, 128 MB of RAM and a 3.5-inch 320×480 multi-touch display. Apple's last update for it was iPhone OS 3.1.3.

PocketJS targets exactly that firmware. Current Xcode still emits ARMv6 and ld-classic still links a 2008 sysroot, but it crashes on the Objective-C relocations modern Clang emits — so the host is plain C that registers its view and delegate through the Objective-C runtime API. The guest, its asset pack and QuickJS ride inside the executable as Mach-O sections.

Processor
ARM11 · 412 MHz
Memory
128 MB
Display
320 × 480
An original 8 GB iPhone showing the iPhone OS 3.1.3 home screen
An original 8 GB iPhone showing the iPhone OS 3.1.3 home screen. Photo: Pavel Ševela · CC BY-SA 3.0
01

Hardware

curated here · sources below

Compute

SoC
Samsung S5L8900
CPU
ARM1176JZ(F)-S (ARMv6), 620 MHz rated, run at 412 MHz
GPU
PowerVR MBX Lite, 103 MHz, OpenGL ES 1.1 — no shaders
RAM
128 MB eDRAM
Storage
4 / 8 / 16 GB flash

Display & input

Display
3.5″ TFT LCD, 320 × 480, 163 ppi
Touch
Capacitive multi-touch
Sensors
Accelerometer, proximity, ambient light
Camera
2 MP

Connectivity

Radio
Quad-band GSM, EDGE
Wireless
Wi-Fi 802.11b/g, Bluetooth 2.0
Ports
30-pin dock connector, 3.5 mm (recessed)
Battery
1400 mAh

Body

Dimensions
115 × 61 × 11.6 mm
Weight
135 g
Released
29 June 2007
OS
iPhone OS 1.0 → 3.1.3 (final)
02

PocketJS on this machine

Guest · QuickJS + native core

What runs

The host is built against an iPhone OS 1.1.4 ABI floor and targets 3.1.3, probing both UIKit generations at runtime with respondsToSelector: and dlsym. Two render paths exist: the software rasterizer, default, holds 60 fps at ~7.6 ms per frame because both rasterize and composite are limited to the damaged rectangle; the OpenGL ES 1.1 backend for the MBX Lite is opt-in, pixel-verified, and slower because it re-submits the whole DrawList every frame. Deployment is a signed transaction with byte-exact readback and rollback over key-only USB SSH.

What is proven

Live-device schema-2 receipt: a live PID, advancing heartbeat, a completed touch release and an application-reported hero_tap; device framebuffer read back and diffed against the reference core (mean channel difference 0.04/255). The target stays outside the production registry, with a test asserting it.

Record

  1. Erase restore to the pinned 3.1.3 CustomHJ image; USB SSH and transactional deployment verified.

  2. v0.9.0

    Experimental ARMv6 target for iPhone1,1; the GL backend learns fixed-function ES 1.1.

  3. v0.9.1

    The GPU path was never losing: three measurement bugs corrected; first host verified by reading pixels back off hardware.

  4. v0.9.2

    The software composite follows the damage plan: 22–26 fps becomes 59.99.

Profile · as declared by the demo manifestsource ↗
Profile
iphone2g (private)
Logical viewport
320 × 480
Raster density
Requires
  • input.touch
  • text.glyphs.baked

Acquisition reports

04

Example code

upstream source · highlighted at build time
apps/iphone2g-demo/app.tsxtsx · 15 lines · @6c43f49

The 320×480 touch demo the device receipt reports on.

import Hero from "../hero/app.tsx";
import { reportAppAction } from "@pocketjs/framework/host";

export default function IPhone2GHero() {
  return (
    <Hero
      actionLabel="Tap Hero"
      deviceLabel="running on a 2007 touchscreen."
      headline="JSX on ARMv6."
      onAction={(count) => reportAppAction("hero_tap", count)}
      presentationHz={60}
      runtimeLabel="RUST + QUICKJS + UIKIT"
    />
  );
}
05

Bring-up guide

upstream documents · rendered verbatim

Toolchain, build, deploy and acceptance are owned by pocket-stack/pocketjs. The documents below are rendered from the pinned checkout without edits; relative links point back into the repository at the same revision.

iPhone 2G / iPhone OS 3.1.3 host

3 min read · 637 words

doctor → bootstrap → build → deploy → launch; the two render paths; acceptance.

rendered verbatim fromhosts/iphone2g/README.md@ 6c43f49raw ↗

This is the deliberately private PocketJS host for the original iPhone (iPhone1,1). The connected-device target is iPhone OS 3.1.3 (7E18). Xcode 26 still emits ARMv6 and ld-classic links against the byte-verified stock 1.1.4 (4A102) sysroot, so 1.1.4 is the executable's linker ABI floor rather than the installed-system target.

The host is C on purpose. Modern ld-classic can link ARMv6 code and stock ObjC libraries, but crashes while translating ObjC1 class-reference relocations emitted for an @implementation. runtime.c therefore registers its view and delegate through the Objective-C runtime API. It uses the 3.x application lifecycle and UIKit touch selectors on the current phone, while retaining the older 1.x event path as an ABI fallback. Device input is exposed to PocketJS as the hardware-neutral input.touch capability.

Run the local checks, install the key-only USB deployment helper, build, and deploy the complete demo bundle:

bun iphone2g doctor
bun iphone2g prepare-bootstrap
bun iphone2g install-bootstrap
bun iphone2g build
bun iphone2g deploy
bun iphone2g launch

install-bootstrap preserves the working CustomHJ sshd, device host key, and launchd plist. It installs only the signed pocketjs-device helper, merges the dedicated client key, pins the existing device host key, and disables password SSH only after key authentication and helper verification succeed. The USB tunnel is managed automatically for install, deploy, launch, and status; use bun iphone2g tunnel only when a persistent foreground forward is useful.

After deploy refreshes the application cache and restarts SpringBoard, launch verifies the installed build receipt and asks SpringBoard to open PocketJS through its private URL scheme. Tap the Hero action, then run:

bun iphone2g device-status

That command accepts only a fresh schema-2 status record for the current build. The recorded process must still be alive, its heartbeat must be recent, a touch release must have completed, and the application must have reported a changed hero_tap count. A bounds hit, successful build, or byte-exact installation alone is not live runtime acceptance.

The earlier schema-1 receipt for build ba1c0b15af4fdb72c6a98334332a8954 reported 118 running guest frames and 11 touch sequences. It is retained as historical evidence but is no longer accepted because it lacks process liveness, heartbeat, release completion, and application-action fields. The app bundle carries a dedicated 59-by-60 transparent-corner icon with black enamel, a chrome bevel, and a pre-baked glass highlight for the classic SpringBoard. The phone retained the complete previous app bundle, key-only SSH, and the helper across a Home + Power restart after device-side /sbin/reboot stalled on its shutdown spinner. That is a forced-restart recovery result, not proof that unattended /sbin/reboot completes on this installation.

The host has two render paths. The software rasterizer is the default and holds a locked 60 fps at ~7.6 ms per frame, because both the rasterize and the composite are limited to the damaged rectangle. The OpenGL ES 1.1 backend for the device's PowerVR MBX Lite is opt-in (touch /private/var/tmp/pocketjs-iphone2g.gles1), correct, and pixel-verified, but costs 17-20 ms because it re-submits the whole DrawList every frame.

Both paths are verified against the reference core by capturing the device's own output; docs/IPHONE2G.md documents the marker files, the byte-order and orientation difference between the two captures, and the ES 1.1 state that has no ES 2 equivalent.

Artifacts are written to dist/iphone2g/PocketJSDemo.app. The app contains the generated Solid/PocketJS guest, pinned QuickJS, PocketJS raster core, and UIKit host. Firmware, the decrypted sysroot, Apple Csu and QuickJS sources, pairing records, SSH keys, ramdisks, historical bootstrap packages, and Cargo target cache live only under the shared Pocket Stack cache. They are never copied into the repository.

See docs/IPHONE2G.md for the exact workflow and the archived 1.1.4 recovery incident. The current deployment does not enter DFU, restore firmware, alter activation or baseband state, enable AFC2, replace CustomHJ SSH components, or change fstab; the restored 3.1.3 root and data volumes remain read/write by design.

Photo

Wikimedia Commons · Apple iPhone 2G 8GB (15).jpg — Pavel Ševela, CC BY-SA 3.0.