Stratos uses a plugin system to extend both the backend (Jetstream) and frontend (Angular) with modular, independently loadable capabilities. Plugins add endpoint types, HTTP routes, middleware, and frontend UI packages to the console.
flowchart TD
cfg["plugin-config.yaml (single source of truth)
plugins: cloudfoundry, cfapppush, cfappssh, userinvite,
autoscaler, kubernetes, analysis, monocular"]
be["extra_plugins.go
(backend build)"]
fe["extra_plugins.go
(frontend build)"]
cfg -- go generate --> be
cfg -- frontend prebuild --> fe
Every backend plugin implements api.StratosPlugin
(src/jetstream/api/plugin.go):
type StratosPlugin interface {
Init() error
GetMiddlewarePlugin() (MiddlewarePlugin, error)
GetEndpointPlugin() (EndpointPlugin, error)
GetRoutePlugin() (RoutePlugin, error)
}A plugin returns a concrete implementation from one or more of these
methods and nil for the rest. The sub-interfaces are:
| Interface | Purpose | Methods |
|---|---|---|
EndpointPlugin |
Defines an endpoint type (CF, K8s, metrics) | GetType, Register, Connect, Validate, Info, UpdateMetadata |
RoutePlugin |
Adds HTTP routes to the Echo server | AddAdminGroupRoutes, AddSessionGroupRoutes |
MiddlewarePlugin |
Injects request/response middleware | EchoMiddleware, SessionEchoMiddleware |
Two optional interfaces provide additional lifecycle hooks:
| Interface | Purpose |
|---|---|
StratosPluginCleanup |
Called on shutdown (Destroy()) |
EndpointNotificationPlugin |
Notified when endpoints are added/removed |
A ConfigPlugin interface allows early configuration before plugin
loading via RegisterJetstreamConfigPlugin().
Plugins register themselves in a Go init() function using blank
imports. The init() function calls api.AddPlugin with the plugin
name, an optional dependency list, and an initializer function:
package cloudfoundry
func init() {
api.AddPlugin("cloudfoundry", nil, Init)
}
func Init(portalProxy api.PortalProxy) (api.StratosPlugin, error) {
return &CloudFoundrySpecification{portalProxy: portalProxy}, nil
}Plugins that depend on other plugins declare them explicitly:
func init() {
api.AddPlugin("cfapppush", []string{"cloudfoundry"}, Init)
}The loader in load_plugins.go resolves dependencies recursively.
A plugin with an unmet dependency is skipped with a log warning.
Two Go files control which plugins are compiled into the binary:
| File | Purpose | Maintained by |
|---|---|---|
src/jetstream/default_plugins.go |
Core plugins always included | Developer (manual) |
src/jetstream/extra_plugins.go |
Feature plugins from plugin-config.yaml | Build system (auto-generated) |
src/jetstream/plugin-config.yaml |
Extra plugin list (single source of truth) | Developer (manual) |
default_plugins.go — hardcoded imports for plugins that every
deployment needs regardless of which frontend packages are enabled:
import (
_ "github.com/cloudfoundry/stratos/src/jetstream/plugins/backup"
_ "github.com/cloudfoundry/stratos/src/jetstream/plugins/cloudfoundryhosting"
_ "github.com/cloudfoundry/stratos/src/jetstream/plugins/metrics"
_ "github.com/cloudfoundry/stratos/src/jetstream/plugins/userfavorites"
_ "github.com/cloudfoundry/stratos/src/jetstream/plugins/userinfo"
)extra_plugins.go — auto-generated by the prebuild step. Do not
edit manually. See Decoupled Builds
for details.
main() → portalProxy.loadPlugins()
1. yamlgenerated.MakePluginsFromConfig() ← reads plugins.yaml
2. for each name in api.PluginInits:
addPlugin(name)
→ resolve dependencies (recursive)
→ call Init(portalProxy)
→ store in pp.Plugins map
These are always compiled in via default_plugins.go.
| Plugin | Type | Purpose |
|---|---|---|
| backup | Route | Endpoint and token backup/restore (admin-only) |
| cloudfoundryhosting | Middleware, Config | Stratos-in-CF deployment support — auto-registration, session affinity, CF UAA config |
| metrics | Endpoint, Route | Prometheus endpoint type; correlates metrics with CF and K8s endpoints |
| userfavorites | Route | User favorites/bookmarks persistence in the database |
| userinfo | Route | User profile management — get/update info and password; supports UAA, local, and no-auth modes |
These are compiled in via the auto-generated extra_plugins.go based
on frontend package declarations.
| Plugin | Depends On | Frontend Package | Purpose |
|---|---|---|---|
| cloudfoundry | — | cloud-foundry | CF endpoint type, auth, firehose and app stream endpoints |
| cfapppush | cloudfoundry | cloud-foundry | Application deployment/push workflow from the UI |
| cfappssh | cloudfoundry | cloud-foundry | SSH into CF app instances via WebSocket proxy |
| userinvite | cloudfoundry | cloud-foundry | User invitation system with email and UAA client auth |
| autoscaler | cloudfoundry | cf-autoscaler | Autoscaling policies, metrics, and events for CF apps |
| kubernetes | — | kubernetes | K8s endpoint type with multi-auth (GKE, AWS, Azure, OIDC, cert, token) |
| analysis | kubernetes | kubernetes | Cluster analysis and reporting via Popeye (tech preview) |
| monocular | kubernetes | kubernetes | Helm chart repository management and ArtifactHub integration |
| Plugin | Purpose |
|---|---|
| desktop | Desktop hosting mode — local endpoint/token store overlay; declared by desktop-extensions frontend package |
| yamlgenerated | Generates endpoint type plugins at runtime from plugins.yaml (currently GitHub and GitLab git endpoints) |
cloudfoundry
├── cfapppush
├── cfappssh
├── userinvite
└── autoscaler
kubernetes
├── analysis
└── monocular
(no dependencies)
├── backup
├── cloudfoundryhosting
├── metrics
├── userfavorites
├── userinfo
└── desktop
src/jetstream/plugins.yaml defines lightweight endpoint types that
are created at runtime by the yamlgenerated plugin. These do not
require a Go source directory — they are configured entirely via YAML:
- name: git
sub_type: github
auth_type: Token
user_info: /user
user_info_path: login
- name: git
sub_type: gitlab
auth_type: Bearer
user_info: /user
user_info_path: usernameEach entry generates a plugin that implements EndpointPlugin and
RoutePlugin with standard connect/validate/info flows. This is the
preferred way to add simple token-authenticated endpoint types without
writing Go code.
Frontend packages declare their Angular integration metadata in
package.json under the stratos key:
{
"name": "@stratosui/cloud-foundry",
"stratos": {
"module": "CloudFoundryPackageModule",
"routingModule": "CloudFoundryRoutingModule",
"theming": "sass/_all-theme#apply-theme-stratos-cloud-foundry"
}
}The stratos metadata fields are:
| Field | Purpose |
|---|---|
module |
Angular standalone module class name |
routingModule |
Routing module for lazy-loaded routes |
theming |
SASS theme file and mixin to apply |
assets |
Asset configuration |
Backend plugin declarations are managed centrally in
src/jetstream/plugin-config.yaml, not in individual package.json
files.
The list of extra plugins is defined in
src/jetstream/plugin-config.yaml, which serves as the single source
of truth for both the backend and frontend builds.
The backend uses a standard go generate directive to produce
extra_plugins.go from plugin-config.yaml:
cd src/jetstream && go generate ./...
└── cmd/gen-plugins/main.go
→ reads plugin-config.yaml
→ validates plugin dirs exist
→ writes extra_plugins.go
make build backend runs go generate automatically before
go build.
The frontend prebuild pipeline also reads plugin-config.yaml to
generate the same extra_plugins.go:
bun run prebuild
└── build-orchestrator.js
└── backend.ts → reads plugin-config.yaml
→ validates plugin dirs exist
→ writes extra_plugins.go
bun run build (ng build)
└── Angular CLI compiles frontend
Either build can run independently. The backend only needs Go tooling
(go generate ./...). The frontend prebuild reads the same YAML file
with Node.js. Both produce identical extra_plugins.go output.
The stratos.yaml backend key is still supported as an override
mechanism for custom builds.
# 1. Create plugin directory and main file
mkdir -p src/jetstream/plugins/myplugin// src/jetstream/plugins/myplugin/main.go
package myplugin
import (
"github.com/cloudfoundry/stratos/src/jetstream/api"
"github.com/labstack/echo/v4"
)
func init() {
api.AddPlugin("myplugin", nil, Init)
}
type MyPlugin struct {
portalProxy api.PortalProxy
}
func Init(portalProxy api.PortalProxy) (api.StratosPlugin, error) {
return &MyPlugin{portalProxy: portalProxy}, nil
}
func (p *MyPlugin) Init() error { return nil }
func (p *MyPlugin) GetMiddlewarePlugin() (api.MiddlewarePlugin, error) {
return nil, errors.New("not implemented")
}
func (p *MyPlugin) GetEndpointPlugin() (api.EndpointPlugin, error) {
return nil, errors.New("not implemented")
}
func (p *MyPlugin) GetRoutePlugin() (api.RoutePlugin, error) {
return p, nil
}
func (p *MyPlugin) AddAdminGroupRoutes(echoGroup *echo.Group) {
// Admin-only routes
}
func (p *MyPlugin) AddSessionGroupRoutes(echoGroup *echo.Group) {
echoGroup.GET("/myplugin/data", p.getData)
}
func (p *MyPlugin) getData(c echo.Context) error {
return c.JSON(200, map[string]string{"status": "ok"})
}Add the plugin name to src/jetstream/plugin-config.yaml:
plugins:
- cloudfoundry
- cfapppush
# ...
- mypluginRun go generate ./... from src/jetstream/ to regenerate
extra_plugins.go.
If the plugin should always be compiled in (regardless of frontend
packages), add its import to default_plugins.go instead:
import (
_ "github.com/cloudfoundry/stratos/src/jetstream/plugins/myplugin"
)For simple token-authenticated endpoints, add an entry to
src/jetstream/plugins.yaml instead of writing Go code:
- name: myservice
sub_type: myvariant
auth_type: Token # Token, Bearer, or HttpBasic
user_info: /api/user # REST endpoint to fetch user info
user_info_path: username # JSON path to extract username| File | Purpose |
|---|---|
src/jetstream/api/plugin.go |
Plugin interfaces and registration API |
src/jetstream/load_plugins.go |
Plugin loader with dependency resolution |
src/jetstream/default_plugins.go |
Hardcoded default plugin imports |
src/jetstream/extra_plugins.go |
Auto-generated plugin imports (do not edit) |
src/jetstream/plugin-config.yaml |
Extra plugin list (single source of truth) |
src/jetstream/generate.go |
go:generate directive for extra_plugins.go |
src/jetstream/cmd/gen-plugins/main.go |
Go generator that reads plugin-config.yaml |
src/jetstream/plugins.yaml |
YAML config for runtime-generated endpoint types |
src/jetstream/plugins/yamlgenerated/main.go |
YAML plugin generator |
src/frontend/packages/devkit/src/backend.ts |
Prebuild script that generates extra_plugins.go from plugin-config.yaml |
src/frontend/packages/devkit/src/lib/stratos.config.ts |
Config parser that reads stratos/package metadata |
build/build-orchestrator.js |
Prebuild pipeline orchestrator |