Telemetry and signals
How MainSDK logs, traces and audits on open standards, what is redacted, and what leaves your app and when.
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.
Every app gets structured logs, traces and an append-only audit log from its first run, free and local. Redaction happens in your process before anything is written or exported. Nothing reaches MainSDK Cloud until you log in and opt in.
Open standards
| Standard | Used for |
|---|---|
| OpenTelemetry logs data model | Every log record: severity, body, attributes, resource |
| OpenTelemetry semantic conventions | Where it happened (code.function.name, code.file.path, code.line.number) and what failed (exception.*) |
| W3C Trace Context | traceparent links an error to the request, job and change that caused it |
| RFC 9457 problem details | Every API error, plus our code, fix, retryable, trace_id and rule |
| RFC 3339, UTC | Every timestamp |
| OCSF | Audit and security events, read as-is by security tools |
Event ids are UUIDv7.
Where telemetry goes
flowchart LR
app["Your app<br/>log · spans · decisions · audit"] --> red["Redaction<br/>in process"]
red --> term["terminal<br/>default"]
red --> local[(".mainsdk/events/<br/>7 days, git-ignored")]
red --> buf["Bounded buffer<br/>async, never blocks"]
buf --> otel["Your OTLP endpoint<br/>Datadog, Grafana, Honeycomb …"]
buf -. "only after mainsdk login<br/>and opt-in (phase 2)" .-> cloud["MainSDK Cloud<br/>your region"]telemetry:
to: [terminal, otel, mainsdk] # any mix; default [terminal]
otel: { endpoint: https://otel.pinecart.dev:4318, headers: { authorization: secret:OTEL_TOKEN } }
sample: 0.2 # traces only
budget: 2M # events a month sent to MainSDK CloudMAINSDK_TELEMETRY=1 is the same as adding mainsdk to telemetry.to, and it still needs mainsdk login. Without login, no connection is made to any MainSDK host and the CLI says log in to send telemetry. Logout stops sending before the next batch.
What is redacted, before anything leaves
- Labelled values: fields labelled
personal,sensitive,childorphibecome[redacted:personal]and so on.panis never stored at all. - Secrets: anything resolved from a
secret:reference, and known key formats such asmsk_live_andmsk_test_. - Never collected by default: request and response bodies, cookies, and the
authorization,cookieandset-cookieheaders. - User ids: pseudonymised with a keyed hash per project before export.
- IP addresses: trimmed, IPv4 to /24 and IPv6 to /48.
203.0.113.77is exported as203.0.113.0.
The telemetry contract
telemetry@1 lists every field we collect, its purpose and its redaction rule, published as JSON Schema. A field not in the contract is not exported: the exporter drops it and warns locally with MAIN-LOG-0005.
Sampling never touches enforcement
sample applies to traces only. Errors, policy decisions and audit records are never sampled. Over a budget, we sample what we store, never what we enforce.
The audit log
flowchart LR r1["record n-1<br/>hash h1"] --> r2["record n<br/>prev: h1, hash h2"] --> r3["record n+1<br/>prev: h2, hash h3"]
- Append-only and tamper-evident: each record carries the hash of the previous one.
log.audit.verify()detects any edit, deletion or reorder and returns where the chain broke. - Records are OCSF. Every policy decision is written with its policy version, for example as an OCSF Authorization event with actor, action, resource and decision.
- Never dropped silently: if its sink is unavailable, it buffers to local disk and reports
MAIN-LOG-0010.
Still open: where the audit log lives in production by default, and whether chain checkpoints are signed, and with which key.
When the exporter is down
Export is asynchronous, buffered and bounded. When the endpoint is unreachable, telemetry is dropped with a telemetry.dropped counter and requests keep their speed. See request lifecycle.
Security signals
| What | Phase |
|---|---|
Hosted search, retention and mainsdk logs --follow --env prod from our endpoint | 2 |
| AI traces in the OpenTelemetry GenAI conventions | 2 |
| SIEM streaming per customer organisation | 2 |
| Fleet Defend: signals from inside every app to the SIEM in OCSF, enriched with app, owner, environment and data labels; detections as Sigma rules mapped to MITRE ATT&CK | 4 (Fleet) |
Fleet Defend signals come from the places MainSDK already decides: the sign-in check, the owner-scope check, the rate limiter, the outbound call, the AI call and the agent action. For example, an outbound call to an unknown host that was refused maps to T1041 and T1195. Because the SDK reports what it decided, not raw traffic, the signals are few.
Next
- Cloud and regions: where exported telemetry is stored.
- Request lifecycle: how the buffer stays off the request path.
- Security architecture: the telemetry threats and their controls.
Was this page helpful?