Skip to content

Data brokering

Concept

Every external system a Greenlight app reaches — Slack, GitHub, Salesforce, Snowflake, an internal REST API — is reached through an integration that IT registers once. The app authenticates as a workload identity; Greenlight authorizes the call against the grants for that app, supplies the right upstream credential, and writes an audit record attributed to the acting user when one is present.

The trust model fits in three checkpoints:

CheckpointGuarantee
The workloadThe app (or a developer’s local process) holds no upstream credential — only a Greenlight workload key that can’t reach any upstream directly.
The boundaryGreenlight resolves the approved credential only at request time and uses it at the proxy boundary — injecting it into an HTTP call or opening the connected-database session itself. Once saved, a secret is never displayed again; registration is write-only.
The upstreamEvery call arrives with an attributable, IT-managed identity and is authorized against an explicit grant. Greenlight attempts one terminal audit event with the outcome; an audit-store failure is surfaced rather than silently claiming success.

For a proxied integration, the app never holds the upstream credential at all — it can’t.

Apps that hold their own credentials cause four ongoing problems for IT: credentials end up in source code, credentials drift between dev and prod, audit attribution is per-credential rather than per-user, and revocation means hunting down every app that has a copy.

A broker replaces all four with one operation. Apps authenticate as themselves using a workload identity that nothing outside the cluster can mint. Greenlight holds upstream credentials in Key Vault and substitutes them at the edge of the request. Revoking an integration is a single dashboard action; every app that depends on it loses access on its next call.

Credential brokering diagram: apps never hold credentials, Greenlight brokers them. Your app — with no passwords, tokens, or API keys in code — sends a request with one Greenlight key to the Greenlight broker, which checks what IT approved, attaches the real credential, and logs every call (credentials stay vaulted and rotate instantly). The request continues with the real credential to your data sources: Snowflake, Salesforce, Slack, and on-prem databases.

For a proxied integration:

  1. The app makes an HTTP call to the proxy URL it was handed in its environment (GREENLIGHT_PROXY_URL), with the integration name in the path.
  2. Greenlight validates the workload key and looks up the caller’s granted permission for that integration. Grants are held per principal — the app for a deployed pod, or the person themselves for a personal local run — so the lookup is always “does this caller hold a grant,” never a shared pool.
  3. Greenlight supplies the right upstream credential — substituting an IT-managed service credential, minting an OAuth client-credentials token, exchanging the current user’s identity for an upstream user token, or opening a connected-database session as the approved database identity.
  4. For HTTP, Greenlight forwards the request and proxies the response back. For a connected database, it runs one bounded query and returns a JSON result grid.
  5. Greenlight writes an audit record: the app, the acting user (when present), the integration, the outcome, and safe request metadata. Connected-database audit stores a statement fingerprint, never the SQL text. If the audit store is unavailable, Greenlight logs a safe replay payload and returns an explicit platform error.

The app sees a normal HTTP response. Nothing about the credential substitution is visible from inside the app’s code, and the upstream secret never leaves Key Vault.

How an app reaches a credential: proxied vs injected

Section titled “How an app reaches a credential: proxied vs injected”

How a granted credential reaches the app is its own choice, set by IT per integration:

  • Proxied — the app holds only its Greenlight workload key and calls the proxy URL, placing that key where the upstream API or SDK would normally place its credential; Greenlight swaps in the real credential at the edge. The app never sees the upstream secret, every call is audited, and rotating the credential takes effect on the next request. This is the default for HTTP integrations, the only option when the upstream identity is the signed-in user, and how apps reach a connected database (below).
  • Injected — at deploy time Greenlight places the credential value into the app’s environment under a name IT chooses, and the app calls the upstream directly. The trade-off is deliberate: the raw credential lives in the running app, there’s no per-call audit, and rotating it takes effect on the app’s next deploy rather than its next request.

The grant model is identical either way — the grant always decides which credential the caller gets; only the delivery differs.

Grants are per principal — apps and people. An app’s access is declared in its manifest and activates at merge; a person’s access is requested imperatively (their agent calls requestCredentialAccess) and reaches the same proxy under their own identity — for local scripts and analysis with no app at all. The predictability rule holds on both sides: what an app can reach never depends on who invoked it, and a person acts only under access they explicitly requested and IT (or policy) approved.

An Azure SQL connected database is a database your organization already runs that apps use for bounded CRUD and queries without ever holding a connection string. Azure SQL is the supported connected-database target. Because it speaks TDS rather than HTTP, the proxy exposes a stateless CRUD/query API in front of it. The app sends SQL plus separately bound parameters to /query and gets one JSON result grid back:

POST $GREENLIGHT_PROXY_URL/<integration>/query
{ "sql": "SELECT id, name FROM workers WHERE hired_at > @p1", "params": ["2024-01-01"] }
→ { "columns": [...], "rows": [[...]], "row_count": 42, "truncated": false }

