Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions docs/source/developers/apps/app_catalog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ The catalog
The list of apps SEPAL runs lives in a dedicated repository,
`dfguerrerom/sepal-apps-catalog <https://github.com/dfguerrerom/sepal-apps-catalog>`__.
For docker apps the catalog pins the exact commit SEPAL will run; for jupyter
and shiny apps it records the ``branch`` and SEPAL runs its tip. Either way,
adding or updating an app means opening a pull request against this catalog,
where a maintainer reviews the change before it goes live.
apps it records the ``branch`` and SEPAL runs its tip. Either way, adding or
updating an app means opening a pull request against this catalog, where a
maintainer reviews the change before it goes live.

The catalog ships two files:

Expand Down Expand Up @@ -55,8 +55,9 @@ Identification and routing
- Display name shown on the apps dashboard.
* - ``endpoint``
- always
- How SEPAL runs the app: ``jupyter``, ``shiny``, ``rstudio`` or
``docker``. See :ref:`developers_apps_types`.
- How SEPAL runs the app: ``jupyter`` or ``docker`` for new apps —
``shiny`` is legacy and ``rstudio`` is reserved for the built-in
tool. See :ref:`developers_apps_types`.
* - ``repository``
- always
- ``https://github.com/<owner>/<repo>`` of the app source. SEPAL clones
Expand All @@ -69,10 +70,9 @@ Identification and routing
- Entry point the app-launcher routes to. For jupyter, the Voila URL of
the notebook (e.g.
``/sandbox/jupyter/voila/render/shared/apps/<app>/ui.ipynb``); for
shiny, the sandbox path of the Shiny project; for docker, **must
equal** ``/api/app-launcher/<id>`` exactly — the schema enforces the
prefix and ``check-docker-rules.js`` enforces that ``<id>`` matches
the entry's ``id``.
docker, **must equal** ``/api/app-launcher/<id>`` exactly — the
schema enforces the prefix and ``check-docker-rules.js`` enforces
that ``<id>`` matches the entry's ``id``.

Docker-only fields
~~~~~~~~~~~~~~~~~~~
Expand Down
138 changes: 94 additions & 44 deletions docs/source/developers/apps/app_types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@

Docker vs. Jupyter apps
=======================
*Two ways an app runs on SEPAL, with different lifecycles*
*The two endpoints you'll choose between, and where each one fits*

Every catalog entry declares an ``endpoint`` that tells SEPAL how to run the
app. The two you will meet most often are ``docker`` and ``jupyter`` (``shiny``
and ``rstudio`` behave like ``jupyter`` for lifecycle purposes). The choice
affects how the app is isolated, how it is versioned, and how it is updated.
app. For new apps the practical choice is between ``docker`` and ``jupyter``.

.. note::

``shiny`` is a legacy endpoint and is no longer recommended for new apps —
package the same UX as a Jupyter/Voila app instead. ``rstudio`` is
reserved for the built-in RStudio tool. Both still work, but neither
should be picked for anything new.

At a glance
-----------
Expand All @@ -21,30 +26,37 @@ At a glance
- Jupyter app
* - ``endpoint``
- ``docker``
- ``jupyter`` (also ``shiny``, ``rstudio``)
* - Runs in
- Its own container, on its own port
- The shared user sandbox, served through Voila
- ``jupyter``
* - Where it runs
- Its own container, on its own port, on the shared SEPAL server
- Inside the user's own SEPAL instance (sandbox), served through Voila
* - Where compute happens
- On the shared SEPAL server — all users hit the same instance
- On the user's own SEPAL instance, at the size they have provisioned
* - Versioning
- Pinned to a commit SHA (required)
- Tracks its branch; no commit pin
- Tracks the catalog's ``branch``; no commit pin
* - Code location
- Cloned from ``repository``
- ``shared/apps/<app>/ui.ipynb`` in the sandbox
- Cloned from ``repository`` into the container
- Cloned from ``repository`` into the shared sandbox at
``/home/sepal-user/shared/apps/<app>/``
* - Isolation
- Strong — separate container and dependencies
- Shares the sandbox's Python/system stack (plus an
optional packaged kernel)
* - Lifecycle
- Independent; redeploys on commit change
- Tied to the sandbox and its app-manager-built kernel
- Own micromamba kernel built from ``sepal_environment.yml``
(see :ref:`developers_apps_kernels`)
* - Update trigger
- Catalog PR bumps ``commit`` → app-launcher checks out that commit on
the next launch
- app-manager refreshes the clone to the branch tip and rebuilds the
kernel if the env file changed

Docker apps
-----------

A docker app lives in its own repository and runs in its own container with a
dedicated ``port``. Because it is fully isolated, the catalog schema requires
``repository``, ``branch``, and ``commit`` for every ``endpoint: docker`` entry.
dedicated ``port`` on the shared SEPAL server. Because it is fully isolated,
the catalog schema requires ``repository``, ``branch``, ``commit``, ``port``
and ``path`` for every ``endpoint: docker`` entry.

