DeepSeek Harness Android

English | 中文

app/ is an independent Capacitor Android application for running DeepSeek Harness in a local Ubuntu userspace. Once the runtime is ready, opening the app starts Harness and enters the in-app conversation directly; no external browser is required. Harness service controls, Ubuntu installation and reset, terminals, runtime source details, and optional Shizuku-backed device shell access live under Settings.

Build requirements

  • Node.js ^22.19.0 || >=24.0.0, matching the current DeepSeek Harness engine range. Node.js 11.9 cannot build supported Capacitor releases or the current DeepSeek Harness upstream.
  • JDK 23.0.1, with JAVA_HOME set explicitly when the system default still points to Java 8.
  • Android SDK 35 and a compatible Android NDK.
  • The pinned ARM64 PRoot runner and loader used by the release. The current
    release artifacts come from the Operit2 Android runtime toolchain at commit
    dc4c3a9405dc7ed3ef69b2ac9a6ace65374d77cf.

The Android WebView does not run Node.js. The installed Ubuntu environment must contain Node.js in the exact supported range ^22.19.0 || >=24.0.0; Node.js 23 is not supported by the current Harness.

Local workflow

pnpm install --frozen-lockfile
pnpm run build
pnpm run android:sync

The 0.1.7 CI release is a thin APK. The workflow builds the mobile Harness
conversation frontend, injects it into an Ubuntu 24.04 ARM64 image containing
Node.js 24.19.0 and @deepseek-ai/dsh 0.1.0-rc.6, and publishes three assets
under one v0.1.7-mobile-<run_number> tag: the APK, runtime-manifest.json,
and rootfs.bundle. The manifest contains that same Release's rootfs URL and
exact archive metadata. CI hashes the finished manifest and compiles its URL
and SHA-256 into the matching APK; generated runtime assets and transaction
backups are removed from APK assets before Gradle runs. Installing an official
Release therefore does not require entering a manifest URL or digest.

The manifest bytes must match the APK-pinned digest before parsing. The parsed
manifest then pins the rootfs URL, byte length, architecture, compression, and
SHA-256. Each HTTPS redirect is revalidated against public-destination policy,
and the final content remains digest-bound. Remote archives use an app-private
rootfs-<sha256>.part file, so an interrupted transfer can resume across app
or process restarts. A resumed response must be HTTP 206 with the exact
expected Content-Range; HTTP 200 or 416 causes a clean, verified restart from
byte zero. Network, TLS, timeout, and incomplete-transfer failures enter an
explicit error state while retaining a valid bounded partial file. The UI does
not report extraction until acquisition and archive verification have
completed.

scripts/build-embedded-runtime.py remains the deterministic image builder
used by CI and also supports an explicitly constructed embedded development
build. Embedded and remote acquisition share the same size, digest,
extraction, and atomic-promotion checks. Do not put API keys, passwords,
database credentials, signing passwords, or tokens in .env, Gradle files,
source code, manifests, URLs, or logs.

The native runner files are generated or imported separately and never
committed. A release APK packages both
lib/arm64-v8a/libdsh_proot.so and
lib/arm64-v8a/libdsh_proot_loader.so; both are required. The existing
prepare:runner flow remains available for a separately pinned runner source,
but it does not replace the provenance and license review for the exact two
binaries shipped in an APK.

The runtime manifest fields, CI pinning flow, and security boundaries are
documented in docs/ARCHITECTURE.md. See
docs/RELEASE_CHECKLIST.md before distributing an
APK or runtime asset.

Security checkpoints

  • Native bridge inputs have explicit type, length, format, and state validation.
  • The app has no user-facing login or Android device-credential gate. The
    conversation opens directly, while management operations remain in the
    app's Settings surface. This does not weaken the separate loopback transport
    credential described below.
  • Embedded and downloaded artifacts require exact digests and byte limits;
    downloads additionally require HTTPS, resumable digest-named staging files,
    strict Range-response validation, and atomic promotion.
  • Archive extraction prevents traversal and does not create device nodes. The
    exact compressed stream consumed by the extractor is counted and hashed
    again before promotion, independently enforcing the manifest's compressed
    size and SHA-256 during decompression.
  • Before launching Ubuntu, the app probes the packaged PRoot runner and its
    seccomp compatibility, then requires individually validated bind mounts for
    the generated resolver file, /dev, and /proc. Startup fails closed if a
    required source, guest target, or compatibility probe is unavailable.
  • Harness binds only to Android loopback; no business service is exposed on 0.0.0.0.
  • Every Harness start generates a non-persistent 256-bit credential. A rootfs
    preload authenticates both HTTP and WebSocket upgrades before upstream
    handlers run, and the non-exported internal WebView answers the Basic-auth
    challenge transparently. An open TCP port alone is not considered ready: two
    loopback probes separated by a stability interval must return HTTP 401 with
    the exact expected Basic realm and UTF-8 challenge. The credential is never
    placed in the URL or audit log.
  • Shizuku access requires the declared ShizukuProvider, a visible permission
    grant, an explicit successful UserService connection, and a user-opened
    terminal session. Settings exposes a Connect Shizuku action whenever
    permission exists but the service is disconnected. Binder or UserService
    loss clears active sessions, and connected is true only while a live
    authorized UserService binder is available.
  • Reset is confined to the app-private runtime root and does not follow symbolic links.
  • Owner-only audit files rotate daily and retain at least 90 days of fixed event/result codes.
  • No credential, URL, command, session identifier, terminal content, or sensitive user data is written to application audit files.

Licensing

The original application source is MIT licensed. That does not replace or
weaken the licenses of packaged runtime components. Direct dependency and
runtime redistribution obligations are summarized in
THIRD_PARTY_NOTICES.md. In particular, the PRoot
runner is GPL-2.0-or-later and the referenced Operit2 source/build material is
AGPL-3.0. A distributor must provide the applicable license texts, complete
corresponding source for the exact shipped artifacts (including modifications
and the scripts needed to build them), and clear source-acquisition
instructions for as long as the applicable licenses require. The current
provenance record identifies a source revision and binary digests; it does not
claim a bit-for-bit reproducible rebuild.