Diode SDK Go is a Go library for interacting with the Diode ingestion service utilizing gRPC.
Diode is a new NetBox ingestion service that greatly simplifies and enhances the process to add and update network data in NetBox, ensuring your network source of truth is always accurate and can be trusted to power your network automation pipelines.
More information about Diode can be found at https://netboxlabs.com/blog/introducing-diode-streamlining-data-ingestion-in-netbox/.
- Go 1.24 or later installed
go get github.com/netboxlabs/diode-sdk-goDIODE_SDK_LOG_LEVEL- Log level for the SDK (default:INFO)DIODE_CLIENT_ID- Client ID for OAuth2 authenticationDIODE_CLIENT_SECRET- Client Secret for OAuth2 authenticationDIODE_CERT_FILE- Path to custom certificate file for TLS connectionsDIODE_SKIP_TLS_VERIFY- Skip TLS verification (default:false)DIODE_DRY_RUN_OUTPUT_DIR- Directory to write dry run output files when usingDryRunClient
targetshould be the address of the Diode service.- Insecure connections:
grpc://localhost:8080/diodeorhttp://localhost:8080/diode - Secure connections:
grpcs://example.comorhttps://example.com
- Insecure connections:
package main
import (
"context"
"log"
"github.com/netboxlabs/diode-sdk-go/diode"
)
func main() {
client, err := diode.NewClient(
"grpc://localhost:8080/diode",
"example-app",
"0.1.0",
diode.WithClientID("YOUR_CLIENT_ID"),
diode.WithClientSecret("YOUR_CLIENT_SECRET"),
)
if err != nil {
log.Fatal(err)
}
// Create a device
deviceEntity := &diode.Device{
Name: diode.String("Device A"),
DeviceType: &diode.DeviceType{
Model: diode.String("Device Type A"),
Manufacturer: &diode.Manufacturer{
Name: diode.String("Manufacturer A"),
},
},
Platform: &diode.Platform{
Name: diode.String("Platform A"),
Manufacturer: &diode.Manufacturer{
Name: diode.String("Manufacturer A"),
},
},
Site: &diode.Site{
Name: diode.String("Site ABC"),
},
Role: &diode.DeviceRole{
Name: diode.String("Role ABC"),
Tags: []*diode.Tag{
{
Name: diode.String("tag 1"),
},
{
Name: diode.String("tag 2"),
},
},
},
Serial: diode.String("123456"),
AssetTag: diode.String("123456"),
Status: diode.String("active"),
Comments: diode.String("Lorem ipsum dolor sit amet"),
Tags: []*diode.Tag{
{
Name: diode.String("tag 1"),
},
{
Name: diode.String("tag 3"),
},
},
}
entities := []diode.Entity{
deviceEntity,
}
resp, err := client.Ingest(context.Background(), entities)
if err != nil {
log.Fatal(err)
}
if resp != nil && resp.Errors != nil {
log.Printf("Errors: %v\n", resp.Errors)
} else {
log.Printf("Success\n")
}
}See all examples for reference.
Entities support attaching custom metadata as key-value pairs. Metadata can be used to store additional context, tracking information, or custom attributes that don't fit into the standard NetBox fields.
package main
import (
"context"
"log"
"github.com/netboxlabs/diode-sdk-go/diode"
)
func main() {
client, err := diode.NewClient(
"grpc://localhost:8080/diode",
"example-app",
"0.1.0",
diode.WithClientID("YOUR_CLIENT_ID"),
diode.WithClientSecret("YOUR_CLIENT_SECRET"),
)
if err != nil {
log.Fatal(err)
}
// Create a device with metadata
// Note: Both the device and its nested site can have its own metadata
deviceEntity := &diode.Device{
Name: diode.String("Device A"),
Site: &diode.Site{
Name: diode.String("Site ABC"),
Metadata: diode.Metadata{
"site_region": "us-west",
"site_cost_center": "CC-001",
},
},
DeviceType: &diode.DeviceType{
Model: diode.String("Device Type A"),
},
Role: &diode.DeviceRole{
Name: diode.String("Role ABC"),
},
Metadata: diode.Metadata{
"source": "network_discovery",
"discovered_at": "2024-01-15T10:30:00Z",
"import_batch": "batch-123",
"priority": 1,
"verified": true,
},
}
// Create an IP address with metadata
ipEntity := &diode.IPAddress{
Address: diode.String("192.168.1.10/24"),
Status: diode.String("active"),
Metadata: diode.Metadata{
"last_scan": "2024-01-15T12:00:00Z",
"scan_id": "scan-456",
"response_time": 23.5,
"reachable": true,
"owner_team": "network-ops",
},
}
// Create a site with metadata
siteEntity := &diode.Site{
Name: diode.String("Data Center 1"),
Status: diode.String("active"),
Metadata: diode.Metadata{
"region": "us-west",
"cost_center": "CC-001",
"capacity": 500,
"is_primary": true,
"contact_email": "[email protected]",
},
}
entities := []diode.Entity{
deviceEntity,
ipEntity,
siteEntity,
}
resp, err := client.Ingest(context.Background(), entities)
if err != nil {
log.Fatal(err)
}
if resp != nil && resp.Errors != nil {
log.Printf("Errors: %v\n", resp.Errors)
} else {
log.Printf("Success\n")
}
}In addition to entity-level metadata, you can attach metadata to the entire ingestion request using WithIngestMetadata. This is useful for tracking information about the ingestion batch itself, such as the data source, batch ID, or processing context.
package main
import (
"context"
"log"
"github.com/netboxlabs/diode-sdk-go/diode"
)
func main() {
client, err := diode.NewClient(
"grpc://localhost:8080/diode",
"example-app",
"0.1.0",
diode.WithClientID("YOUR_CLIENT_ID"),
diode.WithClientSecret("YOUR_CLIENT_SECRET"),
)
if err != nil {
log.Fatal(err)
}
// Create entities
entities := []diode.Entity{
&diode.Device{
Name: diode.String("Device A"),
Site: &diode.Site{
Name: diode.String("Site ABC"),
},
},
&diode.Device{
Name: diode.String("Device B"),
Site: &diode.Site{
Name: diode.String("Site XYZ"),
},
},
}
// Add request-level metadata to track the ingestion batch
resp, err := client.Ingest(
context.Background(),
entities,
diode.WithIngestMetadata(diode.Metadata{
"batch_id": "import-2024-01-15",
"source_system": "network_scanner",
"import_type": "automated",
"record_count": 2,
"validated": true,
}),
)
if err != nil {
log.Fatal(err)
}
if resp != nil && resp.Errors != nil {
log.Printf("Errors: %v\n", resp.Errors)
} else {
log.Printf("Success\n")
}
}Request-level metadata is included in the IngestRequest and can be useful for:
- Tracking data sources and ingestion pipelines
- Correlating entities within a batch
- Debugging and auditing data imports
- Adding contextual information for downstream processing
TLS verification is controlled by the target URL scheme:
- Secure schemes (
grpcs://,https://): TLS verification enabled - Insecure schemes (
grpc://,http://): TLS verification disabled
// TLS verification enabled (uses system certificates)
client, err := diode.NewClient("grpcs://example.com", ...)
// TLS verification disabled
client, err := diode.NewClient("grpc://example.com", ...)// Via constructor parameter
client, err := diode.NewClient(
"grpcs://example.com",
"example-app", "0.1.0",
diode.WithCertFile("/path/to/cert.pem"),
)
// Or via environment variable
export DIODE_CERT_FILE=/path/to/cert.pemexport DIODE_SKIP_TLS_VERIFY=trueclient, err := diode.NewClient(
"grpcs://example.com",
"example-app", "0.1.0",
diode.WithCertFile("/path/to/cert.pem"),
diode.WithSkipTLSVerify(),
)Use a DryRunClient to inspect what would be sent to Diode without actually sending any data. When a directory is provided a new JSON file is created for each ingest call.
// Write ingest payload to a timestamped file in /tmp
client, err := diode.NewDryRunClient("example-app", "/tmp")
if err != nil {
log.Fatal(err)
}
_, _ = client.Ingest(context.Background(), []diode.Entity{
&diode.Device{Name: diode.String("Device A")},
})
_ = client.Close()You can include request-level metadata in the dry run output using WithIngestMetadata. This metadata will be included in the JSON output file as part of the IngestRequest:
client, err := diode.NewDryRunClient("example-app", "/tmp")
if err != nil {
log.Fatal(err)
}
// Add request-level metadata
_, _ = client.Ingest(
context.Background(),
[]diode.Entity{
&diode.Device{Name: diode.String("Device A")},
},
diode.WithIngestMetadata(diode.Metadata{
"batch_id": "import-2024-01",
"source": "csv_import",
"validated": true,
"record_count": 150,
}),
)
_ = client.Close()The resulting JSON file will include the metadata in the IngestRequest, making it visible when reviewing the dry run output.
Loaded entities can later be ingested using a real client:
protoEntities, err := diode.LoadDryRunEntities("/tmp/example-app_1750106879725947344.json")
if err != nil {
log.Fatal(err)
}
realClient, err := diode.NewClient(
"grpc://localhost:8080/diode",
"example-app",
"0.1.0",
diode.WithClientID("YOUR_CLIENT_ID"),
diode.WithClientSecret("YOUR_CLIENT_SECRET"),
)
if err != nil {
log.Fatal(err)
}
_, err = realClient.IngestProto(context.Background(), protoEntities)
if err != nil {
log.Fatal(err)
}OTLPClient converts ingestion entities into OpenTelemetry log records and exports them to an OTLP collector over gRPC. This is useful when a collector receives log data and forwards it to Diode.
client, err := diode.NewOTLPClient(
"grpc://localhost:4317",
"otlp-producer",
"0.0.1",
)
if err != nil {
log.Fatal(err)
}
defer client.Close()
_, err = client.Ingest(context.Background(), []diode.Entity{
&diode.Site{Name: diode.String("Site 1")},
})
if err != nil {
log.Fatal(err)
}Each entity is serialised with protobuf field names and emitted as a log record that includes SDK and producer metadata via resource attributes so downstream collectors can enrich and forward the payload. The client raises an OTLPClientError when the export fails. TLS behaviour honours the existing DIODE_SKIP_TLS_VERIFY and DIODE_CERT_FILE environment variables, and the export timeout can be customised via diode.WithOTLPTimeout.
You can add request-level metadata to OTLP exports using WithIngestMetadata. This metadata is automatically mapped to OTLP resource attributes with a diode.metadata. prefix:
client, err := diode.NewOTLPClient(
"grpc://localhost:4317",
"otlp-producer",
"0.0.1",
)
if err != nil {
log.Fatal(err)
}
defer client.Close()
// Add request-level metadata
_, err = client.Ingest(
context.Background(),
[]diode.Entity{
&diode.Site{Name: diode.String("Site 1")},
},
diode.WithIngestMetadata(diode.Metadata{
"environment": "production",
"deployment": "us-west-2",
"version": "1.2.3",
"priority": 5,
}),
)
if err != nil {
log.Fatal(err)
}The resulting OTLP log records will include resource attributes like:
diode.metadata.environment="production"diode.metadata.deployment="us-west-2"diode.metadata.version="1.2.3"diode.metadata.priority=5(as integer)
These attributes are added alongside standard OTLP resource attributes (service.name, service.version, diode.stream, etc.), allowing downstream collectors and observability platforms to filter, route, and enrich the data based on this metadata.
A small helper binary is included to ingest JSON files created by the
DryRunClient and send them to a running Diode service.
Install the helper using go install:
go install github.com/netboxlabs/diode-sdk-go/cmd/diode-replay-dryrun@latestThis installs the command separately from the SDK library obtained with
go get.
Run it by providing one or more JSON files and connection details. Use -file
multiple times to ingest several dry-run files in a single request:
diode-replay-dryrun \
-file /tmp/example-app_1750106879725947344.json \
-file /tmp/other.json \
-target grpc://localhost:8080/diode \
-app-name example-app \
-app-version 0.1.0 \
-client-id YOUR_CLIENT_ID \
-client-secret YOUR_CLIENT_SECRETThe flags -file, -target, -app-name, and -app-version are required. You may
repeat -file to specify multiple files. OAuth2
credentials can be supplied using -client-id and -client-secret or the
DIODE_CLIENT_ID and DIODE_CLIENT_SECRET environment variables.
- ASN
- ASN Range
- Aggregate
- Circuit
- Circuit Group
- Circuit Group Assignment
- Circuit Termination
- Circuit Type
- Cluster
- Cluster Group
- Cluster Type
- Console Port
- Console Server Port
- Contact
- Contact Assignment
- Contact Group
- Contact Role
- Device
- Device Bay
- Device Role
- Device Type
- FHRP Group
- FHRP Group Assignment
- Front Port
- IKE Policy
- IKE Proposal
- IP Address
- IP Range
- IP Sec Policy
- IP Sec Profile
- IP Sec Proposal
- Interface
- Inventory Item
- Inventory Item Role
- L2VPN
- L2VPN Termination
- Location
- MAC Address
- Manufacturer
- Module
- Module Bay
- Module Type
- Platform
- Power Feed
- Power Outlet
- Power Panel
- Power Port
- Prefix
- Provider
- Provider Account
- Provider Network
- RIR
- Rack
- Rack Role
- Rack Type
- Rear Port
- Region
- Role
- Route Target
- Service
- Site
- Site Group
- Tag
- Tenant
- Tenant Group
- Tunnel
- Tunnel Group
- Tunnel Termination
- VLAN
- VLAN Group
- VLAN Translation Policy
- VLAN Translation Rule
- VM Interface
- VRF
- Virtual Chassis
- Virtual Circuit
- Virtual Circuit Termination
- Virtual Circuit Type
- Virtual Device Context
- Virtual Disk
- Virtual Machine
- Wireless Lan
- Wireless Lan Group
- Wireless Link
make lintmake testDistributed under the Apache 2.0 License. See LICENSE.txt for more information.