Skip to main content

airpuls-sdk — command-line tool for airpuls Near-RT RIC xApps

Scaffolds, validates, runs, packages, and deploys xApp projects for the airpuls Near-RT RIC. Distributed as airpuls-sdk-cli; the command it installs is airpuls-sdk.

pip install airpuls-sdk-cli

Pure Python, published as a py3-none-any wheel: the tool installs wherever Python 3.9+ runs, so a project can be created from any developer machine. Linux and macOS are the supported platforms for the full workflow — see Platform support.

Two distributions, two jobs

Distribution What it is Platforms
airpuls-sdk-cli this tool — scaffolding, lifecycle, deployment Linux, macOS (installs anywhere Python runs)
airpuls-ric-sdk the xApp runtime: Python bindings for the C client library Linux x86_64 and aarch64 (binary wheels)

Most commands need only the CLI. Running an xApp locally — xapp run, and xapp test for suites that import the SDK — needs the runtime in the environment that runs the project. xapp new provisions exactly that by default: it creates the project's .venv and installs requirements.txt (production dependencies — the runtime on Linux) and tests/requirements.txt (pytest and the CLI the generated tests import) into it — --no-venv skips the step, and xapp venv provisions an existing checkout at any time. The commands that execute project code resolve their interpreter in that order — an activated virtual environment first, then the project's .venv, then the interpreter running the CLI — so a provisioned project runs correctly whether the CLI is invoked from a shell or by an editor. Installing the runtime beside the CLI works too:

pip install airpuls-sdk-cli[runtime]   # Linux x86_64 / aarch64

On macOS and Windows the runtime has no wheel, which is what the generated Dev Container is for: xapp new writes a .devcontainer/ whose Dockerfile pins an x86_64 Debian base, reproducing the environment the runtime needs, so the project is created natively and run inside the container. airpuls-sdk doctor reports which half is present, and xapp run explains the missing runtime rather than failing on an import error.

The container is built for the host's architecture, so it is native on an Apple-silicon Mac as well as on x86_64, and the runtime installs in both — the whole workflow runs inside it. The PLATFORM build argument in the generated .devcontainer/devcontainer.json forces a specific architecture when one is needed, at emulation speed.

Platform support

Linux macOS Windows
install, xapp new, validate, info, build, add sink, config, doctor yes yes yes
xapp connect, deploy, undeploy, credentials yes yes SSH keys only
xapp run, xapp test yes Dev Container Dev Container
xapp package needs docker needs docker needs docker

xapp deploy builds the launcher's image, so it needs docker as well — except with --no-image, which uploads the entry alone.

The deployment commands drive the OpenSSH tools. Windows ships an OpenSSH client, so key-based authentication works, but three things do not: the interactive password fallback (it uses the POSIX SSH_ASKPASS mechanism), the one-time ssh-copy-id offer, and the restrictive mode applied to a fetched client key. A Windows user therefore needs a key the deployment host already trusts. CI runs the suite on Linux; macOS is covered by development use, and Windows is not exercised at all.

Quick start

airpuls-sdk xapp new my-xapp        # interactive wizard; --defaults for CI
                                    # (generates, git-inits, and provisions
                                    # .venv; --no-venv skips)
cd my-xapp
airpuls-sdk xapp validate           # xapp.yml against xapp.schema.json
airpuls-sdk xapp build              # per-language build, output captured
airpuls-sdk xapp test               # run the project's tests/
airpuls-sdk xapp run                # foreground run against the RIC
airpuls-sdk xapp package            # <name>-<lang>:latest on airpuls-xapp-sdk
airpuls-sdk xapp info               # project summary
airpuls-sdk xapp add sink --file …  # configure a telemetry destination
airpuls-sdk xapp connect            # point the project at a registered RIC
airpuls-sdk xapp deploy             # build + push the image, entry → the host
airpuls-sdk xapp undeploy           # remove the entry from the host
airpuls-sdk xapp credentials        # register identity + fetch mTLS material
airpuls-sdk config ric add lab …    # register a RIC (several allowed)
airpuls-sdk doctor                  # environment diagnostics

