---
title: "See every call, decision and change your app makes"
description: "Get a full picture of a MainSDK app - destinations, drivers, access decisions and a tamper-evident audit log - in your own OpenTelemetry stack or SIEM."
section: "guides"
status: "Building for 0.2"
spec: "log-telemetry"
canonical: "https://mainsdk.dev/docs/guides/see-what-your-app-does"
updated: "2026-10-28"
---

# See every call, decision and change your app makes

> Get a full picture of a MainSDK app - destinations, drivers, access decisions and a tamper-evident audit log - in your own OpenTelemetry stack or SIEM.

Status: Building for 0.2 · Canonical: https://mainsdk.dev/docs/guides/see-what-your-app-does · Updated: 2026-10-28

You will see what your app does, from your own tools: which outside hosts it calls, which driver serves each interface, why each access request was allowed or denied, and who changed what. Everything runs inside your app and goes to the destinations you choose. Nothing is sent to MainSDK until you log in and turn it on.

> **Note:** Logs and the audit log are being built for 0.2 (spec `log-telemetry`), and telemetry is still planned; `mainsdk info` is being built for 0.2 (spec `cli-core`); network decisions are being built for 0.3 (spec `network`). They run from the repository and are not published to npm yet.

## You need

- A MainSDK app with a `main.yaml`.
- Optional: an OpenTelemetry collector or a SIEM that accepts OTLP.

## 1. Ask the app what it is

```bash
npx mainsdk info            # human-readable
npx mainsdk info --json     # for scripts, agents and reviews
```

It lists the project, each interface and its driver, every network destination (yours and each driver's declared hosts), the folders, server facts and programs the code uses (see [Declare what your app uses](https://mainsdk.dev/docs/guides/declare-what-your-app-uses)), tracking settings such as `email.tracking: none`, and every open exception with its owner and expiry. Run it in CI and diff it to catch a new destination or driver in a pull request.

A running app can serve a signed copy at `/.well-known/mainsdk.json`. `mainsdk info https://app.example.com` checks the signature, so a buyer can verify the record without access to your code.

## 2. Turn on the audit log

```yaml title="main.yaml"
logging:
  audit: true
  redact: [personal, sensitive, child, phi]
  retention: 12m
```

Every action and every policy decision is written as an OCSF record with the actor, the action, the resource, the decision and the policy version. Each record carries the hash of the one before it, so an edit, deletion or reorder is detectable:

```ts title="scripts/verify-audit.ts"
import { log } from "mainsdk";

const result = log.verifyAudit({ from: "2026-10-01" });
if (!result.ok) throw new Error(`Audit log broken at ${result.brokenAt}`);
```

The audit log is never sampled or dropped. If its sink is down, records buffer to local disk and `MAIN-LOG-0010` is reported.

## 3. Find out why a request was denied

Every decision has an id. `policy.explain` returns the policy line that made it:

```ts title="app/admin/why/route.ts"
import { auth, policy } from "mainsdk";

export async function GET(req: Request) {
  await auth.require(req, { role: "admin" });
  const id = new URL(req.url).searchParams.get("decision") ?? "";
  return Response.json(policy.explain(id));
}
```

Blocked network calls are decisions too: each one names the destination, the rule and the calling file and line (see [Restrict which hosts and IPs your app can reach](https://mainsdk.dev/docs/guides/restrict-network)).

## 4. Send it to your own tools

```yaml title="main.yaml"
telemetry:
  to: [terminal, otel]
  otel:
    endpoint: https://otel.acme.dev:4318
    headers: { authorization: secret:OTEL_TOKEN }
  sample: 0.2          # traces only; errors, decisions and audit are never sampled
```

Logs follow the OpenTelemetry data model with W3C trace ids, so they land in Grafana, Datadog, Honeycomb or your SIEM next to the rest of your traces. Export is asynchronous and bounded: if the collector is down, telemetry is dropped and counted, and your requests never wait (ADR-0003).

## What is kept out

Redaction runs in your app before anything is written or exported:

- Fields labelled `personal`, `sensitive`, `child` or `phi`, anything resolved from a `secret:` reference, and known key formats.
- Request and response bodies, cookies and `authorization` headers, unless you turn them on.
- User ids are pseudonymised per project, and IP addresses are trimmed (IPv4 to /24, IPv6 to /48).

The `telemetry@1` contract lists every field, its purpose and its redaction rule. A field not in it is dropped (`MAIN-LOG-0005`).

## Verify

```bash
npx mainsdk check --env prod
npx mainsdk info --json
```

`check` passes with no `MAIN-LOG` finding, and `info` lists only the destinations and drivers you expect.

## If it fails

| Code | Cause | Fix |
| --- | --- | --- |
| `MAIN-LOG-0003` | `MAINSDK_DEBUG=driver` was ignored in production. | Use a debug session (phase 2). |
| `MAIN-LOG-0005` | An attribute not in `telemetry@1` was dropped before export. | Remove it, or propose it for the contract. |
| `MAIN-LOG-0010` | The audit sink is unavailable; records are buffering to local disk. | Restore the sink. |
| `MAIN-INFO-0002` | The signed record from a running app doesn't verify. | Treat the record as untrusted and redeploy from a clean build. |

## For agents

```text
Goal: give this app an audit log and send its telemetry to the team's collector.
Read: docs/specs/log-telemetry.md, this page, main.yaml.
Touch only: main.yaml, main.env.prod.yaml.
Do: set logging.audit: true with redact labels; set telemetry.to: [terminal, otel] and otel.endpoint;
keep the collector token as a secret: reference.
Verify: npx mainsdk check --json --agent && npx mainsdk info --json
Never turn off redaction or log request bodies to debug.
```

## What comes later

- **Streaming each customer organisation's audit log to its own SIEM** (phase 2).
- **One view across every app:** a live destination map that alerts on any new host, and reachable-vulnerability reports, in Fleet (phase 4, spec `fleet-observe`).

## Next

- [Declare what your app uses](https://mainsdk.dev/docs/guides/declare-what-your-app-uses): the hosts, folders, facts and programs `info` lists
- [Make your app enterprise ready](https://mainsdk.dev/docs/guides/enterprise-ready)
- [Logs, audit and telemetry](https://mainsdk.dev/docs/interfaces/log), the interface reference
