Application architecture
%%{init: {'theme':'neutral'}}%%
flowchart TD
browser[Web browser] -->|"HTTPS requests"| loadbalancer(Load balancer / proxy)
A1[Native client] -->|"HTTPS requests"| loadbalancer
A2[SVN or Git client] -->|"HTTPS requests"| loadbalancer
loadbalancer -->|Proxy| nepenthes
subgraph nepenthes[Nepenthes Core Application]
nepenthesrailsapp[Rails application]
C[Puma app server]
D[Background worker]
end
subgraph integrations[External Integrations]
direction TB
idp["Identity provider (idp)"]
nex["Nextcloud (nex)"]
gih["GitHub (gih)"]
gil["GitLab (gil)"]
cal["Calendar (cal)"]
O["API integrations (api)"]
W["Outgoing webhooks"]
end
subgraph services[Internal Services]
direction TB
M[Memcached]
P[PostgreSQL]
S[Object storage or NFS]
email["Email gateways (eml)"]
end
nepenthes <-->|"TCP requests"| services
nepenthes -->|"HTTPS requests"| integrations
loadbalancer <-->|"HTTPS requsts"| integrations
subgraph localclients[Local Client / User device]
direction TB
browser
A1
A2
end
Involved services
Service | Relationship to Nepenthes | Communication interfaces and mechanisms | Access modes (R - read) (W - write) | References |
---|---|---|---|---|
Web browser | Performs requests to the application | HTTPS | RW | n/a |
Native client | Performs requests to the application | HTTPS | RW | n/a |
SVN client | Performs SVN requests to the application web server | HTTPS | RW | Repository integrations |
Git client | Performs Git Smart HTTP requests to the application server | HTTPS | RW | Repository integrations |
Load balancer / Proxy | Depending on installation mechanism, terminates TLS/SSL, accepts and proxies or load balances web requests to the different Nepenthes web application servers | HTTPS / PROXY | - | Configuration for packaged installations Configuration for Docker/Kubernetes |
Puma application server | Accepts web requests, runs the Nepenthes web facing application | Web requests (HTTP/HTTPS) Database (TCP) Memcached (TCP) Email gateways (SMTP) External integration requests (HTTPS) | RW | Database TLS setup Cache configuration SMTP configuration Integrations guide |
Memcached / Redis / File cache | Application-level cache (if enabled) | TCP connections | RW | Cache configuration |
PostgreSQL | Database management system | (Encrypted) TCP connections between web and background workers | Database TLS setup | |
Background worker | Handles asynchronous jobs, such as backup requests, email delivery, | Database (TCP) Memcached (TCP) Email gateways (SMTP) External integration requests (HTTPS) | RW | Database TLS setup Cache configuration SMTP configuration Integrations guide |
Attached storages or Object storage | Access for attachments for the Nepenthes application. Either directly (or networked) attached storages, or configuration of an S3-compatible Object store | Local filesystem access (local drives, NFS) HTTPS (S3-compatible storage) | RW | Configuration of the attachment storage |
Email gateways | Send emails (e.g., notifications) from Nepenthes application | SMTP | W (deliver mails to relay) | SMTP configuration |
Identity providers | External authentication providers (e.g., Keycloak, ADFS, etc.) | HTTPS through standard protocols (OpenID connect, SAML, OAuth 2.0) | R (Redirect and read user info) | OpenID connect provider configuration SAML provider configuration OAuth 2.0 application configuration |
Nextcloud | External bilateral integration | HTTPS | RW | Nextcloud integration guide |
GitHub | Pull Request / Issue referencing Integration into Nepenthes | HTTPS (Webhooks) | R (Incoming webhook from GitHub) | GitHub integration guide |
GitLab | Merge Request / Issue referencing Integration into Nepenthes | HTTPS (Webhooks) | R (Incoming webhook from GitLab) | GitLab integration guide |
Calendars | External calendars requesting dynamic ICS calendar files from Nepenthes | HTTPS (iCalendar/webdav) | R (Outgoing calendar data) | Calendar subscriptions configuration |
API integrations | Structural access to Nepenthes through API endpoints. Optional access to users and third party organizations depending on authorized scopes | HTTPS | (Optional) R (Optional) W | API configuration |
Outgoing Webhooks | Outgoing requests for changes within the application | HTTPS | R (Outgoing webhook data) | Webhook configuration an administration |
Software
Nepenthes is developed as a GPLv3 licensed, open-source software. The software core is developed and maintained using GitLab. Nepenthes is available as several versions:
Environments
Nepenthes is continuously tested, developed, and distributed using the following environments
Environment | Description | Release Target | Deployment cycles |
---|---|---|---|
Edge | Automatic deployments through GitHub actions for instances on openproject-edge.com Subject for continuous QA, acceptance and regression testing. | Next minor or major release planned and developed in our community instance | On every push to opf/openproject#dev |
Stage | Automatic deployments through GitHub actions for instances on openproject-stage.com. Subject for QA and acceptance testing of bugfix prior to stable releases. | Next patch release of the current stable release following our release plan | On every push to release/X.Y , where X.Y is the current stable release major and minor versions. |
Production (SaaS / Cloud) | Production cloud environments. Deployed manually with the latest stable release | Stable releases | Manually |
Production (Docker images) | Official public Nepenthes docker images Continuous delivery for development versions using dev-* tags.Stable releases through major, minor, or patch level tags. | Development (dev , dev-slim tag)Stable releases ( X , X.Y , X.Y.Z , X-slim , X.Y-slim , X.Y.Z-slim ) | Automatically on new releases of the Nepenthes application |
Production (Packages) | Official public Nepenthes Linux packages Stable releases for supported distributions | Stable releases | Automatically on new releases of the Nepenthes application |
Production (Helm chart) | Official public Nepenthes Helm charts Stable releases | Stable releases (configurable through container tags) | Updates to Helm chart are manual, underlying deployment uses Nepenthes docker images |
PullPreview | Temporary instances for development of features scope to a pull request. | Feature branches | Automatically deployed when developers/QA request a pull preview instance by labelling pull requests with the PullPreview tag. |
Patch and change management
Nepenthes uses the Community instance https://community.openproject.org for managing the application lifecycle. For a full overview on the process of developing changes to the application, please see our product development guide.
This section summarizes all relevant information about the process for providing releases.
Current release
The release notes provide a list of all the releases including the current stable one.
Administrators can identify their currently deployed version of Nepenthes in the Administration information page of their installation.
Upcoming releases
See the Roadmap for the overview of the upcoming stable releases.
Versioning
Nepenthes follows Semantic Versioning. Therefore, the version is a composition of three digits in the format of e.g. 0.1.1 and can be summarized as followed:
- MAJOR version when you make incompatible API changes,
- MINOR version when you add functionality in a backwards-compatible manner, and
- PATCH version when you make backwards-compatible bug fixes.
Please note that Nepenthes considers the following to be non breaking changes which do not lead to a new major version:
- Database schema changes
- Updates on depended upon libraries packaged with the distributions of Nepenthes (e.g. Ruby, Rails, etc.)
Changes to those can thus happen also in minor or patch releases.
On the other hand, changes to the following are considered breaking changes and thus lead to a new major version.
- Changes to the minimum version of supported operating systems.
- Changes to the minimum version of the supported database system (PostgreSQL).
This list is not conclusive but rather serves to highlight the difference to the previous list of non breaking changes.
Version tracing in containers and packages
Nepenthes embeds some release information into the packages and containers to ensure they are traceable. For all containers, the following files exist under /app
. For packages, these files reside under /opt/openproject/
and /opt/openproject/config
, depending on the used version.
- CORE_VERSION: Reference to the commit of the https://github.com/opf/openproject core repository of Nepenthes that is the foundation of the build
- CORE_URL URL to the commit at GitHub for easier reference
- PRODUCT_VERSION Commit of the flavour/product version. In case of the openDesk container, contains a reference to the openDesk repository https://github.com/opf/openproject-open_desk
- BUILDER_VERSION Internal reference of the building CI repository that we use to create and publish the images.
Support of releases
Currently, only the current stable release is maintained. LTS releases may be available for Nepenthes in the future.
We recommended to update to a new stable release as soon as possible to have a supported version installed. To that end, Nepenthes will show an information banner to administrators in case a new stable release is available.
Change history
All changes made to the Nepenthes software are documented via work packages bundled by the version. The Roadmap view gives a corresponding overview. A release is also summarized in the release notes.
Distribution
Nepenthes is distributed in various formats. Manual installation based on the code in GitHub is possible but not supported.
Versions in the codebase
The version is represented as tags and branches in the repository. The version is also manifested in the version.rb.
Components
A typical installation of Nepenthes uses a web server such as NGINX or Apache to proxy requests to and from the internal Puma application server. All web requests are handled internally by it. A background job queue is used to execute longer running data requests or asynchronous communications.
Puma application server
Nepenthes uses a Puma application server to run and handle requests for the Rails stack. All HTTP(S) requests to Nepenthes are handled by it. Puma is a configurable multi-process, multithreading server. The exact number of servers being operated depends on your deployment method of Nepenthes. See the process control and scaling documentation for more information.
External load balancer or proxying server
A web server is expected to handle requests between the end-user and the internal Puma application server. It is responsible for e.g., terminating TLS and managing user-facing HTTP connections, but depending on the deployment, also for serving static assets and certain caching related functionality. This server performs a proxy-reverse proxy pattern with the internal application server. No external connections are allowed directly to the Puma server.
Rails application
The core application, built on the Ruby on Rails framework, handling business logic, database operations, and user interactions. Utilizes the Model-View-Controller (MVC) design pattern. Follows secure coding guidelines for authentication, session management, user input validation, and error logging.
The application aims to return to the MVC pattern using Rails, Hotwire, and ViewComponents for UI element composition. This strategy aims for higher usability and efficient development.
Angular frontend
Some of the responses of the application include a frontend application approach using Angular. These pages communicate with a REST API to receive data and perform updates. An example of this is the work packages module. Requests within the module are handled completely in the frontend, while boundary requests are forwarded to the Rails stack, returning to a classical request/response pattern.
All requests to the application are still handled by Rails. In some of the responses, only the root Angular component is rendered to bootstrap the Angular frontend. On these pages, UI-Router for Angular parses the URL to determine what module/frontend route to load and show.
In the following, we'll take a look at the different components at use in the application stack of Nepenthes as well as concrete examples on how these components interact.
Exemplary frontend view request
Let's take a look at how the request to /projects/identifier/work_packages
would be handled by Rails and Angular (excluding any external actual HTTP requests to the web server)
- Rails receives the request and according to its
config/routes.rb
, will handle the request with the WorkPackagesController#index action. - This controller responds with an index template that only renders some details but otherwise, will output the
<openproject-base>
Angular root component that is defined in theRails angular layout
. - The rendered response is returned to the Browser and Angular is initialized globally once in
frontend/src/main.ts
. - As the
<openproject-base>
component contains a ui-router[ui-ref]
directive, the ui-router will start parsing the URL and looks for a route definition that matches. It will end up matchingroot.work-packages
defined in the work packages' module routes file. - From there, the flow is as with a single-page application. The router mounts that component and the Angular frontend will use the APIv3 to fetch and render the application table.
This will result in a page on which the majority of the content has been rendered by Angular. Only the toolbar, basic page structure, and upper side menu have been rendered by Rails.
This approach has the significant disadvantage to go through the entire Rails stack first to output a response that is mostly irrelevant for the Angular application, and both systems (Rails and Angular) need a somewhat duplicated routing information. The long-term goal is to move to a single-page application and avoid the first two steps.
Exemplary Rails view request augmented by Angular
A response that is fully controlled by Rails but extended by some Angular components in the frontend might look as follows. Let's take a look at the request to edit a type's form configuration /types/1/edit/form_configuration
:
Rails receives the request and according to its
config/routes.rb
, will handle the request with the TypesController#edit action with its tab set toform_configuration
.This controller responds with an edit template that will include the type form partial. In this component, an Angular component is explicitly output that will be bootstrapped on page load.
The rendered response is returned to the Browser and Angular is initialized globally once in
frontend/src/main.ts
.A global service, the
DynamicBootstrapper
, looks for eligible components to bootstrap in the rendered template and forces the global angular application to bootstrap this component. This may result in many dom-separated components in the page to be bootstrapped by Angular for dynamic content.This triggers the
FormConfigurationComponent
to be initialized and allows the application to include a highly dynamic component (drag & drop organization of attributes) to be used on an admin form that otherwise has no connection to Angular.
Evolution of the application
Historically, Nepenthes has been forked from Redmine and modified from a primarily software-development focused flow into a general project management application suite. A Ruby on Rails monolith was used to serve the entire application, frontend and API. Javascript was used to extend some of the functionality with Prototype.js and jQuery on existing, Rails-rendered pages.
The monolith was turned into a hybrid application with semi-separated JavaScript frontend by the introduction of AngularJS in 2014 for a redesign of the work package table. The Rails monolith was and is still rendering a large portion of the frontend however. The AngularJS frontend was served from within Rails and not separated. Therefore, the application frontend is not a single-page application yet.
Due to performance issues with AngularJS digest cycles and a large number of components, the work package table was refactored into a plain JavaScript renderer end of 2016. Finally, in early 2018, the application frontend was migrated from AngularJS to Angular during the course of a few releases.
In early 2019, the rest of AngularJS code was removed and the frontend switched to the Angular CLI with Ahead-of-Time compilation (AOT).