Gingee Permissions Guide

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.

The Philosophy: Secure by Default (Whitelist 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.

For App Developers: Declaring Permissions (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.

The gingee-cli will read this file directly from your .gin package during installation to prompt the administrator for consent.

Structure of pmft.json

The file contains a single permissions object with two keys: mandatory and optional.

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.

For Administrators: Managing Permissions

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.

The Central Permissions File (settings/permissions.json)

This file is the single source of truth for all application grants on your Gingee server.

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.

Managing Permissions in Glade

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.

Master Permission List

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.
email 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.
pdf 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 overrides (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.

What can be overridden

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.)

API

// 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");

Rules (platform)

  1. App must be granted module_override (only permission needed to install/apply a redirect).
  2. Override target path must resolve inside the app box.
  3. Loading the target uses 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).
  4. No app folder names are special-cased by the engine.

Typical pattern

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"].

Security notes for operators