Inside an xApp project doctor also reports the project environment: whether .venv is provisioned, and whether requirements.txt and tests/requirements.txt are satisfied by what is installed there — a fresh clone warns until airpuls-sdk xapp venv runs.

xapp package and xapp deploy build the project's image on the airpuls/airpuls-xapp-sdk base image, checking exactly the reference the project's Dockerfile FROM names (default latest; xapp new --base-tag pins an older release). An absent base image is reported with its remedy instead of surfacing as a docker build failure, and on a terminal the command offers to pull it from Docker Hub. Declining leaves the docker pull in the message, next to the alternative of building it from an airpuls-ric checkout.

Telemetry sinks

An xApp emits into two channels — timeseries for numeric samples and structured for event records — and xapp add sink configures a destination for them in xapp.yml:

airpuls-sdk xapp add sink                              # asks what to add
airpuls-sdk xapp add sink --file ./events.jsonl        # structured
airpuls-sdk xapp add sink --redis redis://host:6379/0  # both channels
airpuls-sdk xapp add sink --influxdb http://host:8086  # timeseries

Invoked without a destination on a terminal, the command asks which one to add and for the values it needs, each with its default in brackets. The InfluxDB organization and bucket default to what the deployment provisions — its InfluxDB setup creates a fixed xapp_metrics bucket for xApps beside the RIC's own — so a URL alone is a complete destination; --org and --bucket override them.

A destination serving both channels is added to both; --channel timeseries|structured narrows it. Only the keys the destination determines are written — every other setting keeps its schema default — and the manifest is validated before it is saved, so a sink never leaves xapp.yml violating its own contract. An already-configured sink is reported rather than replaced, and comments in the file survive the edit.

Credentials never appear on the command line or in the manifest: --token-env NAME (InfluxDB, default INFLUX_TOKEN) and --password-env NAME (Redis) record the name of the environment variable holding the secret. A secret typed as an argument would be kept in the shell history and written into xapp.yml, which is committed with the project and uploaded to the deployment host, so a value that is not a variable name is refused rather than stored — and the prompts ask for the variable name too, never the secret. Add a destination without switching it on with --disabled.

Deployment

Deployment follows the nrtric-ctrl pattern: every generated project includes an executable <name>.xapp docker-run launcher, and xapp deploy builds the image that launcher references, pushes it to the registry the reference names, then uploads the entry the stack consumes — launcher, xapp.yml, xapp.schema.json — into <nrtric-ctrl>/xapps/<name>/ on a registered host. The project source is never uploaded; the image is the runtime artifact, and the host pulls it.

The uploaded xapp.yml is a copy, adapted to the world the container reads it in — the project's own file is never edited. A relative file sink path moves onto /xapp/data, the directory the launcher mounts from the deployed entry and the only one a container running as the invoking user can write; keep sink paths under the project's data/ directory and one setting serves both sides. What the deploy cannot resolve for you it names instead of rewriting: an absolute path has to be mounted, and a 127.0.0.1 destination means the xApp's own container once it is on the host.

The image is tagged with the launcher's own reference, so what is pushed is what the host resolves. That reference must name a registry — registry.airpuls.de/xapps/<name>:latest, given by --image on xapp new. The scaffold default (<name>-<lang>:latest) names none, so it would push to Docker Hub; xapp deploy refuses it and says so. When the registry rejects the push for want of credentials, the error names the docker login to run.

Two alternatives to the registry:

flag effect
--image-copy build, then stream the image into the host's daemon over SSH (docker save | docker load) — for a host that cannot reach the registry; the image must be built for the host's architecture, a mismatch is refused
--no-image upload the entry alone, building and publishing nothing — a configuration-only redeploy

Several RICs can be registered (config ric add/list/use/remove); operations pick one with --ric, the registry default, or an interactive list. Transfers use the OpenSSH tools: key authentication first (an identity file can be stored per RIC or passed per command), and when the host denies it on an interactive terminal the CLI asks for the SSH password — masked, held only in process memory for that invocation, fed to OpenSSH through askpass so it never reaches a command line or a log — while config ric add offers a one-time ssh-copy-id that makes every later connection passwordless.

