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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ef129bb7acc39a82339ec51f1cd5c539d869889c8cdc5c487f371188138c24af
|
|
| MD5 |
e1e51dcf4dea496e92f4af77f933a8a3
|
|
| BLAKE2b-256 |
c71749857b57c633969de586ff02c7e5c3f3cac7ad1c91f18ce11b85d58e68eb
|
File details
Details for the file py_unidbg_server-1.0.0-py3-none-any.whl.
File metadata
- Download URL: py_unidbg_server-1.0.0-py3-none-any.whl
- Upload date:
- Size: 35.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3e4d09dc0a0f4965a0b441b5d795a840662d1558fd57d6aa83f71a45212276fd
|
|
| MD5 |
d1f27be5151be38e69ea57919c65d171
|
|
| BLAKE2b-256 |
c3af83b91158dcd368a8a7d36859ccad3628a7c8092933ffb11877ed6193a6c5
|