Launching soon. Glion is in private preview; early access is by request.

Docs

Connecting Glion to your infrastructure

Why connecting is the hard part, how Glion knows which page mints each token, the methods ranked, and what happens to a credential.

From the product's docs/CONNECTING.md, last changed there on , exported at commit 27c7e104662b.

This is the document to read if you are adding support for a new system, or if you want to know why connecting feels the way it does.

The four questions

Before an agent can do anything useful on infrastructure it did not build, four questions have to be answered. They are separate problems and conflating them is why this is usually harder than it should be.

question who answers it status
1 What is out there? server/src/discovery/ done
2 May I touch it, and how do I get permission? server/src/onboarding/ this document
3 How do I call it, safely and cheaply? server/src/connectors/, server/src/mcp/ native shipped, MCP shipped
4 What does the model need to know? server/technologies/<signature>/SKILL.md 27 scaffolds

Question 2 is the one that decides whether the product works. A scan of one real network found twenty-six services in under a minute. Connecting to them means twenty-six admin interfaces, each minting a differently-named credential in a different place — “API token”, “API key”, “access key”, “service account”. Nobody works through that list from memory.

MCP does not help here. An MCP server assumes you already hold the credential and have already configured it. That is the entire problem handed back to you.

What Glion does about it

Glion knows what a service is and where it lives, so it also knows which page mints its token. That turns “go and find your API settings” into a link into the operator’s own instance and a single field to paste into.

The knowledge lives in server/src/onboarding/recipes.ts, one recipe per discovery signature:

{
  signature: 'coolify',
  service: 'Coolify',
  options: [
    {
      method: 'native', driverKind: 'coolify', recommended: true,
      label: 'Coolify API',
      why: 'Glion speaks it directly. Nothing to install…',
      credential: {
        kind: 'bearer',
        label: 'An API token',
        where: 'Keys & Tokens → API tokens → Create New Token',
        path: '/security/api-tokens',
        scope: 'read-only is enough unless you want Glion to deploy or restart things',
        looksLike: '1|abcdef… (a number, a pipe, then a long string)',
        docs: 'https://coolify.io/docs/api-reference/authorization',
      },
    },
    SSH_OPTION,
  ],
}

path is joined to the address discovery found, so the console renders a real link. where has to stand on its own without it, because not every service has a stable settings URL.

Three rules, and why

Never ask for a login password. Every recipe asks for a scoped, revocable token. A password is the operator’s identity; a token is a grant they can withdraw without changing anything else. This is enforced by a test, and it is not theoretical — a root password was once pasted into a session on this instance, which put it in the trace and sent it to a model vendor.

Two services genuinely have no API-credential concept (Nginx Proxy Manager is one). Their recipes say so explicitly and tell the operator to create a second, limited user rather than hand over their own login.

Say the least privilege that works. scope is not a disclaimer, it is an instruction: “Give it PVEAuditor on / — that is read-everything, change-nothing.” A sentence like that changes what people click.

Never promise a driver that does not exist. withDriverTruth() reconciles every recipe against the drivers actually compiled into the build, in both directions: a recipe cannot offer a native option that would fail, and a recipe cannot go on saying “driver planned” after one ships. The code is the authority; the prose is not.

Where Glion ships nothing, the recipe says so and offers the honest alternative. An explicit “not yet” costs less trust than a button that half-works.

Some things are deliberately not offered

Two recipes exist purely to refuse:

  • Apache Guacamole stores working logins for every machine it can reach, and an administrator can read those passwords in clear text. The inventory of what it reaches is useful; the credentials behind it are not something an agent should hold.
  • Docker Engine API on 2375 has no authentication at all. If a scan found it open, that is a finding to act on, not a connector to configure.

Both are surfaced with their reasoning, so they read as decisions rather than missing features.

Choosing a connection method

Ranked, best first. The console shows all of them with their trade-offs and lets the operator choose.

1. Native driver — the default

