Search
Ctrl+K
On this page
Documentation to understand the Shuffle architecture and thoughts behind our choices. Important to understand if you want to contribute or decide whether it works for your Tenant.
With a long-term vision of having an Open(API) ecosystem with a hybrid model between cloud and on-prem, this document will be a guide to understand some underlying aspects of Shuffle and how things fit together. Shuffle does NOT require internet to work.
Shuffle Installation models:
The platform is split into two main parts: Server and Workers (Runtime). The server acts as the host of everything from API activity to Workflow validation, while the Workers are another standalone unit, working in a microservice-esque way. The top and bottom part can be installed on different hosts and be clustered.
If you are looking for how to install, configure, or size Shuffle, check out Configuration. For debugging and log collection, see Troubleshooting.
Single Server installation:
Simplified:

To understand Shuffle better, think of it as two layers working together:
shuffle-frontend: React frontend for Shuffle Core (Workflows, Triggers, Apps) and reverse proxy on port 3001.shuffle-security: React frontend for Shuffle Security (Incidents, Alerts, Observables, Host Monitors) on port 3002.shuffle-backend: The core Go API on port 5001. Handles workflow validation, app metadata, file storage, and talks to OpenSearch.shuffle-opensearch: Our document database. Stores your users, workflows, tenant, execution results, apps, and platform health.shuffle-orborus: The execution manager in Go. Polls the backend for new jobs and starts workers.shuffle-worker: Controls an individual workflow execution, figures out which node runs next, and starts up apps.The core services and their network boundaries:
| Component | Layer | Default Port | Health / Probe Endpoint | Responsibility |
|---|---|---|---|---|
shuffle-frontend | Server | 3001 | GET / | Core workflow builder, trigger console, reverse proxy. |
shuffle-security | Server | 3002 | GET / | Dedicated SecOps console: Incidents, Monitors, Vulnerabilities. |
shuffle-backend | Server | 5001 | GET /api/v1/health | Golang REST API, workflow validation, auth, datastore routing. |
shuffle-opensearch | Datastore | 9200 | GET /_cluster/health | Distributed document index for incidents, metrics, and workflows. |
shuffle-orborus | Runtime | Dynamic | Internal daemon loop | Job supervisor; provisions ephemeral workers via Docker or K8s. |
shuffle-worker | Runtime | Ephemeral | Ephemeral RPC | Executes workflow execution graphs and coordinates app containers. |
[!TIP] Manual Service Health Verification via CLI
You can verify the health of your local or clustered deployment using standard commands:API Call
GETcURLPythonHTTPcurl -X GET 'http://localhost:5001/api/v1/health'Response
No response received.
How do workers and apps actually get deployed? We support four primary deployment architectures:
/var/run/docker.sock.shuffle_swarm_executions).shuffle namespace to provision isolated pods and services dynamically.Shuffle uses and is built upon existing, well established frameworks to help the Security community move forward, rather than just increase complexity.
The Shuffle automation engine is entirely built from scratch, relying heavily on Docker and Micro-service, real-time executions. The code for decision making of next nodes can be found here, while the SDK for how Apps perform can be found here. Read more here
The fundamental building blocks of Shuffle are all designed to be modular Docker images, meaning they can run separately in different environments. The list below contains all the necessary parts to execute a workflow. In case you want to contribute, we've added the programming languages as well.
The reason behind the usage of Golang is simple: Stability. Scripting languages like Python are prone to crashing, while Golang is fun, stable and easy to understand.
| Type | Technology | Note |
|---|---|---|
| Frontend | ReactJS | Cytoscape graphs & Material design |
| Backend | Golang | Rest API that connects all the different parts |
| Database | Opensearch | A scalable, NoSQL database used as document store of everything. |
| Orborus | Golang | Runs workers in a specific environment to connect locations |
| Worker | Golang | Deploys Apps to run Actions defined in a workflow |
| app sdk | Python | Used by Apps to talk to the backend |
Shuffle uses semantic versioning. All Docker images can be found on Github or on Dockerhub.
Shuffle is a quite complex platform, with lots of different features to handle anything necessary for automation. We are an API-first platform, meaning we always build the API for the feature, before developing the frontend. This allows us to quickly prototype and release new features, without necessarily breaking it. Most of our API endpoints use the following model to authentication and authorize whether the user has access:
User clicks a button in the frontend
Backend receives a HTTP request with a Session Token
The backend validates it the request in this order:
If valid, the backend processes the request by accessing info in this order:
The data gathered is processed, before Shuffle returns either of these codes (with certain exceptions):
If there's a failure, Shuffle should ALWAYS return with data in this format:
{"success": False, "reason": "Here's the reason it didn't work"}
The frontend takes the data and shows it in the UI.
There are two types of authentication tokens in Shuffle: API/Session access, and App authentication.
Accessing data is done PER TENANT, based on which one your user is in. This means that you need to be a part of the tenant you're using the API for, and it will by default use this one unless you use tenant-Id header in your request. If you are an tenant admin, you have access to all information within an tenant, while tenant-Users has access to read and modify Workflows, Apps, Files, Datastore and Trigger management. If you are a tenant-Reader, you can READ the same data the tenant User can read and modify.
Hashed (bcrypt):
Encrypted (AES-256):
App authentication, Protected Datastore Keys and Files are being encrypted. The seed used for hashing is random for each tenant, and can be set with the environment variable SHUFFLE_ENCRYPTION_MODIFIER in the local version of Shuffle. This is automatically handled in our SaaS offering. How it works:
Run in reverse to decrypt and retrieve the values.
Encryption Code reference
On the cloud instance of Shuffle (https://shuffler.io) the following password policy applies:
If you try to log in too many times in a short amount of time, you will be locked from attempting to log in for a short amount of time. Details can be seen here: CheckPasswordStrength()
In local instances of Shuffle, password policies are minimal (8 characters).
SAML/SSO and MFA is fully available and can be controlled from an tenant perspective. Users can be a part of multiple tenants, but if even one tenant the user is in enables "MFA Required", it will now be required for the users.
MFA: https://shuffler.io/admin?tab=users
SAML/SSO: https://shuffler.io/admin?admin_tab=sso
There are multiple ways to access the API. The first is through the UI and a logged in user. The second is through the API directly with a Bearer token. The third is from a workflow execution.
Session management in Shuffle is currently quite basic, with the goal to drastically improve it in 2024. The current flowchart includes reusing the same session token across anyone using an account, with the only way to log out everyone else being to log our yourself. The goal is to make this use one session token per device.
App authentication is how we authenticate and store an app's configuration. If an app requires authentication, and the user adds the authentication credentials, these credentials will be encrypted and stored in the database, along with being cached in their encrypted form (AES-256). These values can and should NEVER be decrypted to be seen by a process or human other than during a workflow execution.
How are these values being used then? If they're encrypted, how does the app get access to them? Here's how:
The execution model of Shuffle can be defined as such:
A Workflow is triggered. This can be by a trigger, or a manual execution, making a request towards /api/v1/workflows/<workflow_id>/execute
The backend fills in the appropriate gaps (e.g. startnode, source trigger or app authentication), before adding an execution to be retrieved by Orborus at a later stage, with the appropriate priority (0-10, 10 being highest).
Orborus runs as an agent, constantly polling for jobs from Shuffle. By default, it retrieves 10 jobs at a time, before checking whether any can be started. This is done based on how many Workers are running in Docker at this time, compared to the environment variable SHUFFLE_ORBORUS_EXECUTION_CONCURRENCY.
For each of the new jobs, Orborus creates a container for a Worker with the following information: An execution ID and authorization, and whether to run in an optimized way.
A worker is started FOR EACH EXECUTION, which is in control of the entire duration of an execution. It has two modes:
Unoptimized: If a workflow is started manually, the Worker will periodically poll the backend for updates, pointing all apps to send direct information to the backend. This makes it possible for the user to see updates in real-time for debug purposes.
Optimized: If a Workflow is started from a trigger (not manually), the Worker starts an HTTP server, and acts as a temporary backend for this specific execution. This makes it possible to communicate and deploy Apps faster, without straining the backend. When the workflow is finished, it will send the full execution to the backend. This means the frontend MAY not have the full picture until after the workflow execution finishes.
The Worker attempts starting each App's Docker container, starting with the startnode. As it finds that a node has finished, it will check it's status, before starting the following nodes upon success. If the App's Docker image doesn't exist, it will attempt to download in the order of: Backend, Dockerhub. If it doesn't exist, abort the workflow.
The apps that were started by a Worker retrieves the full execution in order to be able to identify variables, check conditions, authorization, download files or from the cache etc. Order of operations in an App's Docker container:
When the Execution in the Worker is in either the FINISHED or ABORTED state, the Worker sends information back to the original Backend about the status of the execution.
To learn about the code behind the execution, check here
As we've hinted at more than a few times, everything in Shuffle is built around Docker containers. This keeps the environment safe even in cases of compromise, and is necessary to allow a user to build an app for pretty much anything. This allows us to sandbox and control how actions are performed, as it's all built on top of our App SDK. On top of all this? It makes sharing and deploying easier between Shuffle users, which is paramount to a scalable, global system.
The most important parts about containerization in Shuffle is this:
Contact us or mail us at help@shuffler.io, and we'll provide you with the necessary information.