LandfallDocs
Architecture

Integrations architecture

Every integration Landfall ships — AWS, Datadog, Coralogix, PagerDuty, GitHub, and whatever comes after — is built on one shared model, not a parallel one-off per provider. This page describes that model once; each integration's own setup guide links back here for the shared parts, using GitHub as the worked example throughout.

Why this is one page, not one per integration

A connected system the investigator agent can't dynamically see, or that can't itself raise an incident, is a second-class integration — it undermines both the "agent crew investigates in the conversation" premise and the "any signal can start a war room" premise the platform is built on. So every integration is held to the same four requirements: a documented setup guide with real screenshots, automatic agent access with no per-provider code, the ability to open an incident, and — this page — one current, shared description of how all of that actually works, updated as integrations are added rather than forked into a new document each time.

The connector framework

Every provider — a telemetry source, a cloud account, a comms/paging provider, a source-control provider — implements the same small contract:

  • IntegrationAdapter — declares the provider's id, its config schema (which fields are secret, which aren't), and a verify() that checks a set of credentials actually authenticate before anything is saved. A credential that can't authenticate is refused, not silently stored as "configured."
  • ConfigStore — the durable, per-tenant, RLS-protected record of what's connected: non-secret fields (org login, installation id, account id) live in Postgres; secret values live in a separate secrets backend and are never returned to any client, ever.
  • SecretsBackend — AWS Secrets Manager in every real environment (LocalStack locally/in CI). Most providers store a per-tenant secret here. GitHub is the one structural exception: see below.

Adding a provider means implementing this contract, not inventing a new connection model. GitHub's adapter is the same shape as AWS's or Datadog's; only what it stores and how it authenticates differ.

GitHub's shape: one platform App, not a per-tenant secret

Most providers ask each tenant for their own API key or role — an independent secret per organization. A GitHub App works differently by GitHub's own design: Landfall registers one App at the platform level (an App id and a private key), and each organization installs that App, choosing which repositories it can see. Landfall's ConfigStore row for a GitHub connection holds only two non-secret, tenant-identifying fields — the org login and the installation id — never a credential. At call time, a platform-level component signs a JWT with the App's private key, exchanges it for a short-lived (about one hour) access token scoped to that one installation, and caches it in memory until shortly before it expires. No installation token is ever persisted; a process restart just means the next call re-mints one.

This is why GitHub's setup guide has no "paste your API key here" step — see Connecting GitHub.

Dynamic agent access — no per-provider agent code

The investigator agent doesn't have a hardcoded list of "sources it knows about." It reads one thing: ctx.scope.capabilities, built fresh for every investigation from the tenant's actually-configured connections. The moment a connection exists — GitHub or otherwise — three things happen automatically, with zero source-specific agent code:

  1. It's listed by the signals service's plugin catalog for that tenant, alongside every other connected source.
  2. It's folded into ctx.scope.capabilities, carrying its real operation descriptors — for GitHub, that's search/read-file/list-commits/diff/ blame/correlate-deploy, each with its own parameter shape.
  3. It's given a generic {source}/query scope-resource key by the same widening step every connected, non-canonical source goes through — GitHub gets no special case here at all.

In plain terms: connecting GitHub doesn't require shipping a new agent tool or a new prompt. The agent's existing generic query mechanism just gains a new source to query, exactly the way it gained AWS, Datadog, or Coralogix — a genuinely new integration widens what the agent can reach without widening the agent's own code.

The incident-triggering model

A source that can only be queried during an investigation is half an integration — the platform's other premise is that any signal can open the investigation in the first place. Every trigger source, GitHub included, goes through the same pipeline (libs/triggers):

  1. Signature verification first. A dedicated guard checks the delivery's signature before anything else runs — an unverified delivery never reaches parsing, let alone incident creation.
  2. Uniform rejection. A bad signature and a signature that's valid but names a tenant that isn't actually connected get the identical refusal from the outside. The distinct reason is recorded only in an internal audit trail — never in the response — so the endpoint can't be used to enumerate which organizations are connected.
  3. Normalize → resolve entity/severity. The provider-specific payload becomes the same shared trigger shape every source produces, with severity derived from that provider's own native severity field where one exists.
  4. externalRef dedup. Before creating an incident, the delivery's own identifier (a GitHub delivery id and alert/run id, a PagerDuty incident id, …) is checked against existing incidents. A match means "already handled" — accepted and ignored, never a second incident. This is the same mechanism PagerDuty and Opsgenie already use, not a new one built for GitHub.

See Triggering Integrations → GitHub for the event types GitHub routes through this pipeline, and how the deploy-critical-workflow opt-in keeps a flaky CI run from opening a war room on its own.

Read-only is enforced at the credential, not by convention

For every integration on this platform, "the agent only reads" is not a prompt instruction — it's a property of the credential itself. GitHub's App has no write scope at all in its registration; there is no code path capable of issuing a write call, because the token GitHub issues literally cannot perform one. The same bar applies across every provider: the credential can't write, so there's nothing relying on application-layer discipline alone to keep it that way.

In one sentence, per integration

IntegrationCredential shapeCan trigger?
AWSPer-tenant IAM role (no keys)Yes — CloudWatch alarm → SNS → webhook
DatadogPer-tenant API + application keyYes — monitor alert webhook
CoralogixPer-tenant API keyYes — alert webhook
PagerDutyPer-tenant read-only API key + webhookYes — incident.triggered/escalated/resolved
GitHubOne platform App; per-tenant installation id onlyYes — security alerts + deploy-critical workflow failures