@foundryprotocol/0gkit-observability
OpenTelemetry instrumentation for 0gkit primitives. One call wires every Storage / Compute / DA / Attestation operation as an OTel span with
0gkit.*semantic attributes. Bundle ≤ 20 KB gzipped.
What it does
instrument0g() patches the public methods of Storage, Compute, and DA
at runtime so every call emits an OTel span. The span name is
0gkit.<primitive>.<method> (e.g. 0gkit.storage.upload). Attributes
include 0gkit.network, 0gkit.size_bytes, 0gkit.gas_native,
0gkit.fee_native, 0gkit.confirm_seconds, 0gkit.root, and so on.
Failures record the exception, set the span status to ERROR, and attach
0gkit.error_code (the SCREAMING_SNAKE code from the
error taxonomy).
When to use it
- Production apps that need to know what's slow, what's expensive, and what's failing in their 0G calls.
- Cost-attribution dashboards:
0gkit.gas_native+0gkit.fee_nativeattributes feed straight into per-team / per-feature cost rollups. - Anywhere you already have an OTel collector (Honeycomb, Datadog, Tempo, Vercel OTel, Grafana Cloud).
Install
pnpm add @foundryprotocol/0gkit-observability @opentelemetry/api
# Only if you don't already have an OTel SDK configured:
pnpm add @opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-http
Quickstart — auto SDK setup
import { instrument0g } from "@foundryprotocol/0gkit-observability";
// At process boot, BEFORE any Storage / Compute / DA call:
await instrument0g({
serviceName: "my-app",
exporter: {
kind: "otlp",
endpoint: "https://api.honeycomb.io/v1/traces",
headers: { "x-honeycomb-team": process.env.HONEYCOMB_API_KEY! },
},
});
// From here, every Storage / Compute / DA call emits a span.
// Nothing else in your app needs to change.
Bring your own SDK (mode: "attach")
If you already configure @opentelemetry/sdk-node (or any other OTel SDK) in
your app, pass mode: "attach" so instrument0g skips SDK setup and only
patches primitives:
import { instrument0g } from "@foundryprotocol/0gkit-observability";
await instrument0g({ mode: "attach" });
Tests — patch fakes with explicit targets
Tests can patch fake classes by passing targets explicitly. This bypasses
the dynamic imports of the real primitives, so it's also synchronous in
practice (the await resolves immediately):
import { instrument0g, disinstrument0g } from "@foundryprotocol/0gkit-observability";
class FakeStorage {
network = "galileo";
async upload() {
return { root: "0xabc" };
}
}
await instrument0g({
mode: "attach",
targets: { storage: { class: FakeStorage, methods: ["upload"] } },
});
// Run tests against an in-memory exporter.
disinstrument0g(); // restores originals
Span attribute reference
All attribute keys live in a single frozen ATTR constant. Every key uses
the 0gkit.* vendor namespace so collectors and cost calculators can filter
on the prefix.
| Constant | Key | Notes |
|---|---|---|
ATTR.NETWORK | 0gkit.network | "galileo" / "aristotle" / "local" |
ATTR.OP | 0gkit.op | "storage.upload", "compute.inference", … |
ATTR.SIZE_BYTES | 0gkit.size_bytes | Bytes uploaded / downloaded / published |
ATTR.SEGMENTS | 0gkit.segments | Storage segments (256 KiB chunks) |
ATTR.GAS_NATIVE | 0gkit.gas_native | Gas units (stringified bigint) |
ATTR.FEE_NATIVE | 0gkit.fee_native | Native wei fee (stringified bigint) |
ATTR.CONFIRM_SECONDS | 0gkit.confirm_seconds | Confirmation latency in seconds |
ATTR.ROOT | 0gkit.root | Storage Merkle root |
ATTR.TX_HASH | 0gkit.tx_hash | Transaction hash |
ATTR.BLOCK_NUMBER | 0gkit.block_number | Block number |
ATTR.MODEL | 0gkit.model | Compute model id |
ATTR.INPUT_TOKENS | 0gkit.input_tokens | Input tokens consumed |
ATTR.OUTPUT_TOKENS | 0gkit.output_tokens | Output tokens generated (or max for estimates) |
ATTR.ERROR_CODE | 0gkit.error_code | SCREAMING_SNAKE code from the error taxonomy |
ATTR.DRY_RUN | 0gkit.dry_run | true for .estimate() and { dryRun: true } calls |
Standard OTel http.* / rpc.* attributes are layered on top by user
instrumentation — we don't duplicate them.
What's wrapped today
| Primitive | Methods |
|---|---|
Storage | upload, download, estimate, exists |
Compute | inference, estimate |
DA | publish, estimate |
Attestation | (free functions — pass an explicit target only) |
Attestation is currently a set of free functions, not a class. The default
target set skips it; if you wire your own attestation client class, pass
targets.attestation to instrument it. See
docs/DECISIONS.md D32
for the rationale.
Bundle size
The public entry bundles to ≤ 20 KB gzipped. Asserted by
bundle-size.test.ts on every CI run. @opentelemetry/api is externalised
(peer dep); the optional SDK + exporter peers are lazy-imported and never
reach the bundle unless the caller asks for them.
Exporter recipes
See also
- Concept: Observability —
0gkit.*attribute semantics + cost attribution playbook - Error codes — every
0gkit.error_codevalue links to a fix page - Source on GitHub
Exports
type AttrKeytype ExporterConfigtype InstrumentConfigtype InstrumentModetype InstrumentTargets