schema

package module
v0.0.0-...-db1d587 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 12, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

Documentation

Overview

Package schema is the intermediate representation the generators read.

It is one pinned answer to "what does this RouterOS have", assembled from artifacts that were each probed off a live device rather than read out of a manual — the manual has been wrong in both directions often enough that it is a cross-check here, never a source.

config/console-tree.json    hack/inspectdump   structure: menus, commands, arguments
config/arg-types.json       hack/typedump      types, as the router states them
config/name-uniqueness.json hack/uniqprobe     whether a name is enforced unique

The IR's job is to turn those into facts an emitter can act on, and — just as importantly — to say where it cannot. A generator that silently invents a field type or an identity key produces exactly the class of bug this repo keeps finding: a resource that reads Synced while being wrong.

Trusting a fact

Provenance is recorded where a generator has a decision to make, rather than stamped on every attribute where it would be uniform noise. Three signals carry it:

  • Menu.Typed is false when hack/typedump never reached the menu, so its fields carry structure but no types.
  • Field.Kind is KindUnknown when neither the console nor a returned value said anything about the field. It is not a guess to be filled in.
  • Field.Evidence is Observed where the type came from reading a value rather than from the console describing the field, which is what happens for a read-only property of a menu that holds no rows.
  • Identity.Verdict is Unprobed when uniqueness was never tested, which is a different claim from Untested — that one was tried and was inconclusive. Neither means "unique".

An emitter should refuse to generate identity from anything but Unique, and should refuse to type a field from KindUnknown.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Access

type Access string

Access is whether a field can be written.

const (
	Writable Access = "writable"
	ReadOnly Access = "read-only"
)

type Class

type Class string

Class is a menu's cardinality, and nothing else.

Cardinality and mutability are independent, and an earlier version of this type conflated them: it had a read-only class, which left no way to say that /ip/firewall/connection holds thousands of rows the device maintains and no caller may write. Whether a menu can be written is Menu.Writable.

const (
	// ClassOrdered holds rows whose order carries meaning — the menu
	// exposes move, and first-match-wins applies. Firewall chains.
	ClassOrdered Class = "ordered-list"
	// ClassList holds rows in no significant order.
	ClassList Class = "list"
	// ClassSingleton holds one implicit record. Such a menu has nothing for
	// `print where` to filter, which is how it can be told apart.
	ClassSingleton Class = "singleton"
)

type Evidence

type Evidence string

Evidence is how firmly a derived fact is held.

const (
	// Probed means a live router demonstrated it.
	Probed Evidence = "probed"
	// Observed means it was read off a value the router returned, rather
	// than declared. Weaker than Probed: it rests on one sample from one
	// device in one state, so a counter reading 0 is indistinguishable from
	// anything else reading 0.
	Observed Evidence = "observed"
	// Inferred means it follows from the console tree's shape alone, which
	// is weaker and has been wrong before.
	Inferred Evidence = "inferred"
)

type Field

type Field struct {
	Name   string   `json:"name"`
	Access Access   `json:"access"`
	Kind   Kind     `json:"kind"`
	Type   string   `json:"type,omitempty"`
	Values []string `json:"values,omitempty"`
	Ranges []string `json:"ranges,omitempty"`
	// Evidence is how the type was arrived at: Probed where the console
	// described the field, Observed where only its value was available.
	//
	// A generator may reasonably treat the two differently. An observed type
	// is a reading of one sample, so a field that happened to be 0, or empty,
	// or absent, is typed weakly or not at all.
	Evidence Evidence `json:"evidence,omitempty"`
	// Sample is the value an observed type was read from, so a reader can
	// disagree with the verdict.
	Sample string `json:"sample,omitempty"`
	// Bool is true when the vocabulary is exactly no/yes.
	//
	// Worth stating because the console and the wire disagree: completion
	// offers no/yes, while a REST read returns "true"/"false" — and a third
	// encoding, present-but-empty, means the flag is set. A generator that
	// takes the console vocabulary for the wire format gets all three wrong.
	Bool bool `json:"bool,omitempty"`
}

Field is one property of a menu.

type IR

type IR struct {
	RouterOSVersion string   `json:"routeros_version"`
	GeneratedBy     string   `json:"generated_by"`
	Sources         []Source `json:"sources"`
	Menus           []Menu   `json:"menus"`
}

IR is the whole device surface, as probed from one RouterOS version.

func Load

func Load() (*IR, error)

Load returns the pinned IR.

func (*IR) Census

func (ir *IR) Census() map[Class]int

Census counts menus by class. The numbers are a property of the device, so a change here means RouterOS moved, not that the IR drifted.

func (*IR) Menu

func (ir *IR) Menu(path string) (Menu, bool)

Menu returns the menu at path.

type Identity

