diff --git a/src/backend/exports.js b/src/backend/exports.js
index 558ab3f72..e8b257d05 100644
--- a/src/backend/exports.js
+++ b/src/backend/exports.js
@@ -30,6 +30,7 @@ const { PuterAIModule } = require("./src/modules/puterai/PuterAIModule.js");
const { BroadcastModule } = require("./src/modules/broadcast/BroadcastModule.js");
const { WebModule } = require("./src/modules/web/WebModule.js");
const { Core2Module } = require("./src/modules/core/Core2Module.js");
+const { TemplateModule } = require("./src/modules/template/TemplateModule.js");
module.exports = {
@@ -49,6 +50,7 @@ module.exports = {
Core2Module,
CoreModule,
WebModule,
+ TemplateModule,
],
// Pre-built modules
diff --git a/src/backend/src/modules/template/README.md b/src/backend/src/modules/template/README.md
new file mode 100644
index 000000000..e272a18fe
--- /dev/null
+++ b/src/backend/src/modules/template/README.md
@@ -0,0 +1,56 @@
+# TemplateModule
+
+This is a template module that you can copy and paste to create new modules.
+
+This module is also included in `EssentialModules`, which means it will load
+when Puter boots. If you're just testing something, you can add it here
+temporarily.
+
+## Services
+
+### TemplateService
+
+This is a template service that you can copy and paste to create new services.
+You can also add to this service temporarily to test something.
+
+#### Listeners
+
+##### `install.routes`
+
+TemplateService listens to this event to provide an example endpoint
+
+##### `boot.consolidation`
+
+TemplateService listens to this event to provide an example event
+
+##### `boot.activation`
+
+TemplateService listens to this event to show you that it's here
+
+##### `start.webserver`
+
+TemplateService listens to this event to show you that it's here
+
+## Libraries
+
+### hello_world
+
+#### Functions
+
+##### `hello_world`
+
+This is a simple function that returns a string.
+You can probably guess what string it returns.
+
+## Notes
+
+### Outside Imports
+
+This module has external relative imports. When these are
+removed it may become possible to move this module to an
+extension.
+
+**Imports:**
+- `../../util/context.js`
+- `../../services/BaseService` (use.BaseService)
+- `../../util/expressutil`
diff --git a/src/backend/src/modules/template/TemplateModule.js b/src/backend/src/modules/template/TemplateModule.js
new file mode 100644
index 000000000..5e22fd36c
--- /dev/null
+++ b/src/backend/src/modules/template/TemplateModule.js
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 Puter Technologies Inc.
+ *
+ * This file is part of Puter.
+ *
+ * Puter is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+const { AdvancedBase } = require("@heyputer/putility");
+
+/**
+ * This is a template module that you can copy and paste to create new modules.
+ *
+ * This module is also included in `EssentialModules`, which means it will load
+ * when Puter boots. If you're just testing something, you can add it here
+ * temporarily.
+ */
+class TemplateModule extends AdvancedBase {
+ async install (context) {
+ // === LIBS === //
+ const useapi = context.get('useapi');
+
+ const lib = require('./lib/__lib__.js');
+
+ // In extensions: use('workinprogress').hello_world();
+ // In services classes: see TemplateService.js
+ useapi.def(`workinprogress`, lib, { assign: true });
+
+ useapi.def('core.context', require('../../util/context.js').Context);
+
+ // === SERVICES === //
+ const services = context.get('services');
+
+ const { TemplateService } = require('./TemplateService.js');
+ services.registerService('template-service', TemplateService);
+ }
+
+}
+
+module.exports = {
+ TemplateModule
+};
diff --git a/src/backend/src/modules/template/TemplateService.js b/src/backend/src/modules/template/TemplateService.js
new file mode 100644
index 000000000..e079ac2c4
--- /dev/null
+++ b/src/backend/src/modules/template/TemplateService.js
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 Puter Technologies Inc.
+ *
+ * This file is part of Puter.
+ *
+ * Puter is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+// TODO: import via `USE` static member
+const BaseService = require("../../services/BaseService");
+const { Endpoint } = require("../../util/expressutil");
+
+/**
+ * This is a template service that you can copy and paste to create new services.
+ * You can also add to this service temporarily to test something.
+ */
+class TemplateService extends BaseService {
+ static USE = {
+ // - Defined by lib/__lib__.js,
+ // - Exposed to `useapi` by TemplateModule.js
+ workinprogress: 'workinprogress'
+ }
+
+ _construct () {
+ // Use this override to initialize instance variables.
+ }
+
+ async _init () {
+ // This is where you initialize the service and prepare
+ // for the consolidation phase.
+ this.log.info("I am the template service.");
+ }
+
+ /**
+ * TemplateService listens to this event to provide an example endpoint
+ */
+ ['__on_install.routes'] (_, { app }) {
+ this.log.info("TemplateService get the event for installing endpoint.");
+ Endpoint({
+ route: '/example-endpoint',
+ methods: ['GET'],
+ handler: async (req, res) => {
+ res.send(this.workinprogress.hello_world());
+ }
+ }).attach(app);
+ // ^ Don't forget to attach the endpoint to the app!
+ // it's very easy to forget this step.
+ }
+
+ /**
+ * TemplateService listens to this event to provide an example event
+ */
+ ['__on_boot.consolidation'] () {
+ // At this stage, all services have been initialized and it is
+ // safe to start emitting events.
+ this.log.info("TemplateService sees consolidation boot phase.");
+
+ const svc_event = this.services.get('event');
+
+ svc_event.on('template-service.hello', (_eventid, event_data) => {
+ this.log.info('template-service said hello to itself; this is expected', {
+ event_data,
+ });
+ });
+
+ svc_event.emit('template-service.hello', {
+ message: 'Hello all you other services! I am the template service.'
+ });
+ }
+ /**
+ * TemplateService listens to this event to show you that it's here
+ */
+ ['__on_boot.activation'] () {
+ this.log.info("TemplateService sees activation boot phase.");
+ }
+
+ /**
+ * TemplateService listens to this event to show you that it's here
+ */
+ ['__on_start.webserver'] () {
+ this.log.info("TemplateService sees it's time to start web servers.");
+ }
+}
+
+module.exports = {
+ TemplateService
+};
+
diff --git a/src/backend/src/modules/template/lib/__lib__.js b/src/backend/src/modules/template/lib/__lib__.js
new file mode 100644
index 000000000..51104b7ba
--- /dev/null
+++ b/src/backend/src/modules/template/lib/__lib__.js
@@ -0,0 +1,3 @@
+module.exports = {
+ hello_world: require('./hello_world.js'),
+};
diff --git a/src/backend/src/modules/template/lib/hello_world.js b/src/backend/src/modules/template/lib/hello_world.js
new file mode 100644
index 000000000..cbef6e3f2
--- /dev/null
+++ b/src/backend/src/modules/template/lib/hello_world.js
@@ -0,0 +1,10 @@
+
+/**
+ * This is a simple function that returns a string.
+ * You can probably guess what string it returns.
+ */
+const hello_world = () => {
+ return "Hello, world!";
+}
+
+module.exports = hello_world;