When SEPAL launches the app it runs the equivalent of:

Expand All @@ -54,31 +66,69 @@ When SEPAL launches the app it runs the equivalent of:
git checkout --detach <commit>

That is why the pinned ``commit`` must be reachable from ``branch`` (see
:ref:`developers_apps_releases`). The app's dependencies are baked into its container, so
its lifecycle is independent of the rest of the platform — it changes only when
you bump its commit in the catalog.

Use a docker app when your tool needs strong isolation, its own service, a
specific port, or a runtime that differs from the SEPAL sandbox.

Jupyter (and shiny, rstudio) apps
---------------------------------
:ref:`developers_apps_releases`). The app's dependencies are baked into its
container, so its update cycle is independent of the rest of the platform — it
changes only when you bump its commit in the catalog.

Because the docker app runs on the shared SEPAL server, every user hits the
same instance. That makes docker a good fit for **lightweight, low-compute
apps that act as a single service for many users** — interfaces over a small
API, lookups, light visualizations, dashboards. It is *not* a good fit for
heavy per-user compute: there is only one container backing all users.

Jupyter apps
------------

A jupyter app does not get its own container. Its code is cloned from the
``repository`` into the shared sandbox under
``/home/sepal-user/shared/apps/<app>/``, and its ``ui.ipynb`` notebook is
rendered with `Voila <https://voila.readthedocs.io>`__ (hence catalog paths
such as ``/sandbox/jupyter/voila/render/shared/apps/<app>/ui.ipynb``).
Crucially, it runs **inside each user's own SEPAL instance** — so the compute
budget is whatever the user has provisioned for themselves.

Versioning is by branch, not by commit: the app-manager keeps the cloned
``shared/apps/<app>/`` working tree on the tip of the catalog's ``branch`` and
rebuilds the app's kernel whenever ``sepal_environment.yml`` (or
``requirements.txt``) changes — see :ref:`developers_apps_kernels`. Pushing to
that branch is enough to ship a new version; no catalog PR is required (the
catalog PR is only needed to *list* the app in the first place).

Because compute lives on the user's instance, a jupyter app is the right
choice for **heavy, scalable, per-user workloads**: anything where the user
needs to be able to size their machine to the job — training pipelines, large
geospatial analyses, model inference, parallel processing.

When to use which
-----------------

A simple rule of thumb:

A jupyter app does not get its own container. Its code is checked out into the
shared user sandbox under ``shared/apps/<app>/`` and its ``ui.ipynb`` notebook is
rendered with `Voila <https://voila.readthedocs.io>`__ (hence catalog paths such
as ``/sandbox/jupyter/voila/render/shared/apps/<app>/ui.ipynb``). It runs against
the sandbox's Python and system libraries rather than a private container, so it
does not carry a pinned ``commit`` — its lifecycle follows the sandbox and the
kernel built for it by the app-manager.

That kernel is where a jupyter app declares the environment it needs. Packaging
that environment correctly — so it is built once and reused — is the subject of
:ref:`developers_apps_kernels`.
.. list-table::
:header-rows: 1
:widths: 24 38 38

Use a jupyter app when your tool is a notebook-based UI that fits the SEPAL
sandbox and benefits from being close to the user's files and the shared
geospatial stack.
* -
- Use ``docker``
- Use ``jupyter``
* - Compute profile
- Light; same load no matter who is using it
- Heavy or variable; each user should be able to scale their own
instance
* - Usage pattern
- Single backend service serving many users
- Per-user notebook session, isolated per user
* - Versioning needs
- You want strict, audited control of *exactly* which commit is live
- Push-to-branch is a fine release model
* - Runtime needs
- Custom system stack, services, ports, or a non-Python runtime
- Standard SEPAL geospatial / scientific Python stack (extended via
``sepal_environment.yml``)

If the answer to most of these points the same way, that's your endpoint. If
the app is genuinely a notebook UI doing heavy compute, jupyter is almost
always the right pick — that is the most common pattern on SEPAL.

.. graphviz::

Expand All @@ -87,9 +137,9 @@ geospatial stack.
node [shape=box, fontname="sans-serif"];

catalog [label="Catalog entry\n(endpoint)"];
docker [label="Docker app\nprivate container + port\npinned commit"];
jupyter [label="Jupyter app\nshared sandbox\nVoila + kernel"];
docker [label="Docker app\nshared SEPAL server\nprivate container + port\npinned commit"];
jupyter [label="Jupyter app\nuser's own instance\nVoila + kernel\nbranch tip"];

