https://github.com/gina-io/gina
Gina I/O - NodeJs MVC framework
https://github.com/gina-io/gina
couchbase event-driven framework http2 javascript mongodb mvc nodejs
Last synced: about 1 month ago
JSON representation
Gina I/O - NodeJs MVC framework
- Host: GitHub
- URL: https://github.com/gina-io/gina
- Owner: gina-io
- License: mit
- Created: 2013-06-28T14:17:24.000Z (almost 13 years ago)
- Default Branch: master
- Last Pushed: 2026-04-22T00:26:27.000Z (2 months ago)
- Last Synced: 2026-04-22T02:24:12.398Z (2 months ago)
- Topics: couchbase, event-driven, framework, http2, javascript, mongodb, mvc, nodejs
- Language: JavaScript
- Homepage:
- Size: 147 MB
- Stars: 10
- Watchers: 5
- Forks: 2
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Governance: GOVERNANCE.md
- Roadmap: ROADMAP.md
- Authors: AUTHORS
Awesome Lists containing this project
README
# Gina
[](https://badge.fury.io/js/gina) [](https://www.npmjs.com/package/gina) [](https://github.com/gina-io/gina/stargazers) [](https://badge.fury.io/gh/gina-io%2Fgina) [](https://opensource.org/licenses/MIT) [](https://nodejs.org) [](https://github.com/gina-io/gina/actions/workflows/test.yml) [](https://socket.dev/npm/package/gina)
> **Documentation:** [gina.io/docs](https://gina.io/docs/) · **Issues:** [GitHub](https://github.com/gina-io/gina/issues) · **Changelog:** [CHANGELOG.md](./CHANGELOG.md)
Node.js MVC framework with built-in HTTP/2, multi-bundle architecture, and scope-based data isolation — no Express dependency.
- **HTTP/2 first.** Built-in `isaac` server with TLS, h2c, ALPN, HTTP/1.1 fallback, and full CVE hardening (Rapid Reset, CONTINUATION flood, RST flood, HPACK bomb) — all on by default.
- **Multi-bundle.** One project hosts multiple independent bundles (API, web, admin, …). Each bundle has its own routing, controllers, models, and config. Share code via the project layer.
- **Scope isolation.** Run `local`, `beta`, and `production` from the same codebase. Scopes propagate through routing, config interpolation, and data (every DB record is stamped with `_scope`).
## Features
| Feature | Detail |
| --- | --- |
| HTTP/2 server | Built-in `isaac` engine — TLS, h2c, ALPN, HTTP/1.1 fallback, 103 Early Hints, CVE-hardened |
| Multi-bundle | One project, N independent bundles with shared config and project layer |
| Scope isolation | `local` / `beta` / `production` — per-request and per-record |
| MVC routing | `routing.json` — declare routes in config, not code; O(m) radix trie lookup |
| Async/await | Controller actions can be `async`; rejections routed to `throwError` automatically |
| ORM / entities | EventEmitter-based entity system; SQL files auto-wired to entity methods |
| Connectors | Couchbase, MongoDB, ScyllaDB / Cassandra, MySQL, PostgreSQL, Redis, SQLite, AI (LLM) — loaded from project `node_modules` |
| AI connector | Any LLM provider via named protocol (`anthropic://`, `openai://`, `ollama://`, …) |
| Template engine | [`@rhinostone/swig`](https://github.com/gina-io/swig) 2.4.0 — maintained fork with CVE-2023-25345 patched; streaming SSE/chunked via `renderStream()`. Nunjucks supported as opt-in via `render.engine = "nunjucks"` |
| Internationalisation | Per-bundle JSON catalogs, `t()` helper, swig + nunjucks `t` filter, CLDR plurals, ICU MessageFormat opt-in via `t.icu()` |
| Observability | Built-in `/_gina/metrics` Prometheus endpoint (opt-in, IP-allowlisted) — Node.js process metrics + HTTP counter / duration histogram with cardinality-safe route labels |
| Hot reload | WatcherService evicts `require.cache` only on file change — zero per-request overhead in dev |
| K8s ready | `gina-container`, `gina-init`, SIGTERM drain, JSON stdout logging |
| Dependency injection | Mockable connectors and config for unit testing |
## Quick start
```bash
npm install -g gina@latest --prefix=~/.npm-global
gina project:add @myproject --path=$(pwd)/myproject
gina bundle:add api @myproject
gina bundle:start api @myproject
open https://localhost:3100
```
## What's in 0.3.14
- **Server-side stack-frame leak prevention on both error wires** — `Controller::throwError` now gates the `stack` field in JSON error responses and the `
` block in the fallback HTML error page on local scope. Beta, testing, production, and any unset scope strip — preventing file paths, library versions, and internal stack frames from reaching API clients and page viewers. Local scope (`NODE_SCOPE_IS_LOCAL=true`) preserves the stack on both wires so the dev toolbar's `data-xhr` panel and the fallback HTML page keep showing the trace inline. Fail-closed shape (gate is `!_isLocalScope`, not `_isProdScope`) so a missing scope var on a fresh deployment cannot reintroduce the leak. Custom error templates dispatched via `renderCustomError` stay consumer-owned — what a consumer view renders from `req.params.errorObject` is unchanged. Consumer-side leak where `err.stack` is passed as the `msg` argument and surfaces in the `error` STRING is NOT covered by the framework gate — those callsites need per-callsite sanitization (`new Error('...').message` instead of `.stack`).
- **Admin-grade `/_gina/*` endpoints IP-allowlisted** (#S7) — `/_gina/info` (process memory / uptime / version / HTTP/2 session counters) and `/_gina/cache/stats` (full cache contents) now require an IP allowlist configured via a new `admin.allowFrom` block in `app.json`. Defaults to loopback only (`["127.0.0.1", "::1"]`); empty array `[]` is explicit deny-everyone. Client IP is read from `req.socket.remoteAddress` only — `X-Forwarded-For` is NOT trusted (reverse proxies could spoof it). `::ffff:IPv4` is normalised so listing `127.0.0.1` matches both forms. `/_gina/health/check` is intentionally unrestricted (k8s liveness probes need it open). Mirrors the `/_gina/metrics` (#OBS1) gate convention but on a separate axis — bundles that disable metrics still get the admin lockdown. **Action required:** bundles that scrape `/_gina/info` or `/_gina/cache/stats` from a non-loopback host (internal monitor, sister K8s pod, etc.) must add the source IP to `app.json admin.allowFrom`. Localhost-only access (typical dev workflow) needs no change.
- **`gina project:rm --force` tolerates partial-breakage states** (#B16) — the whole point of `--force` on a rm command is to honour broken state (manually-deleted directory, failed scaffold mid-disk-full, stuck `projects.json` row with no on-disk artefacts). Pre-fix the framework errored on missing `manifest.json` and missing project directory even with `--force` set. Both now warn and fall back to registry-only removal (`~/.gina/projects.json` + state-store row). Without `--force` both still error so typos and wrong-machine invocations surface immediately. The registry-missing check is unchanged — `--force` cannot help when there is no record to remove.
- **`bundle:list` argv parsing cleanup** (#B15) — two pre-existing surface bugs. Bare `gina bundle:list` no longer emits a spurious red `[error][gina] [ null ] is not a valid project name` stderr line before falling through to the all-projects view. `gina bundle:list --all --format=json` now correctly prints JSON instead of text — the previous in-loop short-circuit dispatched to `listAll()` before the `--format=json` token at a later argv position could set the format. No functional change for valid argv shapes.
- **FormValidator HTMLFormElement guards** — `FormValidator::validateFormById` and `FormValidator::getFormById` now fail loud with an actionable error when the resolved id is not an `HTMLFormElement` (for example when a sibling `` or `
` shares the id with a later-loaded `` from a popin or AJAX fragment), instead of crashing later inside `bindForm` with a cryptic `TypeError` on `.elements.length`. The error names the offending tag and suggests renaming so the id collision is visible from the console message.
- **`@rhinostone/swig` floor bumped to `^2.4.0`** — `2.4.0` adds ternary (`a ? b : c`) and Elvis (`a ?: b`) operator support in template expressions, plus two CLI/build fixes (`mocha` invocation in `make coverage`; `EEXIST` guard in `swig compile -o`). No template-engine API change at the surface gina calls. `swigResolver DEFAULT_MIN` stays at `2.0.0`.
- **Internal housekeeping** — `core/router.js` hot-reload no longer poisons the `require.cache` slot for `controller/index.js` (#B18, mirror of the `refreshCore()` fix shipped in `add6655e`). `script/post_publish.js bumpVersion` now refreshes the `framework/v*/VERSION` file content alongside the `renameSync` of the gitignored sibling, closing a drift family that previously needed manual repair after each alpha bump.
## What's in 0.3.13
- **`${secret:KEY}` placeholder substitution** (#SECRETS1) — bundle JSON configs (`settings.json`, `app.json`, `connectors.json`, `mcp.json`, …) can embed `${secret:KEY}` placeholders that the framework resolves from `process.env[KEY]` at config-load time, before the merged config reaches `getConfig()` or any plugin factory. Anchored to whole-string values; fails closed on an unset/empty var. `gina.plugins.Csrf()` reads `settings.csrf.secret` through it, and `gina bundle:mcp-start` resolves `mcp.json` placeholders such as `server.authToken`. Pluggable-backend interface reserved for a future iteration; only the `process.env` backend ships now.
- **Progressive Web App scaffold** (#R6) — `gina view:add` now drops a `manifest.webmanifest` and a cache-first service-worker stub (`sw.js`) into the bundle's `public/` directory, and wires the manifest ``, a `theme-color` ``, an apple-touch-icon ``, and an inline service-worker registration into the default layout. Zero runtime dependency — static files plus layout tags.
- **HTTP/2 rapid-reset rate limiter** (#H9) — the Isaac engine now bounds new-stream creation per session in a rolling one-second window; over `maxStreamsPerSecond` (default 200) it sends a `GOAWAY` and closes the session. An application-level layer over the OS-level CVE-2023-44487 mitigation in modern Node.js, complementing `maxSessionRejectedStreams` (which counts refused, not created, streams). Configurable via `settings.json` `http2Options.maxStreamsPerSecond`; `/_gina/info` exposes a `rapidResetBlocked` counter.
- **Eval-safety campaign completed** (#M20–#M22) — the multi-release effort to remove `eval` / `new Function` call sites from the published tarball is finished. The validator plugin's `makeObjectFromArgs` was refactored to a segments-array path build; the HTML-event-callback evals and the conditional-binding fallback were dropped; and the logger's circular-require `eval(fs.readFileSync(...))` fallbacks were eliminated by fixing the `merge → helpers → logger` cycle at its source. The one remaining eval is a load-bearing public-API site (user-defined form validators), kept by design with a documented trust model and an invariant test.
- **`@rhinostone/swig` floor bumped to `^2.3.0`** — `2.3.0` drops `yargs` and `terser` from the published package's production dependencies (CLI parsing is now a built-in zero-dependency parser; `terser` moved to `devDependencies`, loaded lazily), so a library install pulls in only `@rhinostone/swig-core` — a smaller transitive tree. No template-engine API or behaviour change.
- **`requireJSON` comment / URL fix** — `requireJSON` no longer mis-parses JSON config files that combine a `//` line comment with a URL string value (`"key": "https://…"`); the comment-stripping pass is now per-line on the leftmost `//`, so comment-bearing configs with URLs load cleanly.
- **Dev-mode hot-reload fix** — `refreshCore()` was rebuilding the `lib` / `plugins` `require.cache` entries with their exports objects instead of `Module` instances, crashing the controller render delegates with `Cannot read properties of undefined` after a hot reload; it now lets `require()` rebuild a proper `Module`. Also removed two dormant internal plugin directories (`core/plugins/lib/file/`, `core/plugins/lib/intl/`) with no known consumers — a slightly smaller npm tarball, no functional change.
## What's in 0.3.12
- **Spec-correct `+` decoding in URL query strings and urlencoded bodies** — fixes two complementary parsers that didn't decode `+` to space per RFC 1866 / WHATWG URL. The Isaac engine's URL query-string parser had no `+` substitution at all in either of its two branches (multi-value `&` loop + single-key `=` no-`&` path); the body parser's `application/x-www-form-urlencoded` content-type test was inverted, leaving `+` literal in `req.post` / `req.put` / `req.patch` values. Both now correctly produce space. Express engine was already spec-correct via `qs` defaults. Closes #B17.
- **Render-pipeline async-race safety** (#M1 family) — `render-swig.js` now captures `local.req` / `local.res` / `local.next` into function-scoped locals at the top of `render()` so post-`await` reads remain race-safe when a second `self.throwError()` fires during an in-flight `renderCustomError`. Same shape extended to `render-nunjucks.js`'s full call chain (`sendHtmlResponse`, `registerGinaFilters`, `writeCache`) and to `render-json.js`'s `writeCache` helper. Separate dev-mode layout cache ENOENT fix: the per-template layout cache write now uses an atomic temp+rename pattern so concurrent renders for the same `{% extends %}` URL no longer collide on the priming-block delete-then-rewrite. Production was unaffected (cached mode skips the path entirely). CVE-2023-25345 path-traversal boundary check preserved verbatim.
- **FormValidator form-reassociated radios — serialize-time scoping** — third sister fix for HTML5 form-reassociated radios sharing a name across sibling forms. The `isRequired` validator's radio-group case walked `document.getElementsByName($el.name)` without filtering by form-owner; submit-time serialization could leak a sibling form's `.checked` radio into the form-under-submission's payload. Now scopes the walk to the validator-bound radio's form-owner, mirroring the equivalent filter applied in 0.3.10's `updateRadio` (`80dd89f9`) and `bindForm` `defaultChecked` cache (`6e544411`). No-op for the normal single-form-owner shape.
- **`@rhinostone/swig` floor bumped to `^2.2.0`** — version-currency drift fix. The 2.1.0 release introduced a multi-flavor architecture (shared `@rhinostone/swig-core` plus per-flavor frontends including `@rhinostone/swig-twig` for Twig syntax); the native `@rhinostone/swig` package remains drop-in compatible with the API surface gina depends on. `swigResolver DEFAULT_MIN` stays at `2.0.0` — the framework does not depend on any new 2.1.0 / 2.2.0-only API.
## What's in 0.3.11
- **Internationalisation** (#I18N1 + #I18N2) — per-bundle JSON catalogs at `bundle/locales/.json`, `t(key, params, culture)` global helper auto-bound on controllers (`self.t()`) and as a swig + nunjucks `t` template filter, CLDR plurals via Node's built-in `Intl.PluralRules`, per-request locale negotiation (URL prefix > cookie > `Accept-Language` > settings default). New `gina i18n:scan / add / export / import` CLI for translator round-trip (PO / CSV / JSON). Optional ICU MessageFormat opt-in via `t.icu()` for gender / select / nested combinators powered by `intl-messageformat`. The legacy `__()` placeholder is rewired as a one-arg alias of `t()` — existing callers keep working.
- **Prometheus metrics endpoint** (#OBS1) — built-in `/_gina/metrics` exposing the standard Prometheus exposition format. Default metrics cover Node.js process state (heap, GC, event loop lag), HTTP request counter and duration histogram with cardinality-safe route labels (`req.routing.rule` with `__not_found__` / `__method_not_allowed__` / `__error__` / `__no_route__` fallbacks). IP-allowlist gated (loopback only by default; does NOT trust `X-Forwarded-For`). Opt-in via `app.json metrics.enabled`; install `prom-client` as a peer dependency.
- **ScyllaDB / Cassandra ORM connector** (#CN5) — entity classes with CQL prepared statements at `models//cql//*.sql`, `@param` type coercion, lightweight-transaction (`IF NOT EXISTS`, `IF version = ?`) `[applied]` boolean handling, and a session store using CQL `USING TTL` for server-side reaping. Wraps the official `cassandra-driver` (Apache Software Foundation; Node.js has no first-party shard-aware driver, so token-aware routing is used). Requires Node 20+.
- **MongoDB ORM connector** (#CN6) — entity classes with JSON pipeline files at `models//pipelines//*.json`, JSDoc-style `@param`/`@return` headers, BSON-type casting (`objectid`, `int`, `long`, `double`, `boolean`, `date`, `timestamp`), `{$arg: N}` and `{$oid: ""}` placeholder shapes, eleven supported operations (`findOne` / `find` / `aggregate` / `countDocuments` / 7 writes), and a session store using a TTL index auto-created on first `set()` with `expiresAt > now` filtering on reads to cover MongoDB's 60-second TTL-monitor lag. Wraps the official `mongodb` driver.
- **`@rhinostone/swig` major bump 1.6.0 → 2.0.1** — upstream stable cycle. The framework's Phase 7 build switched from Closure-compiling `bin/swig.js` to copying the upstream esbuild bundle directly, so `swig-core`'s lazy `require()` works in the browser bundle. Resolver `DEFAULT_MIN` floor lifted to `2.0.0` — projects pinning `swig.useProject: true` need a `^2.0.0` install in their own `node_modules` to satisfy the resolver. Server-side API unchanged.
- **`page.section` auto-promotion** — `route.param.section` is now auto-promoted to `page.section` in the controller setup, for templates that compose include paths from a section name (sub-section dispatch from a single `index.html` that fans out to per-section partials based on the matched route).
- **X-Forwarded-Prefix per-request isolation** — fixes a cross-request webroot prefix leak under reverse-proxy sub-path mounts where the prefix was previously stored on `process.gina.PROXY_PREFIX` (process-global, sticky-after-first-request, leaking into direct/non-proxied calls); now per-request on `request._ginaProxyPrefix`. Combined with a synchronous `window.__ginaWebroot` global on the client to fix a `getDependencies` race where `gina.config.webroot` was undefined at script-tag onload time.
- **Release pipeline hardening** — three independent fixes for the `~/.gina/main.json` `def_framework` drift family: defensive pre-publish gate (`script/check_def_framework_consistency.js`), settings.json side fix in `prepare_version.js`, main.json side fix in `post_install.js` with a strict-semver comparator that skips def_framework updates on older-version reinstalls.
## What's in 0.3.10
- **FormValidator HTML5 form-reassociation hardening** — trilogy of fixes for `` controls. `bindForm` now uses `HTMLFormControlsCollection` (`form.elements`) for owner-aware control collection and attaches per-control listeners on out-of-tree reassociated controls; `unbindForm` symmetrically drains the side-table on cleanup. `updateRadio` scopes the mutual-exclusion peer set by form-owner — same-name radios in different form-owners are no longer cross-fired into each other's loop — and reconciles the IDL `.checked` with the HTML `checked` attribute on init when they disagree. `bindForm`'s `fieldsSet[id].defaultChecked` cache reads the IDL `defaultChecked` property (which mirrors the HTML attribute regardless of the live IDL state) instead of the live `.checked`, so a `type="reset"` action correctly restores the originally-checked option. No-op for the normal single-form-owner shape — only changes behaviour in the form-reassociation layouts that were broken.
- **`X-Forwarded-Prefix` reverse-proxy support** — when a reverse proxy (nginx, Traefik) mounts the bundle on a sub-path and forwards `proxy_set_header X-Forwarded-Prefix /sub;`, the framework composes a public webroot (proxy prefix + bundle internal `server.webroot`) and templates it into `gina.config.webroot`. Client-side URL construction (`/_gina/assets/routing.json` fetch, `gina.min.css` link injection, etc.) now targets the correct upstream through the proxy instead of root-relative URLs that route to whichever bundle answered `/`. Header value is normalised (leading slash, trailing slashes stripped, empty / `"/"` dropped); back-compat preserved when the header is absent.
- See 0.3.9 for the consumer-feedback 11-patch batch (per-request middleware dispatch isolation · Couchbase 4.x JsonTranscoder · `length` filter null safety · `process.env` mirroring · 6 nunjucks render-pipeline patches), and 0.3.8 for the install-script regression hotfix.
See the full [Changelog](./CHANGELOG.md) and [Roadmap](./ROADMAP.md).
## Documentation
Full installation guide, tutorials, configuration reference, and API docs at **[gina.io/docs](https://gina.io/docs/)**.
- [Getting started](https://gina.io/docs/getting-started/)
- [Guides](https://gina.io/docs/guides/)
- [CLI reference](https://gina.io/docs/cli/)
- [Configuration reference](https://gina.io/docs/reference/)
- [Security & CVE compliance](https://gina.io/docs/security)
## Ecosystem
| Package | Description |
| --- | --- |
| [@rhinostone/swig](https://github.com/gina-io/swig) | Maintained fork of the Swig template engine (upstream abandoned since 2015). CVE-2023-25345 patched. |
| [gina-starter](https://github.com/gina-io/gina-starter) | Minimal starter project — one bundle, one route, Docker Compose included |
## Governance
Gina is co-authored by **Martin Luther** ([Rhinostone](https://rhinostone.com)) and **Fabrice DELANEAU** ([fdelaneau.com](https://fdelaneau.com)). Final decisions on direction, API design, and releases rest with Martin Luther. Community contributions and RFCs are welcome and taken seriously. See [GOVERNANCE.md](./GOVERNANCE.md) for details.
## License (MIT)
Copyright © 2009-2026 [Rhinostone](http://www.rhinostone.com/)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.