Glion speaks the service’s own REST API, in-process. See server/src/connectors/native/.

  • Nothing to install. No sidecar, no daemon, no second thing to keep running and updated.
  • Classifications are declared, not guessed. The author of the call and the author of the read/write/destructive label are the same person, in the same diff. classification_source is declared, which outranks annotation and heuristic; only an operator’s own label wins over it. The approval gate keys on a fact rather than an inference over a tool’s name.
  • Responses are projected. GET /api/v1/applications on Coolify returns about ninety fields, two of which are http_basic_auth_username and http_basic_auth_password. A driver names the fields it exposes — an allowlist, so a secret a future release adds is dropped by default rather than exposed by default.
  • Small surface. 13 curated tools at ~72 tokens each, against ~250 tokens per tool for a typical MCP server publishing everything it can do.

1b. Capability manifest — a native driver as data

For a service with an HTTP API and no driver in Glion. A manifest says what a compiled driver says — tools, labels, how each call is made, which fields each answer may pass — as JSON, and the host runs it: no host field, an allowlist at every level, no expression language. Import the service’s OpenAPI document on the Manifests screen, or paste a manifest; it is a draft that runs nothing until an admin signs it. A connector on a signed manifest is a native connector in every way that matters: its tools are granted, gated and approved like any other, and its probes notice when the service changes shape under it.

What it cannot do is compute: an “overview” that calls three endpoints and summarises them is a compiled driver.

2. MCP — the escape hatch

For anything Glion ships no driver for and where a server already exists. Grafana is the good case: Grafana Labs maintain mcp-grafana with a --disable-write flag enforced server-side.

The costs are real. It is one more container to deploy and update. Its descriptions and schemas are third-party text spliced into a system prompt, so normalize() treats them as untrusted and classify() has to infer safety from a name. And it cannot redact a response.

3. SSH — the universal fallback

Reaches anything, including services with no API at all. It is never recommended, because a shell has exactly one operation — “run this” — so the policy engine cannot tell df -h from rm -rf. Every call is judged by a human or not judged at all.

Adding a new service

  1. A discovery signature in server/src/discovery/signatures.ts. It must have a confirm() that sees the service name itself — a port number is never an answer.
  2. A skill scaffold in server/technologies/<signature>/SKILL.md, its header’s signature the directory’s name. Tests require a ## Read-only orientation section, a destructive section, and a ## Verifying section naming where that stack keeps its own machine-readable schema.
  3. A connection recipe in server/src/onboarding/recipes.ts.
  4. Optionally a driver. Prefer a manifest in server/technologies/<signature>/manifest.json: it is loaded at boot, linted by manifest-lint.test.ts, and needs no code. Write a compiled driver in server/src/connectors/native/ only when a manifest’s closed forms cannot say what the tool does — implement NativeDriver, add it to DRIVERS, and write the projection allowlist first.

Steps 1–3 are enforced against each other by tests: every signature must have a scaffold, no scaffold may point at a signature that does not exist, and no recipe may promise a driver that is not compiled in.

Write the driver against a running instance, not from memory. Every skill scaffold tells agents to do this and the same applies to us. It is how the Coolify driver learned that restart, start and stop are POST: Coolify answers a GET with 405 and the message “This endpoint has changed to a POST request.”

What happens to the credential

  1. Posted to /api/onboarding/connect over the console’s authenticated session.
  2. Encrypted with encrypt() before it touches a row; only credential_hint (a few masked characters) is ever readable.
  3. Decrypted at the moment of a call, handed to the driver for that call, and never stored on the object it came from.
  4. Never logged, never returned to the console, never placed in a URL, and never included in an audit row — the audit records the service and the method only, so a credential cannot be reconstructed from the audit log.

Verification is part of the ceremony rather than a separate step. A saved credential that has never been used is a claim; one that has just returned a list of six applications is a fact. discoverConnector() installs the declared tools and calls the service, and the operator sees what came back.