Sysible Linux Operations Platform

Administrator & User Guide
Version 1.0.0 · 2026-10-05

Contents

1. Overview2. Requirements3. Installation4. First sign-in5. The portal6. Accounts and roles7. Configuration8. Apps9. Software & services10. Flashback11. Visualizer12. The gateway and routing13. TLS14. Single sign-on15. The sysiblectl CLI16. Backup and data17. Troubleshooting18. Security model19. Reference: ports and paths20. Reference: environment variables

1. Overview

Sysible Linux Operations Platform (SLOP) is the front door to the Sysible suite. It puts one URL, one TLS certificate and one sign-in in front of every Sysible app on a host, and adds the pieces a platform needs once there is more than one app on it: a user store, an update console, a config time machine and a read-only window onto what everything has been doing.

It is deliberately thin. SLOP does not replace, fork or re-host the apps — it fronts them. Each app is still its own container stack, still built from its own official repository, and still works without SLOP in front of it.

What you get at https://<server-ip>/:

PathWhat is there
/The portal — one card per installed app, each with a live health dot
/controller/Sysible Controller
/slep/Sysible Linux Engineering Platform
/connect/Sysible Connect
/flashback/Flashback — every version of every tracked config file
/visualizer/Visualizer — activity, logs and fleet topology
/login, /accountSign in, and change your own password
/adminAdministration — accounts, configuration, apps, software & services
Screenshot to comeThe portal: one card per app, each with a live health dot.

Drop it at manuals/shots/slop/portal-signed-in.png and re-run python3 tools/generate_manuals.py slop.

What SLOP is not

It is not a hypervisor, an orchestrator, or a replacement for the Controller. It does not manage the apps' lifecycle beyond updating them — starting, stopping and configuring a fleet is the Controller's job, and authoring automation is SLEP's.

2. Requirements

are missing, via Docker's official script with a distribution-package fallback.

history. A fleet with large config sets will want more.

There is no DNS requirement and nothing to configure. SLOP answers on whatever address the host has.

3. Installation

From a clone of the SLOP repository, on the host that will run it:

git clone https://github.com/sysiblesoftware/Sysible-Linux-Operations-Platform slop
cd slop
sudo ./install.sh

That brings up the whole stack. Three forms are accepted:

CommandWhat it does
sudo ./install.shApps and gateway — the whole stack
sudo ./install.sh appsOnly Controller, SLEP and Connect
sudo ./install.sh gatewayOnly the gateway, portal, Flashback, Visualizer and updater

The installer clones Controller, SLEP and Connect into /opt/sysible-src/<repo> and brings each up through the suite's unified sysiblectl CLI — which it also installs, so you can manage everything afterwards from one command. The gateway comes up from the checkout you ran it in.

Nothing is re-hosted. Every app is cloned from its own official repository and built from source on your host.

The installer mints one strong shared secret and wires it into the gateway and every app. That secret is what makes single sign-on safe — see Single sign-on below — so it is written into each app's compose .env and survives every later rebuild.

4. First sign-in

Open https://<server-ip>/ and you will be redirected to /login.

On first run SLOP creates one superuser. The username is admin unless you set SLOP_ADMIN_USER. If you did not set SLOP_ADMIN_PASSWORD, a random password is generated and printed once to the idp service's log:

sudo docker compose logs idp | head -20

You are required to change it at first sign-in. To skip that — only on a host where you set the password yourself — set SLOP_ADMIN_FORCE_CHANGE=0.

Screenshot to comeThe sign-in page. One sign-in covers the portal and every app behind it.

Drop it at manuals/shots/slop/login.png and re-run python3 tools/generate_manuals.py slop.
Your browser will warn about the certificate on first visit. That is expected: by default SLOP mints its own. See TLS for the three ways to resolve it, and do resolve it — clicking through a warning every day teaches everyone to click through warnings.

5. The portal

The portal is the root page. It shows one card per app, each with a live health dot, so "is Connect up?" is answered without opening Connect.

The dots are not guesses. The portal calls same-origin health paths (/healthz/controller, /healthz/slep, and so on) and the gateway proxies each to that app's own health endpoint. Same-origin avoids every cross-domain and CORS problem that a dashboard of links to other hosts would otherwise have.

A dot has three states: healthy, unhealthy, and still being checked. An app that is not installed on this host is not shown as broken — it is simply absent.

6. Accounts and roles

Administration → Accounts (/admin) is where users are created, roles are set, passwords are reset and accounts are removed. It is superuser-only.

There are three roles, and they mean the same thing in every app behind SLOP:

RoleCan
superuserEverything, including managing accounts and updating software
operatorDo the work — run jobs, restore configs, change app state
auditorRead. Nothing an auditor does changes anything
The role is asserted by SLOP and enforced by each app independently. An auditor cannot reach an operator's actions by going to an app directly, because the app re-checks the role itself rather than trusting that the gateway already did.
Screenshot to comeAdministration → Accounts: the user store, roles and password resets.

