Hello World module reference
The cloudsmith-module-hello repository is the canonical reference implementation for CloudSmith modules. It exercises every contract in the ICloudSmithModule lifecycle and every platform integration point (nav slots, health checks, background jobs, API endpoints) in a minimal, well-commented codebase.
Use it as a fork template when building your own module.
Source: github.com/cloudsmith-cloud/cloudsmith-module-hello
What the Hello World module does
| Capability | Detail |
|---|---|
| Nav slot | Registers “Hello World” in the primary sidebar (nav.primary) |
| API endpoint | GET /api/hello — returns a JSON greeting with module ID, version, and timestamp |
| Module page | GET /modules/hello — HTML page rendered in the portal nav slot target |
| Health check | hello-module — always Healthy; surfaced on the Platform Health page |
| Background job | Heartbeat timer — logs a message every 60 seconds |
Repository structure
cloudsmith-module-hello/
├── src/
│ └── CloudSmith.Module.Hello/
│ ├── HelloModule.cs — ICloudSmithModule implementation (all 5 lifecycle hooks)
│ ├── HelloHealthCheck.cs — IHealthCheck implementation
│ ├── HelloEndpoints.cs — Minimal API endpoint registration
│ ├── Program.cs — Host builder and SDK wiring
│ └── appsettings.json — Default configuration
├── module.json — Module manifest (schema-validated at install time)
├── Dockerfile — Multi-stage build: SDK restore → publish → runtime image
└── .github/workflows/ci.yml — CI: build + ghcr.io push on every main commit
ICloudSmithModule lifecycle
Every module must implement ICloudSmithModule. The Module Lifecycle Manager (MLM) calls the five lifecycle methods in the following order:
ConfigureServices(IServiceCollection)
→ ConfigureAsync(IModuleContext, CancellationToken)
→ StartAsync(CancellationToken)
→ [module serves traffic]
→ StopAsync(CancellationToken)
→ DisposeAsync()
| Method | When called | What to do |
|---|---|---|
ConfigureServices |
Host builder construction — DI container is not yet built | Register DI services: repositories, HTTP clients, domain services |
ConfigureAsync |
After DI container is built, before any StartAsync fires |
Obtain a logger from IModuleContext, subscribe to the event bus, run cache warm-up |
StartAsync |
After the host is fully started and ready for traffic | Start background timers and workers |
StopAsync |
On SIGTERM or host stop — 30-second grace period | Signal background workers to stop; flush write buffers |
DisposeAsync |
After StopAsync completes or times out |
Dispose IDisposable fields and event bus subscription handles |
The MLM guarantees that all ConfigureAsync calls across all loaded modules complete before any StartAsync fires. A failure in ConfigureAsync transitions the module to Failed state and logs error CS-MOD-5004. The host continues loading other modules regardless.
IModuleContext
ConfigureAsync receives an IModuleContext that provides:
GetLogger<T>()— Serilog-backed logger pre-enriched withmodule_idandmodule_versionOrgId— organization scope for multi-tenant deploymentsEventBus— subscribe to and publish platform domain events
The [CloudSmithSlotContribution] attribute
Nav slot registrations are declared via [CloudSmithSlotContribution] on the module class. The MLM reads these attributes at load time and wires the nav entries into the portal before StartAsync fires.
[CloudSmithSlotContribution(CloudSmithSlots.NavPrimary, Order = 100)]
[CloudSmithSlotContribution(CloudSmithSlots.ModulePage)]
public sealed class HelloModule : ICloudSmithModule
| Slot | Constant | What it does |
|---|---|---|
nav.primary |
CloudSmithSlots.NavPrimary |
Adds the module’s nav entry to the primary sidebar. The label, icon, and route come from module.json under spec.navSlots. |
module.page |
CloudSmithSlots.ModulePage |
Marks the module as contributing a full portal page, rendered in the nav slot target route. |
The Order parameter on NavPrimary controls the position of the nav entry relative to other modules. Lower numbers appear higher in the list. The Hello module uses order 100, which places it below all built-in platform nav items (order 0–50).
Slot registrations in the attribute must match the corresponding entries in module.json under spec.navSlots. A mismatch causes a warning at load time but does not prevent the module from starting.
module.json
module.json is the module manifest. It is validated against the CloudSmith module manifest JSON schema at install time. A manifest that fails validation causes install to abort with a descriptive error.
{
"$schema": "https://cloudsmith.cloud/schemas/module-manifest/v1.json",
"metadata": {
"id": "cloudsmith-module-hello",
"name": "Hello World",
"version": "0.1.0",
"description": "Reference module demonstrating CloudSmith module install, uninstall, and nav slot registration.",
"author": "CloudSmith",
"publisher": "cloudsmith-cloud",
"license": "Apache-2.0",
"repositoryUrl": "https://github.com/cloudsmith-cloud/cloudsmith-module-hello"
},
"spec": {
"sdkVersion": ">=0.1.0 <1.0.0",
"image": "ghcr.io/cloudsmith-cloud/cloudsmith-module-hello:latest",
"apiBasePath": "/api/hello",
"healthEndpoint": "/health",
"permissionsRequired": [],
"navSlots": [
{
"slotId": "nav.primary",
"id": "hello-world",
"label": "Hello World",
"icon": "Handshake",
"route": "/modules/hello",
"order": 100
}
],
"healthChecks": [
{
"name": "hello-module",
"path": "/health",
"tags": ["informational"]
}
],
"backgroundJobs": [
{
"id": "hello-heartbeat",
"description": "Logs a heartbeat message every 60 seconds.",
"intervalSeconds": 60
}
]
}
}
Required manifest fields
| Field | Type | Description |
|---|---|---|
metadata.id |
string | Stable kebab-case identifier — must match ModuleInfo.Id in code. A mismatch causes CS-MOD-5001 and aborts load. |
metadata.name |
string | Human-readable display name shown in the catalog |
metadata.version |
string | SemVer 2.0 version |
metadata.description |
string | One-sentence description shown in the catalog |
metadata.author |
string | Author name or organization |
spec.sdkVersion |
string | SemVer range for the required CloudSmith.Sdk version |
spec.image |
string | Fully-qualified OCI image reference |
spec.apiBasePath |
string | URL prefix for this module’s API routes |
spec.healthEndpoint |
string | Path the MLM calls to verify the module is running |
Optional fields: spec.navSlots, spec.permissionsRequired, spec.healthChecks, spec.backgroundJobs.
The icon value in spec.navSlots must be a valid Lucide icon name. See lucide.dev/icons.
Health checks
HelloHealthCheck implements IHealthCheck. The MLM registers it with the platform health aggregator and surfaces it on:
GET /health/ready— aggregated platform health endpoint- Platform Management → Platform Health in the portal, under the name
hello-module
Health check tags control how the platform treats a failure:
| Tag | Effect |
|---|---|
informational |
Failure is reported but does not change overall platform health status |
degradable |
Failure sets overall platform health to Degraded (HTTP 200) |
critical |
Failure sets overall platform health to Unhealthy (HTTP 503) |
The Hello module uses informational because it is a reference module with no effect on platform availability. Production modules should use degradable or critical based on their impact.
CI workflow
The CI workflow in .github/workflows/ci.yml runs on every push to main and on all pull requests.
| Trigger | Action |
|---|---|
Push to main |
Build image, push to ghcr.io with :latest and :<git-sha> tags |
| Pull request | Build only — validates the Dockerfile and module manifest; no image push |
The workflow attaches the following OCI labels required for catalog discovery:
org.cloudsmith.module=true
org.cloudsmith.module.id=cloudsmith-module-hello
org.cloudsmith.module.version=0.1.0
org.opencontainers.image.source=https://github.com/cloudsmith-cloud/cloudsmith-module-hello
The platform’s GHCR-based module catalog filters packages by the org.cloudsmith.module=true label. Any image published to a GHCR repository under ghcr.io/<your-org>/ that carries this label will appear in the catalog automatically.
Fork and build your own module
Step 1 — Fork the repository
gh repo fork cloudsmith-cloud/cloudsmith-module-hello \
--clone \
--org <your-github-org>
Rename the fork to <your-org>-module-<name>.
Step 2 — Rename the module
Replace every occurrence of Hello / hello with your module name across these files:
| File | What to change |
|---|---|
src/CloudSmith.Module.Hello/ (directory) |
Rename to src/CloudSmith.Module.<YourName>/ |
CloudSmith.Module.Hello.csproj |
Rename to CloudSmith.Module.<YourName>.csproj |
HelloModule.cs |
Rename file and class; update ModuleInfo.Id and ModuleInfo.Version |
HelloHealthCheck.cs |
Rename file and class; update Name constant |
HelloEndpoints.cs |
Rename file and class; update route prefixes |
Program.cs |
Update MapCloudSmithModule<HelloModule>() and MapHelloEndpoints() references |
Step 3 — Update module.json
Set your module’s identity fields and image reference:
{
"metadata": {
"id": "<your-org>-module-<name>",
"name": "<Your Module Display Name>",
"version": "0.1.0",
"description": "<One-sentence description>",
"author": "<Your Name or Org>",
"publisher": "<your-github-org>"
},
"spec": {
"image": "ghcr.io/<your-github-org>/<your-org>-module-<name>:latest",
"apiBasePath": "/api/<name>",
"navSlots": [
{
"slotId": "nav.primary",
"id": "<name>",
"label": "<Your Label>",
"icon": "<LucideIconName>",
"route": "/modules/<name>",
"order": 200
}
]
}
}
Step 4 — Implement your module logic
| File | Purpose |
|---|---|
HelloModule.cs |
Implement the 5 lifecycle hooks; register event bus subscriptions in ConfigureAsync and dispose them in DisposeAsync |
HelloEndpoints.cs |
Add Minimal API routes; protect them with .RequireAuthorization() |
HelloHealthCheck.cs |
Check backing service availability (database, external APIs) and return Unhealthy when unavailable |
The inline comments in each file explain the contract touchpoints.
Step 5 — Push and install
Push to main in your fork. The CI workflow publishes the image to ghcr.io/<your-org>/<repo>:latest.
To install the module in a running CloudSmith instance:
POST /api/modules/install
Content-Type: application/json
{
"imageRef": "ghcr.io/<your-org>/<repo>:latest",
"manifestUrl": "https://raw.githubusercontent.com/<your-org>/<repo>/main/module.json"
}
After install, the nav entry you configured appears in the portal sidebar within approximately 5 seconds.
To uninstall:
DELETE /api/modules/<module-id>
Related
- Develop a CloudSmith module — full module development guide including the catalog interface and cosign signing
- Browse and manage the module catalog — install, update, and remove modules from the portal and CLI