Greenlight connects as its own managed identity (or a SQL login or service principal you register), so there is often no secret to paste at all. A DBA grants that identity a database role, and that role is exactly what apps can do. db_datareader is the recommended starting role; writes work only with an explicitly granted write-capable role. Greenlight never tries to infer safety from the SQL text, so applications must bind untrusted values rather than interpolate them.

The gateway deliberately is not a remote SQL driver. A call returns at most one capped result grid, some native types require a bounded text cast, and truncated: true means the caller must page with SQL. Sessions are isolated and reset between requests, so there are no cross-request transactions, cursors, or streaming.

Every authorized call requires a canonical audit event — attributed to the acting user when one is present, otherwise to the app or named local developer. It stores a SHA-256 statement fingerprint and outcome metadata — never SQL text or parameter values. An audit-store failure is returned as a platform error rather than an unaudited success. Query errors identify the responsible boundary and separate “might succeed later” from “safe to replay”; see Errors & pagination.

A proxied integration also differs by whose identity the upstream sees — the organization, or the signed-in user. IT picks this when registering the integration.

Identity modelHow access worksUsed for
Service accountOne IT-managed static credential — an API key, bearer, basic auth, or a value placed in a custom header or query parameter — shared across all of the app’s traffic; the upstream sees a single service identitySlack, GitHub, Google Workspace, OpenAI, internal REST APIs
Client credentialsGreenlight mints a machine-to-machine OAuth token from an IT-registered client id and secret; the upstream sees an application identityMachine APIs that issue application tokens but not per-user ones
User-passthroughThe signed-in user’s own identity, via OAuth token exchange; the upstream issues a per-user token and enforces that user’s own permissionsSalesforce, Snowflake

A provider that supports more than one of these is registered as two integrations — for example a service-account salesforce for background syncs and a user-passthrough salesforce-user for per-user views. Each app grants whichever it needs. A full breakdown lives in Integrations.

Some data Greenlight brokers is data Greenlight itself owns. The first case is the org user directory: apps that need a people picker, an assignee dropdown, or to resolve a stored user id back to a name read the org roster through the same proxy, as a first-party integration named greenlight-directory. IT registers it from the catalog in one step — there is no credential to paste — and apps must still request and be granted access, because the roster is personal data, never ambient. The API is read-only and deliberately minimal: each user exposes an id, email, display name, and active flag, and nothing else. Every read lands in the same per-app audit trail as external-API calls, so IT can always answer “which apps can see our people?”

IT doesn’t build most integrations from scratch. The dashboard ships a catalog — a curated, logo’d list of known providers (GitHub, Slack, OpenAI, Salesforce, Snowflake, and more). Picking one prefills the display name, the base URL, the identity model, and where the credential goes on the wire, and links to the upstream’s own instructions for obtaining a key. It also seeds the integration’s first knowledge entries — a connection overview, idiomatic queries, and common errors — so an app’s agent starts with useful context instead of a blank page.

For anything not in the catalog, the Custom path registers any HTTP API by hand: a name, a base URL, the identity model, and one or more credentials. Custom integrations start with no seeded knowledge; IT or an app owner adds the first entries.

A Greenlight app does not carry an upstream password, token, or service-account file. It carries one thing: a workload key, handed to it as an environment variable at deploy time. The key is opaque and app-scoped — it identifies which app is calling, nothing more. It is not an upstream credential and does not represent a human. For service-account integrations, app code sends this key in the same header, basic-auth component, query parameter, or SDK API-key field the upstream normally uses; Greenlight replaces it before the request leaves the proxy.

When Greenlight receives a proxied call, it resolves that key to exactly one app, confirms the app has a granted permission for the integration in the path, and only then supplies the upstream credential. The key is rotated automatically and can be revoked instantly from the dashboard; the next call simply fails. The app’s own code never needs to know any of this is happening.

For user-passthrough integrations the workload key isn’t enough on its own — the call also carries a short-lived token proving which signed-in user it’s on behalf of, so the upstream can issue a token scoped to that user.

The proxy URL, integration name, and workload key are the only Greenlight-specific things the app needs. For a bearer-shaped integration:

const res = await fetch(
`${process.env.GREENLIGHT_PROXY_URL}/slack/api/chat.postMessage`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.GREENLIGHT_DATA_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ channel: '#general', text: 'Hello' }),
}
);

For an API-key-header integration, the same pattern becomes X-Api-Key: process.env.GREENLIGHT_DATA_KEY; for a query-param integration, it becomes ?apikey=${process.env.GREENLIGHT_DATA_KEY}. The broker handles the difference. Always read GREENLIGHT_PROXY_URL from the environment rather than hardcoding it — that’s the contract that lets Greenlight change the URL without touching app code.

When an agent runs the app locally the same brokering applies, with no credential landing on the laptop — see Local development.