Skip to main content

Unidbg Project Host — 1.0

One deployment, many independently managed Unidbg projects. The Python edition provides FastAPI, Swagger UI at /docs, and isolated reusable Python/JPype worker processes. The host itself never starts a JVM.

中文说明 · Architecture · Migration from 0.2 · Standalone Java edition

Install and start

Requires Python 3.11+ and a compatible installed JVM (JDK 17 is tested).

pip install py_unidbg_server==1.0.0
unidbg-server start --core ./unidbg_core --projects ./projects

Open http://127.0.0.1:8888/docs. Keep one host process per runtime directory; do not use multiple Uvicorn/Gunicorn workers against the same catalog. Increase project replicas instead. The CLI uses standard asyncio on Windows and Linux.

deployment/
  unidbg_core/                 shared core JAR inputs
  projects/
    demo/
      project.jar             business code, not a standalone HTTP service
      unidbg-project.json
      resources/              APK/SO/read-only rootfs seeds
  .unidbg-runtime/             host-owned snapshots, state, writable instances

Deploy only trusted local project packages. The host is not a security sandbox. Its management API intentionally defaults to loopback and has no built-in user authentication; put an authenticated proxy in front before exposing it remotely.

Project contract

Java projects keep their own normal main(String[] args) for debugging. Expose any number of public static Object method(String payload) methods. HTTP data is serialized as JSON once before entering that String contract; results are returned as strings. No server SDK, Spring application, or per-project port is required.

{
  "version": "2026.09.1",
  "execution": {
    "replicas": 2,
    "queue_capacity": 32,
    "queue_timeout": 30,
    "call_timeout": 60,
    "idle_timeout": 300
  },
  "lifecycle": {
    "class": "com.example.Project",
    "load_method": "initialize",
    "unload_method": "close",
    "pass_project_dir": true
  },
  "apis": {
    "sign": {
      "class": "com.example.Project",
      "method": "sign",
      "description": "Sign an input using this project's emulator"
    },
    "decrypt": {
      "class": "com.example.Project",
      "method": "decrypt"
    }
  }
}

Lifecycle is optional. load_method, when present, must be static initialize(String projectDirectory) (the configured method name can differ). Use it to warm the emulator before readiness. Cleanup is static close(String projectDirectory) when pass_project_dir=true, otherwise close(). Close emulators, native handles, threads and executors there.

Each replica owns one JVM and a private writable copy of the project resources. All APIs on that replica execute serially; replicas and projects run concurrently. Do not hard-code a writable shared rootfs/cache path across replicas. Stateful sessions spanning multiple calls are not routed with affinity in 1.0: use one replica and avoid rolling updates until the session ends, or externalize state.

Calls, docs and lifecycle

curl -X POST http://127.0.0.1:8888/projects/demo/reload
curl -X POST http://127.0.0.1:8888/projects/demo/apis/sign -H "Content-Type: application/json" -d '{"data":{"value":1}}'
curl http://127.0.0.1:8888/list
curl -X DELETE "http://127.0.0.1:8888/projects/demo?timeout=30"
Endpoint Meaning
POST /call-java Legacy shape: project, class_name, optional method_name=start, data
POST /projects/{project}/apis/{alias} Call a declared alias with {"data": ...}
GET /projects/{project}/apis Cached API declarations and verification facts
GET /list, GET /health Nonblocking generation, readiness, running/queued counts
POST /projects/refresh Discover staged directories and refresh Swagger entries
POST /projects/{project}/reload Snapshot, warm, validate, and switch to a new generation
DELETE /projects/{project}?timeout=30 Persist disablement and drain accepted work
GET /docs, GET /openapi.json Generic routes plus concrete paths for every project alias

For new packages, stage the directory completely before refresh/reload. Do not modify the staging tree during snapshot creation; publish files through an operator-controlled atomic directory switch. Updating business code requires rebuilding the business JAR, not rebuilding/restarting this host.

Reload prepares every new replica before publishing its generation. Failed initialization or an invalid alias leaves the serving version unchanged. Already-accepted requests, including queued ones, stay pinned to the old version. Old resources close only after those leases drain. Allow spare process/memory budget for overlapping generations.

available: null means declared but not yet worker-verified. true means the class and public/static/String signature passed worker initialization, not that every input is business-valid. Readiness counts are separate. Reload or a first call performs verification; /list does not create a JVM per deployed directory.

