Files
core/sdk/README.md
T

5.9 KiB

External Worker SDKs

Compiler authors: the private native release workflow builds and signs Dever runtime packs. It is separate from these external-language Worker SDKs and from application configuration.

These are stdio Worker libraries for Python, JavaScript (Node), and Go. The compiler remains the sole owner of Port schemas. Each checked external Adapter exposes ExternalWorkerContract::sdk_manifest(), including the exact identity and recursive setting, input, output and declared-error types. The managed Python and JavaScript runners package this manifest and bind each Port operation to a same-named function exported by the Adapter entry; JavaScript accepts ESM named exports or CommonJS module.exports properties. Application code does not maintain a second registration table. Python uses Worker.from_manifest(manifest, handlers), JavaScript uses Worker.fromManifest(manifest, handlers), and Go uses NewFromManifest(manifest) followed by RegisterHandlers(handlers). These paths decode inputs and validate results and declared business-error payloads from the same compiler-generated contract. The SDK checks the identity during hello and never adopts a schema supplied by the supervisor.

The lower-level Contract/Worker registration API remains available for protocol fixtures. The setting decoder runs before ready; it receives only the hello.setting value. Handlers return JSON-compatible values or a declared BusinessError. Python handlers are async; JavaScript handlers receive an AbortSignal; Go handlers receive a context.Context. A cooperative cancellation receives one dever.cancelled protocol error and discards its handler result. If a handler ignores cancellation for one second, the Worker fails and admits no further calls. The supervisor owns the Worker process and its stdin lifetime; an in-process Go Serve caller must close or unblock its own io.Reader when returning.

The three example entries use the same fixture identity and contract to demonstrate the lower-level API. fixture-schema-v1 and test/external-sdk/typed_contract.json are test data, not production Port hashes. Worker stdout is reserved for 4-byte big-endian length-prefixed UTF-8 JSON frames. Diagnostics go to stderr. The libraries use no external packages and do not read environment variables or PATH. A managed Worker entry reads only its explicitly packaged manifest path. Managed Python packs require fixed -I -S -B flags; the runner imports its packaged SDK before exposing Adapter source and dependencies on sys.path.

Run the opt-in host SDK check with explicit absolute runtime paths, for example:

/usr/bin/python3 -B test/external-sdk/check.py --python /usr/bin/python3 --node /absolute/path/to/node --go /absolute/path/to/go

Omitted runtime paths report unavailable; the script never discovers language runtimes through PATH. It builds the Go fixtures in a temporary offline cache. This check exercises both the lower-level and compiler-manifest-shaped typed paths on host interpreters. Managed runtime packaging and execution on a machine without host language runtimes require separate acceptance.

Managed dependencies use dever-lock-v5. Python extras and selected wheel METADATA, npm nested instances/peers/optional/bundled dependencies, and authenticated Go module evidence are part of the exact lock. Native Python wheels install into the packaged interpreter prefix and must satisfy its signed tags, extension ABI and loader closure.

Cross builds select the application platform explicitly: dever lib update <project> --target linux-aarch64, followed by dever build <project> --target linux-aarch64 --output <new-file>. Worker source and Port contracts stay unchanged. Locks, interpreters, native dependencies and sandbox assets must match that target. A Go runtime pack additionally declares build.host for its compiler/linker/analyzer; build.goos/goarch and standard-library object headers describe the output architecture. The tools run with a cleared environment and must have the correct target baked in. Missing target assets are errors, with no host runtime fallback. run and ordinary tests remain host operations.

Only explicit Lib preparation may build Python sdists or run npm registry installation hooks. The shared build owner uses signed compiler/header/sysroot/frontend packs in a Linux sandbox with no network, immutable inputs and owned writable scratch space. Python PEP 517 build requirements have their own exact lock; each hook has a fresh process, and the parent retains the metadata snapshot used to validate the final wheel. npm uses its packaged lifecycle runner for preinstall, install and postinstall, including dependency bins and node-gyp. Missing tools do not fall back to host PATH.

Build receipts bind sources, target runtimes, tools, frontend, dependency graph and final output hashes. Worker preparation replays those outputs offline without running installation hooks. Every npm .node output is classified by target/loader checks and an isolated Node load probe; only admitted addons enter the immutable Worker tree, including trees used by threads or child processes. Rejected platform/ABI variants remain in the locked archive. Runtime native dependencies must come from that tree or the signed OS library set, never the compiler sysroot. These rules do not change the Port protocol or require an application-side registration layer.

A generated addon that fails admission fails the build; a pure JavaScript fallback is not evidence of successful native compilation. Build subprocesses have a five-minute deadline and separate 16 MiB stdout/stderr limits. Scratch usage is checked every 250 ms against 512 MiB and 65,536 entries, with owned-process cleanup on failure. This periodic check is not a kernel disk quota and may observe overshoot between checks. The final Worker installation remains limited to 256 MiB and 65,536 entries.