Metadata-Version: 2.4
Name: satdeploy
Version: 0.3.13
Summary: Deployment tool for embedded Linux satellites — push, track, rollback over SSH and CSP
Author: Mahmood Seoud
License: MIT
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: click>=8.0
Requires-Dist: paramiko>=3.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: pyzmq>=25.0
Requires-Dist: protobuf>=4.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-mock>=3.0; extra == "dev"
Dynamic: license-file

<p align="center">
  <img src="docs/assets/hero.webp" alt="A DISCO-2 cubesat in orbit above Earth" width="860">
</p>

<h1 align="center">satDeploy</h1>

![satDeploy pushing and rolling back a local test app in 20 seconds](demo/demo.gif)

<sub><i><code>satdeploy demo</code> against a local throwaway target. Same hashing, backups, git provenance, and rollback code paths as a production deploy.</i></sub>

We flew [DISCO-2](https://discosat.dk/v2_disco-2/), a 3U student CubeSat, and then spent weeks trying to recreate what was on it.


The payload ran a Yocto Linux image with several apps on it, each on its own release cadence, each updated the same way: rebuild locally, copy the binary over USB or SCP, and post "I updated the binary" in Slack. By launch, nobody could list every commit running on the hardware with confidence. After launch, rebuilding the same set on our flatsat took weeks of chasing memory and old tmux sessions, and we still ran into lib version mismatches we hadn't known were there.

satDeploy is what we built so it doesn't happen again. Every deploy is versioned, hash-verified, and tagged with the git commit it came from. Every file can be rolled back with one command. It works over SSH for networked targets on the bench, and over [CSP](https://github.com/spaceinventor/libcsp) (CAN bus, KISS serial, ZMQ) for air-gapped satellite links.

> DISCO-2 is a 3U student CubeSat from Aarhus University, SDU, and ITU Copenhagen, launched on [SpaceX Transporter-16](https://x.com/i/broadcasts/1kJzDMgwZAvKv) (March 30, 2026) to image Arctic glaciers from a 510 km sun-synchronous orbit. Coverage: [Danish Space News](https://danishspacenews.substack.com/p/disco-2-one-of-the-most-ambitious), [The Danish Dream](https://thedanishdream.com/danish-society/science/danish-students-launch-satellite-to-track-melting-arctic/), [project site](https://projects.au.dk/ausat/disco-2).

> **Early stage, but heading to orbit.** We built satDeploy *after* DISCO-2 launched, so the current payload is flying without it. The next uplink window will push satDeploy to the DISCO-2 payload, and every deploy after that will be versioned, hash-verified, and rollback-able from the ground. Right now it runs on our flatsat, and we're actively putting it in front of other satellite teams — the more hardware it sees on the bench, the more rough edges we find and fix together before anything flies. If you run a satellite program, we'd love to see it on your flatsat. [Open an issue](https://github.com/MahmoodSeoud/satBuild/issues/new) or reach out.

## Try it now

Zero dependencies on your laptop beyond Python and git.

```bash
pipx install satdeploy   # or: pip install satdeploy
satdeploy demo
```

`satdeploy demo` sets up a throwaway git repo and a local target directory, then pre-installs `test_app` v1.0.0. Run the real product loop against it:

```bash
satdeploy status              # See what's deployed
satdeploy push test_app       # Deploy v2 (new hash, new commit)
satdeploy rollback test_app   # Undo in one command, git tag carries through
satdeploy demo stop           # Tear it down when you're done
```

The demo runs against a directory on your own machine instead of a satellite, so `test_app` (a small shell script the demo ships) executes right there on your laptop. Against real hardware, the binary lives on the target and you watch its effects there.

## Deploy to real hardware

### SSH (networked target)

Your target has network access. You don't need any C components, just the Python CLI.

```bash
satdeploy init                   # select "ssh", enter your target's IP
```

Then edit `~/.satdeploy/config.yaml`:

```yaml
name: flatsat
transport: ssh
host: 192.168.1.50
user: root
apps:
  controller:
    local: ./build/controller          # path to your local binary
    remote: /opt/bin/controller        # where it goes on target
    service: controller.service        # systemd service to restart (or null)
```

Deploy:

```bash
satdeploy push controller
satdeploy status
satdeploy rollback controller        # undo
satdeploy logs controller            # service logs
```

### CSP (air-gapped target, CAN/serial, experimental)

The CSP path has more moving parts. You need three pieces running:

| Piece | Where it runs | How to get it |
|-------|---------------|---------------|
| Python CLI *or* CSH APM | Ground station | `pip install satdeploy` or [build the APM](docs/building.md#satdeploy-apm-ground-station-native) |
| `satdeploy-agent` | Target satellite | [Yocto recipe or cross-compile](docs/building.md#satdeploy-agent-target-cross-compiled) |
| [CSH](https://github.com/spaceinventor/csh) | Ground station | Bridges ZMQ ↔ CAN/serial |

Start the agent on the target:

```bash
satdeploy-agent -i CAN  -p can0           # CAN bus
satdeploy-agent -i KISS -p /dev/ttyS1     # Serial link
satdeploy-agent -i ZMQ  -p localhost      # ZMQ (local testing only)
```

On the ground station, `satdeploy init` (select "csp") gives you a config like:

```yaml
name: my-satellite
transport: csp
zmq_endpoint: tcp://localhost:9600       # CSH's ZMQ address
agent_node: 55                           # your satellite's CSP node ID
ground_node: 40                          # your ground station's CSP node ID
apps:
  controller:
    local: ./build/controller
    remote: /opt/bin/controller
```

Then `satdeploy push controller` works the same as SSH.

If you just want to see the workflow without any of this, use `satdeploy demo`.

## Docs

- **[Command reference](docs/commands.md)**: every command and flag
- **[Configuration reference](docs/configuration.md)**: full config schema, transports, dependency ordering
- **[Building from source](docs/building.md)**: Python CLI, agent cross-compile, APM build, CSP version pinning

## Requirements

- Python 3.8+
- git (for the demo, and for provenance tracking on real deploys)
- SSH access to target *(SSH transport)*
- `satdeploy-agent` on target + [CSH](https://github.com/spaceinventor/csh) on ground station *(CSP transport)*
- systemd on target

## License

MIT
