Integrations and plugins¶
Responsibility¶
Integrations connect user accounts and external systems. Plugins extend Gnosi with declarative contributions and bounded executable behavior. MCP servers contribute agent tools through a separate protocol boundary.
The integrations HTTP boundary is strictly typed without changing its public payloads. Mail and DAV connection tests validate required string credentials before opening sockets. DAV URLs may target private self-hosted networks such as Nextcloud, while loopback, link-local, multicast, reserved, and unspecified addresses remain blocked.
Account provider choices¶
Settings recognizes the exact known email domains for Google, Microsoft, iCloud, Yahoo and AOL as soon as a complete address is entered, ignoring outer whitespace and domain case. Known domains show only their provider's Continue action and do not ask whether the account is Google. Unknown domains retain explicit provider selection and manual configuration, including Google Workspace addresses. Detection never starts authentication or changes server settings by itself; Continue remains an explicit action. Each provider icon and label is centered together as one inline group, including wrapped translations.
Integration persistence¶
The integration manager stores non-secret account configuration and references to secrets under local data. Each machine reconnects accounts independently. Settings APIs list masked connection state, validate configuration, test connectivity, choose defaults, and disconnect providers without exposing raw tokens.
Google and Microsoft OAuth callbacks create or update provider records. IMAP, SMTP, CalDAV, Drupal, Notion, and similar adapters normalize their own settings into the common integration registry where possible.
Google OAuth keeps pending PKCE verifiers in a state map with a ten-minute
lifetime. Callback state is consumed once, before token exchange or cancellation;
missing, expired and replayed state is rejected. Only
GET /api/auth/google/callback bypasses the app session requirement, because the
system browser does not share the desktop cookie. PKCE and pending-state
validation remain mandatory; login, status and health remain protected.
The desktop intercepts the trusted Google login navigation without replacing
Settings. Electron resolves the authenticated local redirect through
ClientRequest, validates the Google authorization destination, then opens the
system browser. The email already entered in Settings is forwarded as
login_hint; optional desktop=true and ui_locales select a localized result
page with no-store and no-referrer. Web clients retain their frontend
redirect. Returning to Settings refreshes accounts without clearing draft fields.
Configuration and account payloads stay typed at the adapter boundary; status
and health responses remain Pydantic-validated, with typing exceptions confined
to the untyped Google SDK calls.
The Google People adapter narrows discovery responses to Gnosi contact records, refreshes and persists access tokens through the integration manager, preserves ETag-aware updates, and normalizes primary names, addresses, organizations, photos and provider timestamps. Untyped SDK objects remain confined to this adapter and do not cross its typed service functions.
Microsoft OAuth applies the same bounded-state rule: generated authorization states expire after ten minutes and are consumed before token exchange. Token and Graph profile JSON are narrowed inside the route adapter, while its status mapping is Pydantic-validated and redirect handlers are explicitly typed. This blocks stale configuration before network calls and persists the historical mail-account shape without changing redirects or OpenAPI.
Hosted Notion MCP uses OAuth 2.1 dynamic client registration and PKCE. Its typed boundary validates discovery and registration objects, requires a returned client id, preserves the initiating frontend origin, and stores access, refresh, client and pending-state values only through IntegrationManager's secret-aware operations. Disconnect clears all three Notion OAuth records.
Backend ownership and compatibility¶
The configuration domain owns the 23 built-in and third-party plugin HTTP
operations. backend/domains/configuration/api/plugins.py translates HTTP
requests, plugin_lifecycle.py owns dependency-aware activation and runtime
transitions, plugin_models.py owns the Pydantic contracts, and
plugin_state.py is the single owner of the per-process locks and normalized
per-Vault state store.
The typed backend/domains/plugins/ package owns manifest validation,
contained installation paths, ZIP staging and rollback, deterministic export,
grant normalization, and the newline-JSON Node sandbox. Historical
backend/services/plugin_system.py and plugin_sandbox.py remain thin
facades. They own the compatibility constants, injected host-handler registry,
runner path, and late-bound seams; lifecycle and sandbox state is not duplicated
across the boundary.
The Notion integration is owned by backend/domains/notion. Its typed modules
separate REST import conversion, embedded-view recreation, exact-clone phases,
workspace discovery, route-level filesystem/registry persistence, and
read-only clone verification. backend/api/notion_routes.py remains the HTTP
translation and clone-progress state boundary. The three historical
backend/services/notion_{importer,clone,view_recreator}.py paths are explicit
compatibility facades; imports, globals and late-bound monkeypatch seams remain
available while the canonical implementation lives in the domain package.
Notion import preferences require the configured LOCAL_DATA root, while clone
and verification dependencies consume the typed optional active-Vault accessor
directly rather than recasting its result at each route boundary.
Notion route order, methods, paths, payload schemas, descriptions and the
deterministic OpenAPI document remain byte-stable.
backend/api/vault_routes.py remains a temporary composition facade for
legacy imports. It injects path, persistence, runtime, model-selection, and
mutation-lock collaborators and re-exports the historical models and handlers.
The load, save, lifecycle, summary-model, and mutation-lock seams remain
dynamically replaceable for plugins and tests. Some extracted page modules still
import that facade dynamically, and the page and registry boundaries retain
typing escapes. Removing these legacy dependencies remains unfinished; a passing
strict type-check does not prove complete typed separation. Route order, paths,
methods, status codes, payload schemas, operation identifiers, and the generated
OpenAPI contract remain frozen during this structural migration.
The plugin domain and dispatcher share a typed two-argument host-handler contract: bounded arguments plus the calling plugin id. The historical sandbox facade keeps its introspectable one-argument public annotation for compatibility and adapts it exactly once at the internal injection seam. Contract tests freeze that facade signature. Vault RPCs import canonical page, registry and configuration owners lazily, preserving cycle avoidance while removing dynamic compatibility-facade calls.
The built-in web clipper keeps its mapping logic pure. Destination columns are resolved by immutable id, current name or historical alias; explicit opt-outs remain distinct from automatic role detection. Only promptable stored fields are accepted, extension values are coerced by schema type, and stale or derived columns are discarded before the normal Vault write boundary.
Plugin lifecycle¶
stateDiagram-v2
[*] --> Discovered
Discovered --> Validated
Validated --> Installed
Installed --> Enabled
Enabled --> Disabled
Disabled --> Enabled
Installed --> Removed
Disabled --> Removed
Plugin packages declare identity, version, compatibility, permissions, contributions, and integrity information. Installation validates paths, manifest structure, signatures where required, and declared effects. Enabling reconciles managed settings, AI profiles, skills, or tools idempotently. Disabling suspends managed contributions while preserving user-owned overrides.
The Vault configuration composition layer now consumes the strict return types of plugin state, lifecycle and summary services directly. It retains late-bound facade seams for tests and extensions, but no longer casts already-typed state, so persistence and runtime refresh contracts have one authoritative owner. Built-in lifecycle transitions do not resolve a filesystem path. Third-party manifest validation resolves the current Vault's plugin directory lazily, only after identifying the target as external; this keeps request-scoped Vault selection and isolated lifecycle calls deterministic.
Built-in secondary capabilities use the same per-Vault lifecycle boundary. The
authoritative registry declares dependencies, routes, UI surfaces and Settings
destinations. .gnosi/plugins.json schema version 2 records explicit
enabled_builtin and enabled_third_party lists while retaining disabled for
older clients. Migration from an older or missing schema is atomic and
idempotent: resources starts enabled, other optional capabilities start disabled,
and all settings,
permissions and unknown forward-compatible records are retained.
Lifecycle changes go through the general
POST /api/vault/plugins/{id}/lifecycle contract. A change with prerequisites
or enabled dependents first returns a structured conflict; an administrator
then confirms the grouped activation or cascade. Disabled routes fail before
their feature implementation runs, and scheduled external work checks the same
registry. Core maintenance, Markdown, database calendar views, contact fields,
media attachments and drawings do not depend on these plugins.
Plugins Settings owns installation, activation, permission grants, updates and removal. Configuration for active capabilities is exposed under Connections, Knowledge or Advanced. A configure action opens that destination directly and capabilities without global configuration do not create empty pages.
Executable plugin behavior runs through a sandbox boundary with a constrained environment and timeout. Plugins do not receive the complete host environment or arbitrary secret access.
Direct networking stays disabled in both plugin runtimes. A granted network
capability exposes only the host RPC, which rejects private destinations and
bounds methods, redirects, time, and response size. UI frames keep
connect-src 'none'; the parent calls the same backend boundary after checking
the plugin's declared and granted permissions.
Third-party plugins may declare the additive ui:settings permission and call
gnosi.registerSettingsPanel(...). Active and granted panels appear in the
dynamic Extensions group, render inside the existing opaque-origin iframe
sandbox and disappear as soon as the plugin is disabled, revoked or removed.
Reading or writing the plugin's own configuration additionally requires the
existing settings permission. The host API remains at major version 2.
The UI bridge is split into a typed host, permission-gated method adapters, frame lifecycle ownership, and a standalone TypeScript sandbox runtime. The runtime is serialized only after compilation; tests also execute the minified Vite output to prevent captured host dependencies from breaking the iframe. Both sides verify the sending window, not just the message marker or opaque origin. Replies from retired frames or earlier document generations are dropped; mutations are never replayed to a replacement document.
Moving a running iframe with ordinary DOM insertion reloads its document. The Settings host uses state-preserving movement when available, or waits for the requested panel to register again before rendering. Mount cleanup belongs to one panel instance, and contribution snapshot updates do not remount an unchanged panel. Tests cover both movement paths, permission denial, stale responses, and repeated registration. Real-browser QA must additionally check open/close/reopen and plugin replacement against an isolated fixture API.
Marketplace distribution¶
The official plugin index and its detached signature are published as GitHub Release assets. Remote catalog installation requires a trusted signed index and every selected package requires both SHA-256 integrity and a trusted detached Ed25519 signature. Installed provenance records the source URL, checksum, and verified publisher. Local ZIP installation remains available for development, but starts disabled with no grants.
Catalog JSON, per-Vault state, grants and the local trust store are normalized at their dynamic boundaries before reaching typed lifecycle and signature services. Unknown forward-compatible fields remain intact; malformed key maps recover to an empty user-key collection without replacing bundled trust.
Installed plugins can be exported as deterministic ZIPs. Public submission is an administrator operation sent to an explicitly configured moderation broker; Gnosi never embeds a GitHub write token. The broker quarantines the package and publishes it only after CI and human review.
MCP boundary¶
Configured MCP servers are independent processes or remote endpoints. Startup
discovers their tool schemas and normalizes them into the agent catalog. Retry
and Retry-After handling are bounded. One failed server is recorded without
discarding tools from healthy servers.
Example and companion integrations¶
The repository includes example plugin packaging, a Drupal MCP proxy, the LibreOffice citation extension, and a Word citation helper. These are separate clients with narrow backend contracts; they do not share backend filesystem or credential access automatically.
Invariants¶
- Integration secrets live outside Git and the synchronized vault.
- Disconnecting removes or revokes the local credential reference and selected defaults consistently.
- Plugin-managed and user-managed values remain distinguishable.
- Archive extraction and plugin paths cannot escape their installation root.
- Compatibility and permission validation occurs before activation.
- Official indexes and remote packages fail closed when integrity metadata is missing.
- Direct plugin sockets and browser connections never bypass the host RPC.
- A disabled capability cannot start a new route, sync, automation or external effect.
- Disabling or migrating never deletes plugin data, settings, credentials or profiles.
- MCP tool origin and effect remain visible after catalog normalization.
Verification focus¶
Run plugin manifest, signing, sandbox, state-race, AI contribution, MCP routing, retry, and connector tests. A live integration test uses a dedicated test account and must not mutate production data unintentionally.
The optional Genograms plugin uses the built-in plugin registry and per-Vault enablement, with no external integration or AI dependency.
For native Microsoft mail registration and institutional consent, see the Microsoft desktop mail setup guide.
Vault template catalog and moderation¶
In personal mode, Settings → General → File structure → Vaults → From repository opens the verified catalog. Search, category filters and content previews help select a template; installation creates a new Vault. Publish template opens a searchable inventory of included files, exclusions and potential sensitive content. Refreshing the inventory clears the previous acknowledgement.
The catalog builder produces Research Starter Workspace 2.1.0, Study Workspace 1.0.0 and Project Workspace 1.0.0 in Catalan, English, Spanish and French. Generated packages become available only after official signing and release publication; installing the interface does not publish catalog assets.
The private moderation dashboard quarantines uploads and records final approval or rejection. Approved Vault templates expose a review receipt bound to the exact ZIP by SHA-256 and size. The release operator downloads both through the authenticated dashboard, runs python -m extensions.marketplace.reviewed_templates, then supplies --reviewed-dir to the catalog builder. Validation rejects unsafe archives, mismatched identities, credentials and duplicate template identities before loading the official signing key. Receipts are audit records, not cryptographic approval: accept them only through the trusted maintainer process. Approval does not sign or publish; plugin packages use their separate release process.
Optional module activation contract¶
Activation is per vault. Disabling removes optional UI and rejects new guarded requests; it preserves configuration and local data. Deferred work must retain its originating vault and recheck current state before an external operation. An already dispatched provider request cannot be undone: cancellation happens at the next safe boundary, not by deleting local data.
Calendar page-save callbacks now check activation before queuing and executing, and again before the Google event patch. IMAP IDLE checks current Mail state at startup, credential restart and reconnect; start/stop transitions share a lock, and runtime refresh ignores stale lifecycle responses. Pending workers receive a stop signal. An existing IDLE session may take up to its socket timeout (currently 60 seconds) to leave a blocking read; no new reconnect is allowed while disabled.
The matrix records reviewed entry guards and shared-core exceptions. It does not certify every provider implementation or every previously queued job. Shared account configuration, OAuth and explicit connection tests remain available independently of module activation; they must not silently wake optional workers. References and reading remain part of the research promise, although the resources application route currently uses the builtin switch.
| Capability | Reviewed boundary |
|---|---|
genograms |
Graph endpoint and UI; generic relations remain core. |
daily-notes |
Creation endpoint and UI; normal page creation remains core. |
tags-page |
Palette and tag views; generic tag metadata and aggregate reads remain core. |
page-comments |
Comment endpoints and UI. |
share-links |
Share creation, management and public-read checks. |
canvas-cards |
Card UI; drawings and attachments remain core. |
web-clipper |
Public clip endpoint state check. |
project-planning |
Router and UI. |
resources |
Router and UI; enabled by default, research boundary preserved. |
feeds-reader |
Router, feed/newsletter scheduler and source catalog. |
translation |
Translation endpoints and UI. |
contacts |
Router, scheduler and source catalog; generic fields remain core. |
mail |
Router, scheduler, source catalog and current-state IDLE lifecycle. |
calendar |
Router, scheduler, source catalog and deferred page sync; database views remain core. |
social-publishing |
Router, publishing scheduler and UI; ordinary media remains core. |
notion-import |
Import/OAuth routers and source catalog. |
ai-platform |
AI routers, global surfaces and scheduler; existing provider work may finish. |
llm-wiki |
AI prerequisite, lifecycle transition and maintenance scheduler. |
grounded-notebooks |
AI prerequisite, router and global surfaces. |
automations |
Scheduler routes and governed jobs; local formulas and property rules remain core. |