Security is a core principle of the Gingee platform. The permissions system is designed to be secure by default, following the Principle of Least Privilege. This guide explains how permissions are declared by developers and managed by administrators to create a safe and predictable server environment.
Important: Permissions and the app sandbox provide cooperative multi-app isolation on a shared Node.js process. They are not a hard boundary against mutually hostile tenants. Before deploying untrusted apps, read the Gingee Threat Model.
Gingee operates on a strict whitelist model. By default, a sandboxed application has no access to potentially sensitive modules like the filesystem (fs), database (db), outbound HTTP client (httpclient), transactional email (email), generative AI (ai), or the CRON scheduler.
Access to these protected modules must be explicitly granted by a server administrator. If a permission has not been granted, any attempt by an app to require() that module will result in a security error, and the script will fail to execute.
This model ensures that administrators have full control and awareness of an application's capabilities.
pmft.json)When you build an application that you intend to distribute (as a .gin file) or share, you must declare the permissions it requires in a manifest file. This file acts as a formal request to the administrator who will install your app.
pmft.json (Permissions Manifest)web/<your-app-name>/box/pmft.jsonThe gingee-cli will read this file directly from your .gin package during installation to prompt the administrator for consent.
pmft.jsonThe file contains a single permissions object with two keys: mandatory and optional.
mandatory: An array of permission keys that are essential for your app's core functionality. If the administrator denies a mandatory permission, the installation process should be aborted.optional: An array of permission keys for features that are enhancements but not critical. Your application code should be written to handle cases where an optional permission is not granted.Example pmft.json for a blog application:
{
"permissions": {
"mandatory": ["db", "fs"],
"optional": [
"httpclient",
"email",
"ai",
"scheduler",
"websockets",
"queue"
]
}
}
In this example, the blog requires database and filesystem access to function. Optional features (outbound HTTP, transactional email, generative AI) are listed separately so an administrator can grant only what they approve. This file is the definitive source of truth that the gingee-cli will use to generate the interactive consent prompts for the administrator during installation.
As a server administrator, you have the final authority on what an application is allowed to do. Permissions are managed in a central, server-wide file and can be easily edited via the Glade admin panel.
settings/permissions.json)This file is the single source of truth for all application grants on your Gingee server.
project_root/settings/permissions.jsongranted array.Example settings/permissions.json:
{
"glade": {
"granted": ["platform", "fs"]
},
"my-blog-app": {
"granted": ["db", "fs"]
}
}
In this example, my-blog-app was granted its two mandatory permissions, but the administrator chose not to grant the optional httpclient permission.
The easiest way to manage permissions is through the Glade admin panel. On the main dashboard, each application has a Permissions button. Clicking this button will open a modal window where you can safely grant or revoke permissions from the master list.
Saving your changes in this modal will automatically update the settings/permissions.json file and trigger a safe reload of the application to immediately apply the new security rules.
This is the definitive list of all permission keys available in Gingee.
| Permission Key | Description | Security Implication |
|---|---|---|
| platform | PRIVILEGED. Allows the app to use the platform module to manage the lifecycle (install, delete, upgrade, etc.) of other applications on the server. |
Critical. This is the highest level of privilege. Only grant this to a fully trusted administration application like glade. |
| cache | Allows the app to use the caching service for storing and retrieving data, and to call cache.invalidateSysCache to drop engine static/transpile/instance caches for path prefixes under this app (after runtime file replaces; from isolation workers the call is forwarded to the master). |
High. Grants access to the centralized cache service and engine cache invalidation for this app’s WEB/BOX prefixes. |
| db | Allows the app to connect to and query the database(s) configured for it in app.json. |
High. Grants access to the application's primary data store. |
Allows the app to send transactional email via require('email') (configured provider such as SendGrid, or the console logger). Supports per-call config override with email.sendWithConfig. |
High. The app can send outbound email using server- or app-configured credentials (or a runtime key). Can incur cost and deliver messages externally. | |
| messaging | Allows the app to send outbound messages (SMS/MMS/WhatsApp) via require('messaging') (configured provider such as Twilio, or the mock / console logger). Supports per-call config override with messaging.sendWithConfig. |
High. The app can send SMS/MMS/WhatsApp using server- or app-configured credentials (or runtime keys). Can incur cost and deliver messages externally. |
| ai | Allows the app to use generative AI via require('ai') (chat, streaming, multimodal, document parsing, content moderation). Providers include mock and gemini (xai planned). |
High. The app can send prompts, files, and images to external AI providers (unless using mock), with token/cost and data-egress implications. |
| websockets | Allows the app to accept WebSocket connections (app.json → websockets) and use require('websockets') for rooms/broadcast. Multi-node room delivery needs operator websockets.fanout.driver: "redis". |
High. Long-lived connections share the master event loop; apps can push to all of their connected clients. Grant only when needed. |
| queue | Allows the app to enqueue background jobs via require('queue') and execute handlers under box/jobs/. |
High. Deferred privileged work (email, AI, heavy processing) with retries; with Redis, work can run on any node. Operators manage live jobs + DLQ in Glade. |
| scheduler | Allows the app to register CRON jobs declared in app.json → schedules, and to require('scheduler').rebind(names?) to refresh this app’s jobs from disk without platform.reloadApp. Jobs only fire when scheduler.enabled: true. |
High. The app can wake itself on a timer and rebind its own schedules. Sandbox API is rebind only (not cross-app unregister). |
| httpclient | Permits the app to make outbound HTTP/HTTPS requests via require('httpclient') (get / post / put / patch / delete). Also required for scheduler URL targets. Subject to server egress policy (default blocks private/loopback/metadata SSRF targets). |
High. The app can call allowed network destinations; without egress policy this would include internal hosts. |
| fs | Grants sandboxed read/write, directory, listing (readdir / listFiles / listDirs / walk), and stat access within the app's own directories (box and web). |
Medium. Access is jailed to the app's own directory, preventing access to other apps or system files. |
| module_override | Allows $g.overrideModule(specifier, boxRelativePath) so that, for the rest of the request, matching require(specifier) loads an app box script instead. Specifiers: protected bare names (fs, …), other bare names (crypto, url, …), relative (./x) or box-root paths. Only this permission is required to install/apply overrides. See Module overrides below. |
High. Changes what require() means for that request. Restricted/forbidden names cannot be overridden. Wrappers still run under normal gbox jailing. Grant only to trusted apps. |
| Allows the app to generate and manipulate PDF documents. | Medium. Potential CPU intensive operation that might slow down server performance. | |
| zip | Allows the app to create and extract ZIP archives. | Medium. Access is jailed to the app's own directory, preventing access to other apps or system files. |
| image | Allows the app to manipulate image files. | Medium. Potential CPU intensive operation that might slow down server performance. |
module_override)Purpose: Let a trusted app install request-scoped redirects of require specifiers to another script inside the same app box. Scripts keep normal require(...) call sites; middleware (or any gingee handler) decides the binding.
| Specifier kind | Example | Notes |
|---|---|---|
| Protected bare name | fs, db, httpclient |
module_override alone is enough to redirect; the real platform module is only loaded if something (usually the wrapper) requires it under normal rules (then that permission is needed). |
| Other bare name | crypto, uuid, url |
Same — redirect with module_override only. |
| Relative path | ./helper, ../shared/x |
Matched after resolving against the calling script, then as a box-relative key. Prefer map keys like sandboxed/helper (box-relative, no leading ./). |
| Box-root path | lib/util (no ./) |
Matched as path under box/. |
Never overridable: restricted modules (platform, gingee, …), engine/*, and forbidden host builtins (child_process, node:fs, …). (scheduler is protected — apps with the permission may require('scheduler') for rebind only.)
// Replacement script is always box-relative (app-defined layout)
$g.overrideModule("fs", "library/fswrapper.js");
// Relative require from a script under sandboxed/ can match box-relative keys:
$g.overrideModule("sandboxed/helper", "library/helper_wrap.js");
$g.boxRelativeScript — main request script under box/ (e.g. sandboxed/run.js).module_override, $g.overrideModule throws; gbox ignores the map.module_override (only permission needed to install/apply a redirect).applyModuleOverrides: false for that script and its nested requires: normal gbox jailing (permissions, path jail, restricted/forbidden), but the override map is not re-applied (so a wrapper can require('fs') and get the real platform module).default_include middleware
→ if $g.boxRelativeScript is under sandboxed/
→ $g.overrideModule('fs', 'my/wrapper.js')
main script
→ require('fs') → app wrapper (box only)
wrapper
→ require('fs') → modules/fs.js (needs fs grant; overrides off)
→ extra app policy
Sample: web/appsandboxtest/ — full matrix (protected bare fs, other bare crypto, relative ./helper, box-root shared/bare_util, project local_modules / sandbox_kit direct + override, deny restricted/forbidden). Needs fs + module_override (fs for the real module inside the fs wrapper). Host must set box.local_modules: ["./local_modules"].
Instance-cache fixture (no special grants): web/perftest/ — exercises cache.server sandboxed module reuse vs no_cache_regex (test/e2e/perftest.e2e.test.js). Host must set box.local_modules: ["./local_modules"].
require uses regular jailing.httpclient, email, …) on intercepted calls.module_override.