Drop it at manuals/shots/slop/admin-accounts.png and re-run python3 tools/generate_manuals.py slop.

Your account

Any signed-in user can open Your account (/account) to change their own password. Minimum length is 10 characters by default (SLOP_MIN_PASSWORD_LEN). A sign-in lasts 12 hours (SLOP_SESSION_TTL).

Repeated failed sign-ins are throttled: 8 attempts per source in a rolling 5-minute window by default.

7. Configuration

Administration → Configuration (/admin/settings) shows the settings this SLOP is actually running with, each with a line on what it does — the bootstrap admin, password policy, session lifetime, login throttling and the upstreams.

It is read-only on purpose. These are environment variables set on the containers, so the file on disk and the running service can never disagree about what is in effect: the page reports what the process holds, not what somebody meant to set. To change one, edit the compose .env and recreate the stack.

Screenshot to comeAdministration → Configuration: the effective settings, and what each one does.

Drop it at manuals/shots/slop/admin-configuration.png and re-run python3 tools/generate_manuals.py slop.

8. Apps

Administration → Apps (/admin/apps) hosts each app's own administration UI in place, so the settings that belong to the Controller stay in the Controller rather than being copied into a second screen that drifts out of date.

9. Software & services

Administration → Software & services (/admin/updates) is the update console for everything on the host: Controller, SLEP, Connect and SLOP itself.

Each product shows one line of state — up to date, an update available with the two commits, or why it cannot be checked — plus whether its containers are running. Each row offers:

last. One product failing does not stop the rest.

wedged service can be recovered without a shell on the host.

Screenshot to comeAdministration → Software & services: what is behind, and what can be done about it.

Drop it at manuals/shots/slop/admin-updates.png and re-run python3 tools/generate_manuals.py slop.

An update is a real job with a live log. Availability is remembered for a few minutes so that merely browsing the console does not query every git remote on every page; Check now forces a fresh look.

Updating SLOP recreates the gateway and this console, so you are signed out briefly while it rebuilds. That is why it is always updated last in a batch — anything queued behind it would be killed mid-pull. The job is handed to a detached helper so it survives the container it started in.

When an update is refused

A checkout with local modifications to tracked files is not updated, because a pull would overwrite them. The row says so and names the command that shows them. Untracked files — including the .env the installer writes — are not local modifications and do not block anything.

If the console says an update is available and git pull on the host says "Already up to date", check the path shown on the row: SYSIBLE_<APP>_DIR can point somewhere other than /opt/sysible-src/<repo>, and the two of you may be looking at different checkouts.

10. Flashback

Flashback is the config time machine: every version of every tracked config file on every host, with a diff, a download, a viewer and a one-click restore. It ships inside the SLOP stack and is reached at /flashback/.

It has its own manual — see the Sysible Flashback guide.

11. Visualizer

Visualizer is the read-only window onto the platform: who did what across every app, the logs those apps expose, and the fleet drawn as a picture. It ships inside the SLOP stack and is reached at /visualizer/.

It has its own manual — see the Sysible Visualizer guide.

12. The gateway and routing

There is one origin and no DNS. SLOP answers on 443 for whatever address the host has, and everything lives on that origin addressed by path:

https://<server-ip>/              the portal
https://<server-ip>/controller/   → SLOP_CONTROLLER_UPSTREAM (host port 8800)
https://<server-ip>/slep/         → SLOP_SLEP_UPSTREAM        (host port 8810)
https://<server-ip>/connect/      → SLOP_CONNECT_UPSTREAM     (host port 8700)
https://<server-ip>/flashback/    → the flashback service
https://<server-ip>/visualizer/   → the visualizer service

Why paths and not subdomains. One origin means one session cookie and zero DNS: no apex record, no wildcard certificate, no /etc/hosts entries, and it works on a raw IP from any machine on the network. Each app is built with its prefix as its front-end base path, so the browser asks for /controller/assets/...; the gateway strips the prefix and the app sees its own root paths, cookies and websockets unchanged.

Port 80 exists only to redirect to 443.

13. TLS

Caddy owns TLS for every site, and there are three ways to run it.

Internal CA (the default). Caddy mints one certificate under a fixed internal name and serves it for every raw-IP request. This is why the first visit warns. To make the warning go away properly, export the root and trust it on the machines that will use SLOP:

sudo docker compose cp gateway:/data/caddy/pki/authorities/local/root.crt .

Public certificates. Point a real DNS name at the host, give the :443 site that name and set an ACME email in the global block. Caddy fetches and renews Let's Encrypt certificates automatically.

Your own certificate. Mount a certificate and key and use tls /path/cert.pem /path/key.pem.

The apps keep serving their own HTTPS internally; the gateway reaches them over the loopback interface and does not verify those internal certificates, which are self-signed by design.

14. Single sign-on

