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
29 changes: 18 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,9 @@ tool" cases) is in
**Production-ready and running in production.** Releases are multi-arch
(amd64 + arm64) and [cosign-signed]; the
[releases page](https://github.com/StrategicProjects/ruscker/releases)
has the current version. **Current release: [v0.2.49]** — the process
Logs page uses finite cursor polling, so it cannot strand an HTTP/1.1
connection and stall later admin navigation behind a reverse proxy.
always identifies the current version and carries its release notes.

[cosign-signed]: https://strategicprojects.github.io/ruscker/installation.html#verifying-release-artifacts
[v0.2.49]: https://github.com/StrategicProjects/ruscker/releases/tag/v0.2.49

> **~14 MB idle** — measured on a real production deployment serving a
> real 31-spec config, with apps spawning on demand. (The JVM-based
Expand All @@ -113,13 +110,20 @@ What's in the box:
- **Admin panel** — full apps CRUD (including API, scaling, resource and
lifecycle settings), Media, encrypted Credentials, a live-preview
Appearance editor, **Viewer / Editor / Admin** accounts with a password
policy and generator, consolidated per-user editing, server-side
pagination (50 per page) with case-insensitive search (accented letters
included) across usernames, groups and profiles, and an **Activity** section
with two tables — **user activity** (logins and interactive app accesses,
filterable and paginated) alongside the **administrative audit** trail.
The process-log terminal follows new lines with finite cursor requests,
pauses while hidden and never occupies a persistent browser connection.
policy and generator, and group-scoped delegation: Editors administer open
apps plus apps whose `access-groups` overlap their own, and non-Admin users
and memberships in those groups; Admin and break-glass sessions stay global,
while Media remains shared. Out-of-scope app and user ids deliberately
return `404`. User deletion, CSV import, MFA reset, and group
create/rename/delete remain Admin-only. The Users page has consolidated
editing, server-side pagination (50 per page), and case-insensitive search
(accented letters included) across usernames, groups and profiles. The
**Activity** section pairs user activity (logins and interactive app
accesses, filterable and paginated) with the administrative audit trail.
Historical timestamps render in each viewer's browser timezone and table
dates sort by instant. The process-log terminal also shows viewer-local
times, follows new lines with finite cursor requests, pauses while hidden
and never occupies a persistent browser connection.
- **Containers dashboard** — CPU/memory sparklines, live-follow logs and
stop/restart controls, refreshed every five seconds by polling
`GET /admin/dashboard/snapshot`.
Expand Down Expand Up @@ -149,6 +153,9 @@ What's in the box:
with an optional command override, run history/log tail, leader-only HA
firing, no fire-on-create, one catch-up after downtime and a per-run
timeout (1 hour by default); failures raise the `job-failed` alert webhook.
Cron is evaluated in the IANA zone configured by `server.timezone`, and
next/last-run times are labelled in that same zone. Unset or invalid values
use UTC, with invalid names reported as validation warnings.
(Local Docker backend; not yet available with the multi-host backend.)
- **Named-volume management** — the Disk panel lists Docker volumes with
live reference counts and can create labelled volumes; removal is offered
Expand Down
10 changes: 6 additions & 4 deletions book/src/alternatives.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ catalog and operational state live in SQLite, or Postgres for HA.
landing page; anything in a container is first-class.
- **A real admin panel** — apps CRUD, a media library, an encrypted
credentials store, a live monitoring dashboard, an audit log, and user
roles — instead of editing a file and restarting.
roles — instead of editing a file and restarting. Editors are scoped by
their group memberships to open/shared-group apps and non-Admin accounts;
Admin and break-glass access remains global.
- **Sensitive internal apps** — per-app step-up TOTP MFA is enforced before
a container starts, while opt-in identity headers let the app consume the
signed-in username, groups and selected profile claims.
- **Scheduled operations** (local Docker backend) — run the same app
image/environment/volumes to completion on a cron for ETL, reports and
maintenance, with history,
timeouts and failure alerts.
maintenance, with history, timeouts and failure alerts. Cron uses the IANA
zone in `server.timezone` and remains on UTC when unset.
- **Light to run** — **~14 MB idle**, a single static binary, no JVM and
instant startup.

Expand Down Expand Up @@ -104,7 +106,7 @@ the systemd service) and reference them by name in the YAML.
Ruscker schedules onto Docker hosts (over ssh/tcp), not Kubernetes.
- You need **enterprise SSO gating app access per user** (OIDC/SAML/LDAP
at the proxy level) — Ruscker's auth covers admin roles (Viewer /
Editor / Admin) and per-app visibility (`access-groups` /
group-scoped Editor / Admin) and per-app visibility (`access-groups` /
`access-users`), but proxy-level SSO is not yet supported.

If none of those apply, start with the [Quickstart](./quickstart.md).
34 changes: 31 additions & 3 deletions book/src/deploying.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,34 @@ Keep `RUSCKER_MASTER_KEY` backed up: 2FA enrolment returns `503` without it,
and changing it makes existing encrypted credentials and TOTP factors
undecryptable.

### Choose the scheduler clock

Scheduled jobs interpret five-field cron expressions in the IANA timezone
configured in `ruscker.yml`:

```yaml
server:
timezone: America/Recife
```

When the field is absent, cron stays on UTC, so upgrading does not move
existing schedules. An invalid name emits a validation warning and also
falls back to UTC rather than blocking startup. The Schedules page labels
next/last runs in the effective zone, and the startup banner includes that
zone. This setting is intentionally separate from historical admin
timestamps: Activity, audit, Apps, Credentials, Users and process Logs use
the timezone of each viewer's browser.

### Delegate administration by group

Editor memberships are authorization boundaries, not just labels. Editors
can administer open apps, apps whose `access-groups` overlap their own,
and non-Admin Viewer/Editor accounts and memberships in those groups. Media
is shared across Editors. Admin accounts and the break-glass token remain
global; account deletion, user CSV import, MFA reset, and group
create/rename/delete remain Admin-only. Direct requests for an out-of-scope
app or user return `404`, deliberately hiding whether the identifier exists.

## 2. Enable the container backend

The shipped unit serves landing + admin + proxy but not the `--docker`
Expand Down Expand Up @@ -196,9 +224,9 @@ the flag already.
> }
> ```
>
> Since v0.2.49, `/admin/logs/stream` is a retired compatibility endpoint
> that returns `204 No Content`, which tells pages loaded before a rolling
> upgrade to stop reconnecting. Do not proxy it as a long-lived stream.
> `/admin/logs/stream` is a retired compatibility endpoint that returns
> `204 No Content`, which tells pages loaded before a rolling upgrade to
> stop reconnecting. Do not proxy it as a long-lived stream.

## 4. Side-by-side with ShinyProxy

Expand Down
30 changes: 26 additions & 4 deletions book/src/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,19 @@ one warm). Routing is least-connections (interactive) or round-robin
### How does authentication work?

The **admin panel** has user accounts with Viewer / Editor / Admin
roles (plus a break-glass token). The same accounts gate **per-app
visibility**: every spec can declare `access-groups` / `access-users`
and only matching users see the card and reach `/app` / `/api`
(specs with no access keys remain open to anyone) —
roles (plus a break-glass token). An Editor's own group memberships are
also their administrative boundary: they can manage open apps, apps whose
`access-groups` overlap their groups, and non-Admin Viewer/Editor accounts
and memberships in those groups. They can create those accounts and reset
their passwords; account deletion, CSV import, MFA reset, and group
create/rename/delete remain Admin-only. The Media library is shared.
Out-of-scope app and user ids return `404` so guessed identifiers do not
reveal another team's resources. Admin and break-glass sessions remain
unrestricted.

The same accounts gate **per-app visibility**: every spec can declare
`access-groups` / `access-users` and only matching users see the card and
reach `/app` / `/api` (specs with no access keys remain open to anyone) —
see [Per-user access](./configuration.md#per-user-access).
An app can also set `require-mfa: true` for a TOTP step-up before its
container starts; each user enrols one factor and each app chooses how
Expand Down Expand Up @@ -90,6 +99,19 @@ also accepts a pure `${VAR}` env-ref as the password (stored verbatim,
resolved at pull time, so the decryption key is never needed for env-based
credentials).

### Which timezone does Ruscker use?

Stored instants remain UTC. Historical timestamps in Activity, audit, Apps,
Credentials, Users and process Logs render in the viewer's browser timezone;
sortable table dates compare the underlying instant, not the formatted text.
Hover a table timestamp for its full localized date and zone.

Cron has no viewer, so the scheduler uses the IANA zone in
`server.timezone`. The Schedules page labels next/last runs in that zone.
Leaving it unset preserves the historical UTC behavior, so an upgrade does
not move existing jobs. An invalid name warns and also falls back to UTC;
the effective zone is included in the startup banner.

### Can I run more than one instance for high availability?

Yes. Several instances share one Postgres for the catalog and session
Expand Down
13 changes: 13 additions & 0 deletions book/src/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@ in the catalog DB, not the YAML). `ruscker.yml` holds **deployment**
settings; put secrets in `/etc/ruscker/ruscker.env`. After editing either
file, `sudo systemctl restart ruscker`.

One such deployment setting is the scheduler's clock. Cron stays on UTC
unless you opt into an IANA timezone:

```yaml
server:
timezone: America/Recife
```

The Schedules page labels next and last runs in the effective zone. An
invalid name produces a validation warning and falls back to UTC instead of
preventing startup. This setting does not change admin history timestamps,
which render in each viewer's browser timezone.

To actually run the demo apps (and your own containers) enable the Docker
backend: `sudo ruscker-enable-docker`. See [Deploying in
production](./deploying.md) for nginx + TLS, and
Expand Down
16 changes: 11 additions & 5 deletions book/src/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,17 @@ what's next.
env-ref, resolved only at pull time), a landing-page editor (colors,
intros, SEO, social meta, analytics, custom HTML blocks, header/footer
logos with alignment and links), audit log, **user accounts with
Viewer / Editor / Admin roles**, and a live monitoring dashboard
(CPU/memory, live-follow logs, stop/restart). On the local Docker backend,
operators can also manage named Docker volumes and run a spec's image to
completion on a cron schedule, with history, log tails, timeouts and
failure alerts (both are unavailable with the multi-host backend).
Viewer / Editor / Admin roles** and group-scoped Editor delegation, and a
live monitoring dashboard (CPU/memory, live-follow logs, stop/restart).
Editors manage open apps, apps and non-Admin accounts in their own groups,
while Admin and break-glass access stays global; the Media library remains
shared. Admin tables and process logs show timestamps in each viewer's
browser timezone. On the local Docker backend, operators can also manage
named Docker volumes and run a spec's image to completion on a cron
schedule, with history, log tails, timeouts and failure alerts. Cron uses
`server.timezone` (an IANA name) and remains on UTC when it is unset or
invalid; the Schedules page labels the effective zone. Volumes and
schedules are unavailable with the multi-host backend.
- **Sub-path mounting**: serve the whole portal under a prefix via
`server.context-path` or `--base-path`. Health probes (`/healthz`,
`/readyz`) stay at the root for load balancers.
Expand Down
24 changes: 21 additions & 3 deletions book/src/migrating.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,32 @@ says what to do for each. Two to know about:
instead of Shiny's 3838 — apps that previously needed an explicit
port "just work"; an explicit `container-port`/`port:` still wins.

### Preserve or choose the scheduler clock

An existing schedule keeps its historical behavior after migration:
without `server.timezone`, Ruscker evaluates cron in UTC. To write cron in
a local clock, opt in with an IANA name:

```yaml
server:
timezone: America/Recife
```

The Schedules page then labels next and last runs in that zone. Invalid
names produce a validation warning and fall back to UTC instead of blocking
startup; the startup banner reports the effective zone. This does not
control Activity, audit, Apps, Credentials, Users or process Logs, whose
stored UTC timestamps render in each viewer's browser timezone.

## What Ruscker adds

Beyond parity, you also get: a real admin panel (no more hand-editing
YAML), a live monitoring dashboard, per-spec `container-env` /
`container-cmd` injection, per-API rate-limiting and CORS, per-user
and per-group app visibility, per-app step-up MFA, scheduled jobs and named
volume management (local Docker backend), health probes, graceful shutdown,
and **~14 MB idle**.
and per-group app visibility, group-scoped Editor delegation, viewer-local
admin timestamps, per-app step-up MFA, timezone-aware scheduled jobs and
named volume management (local Docker backend), health probes, graceful
shutdown, and **~14 MB idle**.
The JVM-based proxy it replaced on the same machine used about 540 MB. See
[The admin panel](./admin.md).

Expand Down
16 changes: 15 additions & 1 deletion book/src/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ The master key encrypts saved registry credentials and TOTP secrets. Keep
it stable if you reuse this database; without it, 2FA enrolment fails with
`503`.

If you plan to use **Schedules**, choose the clock in which cron expressions
are written by adding an IANA timezone to the service config:

```yaml
server:
timezone: America/Recife
```

Leave it out to retain the UTC default. This setting controls the scheduler
and its labelled next/last-run times; Activity, audit, Apps, Credentials,
Users and process Logs instead follow the timezone of each viewer's browser.

- Ruscker **auto-connects to Docker** when the daemon socket is
reachable, so app containers spawn out of the box. Pass `--no-docker`
to run landing-only (then `/app/*` returns 503); pass `--docker` to
Expand Down Expand Up @@ -124,7 +136,9 @@ limits…).
apps by user / group.
- [The admin panel](./admin.md) — manage specs, images, users, and the
live container dashboard, identity headers, per-app 2FA and schedules
without editing YAML.
without editing YAML. Editors are delegated by group: they see open apps
plus apps and non-Admin accounts in groups they belong to; Admin and the
break-glass token remain global.
- [Deploying in production](./deploying.md) — systemd + nginx, TLS,
multi-host, and active-active HA (including
[mounting the portal under a subpath][base-path] and the
Expand Down
37 changes: 36 additions & 1 deletion book/src/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,44 @@ slow/unreachable Docker daemon will stop or delay updates. nginx buffering
does not affect the dashboard poll; it matters only for the explicitly
enabled per-replica live log stream described in [Deploying](./deploying.md).

## Admin dates or scheduled-job times look wrong

These use two intentionally different clocks:

- Activity, audit, Apps, Credentials and Users table dates render in the
viewer's browser timezone. Process Logs also show the viewer-local clock.
Check the operating system/browser timezone if they are wrong. Hover a
table date for the full localized timestamp and zone; process-log hover
keeps the original UTC token for correlation with downloaded logs.
- The scheduler evaluates cron in `server.timezone`, and the Schedules page
labels next/last runs in that zone. With no setting, it uses UTC. Use an
IANA name such as `America/Recife`, not an abbreviation such as `BRT`.

Run `ruscker validate <config>` after changing the setting. An invalid name
produces a warning and falls back to UTC without blocking boot. The startup
banner's `timezone` field shows the effective scheduler zone.

## An Editor cannot find an app or user

This is normally the group scope working as designed. An Editor sees and
administers:

- apps with no `access-groups` or `access-users`, plus restricted apps whose
`access-groups` overlap the Editor's groups;
- non-Admin users who share at least one of those groups, and memberships in
the Editor's own groups;
- the shared Media library.

An app restricted only by `access-users` has no group boundary and is
Admin-only. Direct requests for an out-of-scope app or user return `404`,
not `403`, so a guessed id cannot reveal another team's resource. Editors
may create Viewer/Editor accounts in their groups and reset their passwords,
but deleting accounts, importing users by CSV, resetting MFA, and
creating/renaming/deleting groups require Admin or break-glass access.

## Admin navigation hangs after visiting Logs

Upgrade to **v0.2.49 or newer**. Older releases automatically opened
Upgrade to a current release. Older builds automatically opened
`/admin/logs/stream` as an infinite SSE response. On a deployment with an
HTTP/1.1 load-balancer or reverse-proxy hop, an intermediary could retain
that response after browser navigation and then reuse the same backend
Expand Down
18 changes: 10 additions & 8 deletions book/src/use-cases.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,14 @@ Multiplex GPUs and isolate users in front of generative tools.
## Scheduled ETL, reports and maintenance

The admin **Schedules** page (local Docker backend) can run a containerized
spec to completion on a five-field UTC cron: nightly ETL, report generation,
cache refreshes or
small maintenance tasks. A job reuses the spec's image, environment,
volumes, resource limits and registry credentials, with an optional command
override. Runs have a configurable timeout (1 hour by default), history and
log tails; failures can trigger the `job-failed` alert webhook.
spec to completion on a five-field cron: nightly ETL, report generation,
cache refreshes or small maintenance tasks. Set `server.timezone` to an IANA
name for the clock the expressions use; absent or invalid values retain the
UTC default. The Schedules page labels next and last executions in that same
zone. A job reuses the spec's image, environment, volumes, resource limits
and registry credentials, with an optional command override. Runs have a
configurable timeout (1 hour by default), history and log tails; failures can
trigger the `job-failed` alert webhook.

## Database admin consoles

Expand Down Expand Up @@ -158,8 +160,8 @@ Surface a DB console as just another card on the portal.

## Who it's for

- **Public sector, universities and research centers** publishing analytics
dashboards for the public or for staff.
- **Universities, research centers and other organizations** publishing
analytics dashboards for broad or internal audiences.
- **BI and data-science teams** that want to ship R/Shiny and
Python/Streamlit apps without standing up a Kubernetes cluster.
- **Consultancies** delivering analytical tools to each client at isolated
Expand Down