type Identity struct {
	// Candidates are fields that could key a row, most preferred first.
	Candidates []string `json:"candidates,omitempty"`
	// Key is the candidate backed by a Unique verdict, or empty.
	Key     string  `json:"key,omitempty"`
	Verdict Verdict `json:"verdict"`
	// Tested is the verdict per field that was actually probed, keyed by field
	// name.
	//
	// A per-field record rather than one answer, because the negative results
	// are the load-bearing ones. Knowing that /ip/firewall/filter does not
	// enforce comment uniqueness is what tells a reconciler that addressing a
	// rule by comment is a convention it must enforce itself — and this
	// provider already addresses rules that way. One aggregate verdict per menu
	// cannot say that: it collapses "this field is a key" and "this field is
	// provably not one" into the same silence.
	Tested map[string]Verdict `json:"tested,omitempty"`
}

Identity is how a row in this menu can be addressed durably.

RouterOS's own .id is not durable: it is reassigned when a row is deleted and recreated, which an episodic applier tolerates and a controller reconciling forever does not.

func (Identity) ProvenDuplicate

func (i Identity) ProvenDuplicate(field string) bool

ProvenDuplicate reports that the router accepted two rows sharing field, so addressing a row by it can match more than one. This is a positive finding, not a missing one, and callers should treat it differently from silence.

func (Identity) Unique

func (i Identity) Unique(field string) bool

Unique reports whether the router was shown to enforce uniqueness on field.

type Kind

type Kind string

Kind is the shape of a field's value space, as the router described it.

const (
	// KindEnum is a closed set; Values is exhaustive.
	KindEnum Kind = "enum"
	// KindOpenEnum suggests values but accepts any literal.
	KindOpenEnum Kind = "open-enum"
	// KindScalar is freeform; Type and Ranges describe it.
	KindScalar Kind = "scalar"
	// KindUnknown is the router declining to say. Not a gap to fill in.
	KindUnknown Kind = "unknown"
)
type Menu struct {
	Path     string   `json:"path"`
	Class    Class    `json:"class"`
	Commands []string `json:"commands"`
	Identity Identity `json:"identity"`
	Fields   []Field  `json:"fields,omitempty"`
	// Typed is false when hack/typedump never reached this menu, so the
	// fields below are structure without types.
	Typed bool `json:"typed"`
	// Writable is whether the menu accepts add or set. It is independent of
	// Class: a menu may hold many rows and accept no writes at all.
	Writable bool `json:"writable"`
	// ClassEvidence distinguishes a class a router demonstrated from one
	// inferred off the command list.
	//
	// The distinction is load-bearing. The obvious inference — no add
	// command means no rows — is false: /interface, /interface/ethernet and
	// the read-only tables all hold rows without one. What settles it is
	// which command could enumerate the menu's properties, since `print
	// where` has nothing to filter on a menu that holds no rows and answers
	// with print's own arguments instead.
	ClassEvidence Evidence `json:"class_evidence"`
}

Menu is one addressable RouterOS menu.

Namespaces are not menus and do not appear: /ip and /system group other menus but expose no print or get of their own, so there is nothing to read.

func (m Menu) Rows() bool

Rows reports whether the menu holds rows that can be addressed individually, as opposed to one implicit record.

type Source

type Source struct {
	Artifact string `json:"artifact"`
	Producer string `json:"producer"`
	Version  string `json:"routeros_version,omitempty"`
	Platform string `json:"platform,omitempty"`
}

Source records one artifact the IR was assembled from.

Platform is the architecture the artifact was probed on, and it is not decoration: the menu tree differs by platform on the same RouterOS version. /system/routerboard exists on an arm64 CHR and not on an x86_64 one, which runs the other way for check-disk and ups. An IR that records only a version therefore claims more than it knows, and the discrepancy stays invisible until a caller reads a menu the tree never had.

Empty means the artifact predates the field rather than that the platform was uniform; the probes stamp it from /system/resource now.

type Verdict

type Verdict string

Verdict is what a uniqueness probe concluded.

const (
	// Unique means a second create with the same name was rejected.
	Unique Verdict = "UNIQUE"
	// Duplicate means it was accepted: the field cannot key a row.
	Duplicate Verdict = "DUPLICATE"
	// Untested means the probe ran and could not conclude — usually the
	// first create failed because CHR lacks the hardware.
	Untested Verdict = "UNTESTED"
	// Unprobed means no probe was ever attempted here. It is a distinct
	// claim from Untested, and neither is evidence of uniqueness.
	Unprobed Verdict = "UNPROBED"
	// Ambiguous means the second create was rejected, but the router's message
	// did not name the field being held constant, so the rejection may have
	// been about something else entirely.
	//
	// It is deliberately not folded into Unique. Reading a bare "already have
	// such tunnel" as proof of a constraint on comment was wrong on five menus
	// — /interface/eoip refuses a second row because the endpoints collide, and
	// ovpn-server because a protocol-port-vrf triple does. A probe cannot vary
	// a field that has only one legal value, so some rejections are genuinely
	// undecidable and saying so is the only honest option.
	Ambiguous Verdict = "AMBIGUOUS"
)

Directories

Path Synopsis
cmd
buildir command
buildir assembles the pinned IR from the probe artifacts.
buildir assembles the pinned IR from the probe artifacts.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL