Oikos loads third-party modules from the repository-level modules/ directory. Each module lives in its own folder and must include a module.json manifest. Modules are separate code: do not edit Oikos core files to install one.
modules/
example-module/
module.json
index.js
style.css
The folder name must match the manifest id.
{
"id": "example-module",
"name": "Example Module",
"version": "1.0.0",
"description": "Adds a small page to Oikos.",
"entry": "index.js",
"style": "style.css",
"icon": "box",
"accent": "#6366F1",
"menu": {
"show": true,
"label": "Example",
"icon": "box",
"order": 100
}
}Required fields:
id: lowercase letters, numbers and hyphens only. Must match the module folder.entry: a relative.jsfile exporting arender(container, context)function.
Optional fields:
style: a relative.cssfile loaded only for this module page.menu.show: set tofalseif the module should not appear in the left menu.menu.label,menu.icon,menu.order: left-menu label, Lucide icon name, and order.accent: a#RRGGBBcolor used for menu highlighting.
import { api } from '/api.js';
import { esc } from '/utils/html.js';
export async function render(container, context) {
const me = await api.get('/auth/me');
container.replaceChildren();
container.insertAdjacentHTML('beforeend', `
<div class="page">
<div class="page__header">
<h1 class="page__title">Example Module</h1>
</div>
<section class="settings-card">
<p>Hello, ${esc(me.user.display_name)}</p>
</section>
</div>
`);
}Modules may import public Oikos browser libraries such as /api.js, /i18n.js, and utilities under /utils/. Modules must follow the same frontend security rules as core Oikos:
- Use
replaceChildren()andinsertAdjacentHTML(). - Escape untrusted values before inserting HTML.
- Do not use external CDNs.
- Do not use
innerHTML. - Do not bypass authentication, authorization, CSRF, or CSP.
Oikos scans modules/ and validates each module.json. Invalid modules are shown as errored in Settings and are not loaded. Disabled modules are not served to the browser and do not appear in navigation. If a module page fails while rendering, Oikos shows an error for that page without changing core application code.
Admins can enable, disable, and order modules in Settings -> General -> Active modules. Copying a new folder into modules/ makes it appear there automatically.
The default docker-compose.yml mounts ${MODULES_DIR:-./modules} to /app/modules. To keep modules outside the Oikos checkout, set MODULES_DIR=/absolute/path/to/oikos-modules in .env and restart the compose service. New or changed module folders are scanned at runtime; rebuilding the image is not required.