Metadata-Version: 2.4
Name: nebula-cli
Version: 1.3.0
Summary: Nebula Cloud command line client and adaptive computer/server cockpit.
Project-URL: Homepage, https://nebulacoder.com
Project-URL: Documentation, https://docs.nebulacoder.com/cli
Project-URL: Issues, https://nebulacoder.com/contact
Author: Nebula Coder
License: MIT
License-File: LICENSE
Keywords: cloud,nebula,systems,terminal,textual,tui
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: System :: Monitoring
Requires-Python: >=3.10
Requires-Dist: cryptography<50,>=42
Requires-Dist: httpx<1,>=0.26
Requires-Dist: keyring<27,>=24
Requires-Dist: psutil<8,>=6.1
Requires-Dist: rich<16,>=13.9
Requires-Dist: textual<8,>=2.1
Requires-Dist: websockets<16,>=13
Provides-Extra: dev
Requires-Dist: build<2,>=1.2; extra == 'dev'
Requires-Dist: pytest-asyncio<2,>=0.24; extra == 'dev'
Requires-Dist: pytest<10,>=8.3; extra == 'dev'
Requires-Dist: respx<1,>=0.22; extra == 'dev'
Requires-Dist: ruff<1,>=0.9; extra == 'dev'
Provides-Extra: packaging
Requires-Dist: pyinstaller<7,>=6.11; extra == 'packaging'
Description-Content-Type: text/markdown

# Nebula CLI 1.3.0

Nebula CLI connects computers and servers, projects, and storage to Nebula Cloud while keeping local diagnostics useful when the network is unavailable. It is a new product package; the historical system-manager example beside this directory is not imported, modified, or upgraded in place.

## Install and start

Python 3.10–3.13 is supported on Linux, macOS, and Windows on x64 and arm64. The universal wheel is the recommended macOS and Windows install; Debian and RPM packages are the recommended Linux installs.

```bash
pipx install https://nebulacoder.com/download/cli/v1.3.0/nebula_cli-1.3.0-py3-none-any.whl
nebula
```

This first installation is necessarily an HTTPS bootstrap: the not-yet-
installed CLI cannot use the public key bundled inside itself to authenticate
its own wheel. Subsequent discovery and installation fail closed unless the
release manifest verifies against that bundled offline-owned key.

Both installed commands are equivalent:

```bash
nebula version
nebulacoder version
```

Bare `nebula` opens one responsive Textual cockpit. First run requires a device name and Computer/Server type, and optionally saves System, Light, or Dark appearance. Home combines live local health with Cloud identity, Computers & Servers loads enrolled devices from the Cloud API, and detailed process/network diagnostics stay under More.

```bash
nebula --theme system
nebula --density balanced
```

## Connect Nebula Cloud

The default endpoint is `https://cloud.nebulacoder.com`. Login uses browser device authorization; a password is never collected by the CLI.

```bash
nebula login
nebula whoami
nebula system enroll --name studio-pc --device-type computer
```

For SSH/headless hosts:

```bash
nebula login --headless
```

Credentials use the operating-system keyring by default. On a host without a working keyring, the owner may explicitly select the mode-0600 file fallback:

```bash
nebula login --secret-storage file --allow-insecure-file-storage
```

The fallback is plaintext at rest, protected only by Unix ownership and permissions. The CLI refuses to read it if permissions differ from 0600.

Named profiles isolate endpoint and account credentials:

```bash
nebula profile add production --endpoint https://cloud.nebulacoder.com --use
nebula profile add local --endpoint http://localhost:3000
nebula --profile local whoami
nebula profile list
```

Non-local endpoints must use HTTPS. Profile configuration follows `$XDG_CONFIG_HOME/nebula/config.json` when set and otherwise uses the user's `.config/nebula` directory; state and fallback credentials use the corresponding user-owned state/data roots on every platform.

## Scriptable commands

```text
login  logout  whoami  profile  project  storage  system  agent
incident  process  network  service  logs  shell  server
doctor  version  update
```

Run `nebula COMMAND --help` for the exact surface. Representative operations:

```bash
nebula project list
nebula project sync PROJECT_ID ./my-project --direction both
nebula storage usage
nebula system list
nebula incident list --system DEVICE_ID
nebula process list --local
nebula network status --system DEVICE_ID
nebula service restart nginx.service --local --yes
nebula logs --system DEVICE_ID --lines 50
nebula server status
nebula update
nebula update --download ./nebula-cli-update.whl
nebula update --install --yes
```

`nebula update` verifies the canonical Ed25519 manifest. `--download` streams
the exact wheel with redirects disabled and checks its signed size and SHA-256.
`--install --yes` passes only that verified local file to pipx; it never asks
pipx to perform a second unauthenticated URL download.

Local state-changing commands require explicit confirmation with `--yes`. Nebula CLI 1.3.0 does not enable remote shell, remote service mutation, or privileged command handlers; those commands return a clear safety-boundary error instead of queuing an action that cannot run.

Project synchronization scans regular UTF-8 text files only, ignores symlinks and Cloud-reserved directories, hashes every transferred file with SHA-256, and stores per-profile base hashes under XDG state. Each scan is limited to 10,000 files, 10 MiB per file, and 15 MiB per request so it remains below Cloud's JSON limit. Pulls validate every relative path and content hash, reject symlink destinations, and replace safe files atomically. If both local and Cloud changed, the CLI exits with conflict code 13 and includes the complete conflict report without overwriting the local file. Binary files and unsupported entries are listed under `skipped`; deletion synchronization is not part of the Cloud v1 protocol.

Global automation options may appear before or after nested subcommands:

```bash
nebula system list --profile production --json
```

JSON mode writes exactly one object to stdout:

```json
{"schema_version":"1.0","ok":true,"data":{},"error":null,"request_id":"..."}
```

Stable exit codes:

| Code | Meaning |
|---:|---|
| 0 | Success |
| 2 | Invalid usage |
| 10 | Authentication required/failed |
| 11 | Authorization denied |
| 12 | Not found |
| 13 | Conflict or replay |
| 14 | Cloud/service unavailable |
| 15 | Remote operation failed |
| 20 | Internal CLI failure |

`NO_COLOR=1` disables styled human output. JSON mode never includes ANSI styling or interactive prompts on stdout.

## Systems agent

`nebula system enroll` creates the machine's Ed25519 private key and CSR locally, consumes the one-use Cloud grant, validates the issued certificate against that key, and stores the identity under `$XDG_STATE_HOME/nebula/agent`. Every private key, certificate, CA, Cloud signing key, agent secret, and descriptor is mode 0600; the enrollment token and agent secret are never printed.

```bash
nebula agent status
nebula agent run --device SYSTEM_ID --once
nebula agent run --device SYSTEM_ID
```

The agent transport boundary provides:

- outbound `wss://` only with an owner-enrolled mTLS certificate;
- mandatory signature verification supplied by the Cloud trust configuration;
- device, protocol, capability, expiry, sequence, nonce, and idempotency validation;
- bounded frames and connection timeouts;
- canonical Ed25519 verification for every Cloud command envelope;
- no built-in shell, service mutation, process mutation, or privileged handler.

When a local, non-production Cloud has no configured agent CA, enrollment is clearly labeled `bearer-rest-development`. The one-time agent secret stays in a mode-0600 file and is sent only in the Authorization header to the configured local endpoint. Production refuses this downgrade and uses mTLS/WSS. A failed second enrollment stage remains recoverable with `nebula agent complete SYSTEM_ID` while the ten-minute grant is valid.

## Development and packaging

```bash
python3 -m venv .venv
. .venv/bin/activate
python -m pip install -e '.[dev]'
ruff check src tests
pytest
python -m build
```

PyPI artifacts are produced in `dist/`. Debian and RPM builds run on native x64/arm64 Linux hosts:

```bash
./packaging/build-deb.sh
./packaging/build-rpm.sh
```

Run native package builds in clean Debian/RPM containers for each supported architecture. The scripts intentionally do not cross-label artifacts or silently reuse a foreign-architecture virtual environment.

macOS `.pkg` and `.dmg` and Windows `.exe` and `.msi` builds are native, signed release jobs. They create a PyInstaller application bundle and then use the platform packaging toolchain:

```bash
./packaging/macos/build-native.sh
powershell -ExecutionPolicy Bypass -File packaging/windows/build-native.ps1
```

The macOS job requires `pkgbuild`, `hdiutil`, and an optional Developer ID identity. The Windows job requires PyInstaller, Inno Setup 6, and WiX Toolset 4. Signing identities are provided only in the native release environment; unsigned local artifacts are never publishable.

## Release signing

CLI release metadata is signed by a dedicated Ed25519 key that is independent
from both native-client updater keys. Only the offline-owned public key belongs
at `build/release-public-key.pem`; never place the private key in this repository
or an immutable release tree. From the repository root, run:

```bash
NEBULA_CLI_RELEASE_PRIVATE_KEY_PATH=/media/offline/nebula-cli-release.pem npm run cli:release:manifest
npm run qa:cli-release-trust
```

The signer refuses a non-Ed25519 key, insecure private-key permissions, a key
that does not match the tracked public key, or a CLI key shared with IDE or
Desktop. It hashes the exact 1.3.0 wheel, sdist, x64/arm64 Debian and RPM packages,
and native x64/arm64 macOS and Windows packages, then replaces
`release-v1.3.0.json` atomically. If the tracked public key or a valid signature
is absent, the Website intentionally returns 404 for the CLI package.
