MainSDK
Search⌘K
How-to

Connect your security and ticket tools

Push assure evidence to your GRC tool, run privacy requests from Transcend or OneTrust, send SBOM and VEX to your vulnerability tool, and open one ticket per failure.

Building for 1.2

Status Status is where the work is: Planned (in a milestone, not started), Building (tickets in progress), Preview (usable, may change), then the stability it ships at.

Stability The promise an interface ships with.

  • Stable Follows semantic versioning; breaking changes need a new major, 12 months of overlap and a codemod.
  • Beta Usable in production; may change before it becomes stable.
  • Experimental May change or go away; needs an explicit opt-in in main.yaml.
Stability levels
Updated 2026-10-28

You will send MainSDK's evidence to the tools your security team uses: control results to ServiceNow IRM, one GitHub issue per failure, and the SBOM and VEX to Dependency-Track. It takes about 30 minutes.

You need

  • A MainSDK app with secrets, and an assure run to push (a standard and a list of control results).
  • A token for each tool, stored as a secret.

1. Declare the connectors

main.yaml
connectors:
  grc:     { driver: servicenow-irm, instance: https://acme.service-now.com, auth: secret:SN_TOKEN }
  vulns:   { driver: dependency-track, url: https://dt.acme.internal, auth: secret:DT_KEY }
  tickets: { driver: github, repo: acme/web, auth: secret:GH_TOKEN }

Always write auth: secret:NAME. A literal token fails mainsdk check.

2. Test them without writing

text
mainsdk connectors test

Each connector is pinged and nothing is written.

3. Push a run

ts
import { app } from "mainsdk";
import {
  type AssureRun,
  type ConnectorsConfig,
  connectorsFrom,
  httpOver,
  pushAssure,
  syncTickets,
} from "mainsdk/connectors";

const a = app();
const c = connectorsFrom(a.config.connectors as ConnectorsConfig, {
  http: httpOver(fetch),
  secrets: (name) => a.secrets.get(name),
  log: (entry) => a.log.info("connector call", { ...entry }),
});

export async function push(run: AssureRun) {
  if (c.grc) await pushAssure(c.grc, run);
  if (c.tickets) await syncTickets(c.tickets, run, { owner: "web-team" });
}

Running it twice opens each issue once. Pass previous (the controls that failed last time) so a fixed control closes its issue.

4. Send a release

ts
import type { Connector, VexStatement } from "mainsdk/connectors";
import { importFindings, pushRelease } from "mainsdk/connectors";

export async function send(vulns: Connector, sbom: Record<string, unknown>, vex: VexStatement[]) {
  await pushRelease(vulns, { app: "web", release: "1.4.0", sbom, vex });
  return importFindings(vulns, "1.4.0");
}

A CVE the VEX marks not_affected comes back suppressed, with its justification.

Check it worked

  • pnpm mainsdk check passes with no MAIN-SECRET-0002 or MAIN-CONNECTOR-0001 finding.
  • mainsdk connectors test reports each connector ok.
  • The audit log has one line per call, and none holds a token.

If it fails

  • MAIN-SECRET-0002: auth is a literal. Move the token to secrets and write secret:NAME.
  • MAIN-CONNECTOR-0001: the driver is not one of the listed ones, or the address is not https.
  • MAIN-CONNECTOR-0002: the tool refused a call. The audit line names the operation and status.

Prompt for your coding agent

Wire my app's assure results into our tools with MainSDK's connectors interface. Push control results to ServiceNow IRM, open one GitHub issue per failure that closes when fixed, send the SBOM and VEX to Dependency-Track, and keep every token in secrets.

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet