2011 · Phone · Apple

iPhone 4S

The retained UI through OpenGL ES 1.1 at Retina density, on iOS 6.1.3.

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

The 4S kept the iPhone 4's glass-and-steel body and added the dual-core A5, Siri and an 8 MP camera. Its 3.5-inch 640×960 Retina display was the densest phone screen of its day. Apple supported it through iOS 9.

The PocketJS host targets the exact iPhone4,1 / iOS 6.1.3 tuple. Modern Xcode no longer ships ARMv7 iOS libraries, so the build derives hash-pinned linker stubs from the operator's own restore image and refuses to run unless the device reports a 640×960 GLES1 drawable for the 320×480 logical viewport.

Processor
Apple A5 · 2 × 800 MHz
Memory
512 MB
Display
640 × 960 Retina
A black iPhone 4S showing its home screen
A black iPhone 4S showing its home screen. Photo: Domenic K. (Flickr) · CC BY 2.0
01

Hardware

curated here · sources below

Compute

SoC
Apple A5 (APL0498)
CPU
Dual-core ARM Cortex-A9, 800 MHz
GPU
PowerVR SGX543MP2
RAM
512 MB LPDDR2
Storage
8 / 16 / 32 / 64 GB

Display & input

Display
3.5″ IPS LCD “Retina”, 640 × 960, 326 ppi
Touch
Capacitive multi-touch
Cameras
8 MP with 1080p video; VGA front
Sensors
3-axis gyroscope, accelerometer, proximity, ambient light, compass

Connectivity

Radio
Quad-band GSM/EDGE, HSDPA 14.4, CDMA/EV-DO (dual-mode)
Wireless
Wi-Fi 802.11b/g/n, Bluetooth 4.0, GPS/GLONASS
Ports
30-pin dock connector, 3.5 mm
Battery
1432 mAh

Body

Dimensions
115.2 × 58.6 × 9.3 mm
Weight
140 g
Released
14 October 2011
OS
iOS 5.0 → 9.3.6 (final); PocketJS targets 6.1.3 (10B329)
02

PocketJS on this machine

Guest · QuickJS + native core

What runs

The host compiles the shared legacy UIKit runtime for ARMv7 and links it against a local sysroot extracted from a validated iOS 6.1.3 restore image: prepare-sysroot reads the dyld shared cache, thins it to ARMv7 and generates TAPI linker stubs. The retained UI core initialises at density 2 and the UIKit view's content scale is set to 2 before the renderbuffer is allocated. Deploys hold a device-side lease, hash every staged file, keep the previous bundle and roll back on failure.

What is proven

Status requires a live PID, advancing heartbeat and frame counter, a byte-exact installed build receipt and an empty runtime error; --require-action adds a completed touch sequence and a hero_tap that changed guest state; capture accepts only the 640×960 GLES1 Retina drawable. Private profile — outside POCKET_TARGETS.

Record

  1. v0.10.1

    The iPhone 4S runs the retained UI through OpenGL ES 1.1 at Retina density, with transaction leases and rollback.

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

Example code

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

The 320×480 logical demo rendered at 2×.

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

export default function IPhone4SHero() {
  return (
    <Hero
      actionLabel="Tap Hero"
      deviceLabel="running on a 2011 iPhone 4S."
      headline="JSX on A5."
      onAction={(count) => reportAppAction("hero_tap", count)}
      presentationHz={60}
      runtimeLabel="RUST + QUICKJS + UIKIT"
      spinnerFrameStep={6}
    />
  );
}
04

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 4S

3 min read · 768 words

Device state, build inputs, build/deploy, hardware acceptance.

rendered verbatim fromdocs/IPHONE4S.md@ 6c43f49raw ↗

The private iPhone 4S target runs PocketJS on an exact iPhone4,1 device with iOS 6.1.3 build 10B329. The application owns a 320×480-point surface backed by the phone's 640×960 Retina display. The target remains outside the public POCKET_TARGETS registry until support is expanded beyond this validated hardware and firmware tuple.

Device state

The device must be restored and jailbroken before the PocketJS tool connects. The validated recovery path uses Legacy iOS Kit's iOS 6.1.3 restore with a signed 9.3.6 baseband and the Aquila filesystem jailbreak. Restoring creates a new partition map and erases the data partition.

The completed bootstrap provides:

  • Cydia and a read/write root filesystem;
  • OpenSSH on device port 22;
  • a dedicated RSA client key and pinned device host key;
  • PasswordAuthentication no after public-key login succeeds;
  • ldid, uicache, and uiopen for application deployment.

