SatOP - a general interoperability platform for satellite command and control and other operations systems for small satellite missions
The SatOP platform is a framework written in Python with core components for logging, a ground station interface, an easily extendable API with FastAPI, an authorisation framework, and a modular plugin interface.
The plugin interface enables easy integration with existing systems and creation of new tools to support the operation in a satellite mission. The ground station interface makes it possible to connect the platform with one or more ground stations, without restricting which commands and functionality the ground station can support.
It has initially been developed as a research and development project at Aarhus University in fall of 2024 by Aleksander Nicklas Nikolajsen and Tobias Frejo Rasmussen in collaboration with DISCO, with the intention of being used for the operations of DISCO-2.
To start a development environment with live-reloading and all plugins enabled:
docker compose up devTODO: Create devcontainer for platform/plugin development.
When running the dev service, the database is automatically reset and seeded with default users for a clean development experience. Each time you run docker compose up dev, the environment will be returned to this clean state.
You can log in with the following credentials:
- Admin User
- Email:
admin@example.com - Password:
adminpassword
- Email:
- Operator User
- Email:
operator@example.com - Password:
operatorpassword
- Email:
The platform requires Python 3.10 or later to run. Install from python.org or if using Linux, your distribution's package repository.
On Ubuntu, you should install the python3-full to ensure you have PIP and virtual environment support. (Optional) You can also install python-is-python3 to be able to use python from the terminal instead of python3:
sudo apt install python3-full python-is-python3
We suggest creating and activate a Python virtual environment. Navigate to a suitable location in your terminal and run:
Windows
python -m venv .venv
.venv\Scripts\activateLinux / macOS
python -m venv .venv
source .venv/bin/activateNow, install the satop-platform package directly from the git repository:
pip install git+https://github.com/discosat/satop-platform.gitWith the platform package installed, it can be run with:
python -m satop_platform [-vv]Access the automatically generated API documentation from a browser:
The platform will save data to the following locations depending on your OS:
Windows: %userprofile%\AppData\Roaming\SatOP
Linux: ~/.local/share/SatOP
macOS: ~/Library/Application Support/SatOP
(Note on Windows: If you have the Windows Store version of Python installed, it might instead be placed in a virtualised folder under %userprofile%\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\Roaming\SatOP. If you manually delete this folder and create the one in AppData\Roaming, this can be circumvented.)
SatOP can be configured to listen to a different IP and port by editing (or creating) config/api.yml in the SatOP directory. E.g. the following will listen on all interfaces on port 1234:
host: 0.0.0.0
port: 1234In the future, more configuration options will become available.
Plugins can be installed on the platform by placing their directory in the
The project includes a dedicated service for running the test suite.
To run all pytest tests:
docker-compose run --rm testFor development, start by cloning the repository
git clone git@github.com:discosat/satop-platform.gitor
git clone https://github.com/discosat/satop-platform.gitEnter the satop-platform directory:
cd satop-platform
Create and activate a Python virtual environment
Windows
python -m venv .venv
.venv\Scripts\activateLinux / macOS
python -m venv .venv
source .venv/bin/activateInstall the satop platform as a development package in the virtual environment, which makes changes take affect as modifications are made.
pip install --editable .It can then be started with
python -m satop_platform [-vv] [--install-plugin-requirements]Note that the --install-plugin-requirements flag will install any requirements specified in the plugins' config.yaml files. This flag is only necessary if its the first time the satop platform will be running or if there's an update to a plugins requirements.
Note: When running the platform manually (outside of the provided Docker development environment), the database will not be automatically seeded. You will need to create the first user and roles manually using the API endpoints described in the 'Authentication and Authorization' section.
A plugin must be placed in the satop_plugins directory
/satop_plugins
- /[plugin_name]
- __init__.py
- config.yaml
- [my_plugin_module].py
The __init__.py should export a class named PluginClass, that is a subclass of Plugin from satop_platform.plugin_engine.plugin.
e.g.
from .my_plugin_module import my_plugin_class as PluginClassThe configuration
name: My Plugin Name
requirements: [] # pip requirements
dependencies: [] # dependencies of other plugins. Given by their configured name
capabilities: # Additional capabilities
- http.add_routes
# Targets are optional. Below are shown the defaults:
targets:
startup:
function: startup
after: satop.startup
shutdown:
function: shutdown
after: satop.shutdownCapabilitites add a way to set extra steps that need to happen as plugins are initialised.
It is not a security feature, as plugins are not isolated/sandboxed processes, so even without the capability implementation, they could in theory do the same process. It is only there as a simple check to ease development.
The following capabilities are supported,
| Capability | Description |
|---|---|
| http.add_routes | The FastAPI APIRouter defined in the plugin will be mounted on the main FastAPI application |
| security.authentication_provider | Allows the plugin access to authentication methods, such as creating JWT tokens, by dependency injection |
The platform's security is built on three core principles: Entities, Roles, and Scopes.
- An Entity is any actor that interacts with the system (a person, a ground station, etc.).
- A Role is a job function with a defined set of permissions (e.g.,
flight-operator,mission-director). - A Scope is a single, granular permission (e.g.,
scheduling.flightplan.approve).
The standard workflow is: Scopes are assigned to Roles, and Roles are assigned to Entities.
The following example demonstrates the multi-step API workflow for creating a new user from scratch. Note: For the Docker-based development environment (docker compose up dev), this entire process is automated, and default users are created for you (see the Docker section above).
1. Create a mission-director Role:
First, an administrator defines a role and assigns the necessary scopes to it.
POST /api/auth/roles
{
"name": "mission-director",
"scopes": [
"scheduling.flightplan.create",
"scheduling.flightplan.read",
"scheduling.flightplan.approve"
]
}2. Create a new person Entity:
Next, create the user entity and assign the mission-director role to them.
POST /api/auth/entities
{
"name": "Jane Doe",
"type": "person",
"roles": "mission-director"
}Response Body:
{
"type": "person",
"roles": "mission-director",
"name": "Jane Doe",
"id": "b3552f9d-9800-4fe1-9770-aafde5083af6"
}3. Connect the Entity to an Authentication Provider:
Link the entity's core ID to a real-world identifier, like an email address. Note the plural providers in the URL.
POST /api/auth/entities/b3552f9d-9800-4fe1-9770-aafde5083af6/providers
{
"provider": "email_password",
"identity": "jane@example.com"
}4. Create the User's Credentials:
This step is specific to the email_password plugin, creating the user's password.
POST /api/plugins/login/user
{
"email": "jane@example.com",
"password": "correct-horse-battery-staple"
}5. Obtain Access and Refresh Tokens: The user can now log in to get their tokens. The response includes both an access and a refresh token. For a smooth frontend experience, it's recommended to also return the user's effective scopes.
POST /api/plugins/login/token
{
"email": "jane@example.com",
"password": "correct-horse-battery-staple"
}Response Body:
{
"access_token": "ey...",
"refresh_token": "ey...",
"scopes": [
"scheduling.flightplan.create",
"scheduling.flightplan.read",
"scheduling.flightplan.approve"
]
}To enable a special test mode for development, you must set the SATOP_ENABLE_TEST_AUTH environment variable before starting the server. This is enabled by default in the dev Docker service.
!! THIS IS INHERENTLY INSECURE AND SHOULD NEVER BE USED IN PRODUCTION !!
PowerShell:
$env:SATOP_ENABLE_TEST_AUTH=1; python -m satop_platformLinux / macOS:
SATOP_ENABLE_TEST_AUTH=1 python -m satop_platformThis allows you to use a fake bearer token that specifies a username and their scopes directly. The format is username;scope1,scope2.
Example:
Authorization: Bearer test-user;scheduling.flightplan.read,*
The following is a starting point for missing features. Add more here and update the list as required.
When starting work on one of the missing features, first create a new issue on GitHub from the list.
- Protect neccessary routes
- Bootstrapping first-user creation when these routes are protected
- Entity modification and management
- Be able to refresh/renew tokens
- Standardize scopes and their naming scheme,
- Add a default role
- Add a way for components and plugins to specify which scopes they add to the system to enable easier user creation.
- Scopes should be hierarchical, so e.g. a user with the "admin" scope would be authorized for routes requiring "admin.user_create".
- Connect the syslog with a graph database to save event and traceability data
- Use the syslaog for all events that require traceability across the platform
- Refactor the plugin engine as a class
- Better structure when passing platform components to plugins
- Support full life-cycle (also shutdown) for plugin targets
- Event-messaging system that allows emitting and listening for global events from any part of the platform.