TLS

tls:// is first-class through the whole flow, and the RIC's own listener configuration is the source of truth for the transport:

  • xapp new with a tls:// endpoint emits the ric.tls file set the schema requires, at the paths the launcher mounts, and registers the xApp's identity with the RIC when a target is already configured.
  • xapp deploy reads the RIC's listener configuration before uploading. Against a tls:// listener it mints the client certificate (CN = deployment name, the RIC's identity binding), places CA, certificate, and key beside the deployed launcher, and — when the project still dials tcp:// — adapts the uploaded xapp.yml copy so a scheme mismatch cannot be deployed. Against a plaintext listener a tls:// project is refused: the CLI never downgrades a secured configuration.
  • xapp connect points the project at a registered RIC and, against a tls:// listener, reads that listener's server certificate to record its Common Name as ric.tls.server_name — so verification holds even when the certificate's subject alternative names do not cover the address the xApp dials. The read is advisory: when it fails the endpoint is still written, with a warning that the names must cover the address. xapp deploy reads the same certificate when it synthesizes the block for a project that never ran xapp connect, and carries an already-recorded identity into the uploaded copy untouched.
  • xapp credentials registers the identity and fetches CA + client pair into the project's certs/ directory, which xapp run picks up automatically.

Output

Subcommand output is never passed through to the terminal: the CLI captures it, reports the outcome with its cause and an excerpt on failure, and writes the full transcript under the project's .airpuls/logs/. Exit codes are typed: 1 generic, 2 usage, 3 validation, 4 subprocess, 5 toolchain/environment.

JSON output for editor integrations

--json, placed before the command (or AIRPULS_SDK_JSON=1), makes every command write exactly one JSON document on stdout instead of rendered text:

airpuls-sdk --json xapp info
airpuls-sdk --json doctor          # one record per environment check
airpuls-sdk --json config ric list # registered targets, for a picker
{"schema": 1, "ok": true, "command": "xapp info",
 "data": {"name": "my-xapp", "endpoint": "tls://ric:36422", "…": "…"},
 "warnings": []}

schema, ok, command, data, and warnings are common to every command; data fields are per command. A failure adds an error block — kind (usage, validation, subprocess, toolchain, interrupted, internal), message, cause, hint, detail, and the transcript path in log — and the process exit status is the same in both modes, so a caller can branch on the status and read the document only when it is non-zero.

--json --help answers with the command surface as data — the subcommands a group holds, and the arguments and options a command accepts, with their defaults and required flags — so an integration can build its own affordances without scraping help text.

The mode also makes every command non-interactive: nothing prompts, so a missing answer fails with a usage error naming the option to pass (xapp new needs its options or --defaults, xapp undeploy needs --yes) rather than blocking on a question nothing can answer. Warnings become warnings entries instead of terminal lines, and the streamed output of xapp run moves to stderr, keeping stdout a single document; its transcript path is in data.log.

Development

The suite runs without the xApp runtime and without a native build:

pip install -e .[test]
pytest tests

Metadata

Release files for airpuls-sdk-cli 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for airpuls-sdk-cli 0.2.0
File Interpreter ABI Platform Actions
airpuls_sdk_cli-0.2.0-py3-none-any.whl Python 3 none any Details for airpuls_sdk_cli-0.2.0-py3-none-any.whl

Release files / airpuls_sdk_cli-0.2.0-py3-none-any.whl

Download URL airpuls_sdk_cli-0.2.0-py3-none-any.whl
Size 126.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ed6f45093796502409217821e284d5e822cc12be88e940423502340b16c41ba9
BLAKE2b-256 checksum
How to use checksums
99ed4dd2ca70e14eda4bb06aa03aeea3b4a3076373d40ec80a7b76c1d2a593f5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.13

Security for airpuls-sdk-cli 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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