Skip to content

Add linux/386 build target for iSH on iOS#233

Open
badoriie wants to merge 2 commits into
Psiphon-Inc:mainfrom
badoriie:main
Open

Add linux/386 build target for iSH on iOS#233
badoriie wants to merge 2 commits into
Psiphon-Inc:mainfrom
badoriie:main

Conversation

@badoriie

@badoriie badoriie commented May 27, 2026

Copy link
Copy Markdown

Background

iSH is a free iOS app that runs an Alpine Linux x86 environment on iPhone using a user-mode x86 emulator. It lets volunteers run server software on a phone they already own — no VPS required.

Since the Conduit iOS app is not currently on the App Store, iSH is a practical path for iOS users to contribute as a Conduit node. I tested this end-to-end on my own iPhone and it works.

What this PR does

1. linux/386 CLI build target (cli/)

Adds build-linux-386 to the Makefile and wires it into build-all, build-all-embedded, and the release workflow so conduit-linux-386 appears in every release.

Three build-time optimisations specific to iSH:

Flag Reason
GO386=softfloat iSH emulates SSE2 in software. Softfloat makes Go emit plain integer instructions instead of SSE2 for float ops, reducing emulation overhead.
netgo Uses Go's pure-Go DNS resolver. Avoids glibc DNS syscall overhead under iSH's emulation layer.
GOMAXPROCS=1 (runtime, cmd/ish_linux_386.go) iSH's %gs thread-local storage emulation is unreliable in signal handler context. With multiple Ps, Go sends SIGURG to preempt goroutines across OS threads, which triggers a fatal: bad g in signal handler crash on startup. Limiting to one P keeps all goroutines on a single scheduler, preventing the cross-thread SIGURG delivery that causes the crash.

The binary is statically linked (CGO_ENABLED=0) so it runs on iSH's Alpine/musl userland without any extra dependencies or setup beyond chmod +x.

2. Concurrent session refresh race condition fix (src/hosted/sessionQueries.ts)

Unrelated to iSH, but found while working in this area.

When a session is about to expire, useHostedAccountProfileQuery and useHostedConduitsQuery both mount and independently call withHostedSessionRecovery → ensureHostedSession → refreshHostedSession. Because they run concurrently before either has written the refreshed token back to the cache, they both see the expiring token and both call sessionClient.refresh() — producing redundant parallel refresh requests.

Fixed by coalescing concurrent calls per queryClient+baseUrl into a single in-flight promise using a WeakMap<QueryClient, Map<string, Promise<HostedSession>>>. All concurrent callers share the same request. WeakMap keying on queryClient gives each test instance an isolated map automatically, with no teardown needed.

Testing

iSH (iPhone):

apk add curl
curl -L -o conduit https://github.com/badoriie/conduit/releases/download/release-cli-2.1.5-beta.9/conduit-linux-386
chmod +x conduit
./conduit start --max-common-clients 2

Confirmed working — binary starts, announces, and relays traffic.

Session refresh fix:

npm run test

All 483 tests pass. The previously failing test (refreshes expiring sessions via session client) now correctly expects sessionClient.refresh to be called once.

@badoriie
badoriie marked this pull request as draft May 27, 2026 18:29
@badoriie
badoriie marked this pull request as ready for review May 27, 2026 19:34
badoriie added 2 commits May 27, 2026 21:36
iSH (https://ish.app/) is a Linux x86 emulator for iOS that lets
volunteers run server-side software on an iPhone. This adds first-class
support for running a Conduit node on iSH, opening up Conduit to people
who only have an iOS device and want to help users in restricted
countries where the iOS app is not yet on the App Store.

Changes:
- New `build-linux-386` Makefile target producing statically linked
  x86 32-bit binaries (conduit-linux-386, conduit-monitor-linux-386)
- Built with GO386=softfloat: iSH emulates SSE2 instructions in
  software; softfloat makes Go emit simpler integer instructions
  instead, reducing emulation overhead
- Built with netgo tag: uses Go's pure-Go DNS resolver, avoiding
  glibc DNS syscall overhead under iSH's emulation layer
- GOMAXPROCS=1 set at startup (cmd/ish_linux_386.go): iSH's %gs
  thread-local storage emulation is unreliable in signal handler
  context. With multiple Ps, Go sends SIGURG to preempt goroutines
  across OS threads, triggering a "bad g in signal handler" crash.
  Keeping all goroutines on a single P prevents this.
- conduit-linux-386 added to release artifact uploads and GitHub
  release assets in the CLI release workflow
- GO_BUILD and GO_BUILD_MONITOR macros extended with an optional
  extra-env parameter so GO386=softfloat can be passed without
  duplicating build logic

Tested on iPhone running iSH with Alpine Linux. The binary starts
and successfully relays traffic with --max-common-clients 2.

Usage on iSH:
  apk add curl
  curl -L -o conduit https://github.com/.../conduit-linux-386
  chmod +x conduit
  ./conduit start --max-common-clients 2
Multiple background queries (useHostedAccountProfileQuery,
useHostedConduitsQuery) each call withHostedSessionRecovery on mount.
When the loaded session is expiring, all of them independently call
sessionClient.refresh() before any one of them has written the
refreshed token back to the query cache. This results in redundant
parallel refresh requests.

Fix by coalescing concurrent refresh calls for the same
queryClient+baseUrl into a single in-flight promise, using a
WeakMap<QueryClient, Map<baseUrl, Promise>> so all concurrent callers
share one network request. WeakMap keying on queryClient ensures
each test gets an isolated map without any teardown needed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant