Status: Phases 0–7 shipped and running in production, followed by continuing security, operations, and admin work. Phase 8's external identity-provider integrations are optional and demand-driven. For the narrative, up-to-date version with a timeline diagram, see the Roadmap chapter on the docs site. The phase checklists below are the original planning detail; every item in Phases 0–7 has since shipped, so their boxes are ticked. Phase 8 shows what remains.
Phased plan from "config parsing only" to "production-ready ShinyProxy replacement".
Each phase has a clear deliverable. Skip ahead if a phase doesn't apply to your use case (e.g. skip phase 4 if you don't need a dashboard).
Status: ✅ shipped.
- Cargo workspace with 6 crates
- YAML schema covering ShinyProxy + Ruscker extensions
- Env var interpolation (
${VAR},${VAR:-default}) - Two-phase validation (raw text scan + typed model checks)
- CLI binary with
validate,show,inspect - Integration tests against the real
application.yml - Per-crate developer docs (module-level)
- Mockups in
docs/mockups/ - Architecture, roadmap, ADRs
Deliverable: ruscker validate examples/application.yml produces
a useful report.
Goal: replace the Thymeleaf-based ShinyProxy landing with Askama templates enhanced by HTMX and Alpine.js. Specs and template properties drive what gets rendered.
- Add a new
ruscker-webcrate (or fold intoruscker-admin) - Askama templates matching
docs/mockups/landing-page.html - Type badge colors and access icons driven by template properties
- Filter chips with counts (matching
docs/mockups/landing-filters-cards-refined.html) - Light/dark theme toggle (cookie + system preference)
- Image library: serve static assets from a configurable directory
- Browser assets with zero Node build step
Deliverable: visit localhost:8080 and see the bundled
examples/application.yml portal rendered, looking visually
equivalent to today but in Tailwind. Cards are not yet clickable
(no proxy yet).
Goal: edit specs via web UI, no YAML editing required.
- SQLite schema + sqlx migrations
- Importer:
ruscker import application.yml --db ruscker.db - Exporter:
ruscker export --db ruscker.db > application.yml - Database-backed user login plus a break-glass environment token
- Apps list page (filter, search, bulk actions)
- Add/Edit spec form with type-driven fields (mockup:
admin-add-edit-app.html) - Image library: upload, convert to WebP, optimize
- Credentials store: named entries, encrypted at rest
- Landing-page section editor (mockup:
admin-landing-editor.html) - Audit log of all admin actions
Deliverable: the operator can fully manage Ruscker via web UI without touching files.
Goal: actually run apps. The technically hardest phase.
-
LocalDockerBackendvia bollard- Image pulling with registry credentials
- Container spawn with resource limits, env, volumes
- Health-check polling (TCP followed by HTTP readiness)
- Graceful stop (drain → SIGTERM → SIGKILL)
- HTTP request forwarding via hyper
- WebSocket upgrade hijack + bidirectional pump
- Path rewriting for Shiny's relative URLs
- Sticky session cookie signing (HMAC-SHA256 via
ring) - Session store (
DashMapimpl ofSessionStore) - Replica pool per spec with selection in
routes::proxy - Auto-scaler: spawn when utilization > threshold, retire when under threshold for grace period
- Heartbeat handling (per-spec timeout overrides,
-1= never) - API spec branch: HTTP-only, round-robin, no cookie
Deliverable: end-to-end Shiny session works. Open Aurora Prime card on the landing, app loads, reactivity works, session survives page refresh.
Goal: visibility into running containers.
-
/admin/dashboardpage (mockup:admin-dashboard.html) - Metric cards: containers, sessions, memory, CPU
- Live container table with utilization bars (short polling via
GET /admin/dashboard/snapshot) - Sessions-over-24h sparkline chart
- Recent events feed
- Logs streaming page with filter + regex + follow mode
- Container detail view: live CPU/mem charts, environment, logs
- Alert notification webhook (#930) — spawn-failed / replica-down / saturated events POST JSON to an operator-configured URL (admin System tab), with retry, cooldown dedup and a test button
- Prometheus metrics endpoint at
/metrics
Deliverable: open the dashboard, see everything that's running, drill into any container.
- Graceful shutdown (drain active sessions before exit)
- structured logging via
tracing+tracing-subscriber - Rate limiting on API specs (token bucket)
- CORS support for API specs
- Configurable max body sizes
- Health endpoint
/healthz - Readiness endpoint
/readyz - Multi-stage Dockerfile for Ruscker itself
- systemd unit file
- Documentation: deployment guide, migration guide from ShinyProxy, troubleshooting
Deliverable: ready to drop into production.
-
MultiHostDockerBackend(talks to multiple Docker hosts) - Bin-pack vs spread strategies
- Per-spec anti-affinity ("replicas must be on different hosts")
- Postgres
SessionStoreimplementation - PostgreSQL-backed admin catalog matching the SQLite schema
- Coordination via Postgres advisory locks
- Failover testing
- OIDC integration (Keycloak, Auth0, Google)
- SAML for enterprise
- LDAP directory integration
- Role-based access control (Viewer / Editor / Admin) — shipped
- Per-spec access lists (only group X can use this app) — shipped in Phase 6
- Per-app step-up MFA — one user-owned TOTP factor, recovery codes, trusted-device grants, and per-spec proof-freshness policy
- Opt-in identity headers for signed-in users on HTTP and WebSocket — ShinyProxy-compatible user/group headers plus selected profile claims
Only the external identity-provider items (OIDC / SAML / LDAP) remain; the database-backed role model, per-spec access lists, step-up MFA, and identity forwarding already ship.
- Scheduled jobs — Admin Schedules page, cron ETL semantics,
no fire-on-create, collapsed downtime catch-up, leader-only HA firing,
the spec's image/environment/volumes/credentials, optional command
override, run history/log tail, one-hour default timeout, and the
job-failedalert webhook - Named-volume management — Admin Disk Volumes card with live reference counts; create labels Ruscker-owned volumes, and remove is offered only for Ruscker-created volumes with zero live references and no catalog reference
- Users-page scaling — 50-row server-side pagination and server-side accent-aware, case-insensitive search across username, groups, and profile fields on SQLite and Postgres
- Password policy and password generator
- Consolidated per-user edit page, including profile, groups, role, password reset, 2FA status/reset, and deletion
- Clearer admin navigation and titles: Containers and Activity/Atividades, with management-oriented page titles
- Cookie-boundary hardening for shared-origin apps: reserved
Set-Cookiefiltering and cookie-clearingClear-Site-Dataneutralization -
ruscker.ymlas the canonical service config, withapplication.ymlretained for ShinyProxy import/fallback
- Kubernetes backend — possible, but not until there's demand.
KubernetesBackendis a long-term aspiration, not a phase. - App ecosystem features — pause/resume, snapshots, etc. These are ShinyProxy Pro features. Not in MVP.
- Multi-tenancy — separate organizations / billing. Possibly a commercial extension; not in open source.
- Public marketplace of apps — outside scope.
After Phase 5, Ruscker can be deployed as a drop-in replacement for existing ShinyProxy installations. The migration path:
ruscker import application.yml --db /var/ruscker/ruscker.db- Stop ShinyProxy
- Start Ruscker on the same port
- Verify with the same browser URL
Phases 6+ are for organizations with more demanding needs.