The default local files are:

~/.cache/pocket-stack/iphone4s/ssh/id_rsa
~/.cache/pocket-stack/iphone4s/ssh/known_hosts

POCKETJS_IPHONE4S_KEY, POCKETJS_IPHONE4S_KNOWN_HOSTS, and POCKETJS_IPHONE4S_UDID override those paths and the selected USB device.

Build inputs

Modern Xcode no longer contains ARMv7 iOS libraries. prepare-sysroot reads the validated iOS 6.1.3 dyld shared cache from the operator's local CustomAJ restore IPSW, builds Apple's pinned dyld-210.2.3 extractor, thins the recovered images to ARMv7, and generates local TAPI linker stubs from their exported symbols.

Apple system binaries remain under ~/.cache/pocket-stack/iphone4s and are never copied into the repository or npm package. The checked-in manifest pins the firmware, dyld source, Csu source, QuickJS source, shared cache, and every system image used by the linker.

export POCKETJS_IPHONE4S_IPSW=/path/to/iPhone4,1_6.1.3_10B329_CustomAJ-ECID.ipsw
bun iphone4s setup-sources
bun iphone4s prepare-sysroot
bun iphone4s doctor

When the CustomAJ IPSW is under the default Legacy iOS Kit cache and exactly one matching file exists, the environment variable is not required.

Build and deploy

bun iphone4s build
bun iphone4s deploy
bun iphone4s launch
bun iphone4s status

build resolves apps/iphone4s-demo/pocket.json, builds the Solid guest, builds the no-std retained UI core for ARMv7, embeds the JS and PAK into the executable, links against the derived iOS 6 stubs, and signs the bundle with ldid. The build ID hashes the guest and artwork plus every object, archive, and linker stub used by the native link. The host runtime is first compiled with a fixed 32-byte identity placeholder so all transitive headers and compiled code affect that ID before the final ID-bearing object is produced. launch and status also require the complete installed file-hash receipt to match the local receipt.

The iPhone 4S host requires OpenGL ES 1.1, initializes the retained UI core at the resolved density 2, and sets the UIKit view's content scale to 2 before allocating its renderbuffer. A valid runtime receipt must report the gles1 renderer, density 2, and a 640×960 drawable for the 320×480 logical viewport. It fails instead of silently presenting the software rasterizer through a CAEAGLLayer when that contract cannot be established.

SpringBoard artwork uses hosts/iphone2g/Icon.png as its single source. The 1× PocketClassic-v3.png is copied byte-for-byte. The Retina PocketClassic-v3@2x.png is independently rasterized from hosts/iphone4s/Icon.svg with 8× supersampling, retaining the original icon's transparent rounded corners, chrome bevel, enamel face, Pocket mark, and curved glass highlight without duplicating each 1× source pixel into a 2×2 block. The versioned basename prevents SpringBoard from reusing artwork cached under an older bundle resource name. UIPrerenderedIcon keeps iOS from adding a second gloss treatment.

deploy verifies the exact device identity before opening a fresh UDID-scoped USB tunnel. It acquires and renews a device-side lease, uses transaction-specific paths, checks every staged file with device-side SHA-256, keeps the previous bundle through ownership, signature, and application-cache validation, and rolls back on failure. A later deployment atomically takes over an expired or incomplete lease and reconciles only the previous transaction's validated archive, stage, unpack, and backup paths.

Hardware acceptance

The host writes /private/var/tmp/pocketjs-iphone4s.status from the device frame loop. status requires a live PID, an advancing heartbeat and guest frame counter, a byte-exact installed build receipt, a matching build ID, and an empty runtime error. The record reports the actual renderer, drawable size, raster density, and clock instead of inferring them from the build.

bun iphone4s status --require-action
bun iphone4s capture

--require-action additionally requires a completed touch sequence and a hero_tap action that changed guest state. Touch down resolves the committed frame's bounds hit once and carries that hit fact through the contact.

capture accepts only the 640×960 GLES1 Retina drawable, requests the next device-rendered frame, downloads its bottom-up RGBA pixels, and writes the physical-resolution dist/iphone4s/device-frame.png. The one-shot marker is created as mobile and removed in cleanup; leaving a root-owned marker in the sticky temporary directory would force a full GPU readback on every frame.

Photo

Wikimedia Commons · Apple iPhone 4S (7997974054).jpg — Domenic K. (Flickr), CC BY 2.0.