Installable applications
An installable app is a Go package that provides a cicadel.App: routes, commands, components, and optionally database migrations. Install its module, configure its factory, and register the returned app in your project.
First-party installable packages are core defaults composed through the same public contracts as your own packages. Standard scaffolding registers accounts and formsets and supplies migration and query-generation commands. Installing an additional third-party module with go get does not activate it.
#Ask your agent
Read https://cicadel.fauzaanu.com/en/0.1.0/topics/installable-apps/index.md.
Add [package and version] to my existing Cicadel application.
Inspect its constructor and required configuration, then register it in
project/apps.go without replacing existing apps. Check its dependencies and routes.
Review any database migrations before applying them. Run app checks and tests.#Install a published package
Use the module path and version provided by the package author:
go get example.com/your-org/greetings@v0.1.0example.com/your-org/greetings is a placeholder, not a published Cicadel package. The next section shows how to create a local package with this API.
In project/apps.go, import the package and add its constructor to your existing app list:
package project
import (
"os"
greetings "example.com/your-org/greetings"
"github.com/fauzaanu/cicadel"
"github.com/fauzaanu/cicadel/contrib/formsets"
"gorm.io/gorm"
)
func InstalledApps(_ *gorm.DB) ([]cicadel.App, error) {
return []cicadel.App{
formsets.App(),
greetings.New(greetings.Options{
Greeting: "Welcome",
Output: os.Stdout,
}),
}, nil
}This adds a package while retaining the standard formset registration; the project builder composes accounts. Preserve your other entries when adding it to an existing project. Database-backed factories can use the supplied *gorm.DB; follow that package’s constructor signature.
#Create and use a local package
From an existing Cicadel project, scaffold a separate module beside it:
go tool cicadel startpackage ../greetings example.com/your-org/greetings greetings
cd ../greetings
go get github.com/fauzaanu/cicadel@main
go mod tidy
go test -race ./...
cd ../myapp
go mod edit -replace=example.com/your-org/greetings=../greetings
go get example.com/your-org/greetings@v0.1.0Register it with the Go example above, then run:
go run manage.go apps
go run manage.go check
go run manage.go greetings:hello
# Welcome
go run manage.go runserverGET /greetings/ returns {"greeting":"Welcome"}. The package scaffold includes this route, a command, typed options, and tests. Use startapp notes instead when you only need an app inside one project.
#What a package returns
This small app exports one named route:
package hello
import (
"net/http"
"github.com/fauzaanu/cicadel"
"github.com/fauzaanu/cicadel/response"
"github.com/fauzaanu/cicadel/urls"
)
func New() cicadel.App {
return cicadel.App{
Name: "hello",
Manifest: &cicadel.Manifest{
Module: "example.com/hello",
Version: "v0.1.0",
API: cicadel.AppAPIVersion,
},
Routes: []urls.Route{
urls.Path("GET", "/hello/", "hello:index",
func(w http.ResponseWriter, r *http.Request) {
if err := response.JSON(w, http.StatusOK, map[string]string{"message": "Hello"}); err != nil {
response.Error(w, http.StatusInternalServerError)
}
}),
},
}
}Name owns the route and command namespace. Keep it stable across upgrades, especially when the app owns migration history. Go selects the module version; Manifest.Version describes the package’s compatibility metadata.
#Publish static files
Put public CSS, images and scripts in an embedded directory and return it as
App.Static. The host serves each file at /static/<app name>/<path> with a
content-hashed URL and collects them with go run manage.go collectstatic DIR.
Resolve URLs in handlers with static.URL(r.Context(), "greetings", "site.css").
Do not add your own asset routes; App.Resources stays private. The startpackage
scaffold publishes assets/static/. See static files.
#Add migrations and dependencies
For a package with PostgreSQL tables, embed recorded SQL migrations and declare the database requirement:
//go:embed migrations/*.sql
var migrations embed.FS
func migrationFiles() (fs.FS, error) {
return fs.Sub(migrations, "migrations")
}Use the returned filesystem as App.Migrations, and set Manifest.Providers to []string{"postgres"}. Import embed and io/fs. Handle the filesystem error in your factory, then run the host’s go run manage.go migrate before serving requests. Follow the migration guide for generated model migrations.
If your package needs another installed app, declare it in Manifest.Requires:
Requires: []cicadel.Requirement{
{Name: "accounts", Version: cicadel.VersionRange{
Min: "v1.0.0", Before: "v2.0.0",
}},
},These are example compatibility bounds, not a claim that an accounts v1 release exists. The host must register the dependency too. Missing or incompatible dependencies fail startup checks.
#Remove a project app
To disable an app, remove its constructor from InstalledApps. Remove unused imports and run go mod tidy. This does not delete tables or stored data; data removal must be a separate, deliberate operation.
For component exports, see component libraries. For package lifecycle and compatibility details, see the package authoring reference.
#External translation helpers
Localization remains a third-party responsibility. An installable can supply
request middleware through App.Middleware, store its language selection under
an unexported context key, and expose a plain-text helper accepting
context.Context. Render the result with normal .templ text expressions or
ui.Text; translated strings must not become trusted HTML merely because they
came from a catalog. Keep language selection in the request context rather than
in package globals so concurrent requests cannot change each other’s output.
App middleware wraps that app’s routes. It does not implicitly translate host routes or other installed apps. Hosts that want translated components on their own routes must explicitly compose the extension’s middleware there. Decide fallback languages, missing-key handling and catalog ownership in the extension; Cicadel does not prescribe translation catalogs or language negotiation.
internal/scaffold/translation_test.go verifies this public boundary with a
separately versioned module installed through a local Go proxy into a generated
consumer. It covers Unicode, fallback text, escaped untrusted keys, concurrent
requests, isolation from host routes, unregistration, and go mod tidy removal
followed by rebuilding the generated application. Its tiny two-language lookup
is test data, not a production translation implementation.
Disabling a package means omitting its registration from InstalledApps while
keeping its Go module available. Routes, commands, static files and owned resources disappear
from the application; existing PostgreSQL data and recorded migration history
remain. Restore registration before running migrations: the migration runner
rejects an incomplete catalog containing previously applied package migrations.
Re-enabling the same package restores its surfaces and allows its recorded
history to be checked again. TestInstallUpgradeRemoveExternalPackage exercises
this sequence after a version upgrade, before removing the module entirely.