Queue overflow/wait timeout returns 429. Disabled projects return 409. Java execution timeout returns 504 and discards the worker; a subsequent request can create a replacement. Worker disconnection returns 502. Submitted calls are never automatically replayed because their effects may already have occurred. Cancelling a client wait does not return an executing emulator to the pool.

A DELETE drain timeout returns 409 but remains disabled and drains in the background. Explicit reload re-enables it. Idle eviction, unlike disablement, keeps the project enabled and lazily reloadable. Idle timeout 0 disables eviction.

Settings

Use process environment variables (a .env.example is provided as a reference; the CLI does not implicitly load .env):

Variable Default
UNIDBG_RUNTIME_DIR <base>/.unidbg-runtime
UNIDBG_MAX_PROCESSES 16, including warming/draining generations
UNIDBG_STARTUP_TIMEOUT 60 seconds per replica
UNIDBG_SHUTDOWN_TIMEOUT 60 seconds for owned calls/control tasks
UNIDBG_MAX_MESSAGE_BYTES 4 MiB per HTTP body/IPC frame
UNIDBG_MAX_PROJECT_BYTES 1 GiB per project copy

Core/project/base/host/port environment settings remain available to ASGI factory users through ServerSettings.from_environment(). CLI core/project/host/port arguments take precedence. unidbg-server edit copies an editable ASGI factory.

Stopped Python worker instance directories are reclaimed after path/link checks. If a project created links, oversized output or locked files, cleanup retains the directory and logs a warning rather than traversing/retrying destructively. Immutable release snapshots are retained for diagnosis and restart; plan disk retention explicitly. Do not delete a selected or draining release. External files, subprocess trees created by business code, and external side effects are owned by the project and are not magically reclaimed by terminating one JVM.

Python API

Version 1 removes hidden global JVM state from the public API:

import asyncio
from py_unidbg_server import ServerSettings, create_manager

async def main():
    settings = ServerSettings.build()
    async with create_manager(settings) as manager:
        result = await manager.call("demo", '{"value":1}', api="sign")
        print(result)
        await manager.unload("demo")

asyncio.run(main())

Use the same manager on its owning event loop; do not create it per request. An OS file lock prevents concurrent hosts from mutating one runtime directory. The in-memory request queue is not a durable job queue: host failure can lose responses and clients must decide whether an unknown outcome is safe to retry.

Verification and distribution

pip install -e ".[dev]"
pytest -q
python -m build
python -m twine check dist/*

Tests cover units, HTTP composition, and real compiled JARs/child JVMs: multiple APIs, parallel instances, hot replacement, queue bounds, cancellation, process failure, timeout, and durable disablement. They do not prove arbitrary third-party Unidbg native backends are thread-safe or that a particular APK/SO business call is correct. Validate your real artifacts on the target OS.

The java-host/ directory is a separate JDK 17/Maven implementation. It is committed and tested on GitHub, but explicitly excluded from Python wheel/sdist and is not published to PyPI or Maven Central by this repository.

BSD-3-Clause license.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

py_unidbg_server-1.0.0.tar.gz (32.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

py_unidbg_server-1.0.0-py3-none-any.whl (35.7 kB view details)

Uploaded Python 3

File details

Details for the file py_unidbg_server-1.0.0.tar.gz.

File metadata

  • Download URL: py_unidbg_server-1.0.0.tar.gz
  • Upload date:
  • Size: 32.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for py_unidbg_server-1.0.0.tar.gz
Algorithm Hash digest
SHA256 ef129bb7acc39a82339ec51f1cd5c539d869889c8cdc5c487f371188138c24af
MD5 e1e51dcf4dea496e92f4af77f933a8a3
BLAKE2b-256 c71749857b57c633969de586ff02c7e5c3f3cac7ad1c91f18ce11b85d58e68eb

See more details on using hashes here.

File details

Details for the file py_unidbg_server-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for py_unidbg_server-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3e4d09dc0a0f4965a0b441b5d795a840662d1558fd57d6aa83f71a45212276fd
MD5 d1f27be5151be38e69ea57919c65d171
BLAKE2b-256 c3af83b91158dcd368a8a7d36859ccad3628a7c8092933ffb11877ed6193a6c5

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 files

0.2.0

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page