Connection sources

Connections are managed like DataGrip data sources: first-class, named, testable objects the user adds and organizes — not a deployment detail.

Status
current
Phase
2

This is an engineering document. Specs are written for people building DataGripe: they record what a subsystem does and why the alternatives were rejected, and some of them describe things that are not built yet. If you are using DataGripe, the documentation is the place to be — it is maintained for you and this is not.

Supersedes: nothing (extends docs/initial_idea.md §6, §8)

Goal

Connections are managed like DataGrip data sources: first-class, named, testable objects the user adds and organizes — not a deployment detail. In addition to user-created connections stored in the application database, operators can predefine connections via configuration (environment variables or a config file) so a deployment can ship with ready-made data sources.

Non-goals

  • Editing predefined connections in the UI (they are read-only in the UI; change the config and restart/reload).
  • Per-user overrides of predefined connections (v1: predefined connections are visible to every member of the workspaces listed in their definition).
  • Hot reload of the config file (restart required; hot reload is an open question below).

Definitions

Term Meaning
Managed connection Created in the UI, stored in connections + connection_secrets
Predefined connection Declared in server configuration; materialized read-only
Git datasource Read from a repository's .datagripe/ (docs/spec/git-datasources.md)

Both kinds implement the same ConnectionMetadata contract (packages/contracts/src/connections.ts) so the explorer, editor, and execution paths never branch on origin.

Design

Configuration format

Predefined connections come from one JSON file, path given by CONNECTIONS_FILE (default: connections.json at the repo root, ignored by git). Environment variables may supply secrets by reference so the file itself stays commit-safe:

{
  "connections": [
    {
      "id": "local-dev",
      "name": "Local Dev Postgres",
      "adapter": "postgres",
      "host": "localhost",
      "port": 5432,
      "database": "datagripe",
      "username": "datagripe",
      "passwordEnv": "DEV_PG_PASSWORD",
      "tlsMode": "disable",
      "params": { "search_path": "sales,public" },
      "readOnly": true,
      "workspaces": ["*"]
    }
  ]
}
  • id is stable, kebab-case, unique within the file.
  • Secrets are declared as passwordEnv: "VAR_NAME" (indirection into the process environment). Inline password is accepted but documented as development-only.
  • params are PostgreSQL runtime parameters sent when the connection opens (packages/contracts/src/connectionParams.ts). An allowlist, not free-form: an unrecognised one is a FATAL at connect time rather than a warning, so a typo here would be a datasource that cannot connect at all. Optional, and never a secret.
  • workspaces lists workspace names or "*" for all workspaces.
  • The file is validated at startup with a Zod schema in packages/contracts (predefinedConnectionsFileSchema); invalid files fail boot with per-entry error messages.

Server behavior

  1. At boot, apps/server/src/config.ts loads and validates the file.
  2. Predefined connections are exposed through the same workspace.open payload and connection.test action as managed ones. The response marks them source: "predefined" (managed connections report source: "managed"); ConnectionMetadata gains this field.
  3. Secrets for predefined connections never touch the application database: the server resolves passwordEnv into memory at boot, decrypt-where-used semantics do not apply, and the value is held only in the connection manager.
  4. connection.create/update/delete targeting a predefined id returns FORBIDDEN with code CONNECTION_READ_ONLY.
  5. Authorization: a predefined connection is usable only in the workspaces its definition names.

Schema impact

connections rows remain managed-only. Predefined connections are not inserted into connections; query_executions.connection_id gains a companion connection_ref text (e.g. predefined:local-dev) so history and audit survive without a foreign key. The documents.default_connection_id column cannot reference predefined ids; documents opened against a predefined connection store that preference client-side (IndexedDB) until a nullable default_connection_ref column is added in a later migration — tracked as an open question.

ConnectionMetadata.id is a plain string (UUID for managed, kebab-case slug for predefined) with a source: "managed" | "predefined" field, so clients never branch on id shape.

Conflict rules

  • A predefined id colliding with another predefined id fails boot.
  • Predefined and managed connections live in disjoint id namespaces (predefined ids are slugs, managed ids are UUIDs), so no runtime collision is possible.

Client behavior

  • The explorer renders one list, with a subtle badge on predefined entries.
  • The connection dialog's save button is disabled for predefined entries; the form renders read-only with a "Defined by server configuration" hint.
  • Test-connection works identically for both kinds.

Open questions

  • Hot reload of CONNECTIONS_FILE (SIGHUP or file watch) vs restart-only.
  • Whether documents should be able to persist default_connection_ref server-side (schema change) instead of client-side.
  • Per-workspace visibility scoping UX once real auth exists (workspaces currently matches by name; switch to ids when seeded workspaces exist).

This page as Markdown, for an agent or a diff.