In Community Edition SLOP owns identity. It ships a small identity provider with the user store, the login page and the account screens, and the apps behind it no longer show their own login.

On every proxied request the gateway asks the IdP whether the browser is signed in. A yes lets the request through; a no redirects to /login?next=… so you land back where you were going.

The trust boundary

Before proxying, the gateway does three things, and the order matters:

  1. Strips any client-supplied X-Sysible-User, X-Sysible-Role and

X-Sysible-Auth headers. A browser must never be able to assert who it is.

  1. Injects the real identity from the IdP.
  2. Stamps a shared secret proving the request came through the gateway.

Each app honours an asserted identity only when its trust flag is on *and* the shared secret matches. Someone reaching an app directly cannot know the secret, so they cannot forge the headers — and if the secret is unset the apps fail closed and ignore identity headers entirely rather than trusting them.

This means the shared secret is the whole boundary. Treat it like a private key: it lives in each app's .env at mode 0600, and it should never be passed on a command line, where any local user can read it out of /proc.

15. The sysiblectl CLI

Everything on the host is managed with one command. SLOP is a product like the others:

sysiblectl status                  # every product, at a glance
sysiblectl slop restart            # restart the gateway stack
sysiblectl controller update       # pull and rebuild one product
sysiblectl update all              # everything
sysiblectl slop logs               # follow a product's logs

The verbs are start, stop, restart, status, logs, update, rebuild, backup and destroy. rebuild is the one that builds images from source; start will build first if a product has never been built on this host.

16. Backup and data

SLOP keeps its state in named Docker volumes:

VolumeHolds
slop-idp-dataThe user store — accounts, roles, password hashes
slop-caddy-dataTLS certificates and the internal CA
flashback-dataEvery captured config version

The app checkouts under /opt/sysible-src are ordinary git clones and can be re-cloned. The volumes cannot — back them up. sysiblectl slop backup captures the stack's volumes; losing slop-idp-data means recreating every account, and losing flashback-data means losing the config history.

17. Troubleshooting

The portal shows grey dots. The dot means "still checking". If it stays grey, that app's health endpoint is not answering — check sysiblectl status and the app's own logs. Grey is not the same as red: red means it answered and said it was unhealthy.

A browser warning on every visit. Expected with the default internal CA. Trust the root certificate (see TLS) rather than clicking through.

An app shows its own login page. Its SSO trust flag or shared secret is missing, so it has failed closed and fallen back to local authentication. Check that app's .env for SYSIBLE_SSO_SHARED_SECRET and its trust flag.

Sign-in says too many attempts. The throttle has tripped: 8 failures per source in 5 minutes by default. Wait it out or raise SLOP_LOGIN_MAX_ATTEMPTS.

An update will not start. See When an update is refused above.

18. Security model

which is both the correct cookie for a raw IP and the thing that makes a single sign-in cover every app.

it is handed. Defence does not depend on the gateway alone.

ignored, not trusted. No pinned fingerprint means a vendor key is refused, not installed.

It therefore accepts exactly one thing from a caller: a key from a fixed allowlist. It never accepts a path, a repository, a branch or a command.

nosniff, a frame policy and a referrer policy.

your host, and vendor signing keys are pinned by fingerprint — an unrecognised key fails the build rather than being trusted.

19. Reference: ports and paths

PortWhoNotes
80GatewayRedirects to 443
443GatewayThe only published front door
8800ControllerIts own console, fronted at /controller/
8810SLEPFronted at /slep/
8700ConnectFronted at /connect/
9000Controller backendThe agent/API port

The IdP, Flashback's console, the Visualizer and the updater are not published on the host. They are reachable only through the gateway, which is the point.

20. Reference: environment variables

Set these in the compose .env next to docker-compose.yml.

VariableDefaultMeaning
SLOP_ADMIN_USERadminFirst-run superuser name
SLOP_ADMIN_PASSWORDgeneratedFirst-run password; printed once to the idp log if unset
SLOP_ADMIN_FORCE_CHANGE1Require a password change at first sign-in
SLOP_MIN_PASSWORD_LEN10Minimum password length
SLOP_SESSION_TTL43200Sign-in lifetime, in seconds (12 hours)
SLOP_LOGIN_MAX_ATTEMPTS8Failed sign-ins per source before throttling
SLOP_LOGIN_WINDOW_S300The window those failures are counted over
SYSIBLE_SSO_SHARED_SECRETgeneratedProves a request came through the gateway
SLOP_CONTROLLER_UPSTREAMhost.docker.internal:8800Where the Controller is
SLOP_SLEP_UPSTREAMhost.docker.internal:8810Where SLEP is
SLOP_CONNECT_UPSTREAMhost.docker.internal:8700Where Connect is
SYSIBLE_SRC_DIR/opt/sysible-srcWhere the app checkouts live
SYSIBLE_FLASHBACK_KEEP50Versions kept per file (see the Flashback guide)