name: project-snmp-trap-profiles-authoring description: Use when editing Netdata SNMP trap profile YAMLs, trap profile metric rules, the trap profile-format documentation, the snmp-trap-profile-gen Go helper, or running a regeneration of the OOB trap profile pack. Enforces the closed 8-category / 8-severity taxonomy, the file-scoped varbinds-table pattern, metric cardinality discipline, and stock/operator separation.
SNMP Trap Profile Authoring
Use this skill before editing files under:
src/go/plugin/go.d/config/go.d/snmp.trap-profiles/src/go/cmd/snmptrapprofilegen/(shipped helper source).agents/sow/specs/snmp-traps/netdata.md(when the change touches profile schema or trap subsystem decisions that the profiles encode)
The authoritative schema reference is
src/go/plugin/go.d/config/go.d/snmp.trap-profiles/profile-format.md. The
authoritative subsystem design is
.agents/sow/specs/snmp-traps/netdata.md. This skill is the working checklist
that keeps repository edits aligned with both.
SNMP Trap SOW Spec Organization
The SNMP trap SOW spec directory intentionally separates Netdata product contracts from research evidence.
- Netdata-owned specs and decisions live directly under
.agents/sow/specs/snmp-traps/:netdata.mdtrap-metrics-profiles.mdnetdata-snmp-hub-architecture.mddecisions/
- Research evidence lives under
.agents/sow/specs/snmp-traps/research/:domain/for general SNMP trap observability research;playbooks/for operational playbooks and skill-distillation source material;netdata-existing/for inventories of existing Netdata subsystems used as design inputs;external-systems/for per-product studies of other trap implementations;comparison/for cross-system matrices, stress tests, and synthesis.
Do not put new research files beside the Netdata specs. Research can inform
specs, but it is not itself a Netdata product contract. When a research finding
becomes a product rule, copy the accepted rule into a top-level spec or
decisions/ entry and cite the research path as evidence.
Trap OID .0. tolerance
Profile authors should use the canonical trap OID form produced by the source
MIB/tooling. The receiver lookup is exact-match-first and then tolerates the
SMIv1 / SMIv2 trap-OID ambiguity by adding or removing a single .0. segment
immediately before the final OID arc on primary miss. This tolerance is
trap-OID-only: do not normalize or alternate-match varbind OIDs.
Required checks before changing a profile
Trap
name:must be MIB-qualified. Every trap entry'sname:field uses the canonical SMI form<MIB-MODULE>::<symbol>(e.g.IF-MIB::linkDown,CISCO-CONFIG-MAN-MIB::ccmCLIRunningConfigChanged). Vendors reuse bare symbolic names across product-line MIB modules; the bare symbol is NOT globally unique. The qualified form matches whatsnmptranslate/snmptrapd/ MIB browsers produce and is what the plugin writes to theTRAP_NAMEjournal field. Rule: if the OID changes, thename:slug MUST change.Resolve every varbind reference. A name in a trap entry's
varbinds:list MUST exist in the file-scopedvarbinds:table or be an inline dict on the trap entry. Dangling name references are a bug — they render as empty values in restricted templates and produce misleading journal messages.Identify the source MIB object for every varbind. Check the object's
MAX-ACCESS.not-accessibleindex objects must still be declared in the table (they ship insideTRAP_JSONand, when non-sensitive/non-redundant, indexedTRAP_VAR_*fields), but never as adescription:template variable on its own — varbinds an SNMP entity will not send in a trap PDU never resolve at runtime.File-scoped
varbinds:table entries require bothoidandtype. Varbind records withresolved: false(the MIB-extractor couldn't resolve the OBJECT-TYPE through the IMPORTS chain) MUST be dropped from both the table and the per-trap reference list. An empty{}entry undervarbinds:violates the schema and is rejected by the plugin at profile load.Categories: closed set of 8.
categorymust be one ofstate_change,config_change,security,auth,license,mobility,diagnostic,unknown. Do not introduce new categories. Cross-cutting concerns (compliance scope, tenant, datacenter, change window…) belong inlabels:, not as new category slugs. Operator-authored OIDs default tounknownand the operator overrides category/severity/labels in plugin config — there is no separate "custom" category slug.Severities: closed set of 8, mapped to syslog PRIORITY.
severitymust be one ofemerg,alert,crit,err,warning,notice,info,debug(full names — notwarn). The plugin maps these toPRIORITY=0..7on the journal entry.emergis reserved for true vendor catastrophe; default towarning/notice/infofor routine events.debugis rare and only for traps the MIB itself marks as debug-level.Cardinality discipline on
labels:. Label templates must reference bounded-cardinality varbinds only. Reject (do not commit) labels that reference MAC addresses, source IPs, usernames, packet contents, RAID slot IDs, or any per-event identifier. High-cardinality content belongs indescription:(rendered into MESSAGE), indexedTRAP_VAR_*journal fields, andTRAP_JSON, not in metric-propagating labels.Label keys use a structurally-safe namespace. All labels (from profile
labels:AND operator configlabels:) emit asTRAP_TAG_<KEY_UPPERCASE>journal fields. The dedicatedTRAP_TAG_*namespace removes any risk of collision with the plugin-controlledTRAP_*field set (TRAP_OID,TRAP_NAME,TRAP_CATEGORY, etc. — see spec §11). The only remaining validation is the lowercase-key syntax rule ([a-z][a-z0-9_]*). Pick label keys that read clearly.Stock vs operator separation. Files under
src/go/plugin/go.d/config/go.d/snmp.trap-profiles/default/are stock vendor-curated profiles, regenerated bysrc/go/cmd/snmptrapprofilegen/and shipped from Netdata as/usr/libexec/netdata/plugins.d/snmp-trap-profile-gen. Do not hand-edit them for site-specific concerns; site overrides belong under/etc/netdata/go.d/snmp.trap-profiles/and are documented inprofile-format.md§ "Operator overrides".Profile metrics use the validated
metrics:/charts:schema. Trap profiles may define optional trap-to-metric rules only through the schema insrc/go/plugin/go.d/config/go.d/snmp.trap-profiles/profile-format.md. Listener jobs decide enablement withprofile_metrics. Do not add ad hoc metric fields, unbounded labels, or site-specific metric choices to stock profiles. Site-specific metric rules belong in operator profile files under/etc/netdata/go.d/snmp.trap-profiles/.Required profile-metric authoring checks:
- Rule types are only
counter,sample, andstate; use canonical fields for stock/generated profiles and keep compact aliases for operator-authored examples. where:predicates are ANDed and may useequals,in,exists,absent,greater_than,less_than,range, andnot; never combinenotwithexistsorabsent, and never define a predicate without a condition operator.samplerules may read only numeric varbind types documented inprofile-format.md;TimeTicksis converted to seconds beforescale.Counter32,Counter64, andTimeTicksare valid for sample rules, not resource identity keys.staterules use either separateproblem_trap/clear_trapOIDs or same-OIDstate.set_when/state.clear_whenpredicates.state.ttlmust be a valid Go duration string, andstate.ttl_behaviorcurrently supports onlyclear_and_expire.identity.resource.key_from_varbindMUST reference an integer-like bounded varbind (INTEGER,Integer32,Unsigned32, orGauge32). Never use strings, MACs, usernames, addresses, payloads, or event IDs as metric resource keys.- All rules sharing one chart MUST have the same label shape. Do not mix resource and non-resource rules in one chart, and do not mix multiple resource classes in one chart.
- Charts that can create source or resource instances MUST have bounded lifecycle settings. Expired series are removed; if the same identity appears again, the next committed trap creates a fresh series.
missing: unknown_dimensionis allowed only with resource identity.missing: dropincrements rule-miss diagnostics;missing: errorincrements extraction-failure diagnostics.- Metric names, chart IDs, and chart contexts MUST NOT collide with
built-in receiver charts, the built-in
profile_metric_diagnosticschart, or any other loaded profile rule/chart. Reserved metric prefixes includesnmp_trap_events_,snmp_trap_severity_,snmp_trap_errors_,snmp_trap_dedup_,snmp_trap_pipeline_,snmp_trap_source_,snmp_trap_sources_,snmp_trap_metric_, andsnmp_trap_profile_metrics_. Built-in source receiver metrics are automatic; profile rules should describe vendor or site semantics, not duplicate receiver pipeline health. auto_safe: truemeans the rule is safe for broad trap hubs: bounded labels, bounded resource identity, no sensitive values, and no surprising high cardinality. Stock rules need review evidence before enabling it.- Profile metrics update only after the trap is successfully committed to the configured journal and/or OTLP backend. Dedup-suppressed and write-failed traps do not update profile metrics.
- Rule types are only
No
journal_fields:list in profiles. The plugin derives indexedTRAP_VAR_*journal fields automatically from received non-sensitive, non-redundant event varbinds, and keeps the structured audit copy inTRAP_JSON. There is no profile knob to hand-author per-OID journal field names.display_hintis reserved, not yet emitted.profile-format.mddocumentsdisplay_hint(e.g.1x:for MAC,1d.1d.1d.1dfor IPv4) as a future varbind field. The extractor keeps display hints in intermediate JSONL whengomibexposes them, but the stock profile emitter does not writedisplay_hinttoday. Do not adddisplay_hintkeys by hand to stock profiles — they would be silently overwritten on regeneration. When the plugin's renderer needs display-hint formatting, the emitter and loader will be updated in the same regeneration cycle.
Required checks when editing the generator (src/go/cmd/snmptrapprofilegen/)
The shipped helper must remain a single Go binary. It is built by CMake as
snmp-trap-profile-gen, installed underusr/libexec/netdata/plugins.d/, and packaged in theplugin-gocomponent. Do not add Python, CGO, SQLite, or runtime MIB compiler dependencies to the shipped operator path.Extraction must remain incremental. The full corpus is too large to load as one global MIB universe. Keep the batch-based gomib loading path, deterministic source priority, duplicate-module
source-conflicts.json, OID-levelconflicts.json, and bounded memory validation. If source discovery changes, rerun at least a representative multi-vendor corpus before touching the stock pack.Classification cache stays reviewable text. The cache is deterministic JSONL keyed by the classifier input hash. Do not switch to SQLite or another opaque cache for committed or CI-reviewed state.
LLM output validation is mandatory. Model responses must validate against the JSON Schema and the semantic validators: closed category, closed severity, exact template helper allowlist, and uniform description style ending with
on {{hostname}}.. Retry invalid responses up to five total attempts before mechanical fallback, or hard failure under--require-llm.YAML emission is the producer of files in
default/. It must:- MIB-qualify every trap
name:as<MIB-MODULE>::<symbol>so the slug is globally unique; - dedup varbinds into the file-scoped table (do not regress to per-trap
inline varbinds — see netdata.md §7 and
profile-format.md); - drop varbind records with no resolvable
oid(extractor'sresolved: falsecases) from both the table and the per-trap reference list — never emit empty{}table entries; - drop internal pipeline metadata (
enrichment_source,enrichment_attempts) from the YAML output; - keep
catalogue.jsonin sync (operator grep-before-install tool); - emit deterministic output (sorted varbind names, traps sorted by OID then name) so regenerations produce reviewable diffs.
- MIB-qualify every trap
PEN registry handling must use the bundled snapshot by default. CMake installs
src/go/plugin/go.d/config/go.d/snmp.profiles/metadata/iana-enterprise-numbers.txtunderusr/lib/netdata/conf.d/go.d/snmp.profiles/metadata/.--refresh-penmay fetch the current IANA registry when explicitly requested.Regenerating the stock pack uses the Go helper:
cd src/go go run ./cmd/snmptrapprofilegen generate \ --source-dir /path/to/mibs \ --all \ --classify \ --require-llm \ --concurrency 20 \ --out-dir /tmp/snmp-trap-profile-gen-output \ --profiles-out-dir ../../src/go/plugin/go.d/config/go.d/snmp.trap-profiles/default \ --catalogue ../../src/go/plugin/go.d/config/go.d/snmp.trap-profiles/catalogue.jsonThe installed operator equivalent is:
/usr/libexec/netdata/plugins.d/snmp-trap-profile-gen generate \ --source-dir ./mibs \ --all \ --out-dir ./snmp-trap-profile-gen-outputThe shipped pack lives under
src/go/plugin/go.d/config/go.d/snmp.trap-profiles/default/. Operator output should be copied fromsnmp-trap-profile-gen-output/profiles/into/etc/netdata/go.d/snmp.trap-profiles/.
Required checks when changing categories or severities (taxonomy work)
These are closed sets enforced in three places that must stay in sync:
src/go/cmd/snmptrapprofilegen/main.go—validCategories,validSeverities,severityPriority, JSON Schema, and classifier prompt text.src/go/plugin/go.d/config/go.d/snmp.trap-profiles/profile-format.md— the operator-facing category and severity tables..agents/sow/specs/snmp-traps/netdata.md— §3 (category taxonomy) and §11 (PRIORITY mapping).
A taxonomy change without all three updates is incomplete and will be rejected at review. Any taxonomy change also requires a re-run of the Go helper's classification path against the full corpus (the existing classifications were done under the prior taxonomy and are now stale).
File size discipline
Stock profile YAMLs stay raw in the repository so changes are reviewable in
git diff. Installed/package stock vendor profiles MUST be compressed as
.yaml.zst; the runtime loader supports raw .yaml, compressed .yaml.zst,
and draft-era .yaml.gz compatibility. Operator/user profiles under
/etc/netdata/go.d/snmp.trap-profiles/ SHOULD stay uncompressed .yaml for
editability. If a single vendor file grows past ~10 MB in the repository,
revisit description verbosity rather than hiding unreviewable generated bloat
behind compression.