catalog -> docker [label="docker"];
catalog -> jupyter [label="jupyter / shiny / rstudio"];
catalog -> jupyter [label="jupyter"];
}
2 changes: 1 addition & 1 deletion docs/source/developers/apps/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ workflow.
`dfguerrerom/sepal-apps-catalog <https://github.com/dfguerrerom/sepal-apps-catalog>`__
repository. Getting an app onto SEPAL — or updating one — means opening a
pull request there. For docker apps, the catalog also pins the *exact
commit* SEPAL will run; for jupyter and shiny apps, the catalog records the
commit* SEPAL will run; for jupyter apps, the catalog records the
``branch`` and SEPAL still runs the tip of that branch. The pages below
walk through both flows.

Expand Down
42 changes: 38 additions & 4 deletions docs/source/developers/apps/kernels.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ Request a new kernel
*Package an environment once and share it with every SEPAL user as a named Jupyter kernel*

A "kernel" in the SEPAL catalog is really just a special-purpose jupyter app:
the same catalog entry, the same PR flow, the same release lifecycle. The only
difference is *intent* — the deliverable is the environment, not a notebook UI.
If you have read :ref:`developers_apps_catalog`, you already know 90% of how to
ship one.
the same catalog entry, the same :ref:`PR flow <developers_apps_catalog>`,
the same :ref:`release lifecycle <developers_apps_releases_jupyter>`. The only
difference is *intent* — the deliverable is the environment (and, often,
training notebooks and data alongside it), not an app UI. If you have read
:ref:`developers_apps_catalog`, you already know 90% of how to ship one.

Why request a kernel
--------------------
Expand Down Expand Up @@ -74,6 +75,39 @@ A packaged kernel usually has no dashboard UI of its own — it exists to back
the environment and registers the kernel without showing a tile in the apps
dashboard.

.. _developers_apps_kernels_content:

Ship notebooks and data alongside the kernel
---------------------------------------------

The same clone the app-manager uses to build the kernel lands at
``/home/sepal-user/shared/apps/<app>/`` **inside every user's sandbox**.
Anything you put in the repository is therefore reachable from every user's
JupyterLab — not just the ``sepal_environment.yml``.

This makes the same mechanism useful for a second job: shipping **training
notebooks and supporting data** to a cohort of users. For a workshop or a
training, you can put the curriculum in the repo alongside the environment
file. Each participant gets the notebooks already on their instance under
``shared/apps/<your-training>/``, with the matching ``(venv) <your-training>``
kernel pre-built and ready to select — no per-user clone, no per-user pip
install, no "it works on my machine".

Practical conventions for that case:

- Keep notebook files at the top level (or in a clearly named subdirectory)
so participants can find them via ``shared/apps/<your-training>/``.
- Keep sample inputs in the repo only if they are small. For larger
datasets, point notebooks at a shared SEPAL location or an external URL
so the clone stays light.
- Tag a ``release`` branch for each cohort you run, so an in-flight push to
``main`` does not surprise a session that is already underway. See
:ref:`developers_apps_releases_branch`.

The catalog entry itself stays the same — ``"hidden": true``, ``"endpoint":
"jupyter"``, ``sepal_environment.yml`` in the repo. The notebooks just ride
along in the same clone.

Worked example: ``sepal-sam``
------------------------------

Expand Down
22 changes: 11 additions & 11 deletions docs/source/developers/apps/releases.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@ Create a release

Once your app is in the catalog (see :ref:`developers_apps_catalog`), the way
you ship a new version depends on the app's ``endpoint`` (see
:ref:`developers_apps_types`). Docker apps and jupyter/shiny apps (including
kernels) use different release mechanisms:

- **Jupyter and Shiny apps** (and :ref:`kernels <developers_apps_kernels>`,
which are just hidden jupyter apps) are not pinned to a commit. The
app-manager checks out the tip of the catalog's ``branch`` (defaulting to
``HEAD``) every time the sandbox refreshes the app, so a release is just a
push to that branch in your source repository — no catalog change is
needed.
:ref:`developers_apps_types`):

- **Jupyter apps** (and :ref:`kernels <developers_apps_kernels>`, which are
just hidden jupyter apps) are not pinned to a commit. The app-manager
checks out the tip of the catalog's ``branch`` (defaulting to ``HEAD``)
every time the sandbox refreshes the app, so a release is just a push to
that branch in your source repository — no catalog change is needed.
- **Docker apps** are pinned in the catalog to an exact commit SHA. SEPAL
keeps running the old commit until that ``commit`` field is advanced, so a
release is, in practice, a pull request against the catalog.
Expand All @@ -23,8 +21,8 @@ The rest of this page describes each flow in turn.

.. _developers_apps_releases_jupyter:

Jupyter, Shiny and kernel apps
------------------------------
Jupyter and kernel apps
-----------------------

These endpoints don't take a ``commit`` field in the catalog — only
``branch`` (which defaults to ``HEAD``). On every sandbox refresh the
Expand All @@ -38,6 +36,8 @@ Push the change to the branch the catalog tracks for your app. The next time
the user's sandbox refreshes the app, the new tip is picked up automatically.
No catalog PR is required.

.. _developers_apps_releases_branch:

The release branch convention
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
Loading