Skip to content

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.