Documentation
¶
Overview ¶
Package netdev is the network-device operations capability (NETDEV_SPEC): connection transport (internal/netdev/transport), device inventory, and the credential plumbing. P0 ships the transport layer and the security seams (profile tool seal, secret namespace); drivers, tools, and UI land in P1+.
State history (NETDEV_SPEC_V2 §状态历史): pre-mutation snapshots of the ops state files, modeled after the coding-mode checkpoints (Claude Code / ZCode alignment). Every state transition (proposal approve/execute/rollback, job lifecycle, cutover lifecycle, template saves, finding verdicts, inventory and config edits) snapshots the entity files it is about to overwrite; a suffix rewind then restores every file touched since a chosen event to its pre-event content.
Layer contract — restoring here reverts LOCAL RECORDS ONLY. Device-side rollback stays on the proposal/cutover path; the UI must say so. The audit log remains the immutable what-happened trail; state history is the restorable what-it-looked-like-before trail. Excluded by design: append-only journals (audit.jsonl, inspections, port events), telemetry (series, metrics.db), add-only libraries (backups, srvconf), and secrets.enc.json.
Index ¶
- Constants
- Variables
- func AckFindingByID(id string) error
- func AppendAudit(e Audit) error
- func AppendInspectionRow(row InspectionJournalRow) error
- func AppendPortEvent(ip string, port int, kind string)
- func ApplyExtraRead(cfg *config.Config)
- func AssessmentActive(nd config.NetDevConfig) error
- func AuditChainHead() string
- func AuditPath() string
- func CancelDiscoverRuns() int
- func CaseBundle(caseID string) (string, error)
- func CaseReport(c *IncidentCase) string
- func CasesDir() string
- func CleanupSeries()
- func ClearFindings() (int, error)
- func CloseProposalWatch(id string) error
- func CountPromotions() int
- func CtxStateActor(ctx context.Context, actor string) context.Context
- func DeleteCase(id string) error
- func DeleteDiscoveredHost(ip string) error
- func DeleteSecret(kind, name string) error
- func DeleteTemplate(id string) error
- func DeviceLayersPath() string
- func DiffBackups(device, idA, idB string) (string, error)
- func DiscoveredDir() string
- func DismissFinding(id string) error
- func EnsureNotifier(cfg *config.Config)
- func EnsureSyslogReceiver(cfg *config.Config)
- func EnsureTrapReceiver(cfg *config.Config)
- func ExtendScopesCandidates(existing, candidates []string) ([]string, error)
- func FalsePositiveFindingByID(id string) error
- func FindingsDir() string
- func FlapCount(device string, window time.Duration) int
- func FlushSyslogCounts()
- func GetBackupText(device, id string) (string, error)
- func GetSecret(kind, name string) (string, bool, error)
- func GoldenDir() string
- func HumanTTYKillAll() int
- func HumanTTYResize(deviceName string, cols, rows int) error
- func HumanTTYStop(deviceName string)
- func ImportCVEFeed(raw string) (int, error)
- func ImportStageFile(content string) (string, error)
- func InferDeviceRole(d config.NetDevDevice) (role, source string)
- func InitAuditAnchoring(cfg *config.Config)
- func JobPause(id string) error
- func ListConsolePorts() []string
- func LoadDeviceLayers() map[string]int
- func LogAllowedRoots(d config.NetDevDevice) []string
- func NeighborCommand(driverKey string) (string, bool)
- func NotifyConfigured() bool
- func NotifyPushText(kind, title, text string)
- func OpenFindingTallies() (critical, warning, info int)
- func P90IfDown(device string, window time.Duration) (int, bool)
- func PauseDiscoverRun(id string) bool
- func ProjectForDevices(cfg *config.Config, devices []string) string
- func ProposalsDir() string
- func ReadHumanTTYRecording(path string) (string, error)
- func RecordDeviceLayer(device string, layer int) error
- func RecordDiscovered(source string, hosts []DiscoverHostResult) error
- func RecordDiscoveredHTTP(ip string, port int, fp *HTTPFingerprint) error
- func RecordDiscoveredHints(source, ip, vendor, role string) error
- func RecordDiscoveredPorts(source, ip, hostname string, ports []DiscoveredPort) error
- func RecordDiscoveredSwept(source string, hosts []DiscoverHostResult, swept []int) error
- func RecordMetricPoint(device string, p MetricPoint) error
- func RecordPromotion(device, ip string)
- func RecordSeries(device, metric string, v float64)
- func Redact(s string) string
- func RedactCounted(s string) (string, int)
- func RegisterTools(reg *tool.Registry, cfg *config.Config)
- func RenderAttackPaths(r *AttackPathReport) string
- func ResetEscalationStateForTest()
- func ResolveFindingByID(id string) error
- func RoleFromName(name string) (role, source string)
- func RunningConfigCommand(driverKey string) (string, bool)
- func SanitizeForLive(s string) string
- func SaveCase(c *IncidentCase) error
- func SaveDiscoveryRun(r *DiscoveryRunState) error
- func SaveFinding(f *Finding) error
- func SaveLastBaseline(s BaselineSummary)
- func SaveProposal(p *Proposal) error
- func SaveRollingFinding(f *Finding) error
- func SaveScheduleStamp(st ScheduleStamp)
- func SaveTemplate(t *Template) error
- func SaveTopologyDesign(d *TopologyDesign) error
- func SecretHealth() (count int, lastChanged time.Time)
- func SecretKey(kind, name string) (string, error)
- func SeriesRead(device string, window time.Duration) map[string][]SeriesPoint
- func SetAuditPath(p string)
- func SetBackupsDir(p string)
- func SetFindingObserver(fn func(*Finding))
- func SetGoldenFromBackup(device, versionID string) error
- func SetHealthObserver(fn func(DeviceHealth))
- func SetNotifyPusher(p NotifyPusher)
- func SetSecret(kind, name, value string) error
- func SharedRemoteNode(cfg *config.Config) (*trustdomain.Node, error)
- func SrvConfDiff(idA, idB string) (string, error)
- func SrvConfText(id string) (string, error)
- func StampDiscoveredLayer(layer int, ips []string) error
- func StampDiscoveredVantage(vantage string, ips []string) error
- func StartEscalationWatcher()
- func StateEventDiff(id int) []diff.Change
- func StateEventSnap(kind, entity, actor string, absPaths ...string) int
- func SyslogReceiverStatus() (listening bool, port int, buffered int)
- func SyslogTail(device string, tailN int, grep string) []string
- func TrapReceiverStatus() (listening bool, port int, buffered int)
- func TrapTail(device string, tailN int) []string
- func TrustHostKey(fingerprint string) error
- func ValidateRestoreFrom(id string, steps []ProposalStep) error
- type AggregatedFinding
- type AttackPath
- type AttackPathReport
- type AttackPathStep
- type Audit
- type AuditChainStatus
- type AuditWindowStats
- type BackupVersion
- type BannerInfo
- type BaselineAggView
- type BaselineSummary
- type BaselineViolation
- type CVEEntry
- type CVEMatch
- type CaseEntry
- type CaseIOC
- type ChainEdge
- type ChainNode
- type CutSuggestion
- type CutoverBoard
- type CutoverBoardAudit
- type CutoverBoardDevice
- type CutoverBoardJob
- type CutoverBoardStep
- type CutoverGate
- type CutoverRun
- type CutoverStep
- type DeviceHealth
- type DiscoverHostResult
- type DiscoverPlan
- type DiscoverPortProbe
- type DiscoveredHost
- type DiscoveredPort
- type DiscoveryBoard
- type DiscoveryFunnelStep
- type DiscoveryLayerRow
- type DiscoveryRunState
- type Evidence
- type ExecResult
- type ExpectedStateView
- type ExposureBoard
- type ExposureMatrixRow
- type ExposurePoint
- type Finding
- type GoldenDrift
- type GoldenInfo
- type HTTPFingerprint
- type HealthSnapshot
- type HumanTTYRecording
- type HumanTTYState
- type IfBriefCounts
- type IfHealth
- type ImportDBSource
- type ImportDevice
- type ImportDeviceConflict
- type ImportPreview
- type ImportTopoPreview
- type IncidentCase
- type InspectionJournalRow
- type InvestigationChain
- type Job
- type JobBudget
- type JobStep
- type JobStepState
- type LiveDeviceState
- type LiveEvent
- type LiveSnapshot
- type LocateHit
- type LocateResult
- type LogFollowEvent
- type LogSearchHit
- type LogSearchResult
- type LogSourceProbe
- type Manager
- func (m *Manager) ApproveProposal(id string, confirm2 bool) (*Proposal, error)
- func (m *Manager) BackupDiffCurrent(ctx context.Context, device, id string) (string, error)
- func (m *Manager) BuildDiscoveryBoard() *DiscoveryBoard
- func (m *Manager) BuildExposureBoard() *ExposureBoard
- func (m *Manager) BuildInvestigationChain(caseID, findingID string, hours int) *InvestigationChain
- func (m *Manager) Cfg() *config.Config
- func (m *Manager) Close()
- func (m *Manager) CredentialInventory() string
- func (m *Manager) CutoverAbort(id string) (*CutoverRun, error)
- func (m *Manager) CutoverContinue(id string) (*CutoverRun, error)
- func (m *Manager) CutoverRollback(ctx context.Context, id string) (*CutoverRun, error)
- func (m *Manager) CutoverStart(def *CutoverRun) (*CutoverRun, error)
- func (m *Manager) DBQuery(ctx context.Context, sourceName, query string) (string, error)
- func (m *Manager) DeleteProposal(id string) error
- func (m *Manager) DiscoverLayer(ctx context.Context, vantage string, cidrs []string, ports []int) ([]DiscoverHostResult, error)
- func (m *Manager) DiscoverPrecheck(ctx context.Context, vantage string) (*DiscoverPlan, error)
- func (m *Manager) DiscoverResume(ctx context.Context) ([]DiscoverHostResult, error)
- func (m *Manager) DiscoverTCP(ctx context.Context, via, cidr string, ports []int) ([]DiscoverHostResult, error)
- func (m *Manager) DockerGet(ctx context.Context, deviceName, what, arg string, tailN int) (string, error)
- func (m *Manager) EnsureHealthPoller()
- func (m *Manager) Exec(ctx context.Context, deviceName, command string) ExecResult
- func (m *Manager) ExecuteProposal(ctx context.Context, id string) (*Proposal, error)
- func (m *Manager) ExpectedState() ExpectedStateView
- func (m *Manager) ExportState() (string, error)
- func (m *Manager) FirewallGet(ctx context.Context, deviceName, what string) (string, error)
- func (m *Manager) HandoffReport(since time.Time) string
- func (m *Manager) HealthSnapshot() HealthSnapshot
- func (m *Manager) HumanTTYStart(deviceName string, onData func(chunk string)) (*HumanTTYState, error)
- func (m *Manager) HumanTTYWrite(deviceName, input string) error
- func (m *Manager) ImportApply(path string, addNames, takeOvernames []string) (int, error)
- func (m *Manager) ImportPreview(path string) (*ImportPreview, error)
- func (m *Manager) JobResume(id string) (*Job, error)
- func (m *Manager) JobStart(def *Job) (*Job, error)
- func (m *Manager) KillAllConnections() int
- func (m *Manager) KubeGet(ctx context.Context, deviceName, what, namespace, name string, tailN int) (string, error)
- func (m *Manager) LiveState() LiveSnapshot
- func (m *Manager) Locate(ctx context.Context, target string) LocateResult
- func (m *Manager) LogFollow(deviceName, source string, onEvent func(LogFollowEvent)) error
- func (m *Manager) LogFollowActive(deviceName string) string
- func (m *Manager) LogFollowStop(deviceName string)
- func (m *Manager) LogRead(ctx context.Context, deviceName, source string, tailN int, since, grep string) ExecResult
- func (m *Manager) LogSearch(ctx context.Context, pattern string, devices, sources []string, since string) LogSearchResult
- func (m *Manager) MatchCVEs() ([]CVEMatch, error)
- func (m *Manager) MatchCVEsToFindings() (*Finding, error)
- func (m *Manager) NetconfRPC(ctx context.Context, deviceName, inner string) (string, error)
- func (m *Manager) NetprobeSweep(ctx context.Context, cidr string, doICMP bool) (*NetprobeSweepResult, error)
- func (m *Manager) NmapSweep(ctx context.Context, cidr string) (*NmapSweepResult, error)
- func (m *Manager) PollHealthOnce(ctx context.Context)
- func (m *Manager) ProbeLogSources(ctx context.Context, deviceName string) LogSourceProbe
- func (m *Manager) ProposalNeedsConfirm2(p *Proposal) bool
- func (m *Manager) RedfishGet(ctx context.Context, deviceName, path string) (string, error)
- func (m *Manager) RejectProposal(id, reason string) (*Proposal, error)
- func (m *Manager) RemoteWorkHandler() nettrans.WorkHandler
- func (m *Manager) RollbackProposal(ctx context.Context, id string) (*Proposal, error)
- func (m *Manager) RunBackup(ctx context.Context, device string) ([]BackupVersion, error)
- func (m *Manager) RunBaseline(ctx context.Context) (*Finding, error)
- func (m *Manager) RunBaselineFor(ctx context.Context, devices []string) (*Finding, error)
- func (m *Manager) RunGoldenCheck(ctx context.Context, device string) ([]GoldenDrift, error)
- func (m *Manager) RunInspection(ctx context.Context) (*Finding, error)
- func (m *Manager) RunJobSync(ctx context.Context, def *Job) (*Job, error)
- func (m *Manager) SFTPBrowse(ctx context.Context, deviceName, dirPath string) ([]string, error)
- func (m *Manager) SFTPDownload(ctx context.Context, deviceName, remotePath string) ([]byte, error)
- func (m *Manager) SetLiveObserver(fn func(LiveEvent))
- func (m *Manager) SnmpQuery(ctx context.Context, deviceName, oid, mode string) (string, error)
- func (m *Manager) SrvConfDrift(ctx context.Context, path string, devices []string) ([]SrvConfDriftRow, error)
- func (m *Manager) SrvConfSnapshot(ctx context.Context, deviceName, path string) (*SrvConfVersion, error)
- func (m *Manager) TemplateApply(t *Template, userVars map[string]string) (*Proposal, error)
- func (m *Manager) TemplateRender(t *Template, userVars map[string]string) ([]TemplatePreviewDevice, error)
- func (m *Manager) TestConnection(ctx context.Context, deviceName string) TestResult
- func (m *Manager) Timeline(device string, hours int) []TimelineEvent
- func (m *Manager) TopologySnapshot(ctx context.Context) (*TopologyGraph, error)
- func (m *Manager) Triage(ctx context.Context, deviceName string) TriageReport
- func (m *Manager) TurnBegin()
- func (m *Manager) ValidateProposal(p *Proposal) error
- func (m *Manager) WeakCredCheck(ctx context.Context, deviceName, tier, dictPath string) (WeakCredResult, error)
- func (m *Manager) WeeklyReport() string
- type MetricPoint
- type NetDevEvent
- type NetprobeHost
- type NetprobeSweepResult
- type NmapHost
- type NmapHostService
- type NmapSweepResult
- type NotifyPusher
- type PlanStep
- type PortEvent
- type PrecheckResult
- type ProbeFile
- type Proposal
- type ProposalStep
- type Result
- type SSHClient
- type ScheduleStamp
- type SeriesPoint
- type Session
- type SrvConfDriftRow
- type SrvConfVersion
- type StateEventMeta
- type StateLiveEntity
- type StateRestoreResult
- type SubnetClass
- type SyslogCountRow
- type SyslogStatusView
- type Template
- type TemplatePreviewDevice
- type TemplatePreviewStep
- type TemplateStep
- type TestResult
- type TimelineEvent
- type TopoImportStats
- type TopoReconcile
- type TopologyDesign
- type TopologyEdge
- type TopologyGraph
- type TopologyNode
- type TriSourceAgg
- type TriageReport
- type TriageSection
- type WeakCredResult
Constants ¶
const ( FindingActive = "active" FindingAck = "ack" FindingResolved = "resolved" FindingFalsePos = "false-positive" )
Finding lifecycle states (beyond "" for human/AI findings).
const ( WeakTierBasic = "basic" // fixed vendor-default/empty/username set WeakTierDict = "dictionary" // user-supplied dictionary file )
Weak-credential tiers.
const ( AuditOK = "ok" AuditDeviceError = "device-error" AuditRefused = "refused" AuditFailure = "failure" )
Audit statuses.
const ( CutoverRunning = "running" CutoverHold = "hold" // stopped at a decision point / failed gate / countdown spent CutoverDone = "done" CutoverFailed = "failed" CutoverAborted = "aborted" )
Cutover statuses.
const ( CutoverStepPending = "pending" CutoverStepRunning = "running" CutoverStepGating = "gating" CutoverStepDone = "done" CutoverStepFailed = "failed" CutoverStepSkipped = "skipped" CutoverStepRolled = "rolled-back" CutoverStepApproved = "approved" // a proposal step that finished its change )
Cutover step statuses.
const ( ChainKindEvent = "event" ChainKindAction = "action" ChainKindEvidence = "evidence" ChainKindConclusion = "conclusion" ChainKindRemediation = "remediation" ChainKindVerification = "verification" )
Chain kinds — the six semantic columns.
const ( SourceDiscover = "discover" SourceNmap = "nmap-import" SourceNetprobe = "netprobe" SourceTopo = "topo-neighbor" SourceLocateARP = "locate-arp" )
Discovery sources (spec §4.2.2): where a lead came from. layer-discover arrives with F4.
const ( SeverityInfo = "info" SeverityWarning = "warning" SeverityCritical = "critical" )
Severities.
const ( JobRunning = "running" JobPaused = "paused" JobDone = "done" JobFailed = "failed" // a step failed with on-fail=abort — later steps untouched JobAborted = "aborted" // human pressed stop )
Job statuses.
const ( JobStepPending = "pending" JobStepRunning = "running" JobStepOK = "ok" JobStepFailed = "failed" JobStepSkipped = "skipped" )
Step statuses.
const ( JobOnFailPause = "pause" // default — freeze for a human, resumable JobOnFailAbort = "abort" // mark the job failed, skip the rest JobOnFailContinue = "continue" // log and move on (fail streak still counts) )
OnFail policies.
const ( LiveConn = "conn" // connection/session state change LiveCmdStart = "cmd_start" // a command began (classified, about to run) LiveCmdOutput = "cmd_output" // incremental cleaned+redacted output text LiveCmdEnd = "cmd_end" // command finished (ok / device-error / failure) LiveCmdRefused = "cmd_refused" // a guardrail/classifier refusal (visible!) LiveTurnBegin = "turn" // a new user turn: per-turn budget counters reset )
Live event kinds.
const ( LiveConnConnected = "connected" LiveConnConnecting = "connecting" LiveConnReconnect = "reconnecting" LiveConnStopped = "stopped" LiveConnIdleClosed = "idle-closed" // reaper closed the idle session )
Connection states for LiveConn events.
const ( ProposalDraft = "draft" ProposalApproved = "approved" ProposalExecuting = "executing" ProposalDone = "done" ProposalPartial = "partial" // some steps applied, later ones skipped — frozen for a human ProposalFailed = "failed" // rollback attempted and failed — alert ProposalRejected = "rejected" // human vetoed (completion-spec §4.1): agent sees the reason next turn )
Proposal statuses.
const ( StepCLI = "cli" StepK8sApply = "k8s-apply" StepSQLMigration = "sql-migration" StepFileUpload = "file-upload" StepCertReplace = "cert-replace" StepRestoreVerify = "restore-verify" // §7.3 备份恢复演练 )
Structured step types (§7.1): the proposal step grew from a CLI command string into a discriminated union. Type "" behaves as cli everywhere — old proposals deserialize unchanged.
const ( ProposalWatching = "watching" ProposalClosed = "closed" )
Proposal is one change proposal.
const ( RoleRouter = "router" RoleSwitch = "switch" RoleFirewall = "firewall" RoleIPS = "ips" RoleVPN = "vpn" RoleBastion = "bastion" RoleServer = "server" RoleAP = "ap" RoleCloud = "cloud" RoleUnknown = "" )
Role values (NETDEV_IMPORT_AND_FINGERPRINT_SPEC §2.1). Keep in sync with the frontend TopoIcon set and the §2.3 word table below.
const ( RoleSourceConfig = "config" RoleSourceKind = "kind" RoleSourceGroup = "group" RoleSourceModel = "model" RoleSourceVendor = "vendor" RoleSourceNeighbor = "neighbor" RoleSourceLabel = "label" RoleSourceNone = "none" )
RoleSource records how a role was derived, coarse-grained for the UI's confidence hints: config > kind > group > model > vendor > neighbor/label (import/remote) > none.
const ( SecretKindPassword = "password" // device / hop login passwords SecretKindPassphrase = "passphrase" // private-key passphrases SecretKindSNMPAuth = "snmp-auth" // SNMPv3 auth passwords SecretKindSNMPPriv = "snmp-priv" // SNMPv3 privacy passwords SecretKindKubeconfig = "kubeconfig" // kind=k8s targets: the whole kubeconfig YAML SecretKindAPIToken = "api-token" // kind=firewall targets: vendor REST API tokens )
Secret kinds.
const ( StateActorUser = "user" // desktop bridge (a human clicked) StateActorAgent = "agent" // LLM tool call (netdev_propose, netdev_finding…) StateActorIM = "im" // bot gateway approval / ack StateActorSystem = "system" // runners, schedulers, watchers )
Actors for event attribution.
const ( StateEventPropose = "propose" StateEventApprove = "approve" StateEventReject = "reject" StateEventExecute = "execute" StateEventRollback = "rollback" StateEventDelete = "delete" StateEventCloseWatch = "close-watch" StateEventJobStart = "job-start" StateEventJobPause = "job-pause" StateEventJobResume = "job-resume" StateEventJobAbort = "job-abort" StateEventJobFreeze = "job-freeze" StateEventJobFinish = "job-finish" StateEventCutoverStart = "cutover-start" StateEventCutoverGo = "cutover-continue" StateEventCutoverBack = "cutover-rollback" StateEventCutoverAbort = "cutover-abort" StateEventCutoverHold = "cutover-hold" StateEventCutoverDone = "cutover-finish" StateEventTplSave = "template-save" StateEventTplDelete = "template-delete" StateEventTplApply = "template-apply" StateEventFindAck = "finding-ack" StateEventFindFP = "finding-false-positive" StateEventFindResolve = "finding-resolve" StateEventFindDismiss = "finding-dismiss" StateEventFindClear = "findings-clear" StateEventSettings = "settings-save" StateEventPromote = "hosts-promote" StateEventLeadDismiss = "lead-dismiss" StateEventImport = "import-apply" StateEventTopo = "topo-apply" StateEventGolden = "golden-set" StateEventCaseSave = "case-save" StateEventCaseDelete = "case-delete" // StateEventRestoreKeep marks the synthetic reverse event written just // before a restore; restoring back to it replays (redoes) the rewound // changes. StateEventRestoreKeep = "restore-keep" )
Event kinds — structured values stored per event and localized in the frontend via ndv.hist.kind.*.
const ( TestOK = "ok" TestUnknownHostKey = "unknown-host-key" TestAuthFailed = "auth-failed" TestError = "error" )
Test statuses.
const EscalationTimeout = 15 * time.Minute
EscalationTimeout: critical finding unhandled for this long → escalate.
const SecretNamespace = "netdev"
SecretNamespace is the key prefix under the secret store where every netdev credential lives: "netdev/<kind>/<name>" (e.g. "netdev/password/NETDEV_PWD_L1"). Grouping under one prefix lets sandbox deny rules, audits, and future export tools address device credentials as a set, separate from provider keys and cowork mail passwords.
const SourceLayer = "layer-discover"
SourceLayer is F4's store source constant.
Variables ¶
var HostKeyPrompt transport.HostKeyPrompt
HostKeyPrompt is the interactive TOFU hook. nil (default) = strict mode: unknown host keys are rejected. The desktop settings panel (P1 UI) injects a dialog-backed prompt here at startup.
Functions ¶
func AckFindingByID ¶
AckFindingByID marks one finding acknowledged (seen, being worked).
func AppendAudit ¶
AppendAudit writes one entry. Failures are returned (the caller logs); an audit failure must not silently vanish — but it also must not block the diagnostic hand (the command already ran; the audit gap is itself observable in ops review).
func AppendInspectionRow ¶
func AppendInspectionRow(row InspectionJournalRow) error
AppendInspectionRow appends one R1 line (best-effort; also kicks the once-a-day compaction check).
func AppendPortEvent ¶
AppendPortEvent appends one R2 line (best-effort, called from discovered.go).
func ApplyExtraRead ¶
ApplyExtraRead wires the [netdev.extra_read] vendor tables into the drivers' runtime read extensions — the B-1 knowledge-growth path: users teach the read table (via settings or the one-click refusal chip), never the model. boot's RegisterTools and the desktop settings bridge call it after every config change so the tables go live without a restart.
func AssessmentActive ¶
func AssessmentActive(nd config.NetDevConfig) error
AssessmentActive validates the engagement envelope. nil = active.
func AuditChainHead ¶
func AuditChainHead() string
AuditChainHead returns the local audit chain head ("" when nothing is chained yet) — the value cross-anchored to the trust domain (spec §八).
func AuditPath ¶
func AuditPath() string
AuditPath returns the audit file location (test override included).
func CancelDiscoverRuns ¶
func CancelDiscoverRuns() int
CancelDiscoverRuns cancels every in-flight discovery run; returns how many.
func CaseBundle ¶
CaseBundle writes the 复盘报告 (case + related findings + audit excerpt) into the state dir and returns the path — the FDE/蓝队交接件 v1.
func CaseReport ¶
func CaseReport(c *IncidentCase) string
CaseReport renders the case as a 复盘 markdown page.
func CleanupSeries ¶
func CleanupSeries()
CleanupSeries drops points older than the retention (called opportunistically on app start; a rewrite-in-place under the lock).
func ClearFindings ¶
ClearFindings deletes every persisted finding and returns how many — the queue's "clear all" (double-confirmed in the UI). Dev/test droppings are the main payload; real queues rebuild from the next inspection run.
func CloseProposalWatch ¶
CloseProposalWatch manually ends the watching period.
func CountPromotions ¶
func CountPromotions() int
CountPromotions returns the historical total of 待确认区→纳管 promotions.
func CtxStateActor ¶
CtxStateActor tags a context with the state-history actor for transitions invoked through it — the cutover runner executes proposals as "system" while the desktop/IM bridges keep the "user" default.
func DeleteDiscoveredHost ¶
DeleteDiscoveredHost removes one lead (dismissed by the user, or consumed by promotion).
func DeleteSecret ¶
DeleteSecret removes one netdev credential.
func DeviceLayersPath ¶
func DeviceLayersPath() string
DeviceLayersPath exposes the layer ledger's file location for the state-history snapshot (promotions rewrite it).
func DiffBackups ¶
DiffBackups returns the unified diff between two of a device's versions (a = old, b = new), reusing the repo's myers diff.
func DismissFinding ¶
DismissFinding deletes one finding by id — the findings queue's per-item ×. An unknown id is an error so the UI can tell "already gone" from "deleted".
func EnsureNotifier ¶
EnsureNotifier wires the outlets from config (idempotent; SharedManager calls it on every config load so settings changes go live without restart).
func EnsureSyslogReceiver ¶
EnsureSyslogReceiver starts (or stops on port 0 / config change) the UDP listener. Idempotent; called from the desktop bridge on settings load/save. Every call also refreshes the receiver's view of the inventory, so devices added after startup still map by address.
func EnsureTrapReceiver ¶
EnsureTrapReceiver starts (port > 0) or stops (port 0) the trap listener. Idempotent; called from the desktop bridge with every settings load.
func ExtendScopesCandidates ¶
ExtendScopesCandidates validates candidate CIDRs (from a precheck plan the user confirmed on the plan card) against the existing scopes and returns only those not already covered (PENLAB_CAPABILITY_GAPS P0-1). The caller persists the extension and audits it — the never-off scope guardrail is EXTENDED by an explicit human decision, never bypassed.
func FalsePositiveFindingByID ¶
FalsePositiveFindingByID marks one finding a false positive AND learns the Source key into the suppression table.
func FlapCount ¶
FlapCount counts reachability up↔down transitions within the window. The row limit assumes a poll interval ≥10s (window/10 rows covers the window even at the densest supported cadence).
func FlushSyslogCounts ¶
func FlushSyslogCounts()
FlushSyslogCounts appends pending buckets to the day file (best-effort, drains the buffer). Called by the receiver's 30s flusher and by tests.
func GetBackupText ¶
GetBackupText returns a version's redacted config text.
func GoldenDir ¶
func GoldenDir() string
GoldenDir stores one baseline per device (<name>.conf + <name>.meta).
func HumanTTYKillAll ¶
func HumanTTYKillAll() int
HumanTTYKillAll is the emergency-stop hook (§6.1 紧急停止同样杀人工终端).
func HumanTTYResize ¶
HumanTTYResize forwards the frontend terminal's size changes to the PTY.
func HumanTTYStop ¶
func HumanTTYStop(deviceName string)
HumanTTYStop closes the human terminal session (idempotent).
func ImportCVEFeed ¶
ImportCVEFeed validates and caches a user-supplied feed (JSON string). Accepts the simplified schema or a raw NVD JSON export (both converted to the simplified cache format). Returns the imported count.
func ImportStageFile ¶
ImportStageFile persists an in-browser-read export payload to the state dir and returns the staged path — Wails' file dialog gives the frontend the file CONTENT (not an OS path the Go side can reopen), so the wizard stages content first, then previews/applies by path.
func InferDeviceRole ¶
func InferDeviceRole(d config.NetDevDevice) (role, source string)
InferDeviceRole is the §2.3 priority chain for an inventory device: explicit config role > data-plane kind > group words > model/name words > vendor default > unknown. The neighbor-platform rung arrives with F2.
func InitAuditAnchoring ¶
InitAuditAnchoring arms the hook with the shared embedded node. Called wherever the trust-domain runtime starts (RegisterTools, the CLI daemon's executor branch); hosts without trustdomain never arm — the hook is a no-op costing one mutex trip per audit entry.
func JobPause ¶
JobPause asks a running job to freeze at the next boundary (between steps or retries). The in-flight attempt finishes — commands are short and read-only.
func ListConsolePorts ¶
func ListConsolePorts() []string
ListConsolePorts enumerates the machine's present serial ports (platform implementations in console_windows.go / console_other.go).
func LoadDeviceLayers ¶
LoadDeviceLayers returns device → recursion depth (nil map when none).
func LogAllowedRoots ¶
func LogAllowedRoots(d config.NetDevDevice) []string
LogAllowedRoots returns the device's log whitelist: /var/log (the classic system-log root, always allowed) plus the device's log_paths entries.
func NeighborCommand ¶
NeighborCommand returns the read command that yields neighbor information for the driver key (huawei-vrp → LLDP brief, cisco-ios → CDP detail).
func NotifyConfigured ¶
func NotifyConfigured() bool
NotifyConfigured reports whether at least one outlet is live (the test button's guard: clicking with nothing configured must explain, not no-op).
func NotifyPushText ¶
func NotifyPushText(kind, title, text string)
NotifyPushText pushes one outbound text (the daily briefing, future digests) through every configured outlet. The webhook variant carries it as JSON {kind, title, text}.
func OpenFindingTallies ¶
func OpenFindingTallies() (critical, warning, info int)
OpenFindingTallies counts open findings by severity (R1 rows carry the run-end tallies — the risk-score trend source). Status "resolved" is out.
func P90IfDown ¶
P90IfDown is the 90th percentile of ifDown over the window; ok=false when history is too thin to judge "usual" (callers treat that as no signal).
func PauseDiscoverRun ¶
PauseDiscoverRun cancels ONE run by id (the bridge's 暂停 button); false when the run already finished.
func ProjectForDevices ¶
ProjectForDevices maps a device list to the owning project by group membership: the project whose groups contain the most of the listed devices wins; ties go to config order. Ungrouped devices count as the "未分组" group (mirroring the frontend's inScope bucket). Pseudo-devices ("(all)", "(unknown)", "(cve-feed)") and empty lists match nothing → "".
func ProposalsDir ¶
func ProposalsDir() string
ProposalsDir stores one JSON per proposal under the netdev state dir.
func ReadHumanTTYRecording ¶
ReadHumanTTYRecording returns one recording's (already redacted) text. The path is restricted to the recordings dir — evidence files elsewhere are not readable through this surface.
func RecordDeviceLayer ¶
RecordDeviceLayer stamps one device's layer (idempotent).
func RecordDiscovered ¶
func RecordDiscovered(source string, hosts []DiscoverHostResult) error
RecordDiscovered merges one scan batch into the store (upsert per IP: sources dedupe, ports merge by number, hints only strengthen). Errors are returned but callers treat recording as best-effort — a store hiccup must never fail the discovery run itself. New ports fire R2 newly-opened events.
func RecordDiscoveredHTTP ¶
func RecordDiscoveredHTTP(ip string, port int, fp *HTTPFingerprint) error
RecordDiscoveredHTTP files F3's application fingerprint for one ip:port (upsert; the port row is created if the lead only ever had table sources).
func RecordDiscoveredHints ¶
RecordDiscoveredHints strengthens one lead's vendor/role hints (F2: SNMP sysDescr or neighbor platform). Existing hints win — hints never downgrade.
func RecordDiscoveredPorts ¶
func RecordDiscoveredPorts(source, ip, hostname string, ports []DiscoveredPort) error
RecordDiscoveredPorts records a lead whose ports came from a non-tunnel source (nmap import: service name instead of banner).
func RecordDiscoveredSwept ¶
func RecordDiscoveredSwept(source string, hosts []DiscoverHostResult, swept []int) error
RecordDiscoveredSwept is RecordDiscovered for a FULL port sweep: the caller probed exactly the swept list, so a stored port in that list that no longer answers is newly-closed (R2 event; the row drops). Hosts whose ports all closed never appear in results — their rows age out via LastSeen, a documented limitation (the partial-close signal is what matters).
func RecordMetricPoint ¶
func RecordMetricPoint(device string, p MetricPoint) error
RecordMetricPoint appends one poll rollup and trims the ring.
func RecordPromotion ¶
func RecordPromotion(device, ip string)
RecordPromotion appends one promotion line (best-effort).
func RecordSeries ¶
RecordSeries appends one point (best-effort; failures are silent — the timeline is a convenience layer, never a blocker).
func Redact ¶
Redact applies every pattern; later patterns see earlier replacements, so a line can only lose more, never regain, secret material.
func RedactCounted ¶
RedactCounted is Redact plus the number of substitutions it made. The count powers the transparency reminder appended to tool output ("N 处敏感字段已 脱敏") and the audit entry's redacted field — the user can see redaction happened without ever seeing what it hid.
func RegisterTools ¶
RegisterTools adds the netdev tool family to the registry. boot calls this ONLY inside the netdev profile branch, so dev/cowork sessions never see these tools (the reverse half of the hard seal; NETDEV_SPEC §7.1).
func RenderAttackPaths ¶
func RenderAttackPaths(r *AttackPathReport) string
RenderAttackPaths turns a report into the OpsResult-style text block the UI and the daily briefing both carry. 推演 watermark first line, always.
func ResetEscalationStateForTest ¶
func ResetEscalationStateForTest()
ResetEscalationStateForTest clears the once-map (tests only).
func ResolveFindingByID ¶
ResolveFindingByID manually resolves one finding (the 发现 card's button).
func RoleFromName ¶
RoleFromName classifies a node that exists only as a label — an unmanaged LLDP/CDP neighbor or an imported design shape. Weaker than the inventory chain (no vendor/config to lean on): keyword hit or unknown.
func RunningConfigCommand ¶
RunningConfigCommand returns the read command that dumps the running configuration for a driver key.
func SanitizeForLive ¶
SanitizeForLive is the exported seam used by tests and the desktop bridge to apply the same chunk cleanup the session tap applies.
func SaveDiscoveryRun ¶
func SaveDiscoveryRun(r *DiscoveryRunState) error
SaveDiscoveryRun persists the run state (idempotent overwrite).
func SaveFinding ¶
SaveFinding validates and persists one finding.
func SaveLastBaseline ¶
func SaveLastBaseline(s BaselineSummary)
SaveLastBaseline persists the latest baseline summary (best-effort).
func SaveProposal ¶
SaveProposal persists p (create or update).
func SaveRollingFinding ¶
SaveRollingFinding saves f as the SINGLE rolling entry for its Source: an existing finding with the same Source (any status) is updated in place (ID and original raise time preserved) instead of piling one copy per run. Used by the per-run info summaries (inspection/baseline) — their history lives in the inspection journal; the findings queue keeps one live card.
func SaveScheduleStamp ¶
func SaveScheduleStamp(st ScheduleStamp)
SaveScheduleStamp persists the latest stamp (best-effort, single file).
func SaveTemplate ¶
SaveTemplate persists t (create or update; ID assigned on create).
func SaveTopologyDesign ¶
func SaveTopologyDesign(d *TopologyDesign) error
SaveTopologyDesign persists the confirmed design snapshot (idempotent overwrite; audited by the bridge caller).
func SecretHealth ¶
GetSecret reads one netdev credential.
netdev credentials are ALWAYS fetched explicitly through this accessor by the transport layer inside a netdev session — they are never loaded into the process environment (secret.Store.LoadIntoEnv), which coding/office bash children would inherit. That is the data-plane half of the profile isolation (NETDEV_SPEC §7.2). SecretHealth reports the netdev-namespace credential count and the vault file's last-change time (age granularity is the whole store — per- entry timestamps would need a schema bump; honest, coarse, cheap).
func SecretKey ¶
SecretKey builds the namespaced store key for one credential. name is the config-declared env-style identifier (the password_env value), so the key is stable across hosts that share a credential name.
func SeriesRead ¶
func SeriesRead(device string, window time.Duration) map[string][]SeriesPoint
SeriesRead returns one device's points (all metrics) inside the window.
func SetAuditPath ¶
func SetAuditPath(p string)
SetAuditPath overrides the audit file location (tests). Switching files invalidates the cached chain head — chaining a fresh file off the previous file's head would break verification at its very first line.
func SetBackupsDir ¶
func SetBackupsDir(p string)
func SetFindingObserver ¶
func SetFindingObserver(fn func(*Finding))
SetFindingObserver installs the saved-finding callback (replaces any previous one). Same pattern as SetHealthObserver.
func SetGoldenFromBackup ¶
SetGoldenFromBackup copies one backup version's (already redacted) text as the device's baseline. Human-triggered from the 备份时间线.
func SetHealthObserver ¶
func SetHealthObserver(fn func(DeviceHealth))
func SetNotifyPusher ¶
func SetNotifyPusher(p NotifyPusher)
func SharedRemoteNode ¶
func SharedRemoteNode(cfg *config.Config) (*trustdomain.Node, error)
SharedRemoteNode lazily opens this host's embedded trust-domain node: identity + persisted ledger + peers from trustdomain.bootstrap_peers. One instance per process (the CLI daemon and agent tools must share it).
func SrvConfDiff ¶
SrvConfDiff returns the unified diff between two snapshots (a = old).
func SrvConfText ¶
SrvConfText returns one snapshot's content.
func StampDiscoveredLayer ¶
StampDiscoveredLayer stamps the recursion depth on a batch of leads.
func StampDiscoveredVantage ¶
StampDiscoveredVantage marks which vantage produced a batch of leads (best-effort provenance stamp after RecordDiscovered).
func StartEscalationWatcher ¶
func StartEscalationWatcher()
StartEscalationWatcher launches the escalation sweep goroutine once per process. Sweep cadence is half the timeout so the worst-case detection lag is timeout + half — acceptable for the minimal version.
func StateEventDiff ¶
StateEventDiff renders what a rewind to before this event would revert, reusing the checkpoint store's preview (old = current on-disk, new = snapshotted pre-event).
func StateEventSnap ¶
StateEventSnap records one restorable event: the current (pre-mutation) content of the given absolute paths. Call it immediately before the mutation, co-located with the transition's audit entry. Best-effort — a snapshot failure logs and never blocks the mutation itself. Returns the event id, or -1 when nothing was snapshotted.
func SyslogReceiverStatus ¶
SyslogReceiverStatus reports the listener state for the UI.
func SyslogTail ¶
SyslogTail reads a device's ring buffer (newest last), optional grep filter (regex, literal fallback), bounded to tailN.
func TrapReceiverStatus ¶
TrapReceiverStatus reports the listener state for the UI.
func TrustHostKey ¶
TrustHostKey durably trusts a first-seen key the human confirmed. The fingerprint must match one captured by a recent TestConnection — trusting an arbitrary fingerprint without the presented key would record nothing real.
func ValidateRestoreFrom ¶
func ValidateRestoreFrom(id string, steps []ProposalStep) error
ValidateRestoreFrom checks a restore proposal's source version (备份→恢复 闭环): the id must exist in the vault, belong to its own device prefix, and that device must carry a step in the proposal — a restore proposal touches the device the version came from.
Types ¶
type AggregatedFinding ¶
type AggregatedFinding struct {
Key string `json:"key"` // Source or title
Count int `json:"count"`
Open int `json:"open"` // active+ack
Severity string `json:"severity"` // highest among members
Devices []string `json:"devices"`
Title string `json:"title"` // representative title
Newest time.Time `json:"newest"`
Members []*Finding `json:"members,omitempty"` // newest first, capped
Suppressed int `json:"suppressed"` // ≥2 = 此键已被误报学习降级
}
AggregatedFinding is the queue's collapsed view: one row per 根因键 (Source;人工/AI Finding 用 Title), with the member findings hidden inside.
func AggregateFindings ¶
func AggregateFindings() []AggregatedFinding
AggregateFindings collapses the finding list by 根因键 (§4.10 同类聚合).
type AttackPath ¶
type AttackPath struct {
ExposureDevice string `json:"exposure_device"`
Reason string `json:"reason"` // the finding that marks the exposure
FindingID string `json:"finding_id,omitempty"`
Steps []AttackPathStep `json:"steps"`
EndDevice string `json:"end_device"`
EndRole string `json:"end_role,omitempty"`
EndManaged bool `json:"end_managed"`
Hops int `json:"hops"`
Score int `json:"score"`
}
AttackPath is one simulated route from an exposure point to a reachable asset.
type AttackPathReport ¶
type AttackPathReport struct {
GeneratedAt string `json:"generated_at"`
Simulated bool `json:"simulated"` // constant true — 推演, not 实测
EdgeSources []string `json:"edge_sources"`
Nodes int `json:"nodes"`
Edges int `json:"edges"`
ExposurePoints []ExposurePoint `json:"exposure_points"`
Paths []AttackPath `json:"paths"`
Cuts []CutSuggestion `json:"cut_suggestions"`
}
AttackPathReport is the whole simulation output.
func BuildAttackPaths ¶
func BuildAttackPaths(g TopologyGraph, findings []*Finding) *AttackPathReport
BuildAttackPaths runs the simulation. Pure function: graph + findings in, report out. Callers assemble the graph from zero-session sources (IP-plan inference, design imports, persisted snapshots) — the sim itself connects to nothing.
type AttackPathStep ¶
type AttackPathStep struct {
From string `json:"from"`
To string `json:"to"`
Via string `json:"via"` // edge source: bastion | design | lldp | cdp
}
AttackPathStep is one hop with the edge that supports it.
type Audit ¶
type Audit struct {
Time time.Time `json:"time"`
Device string `json:"device"`
Via []string `json:"via,omitempty"`
Command string `json:"command"`
Class string `json:"class"` // read | write | dangerous | unknown (classifier verdict)
Status string `json:"status"` // ok | device-error | refused | failure
OutputBytes int `json:"output_bytes,omitempty"`
Error string `json:"error,omitempty"`
// Hash chains entries (B-batch): sha256 over the previous entry's hash and
// this entry's canonical JSON (hash field excluded). Any tampering with an
// old line — or a line removed — breaks every later hash. Entries written
// before the chain landed have empty hashes; the chain starts at the first
// hashed entry.
Hash string `json:"hash,omitempty"`
}
Audit records every device interaction in an append-only JSONL file under <user config>/fairpeer/netdev/audit.jsonl — one of the never-off guardrails (NETDEV_SPEC invariant 4). Command text and outcome are recorded; raw output is deliberately NOT stored (device output can carry secrets — the redactor and evidence integration land in P2; until then only the size and a hash are kept).
type AuditChainStatus ¶
type AuditChainStatus struct {
Total int `json:"total"`
Chained int `json:"chained"`
OK bool `json:"ok"`
FirstBroken string `json:"firstBroken,omitempty"`
}
AuditChainStatus is the verification verdict for the audit tab's badge.
func VerifyAuditChain ¶
func VerifyAuditChain() AuditChainStatus
VerifyAuditChain re-computes every hashed entry's hash in sequence. Legacy entries without hashes are skipped (the chain starts at the first hashed entry); a mismatch marks the chain broken from that entry on.
type AuditWindowStats ¶
type AuditWindowStats struct {
ByClass map[string]int
Read24h int
Write24h int // write + proposal-write
Guardrail24h int
ProposalWrites int
ProposalRollback int
Entries int // window size (honest denominator for the mix)
}
AuditWindowStats is the aggregate the overview stats bar and the cutover board read. Read24h/Write24h/Guardrail24h mirror the briefing counters.
func AuditWindow ¶
func AuditWindow(days int) *AuditWindowStats
AuditWindow aggregates the audit chain inside the window (days, default 30, cap 90). Cached by (file size, mtime) — the chain is append-only, so the cache key is a single comparison.
type BackupVersion ¶
type BackupVersion struct {
ID string `json:"id"` // <device>@<unixnano>
Device string `json:"device"`
At string `json:"at"` // 01-02 15:04:05
Bytes int `json:"bytes"` // redacted text length
Lines int `json:"lines"`
}
func ListBackups ¶
func ListBackups(device string) []BackupVersion
ListBackups returns a device's versions, newest first ("" = every device).
type BannerInfo ¶
type BannerInfo struct {
// Kind: ssh | http | ftp | smtp | "" (other/unparsed).
Kind string `json:"kind,omitempty"`
// Product is the software token from the banner ("Huawei-1.0",
// "OpenSSH_9.6", "nginx/1.24.0", "vsftpd 3.0.3").
Product string `json:"product,omitempty"`
// Version is the product's version when it splits cleanly.
Version string `json:"version,omitempty"`
// VendorHint: huawei | cisco | zte | hillstone | vmware | linux | "".
VendorHint string `json:"vendor_hint,omitempty"`
// RoleHint is a topology-role suggestion; only set when the banner
// strongly implies a class (VMware → server). Network-vendor SSH banners
// deliberately stay empty — Huawei ships the same SSH line on switches,
// routers and firewalls, and a wrong icon is worse than none.
RoleHint string `json:"role_hint,omitempty"`
}
BannerInfo is the parsed form of one port banner.
func ParseBanner ¶
func ParseBanner(raw string) BannerInfo
ParseBanner classifies a captured banner. Empty/unprintable input returns the zero value — the caller treats absence of hints as absence of data, never as a negative result.
type BaselineAggView ¶
type BaselineAggView struct {
Devices int `json:"devices"`
Checked int `json:"checked"`
Rules int `json:"rules"`
Hits int `json:"hits"`
At string `json:"at"`
}
BaselineAggView is the JSON-persisted last-run summary.
func LoadLastBaseline ¶
func LoadLastBaseline() *BaselineAggView
LoadLastBaseline reads it back; nil when never run (引导态,不是 0).
type BaselineSummary ¶
type BaselineSummary struct {
Devices int `json:"devices"`
Checked int `json:"checked"` // devices whose config was actually read
Rules int `json:"rules"` // applicable rule count across devices
Hits int `json:"hits"` // total violations
At string `json:"at"`
}
BaselineSummary is the aggregate the UI and the agent tool see.
type BaselineViolation ¶
type BaselineViolation struct {
Rule string `json:"rule"`
Title string `json:"title"`
Severity string `json:"severity"`
Suggestion string `json:"suggestion,omitempty"`
Evidence []string `json:"evidence"`
}
BaselineViolation is one rule hit on one device.
func CheckBaseline ¶
func CheckBaseline(driverKey, config string) []BaselineViolation
CheckBaseline runs the rule battery over one config text and returns the violated rules with their evidence lines (already-redacted text). Pure function — unit-testable without any device.
type CVEEntry ¶
type CVEEntry struct {
ID string `json:"id"` // CVE-2024-xxxx
Desc string `json:"desc"`
Products []string `json:"products"` // lowercase vendor/product substrings
Severity string `json:"severity"` // critical | high | medium | low
}
CVEEntry is one simplified feed item.
type CVEMatch ¶
type CVEMatch struct {
Device string `json:"device"`
CVEID string `json:"cve_id"`
Desc string `json:"desc"`
Severity string `json:"severity"`
Product string `json:"product"` // matched product substring
}
CVEMatch is one device ↔ CVE hit.
type CaseEntry ¶
type CaseEntry struct {
Time time.Time `json:"time"`
Kind string `json:"kind"` // finding | log | audit | triage | note
Device string `json:"device,omitempty"`
Text string `json:"text"`
Ref string `json:"ref,omitempty"` // e.g. finding id
}
CaseEntry is one reference card pinned on the case timeline.
type CaseIOC ¶
type CaseIOC struct {
Value string `json:"value"`
Type string `json:"type"` // ip | hash | domain | keyword
Note string `json:"note,omitempty"`
AddedAt time.Time `json:"added_at"`
}
CaseIOC is one indicator in the case ledger.
type ChainEdge ¶
type ChainEdge struct {
From string `json:"from"`
To string `json:"to"`
Label string `json:"label"`
}
ChainEdge is one typed relation (触发/执行/产生/证实/处置/验证/闭环).
type ChainNode ¶
type ChainNode struct {
ID string `json:"id"`
Kind string `json:"kind"`
Label string `json:"label"`
Device string `json:"device,omitempty"`
At string `json:"at,omitempty"`
// RefType/RefID deep-link the card to its source row (finding/audit/
// proposal/case-entry); the dash never carries the full text.
RefType string `json:"ref_type,omitempty"`
RefID string `json:"ref_id,omitempty"`
Status string `json:"status,omitempty"` // finding severity | proposal status
// Group>0 marks a collapsed sibling group ("N× 命令采集").
Group int `json:"group,omitempty"`
}
ChainNode is one card on the investigation chain (title-level only).
type CutSuggestion ¶
type CutSuggestion struct {
From string `json:"from"`
To string `json:"to"`
Via string `json:"via"`
PathsRemoved int `json:"paths_removed"`
}
CutSuggestion ranks edges by how many simulated paths they carry — cutting the top edges removes the most reach (a prioritization hint for proposals, never an executed change).
type CutoverBoard ¶
type CutoverBoard struct {
ID string `json:"id"`
Name string `json:"name"`
Status string `json:"status"`
Deadline string `json:"deadline,omitempty"`
RemainingSec int64 `json:"remaining_sec"` // >0 running; <=0 window over
Frozen int `json:"frozen"` // affected device count
Steps []CutoverBoardStep `json:"steps"`
Devices []CutoverBoardDevice `json:"devices"`
RollbackReady bool `json:"rollback_ready"`
RollbackNote string `json:"rollback_note,omitempty"`
Jobs []CutoverBoardJob `json:"jobs"`
Audit []CutoverBoardAudit `json:"audit"`
Report string `json:"report,omitempty"`
HasActive bool `json:"has_active"` // any running/hold cutover exists
Found bool `json:"found"` // false = no cutover at all → empty state
}
CutoverBoard is the whole cutover screen payload.
func BuildCutoverBoard ¶
func BuildCutoverBoard(id string) *CutoverBoard
BuildCutoverBoard assembles the cutover screen. id=="" picks the active cutover, else the newest one (终态复盘).
type CutoverBoardAudit ¶
type CutoverBoardAudit struct {
Time string `json:"time"`
Device string `json:"device"`
Command string `json:"command"`
Status string `json:"status"`
}
CutoverBoardAudit is one command-stream row for this cutover.
type CutoverBoardDevice ¶
type CutoverBoardDevice struct {
Device string `json:"device"`
Status string `json:"status"` // done | running | pending | mixed
RollbackReady bool `json:"rollback_ready"`
}
CutoverBoardDevice is one affected device's own progress + rollback point.
type CutoverBoardJob ¶
type CutoverBoardJob struct {
ID string `json:"id"`
Name string `json:"name"`
Status string `json:"status"`
ActiveMS int64 `json:"active_ms"`
Commands int `json:"commands"`
MaxWallSec int `json:"max_wall_sec,omitempty"`
MaxCommands int `json:"max_commands,omitempty"`
}
CutoverBoardJob is a live job row (budget burn view; jobs are fleet-wide, not tied to the cutover — labeled as such in the UI).
type CutoverBoardStep ¶
type CutoverBoardStep struct {
Label string `json:"label"`
Status string `json:"status"`
Device string `json:"device,omitempty"`
ProposalID string `json:"proposal_id,omitempty"`
Gate bool `json:"gate,omitempty"`
Decision bool `json:"decision_point,omitempty"`
EstSec int `json:"est_sec,omitempty"`
StartedAt string `json:"started_at,omitempty"`
EndedAt string `json:"ended_at,omitempty"`
}
CutoverBoardStep is one pipeline node (proposal steps + gates + reads).
type CutoverGate ¶
type CutoverGate struct {
Device string `json:"device"`
Command string `json:"command"`
Expect string `json:"expect"`
SustainSec int `json:"sustain_sec,omitempty"` // default 30
TimeoutSec int `json:"timeout_sec,omitempty"` // default 2×sustain+90
}
CutoverGate is a semantic verification gate: Command's output must match Expect CONTINUOUSLY for SustainSec (e.g. "OSPF 邻居 Full 且持续 60s").
type CutoverRun ¶
type CutoverRun struct {
ID string `json:"id"`
Name string `json:"name"`
Deadline time.Time `json:"deadline"` // 总倒计时(割接窗口结束)
Steps []CutoverStep `json:"steps"`
Status string `json:"status"`
HoldNote string `json:"hold_note,omitempty"`
Cursor int `json:"cursor"`
// 割接前后基线快照(device → backup id)+ 对比报告。
PreSnapshot map[string]string `json:"pre_snapshot,omitempty"`
PostSnapshot map[string]string `json:"post_snapshot,omitempty"`
Report string `json:"report,omitempty"`
CreatedAt time.Time `json:"created_at"`
StartedAt *time.Time `json:"started_at,omitempty"`
EndedAt *time.Time `json:"ended_at,omitempty"`
}
CutoverRun is one cutover execution.
func ListCutovers ¶
func ListCutovers() ([]*CutoverRun, error)
ListCutovers returns runs newest-first.
type CutoverStep ¶
type CutoverStep struct {
Label string `json:"label"`
EstSec int `json:"est_sec,omitempty"` // 预计耗时(倒计时排程显示)
ProposalID string `json:"proposal_id,omitempty"` // execute an APPROVED proposal
Device string `json:"device,omitempty"` // …or one sealed read command
Command string `json:"command,omitempty"`
Gate *CutoverGate `json:"gate,omitempty"` // post-step verification gate
DecisionPoint bool `json:"decision_point,omitempty"` // 回退决策点:完成后 hold 等人
Impact string `json:"impact,omitempty"` // 影响描述(决策点并列展示)
Status string `json:"status"`
StartedAt *time.Time `json:"started_at,omitempty"`
EndedAt *time.Time `json:"ended_at,omitempty"`
Output string `json:"output,omitempty"`
Error string `json:"error,omitempty"`
}
CutoverStep is one runbook entry.
type DeviceHealth ¶
type DeviceHealth struct {
Device string `json:"device"`
Time time.Time `json:"time"`
Reachable bool `json:"reachable"`
UptimeSec int64 `json:"uptimeSec"`
Interfaces []IfHealth `json:"interfaces"`
LastError string `json:"lastError,omitempty"`
// 水位(SNMP OID 扩展):0 = 未采集到(厂商 OID 不适用或设备不答)。
CpuPct int `json:"cpuPct,omitempty"`
MemPct int `json:"memPct,omitempty"`
// 接口 octets 计数器累计和(读取端差分成 bps;0 = 未采集)。
InOct uint64 `json:"inOct,omitempty"`
OutOct uint64 `json:"outOct,omitempty"`
}
DeviceHealth is one device's latest poll result.
func (DeviceHealth) IfDown ¶
func (h DeviceHealth) IfDown() int
func (DeviceHealth) IfUp ¶
func (h DeviceHealth) IfUp() int
UpCount/DownCount helpers for the UI (oper-down among admin-up = real alarms).
type DiscoverHostResult ¶
type DiscoverHostResult struct {
IP string `json:"ip"`
Ports []DiscoverPortProbe `json:"ports"`
}
DiscoverHostResult is one live host.
type DiscoverPlan ¶
type DiscoverPlan struct {
Vantage string `json:"vantage"`
Steps []PlanStep `json:"steps"`
ArpKnown int `json:"arp_known"`
Warnings []string `json:"warnings"`
}
DiscoverPlan is the confirm-before-probe object (spec §4.5 precheck flow).
type DiscoverPortProbe ¶
type DiscoverPortProbe struct {
Port int `json:"port"`
Open bool `json:"open"`
Banner string `json:"banner,omitempty"` // first bytes, e.g. "SSH-2.0-..."
}
DiscoverPortProbe is one port's outcome on one host.
type DiscoveredHost ¶
type DiscoveredHost struct {
IP string `json:"ip"`
Hostname string `json:"hostname,omitempty"`
VendorHint string `json:"vendor_hint,omitempty"`
RoleHint string `json:"role_hint,omitempty"`
FirstSeen time.Time `json:"first_seen"`
LastSeen time.Time `json:"last_seen"`
Sources []string `json:"sources"`
Ports []DiscoveredPort `json:"ports"`
// Vantage records which managed device saw this lead (F4 provenance —
// the human-driven recursion's audit trail); Layer is the recursion depth
// it was found at (inventory vantage → 1).
Vantage string `json:"vantage,omitempty"`
Layer int `json:"layer,omitempty"`
}
DiscoveredHost is one unmanaged asset lead.
func ListDiscoveredHosts ¶
func ListDiscoveredHosts() ([]*DiscoveredHost, error)
ListDiscoveredHosts returns leads newest-first (LastSeen).
type DiscoveredPort ¶
type DiscoveredPort struct {
Port int `json:"port"`
Banner string `json:"banner,omitempty"`
Parsed BannerInfo `json:"parsed,omitempty"`
// HTTP carries F3's opt-in application fingerprint (title/Server/cert)
// when http_probe is enabled and the port answered one standard GET.
HTTP *HTTPFingerprint `json:"http,omitempty"`
At time.Time `json:"at"`
// FirstSeen stamps the port's first observation (R2 journal basis:
// newly-opened/newly-closed events are the diff between sweeps).
FirstSeen time.Time `json:"first_seen,omitempty"`
}
DiscoveredPort is one port observation on one host.
type DiscoveryBoard ¶
type DiscoveryBoard struct {
Leads int `json:"leads"`
Fingerprinted int `json:"fingerprinted"`
Pending int `json:"pending"`
Promoted int `json:"promoted"`
Managed int `json:"managed"`
SubnetsDone int `json:"subnets_done"`
SubnetsTotal int `json:"subnets_total"`
LayerDepth int `json:"layer_depth"`
MaxHops int `json:"max_hops"`
RunStatus string `json:"run_status,omitempty"`
RunVantage string `json:"run_vantage,omitempty"`
RunUpdatedAt string `json:"run_updated_at,omitempty"`
Funnel []DiscoveryFunnelStep `json:"funnel"`
Layers []DiscoveryLayerRow `json:"layers"`
TriSource TriSourceAgg `json:"tri_source"`
PortEvents []PortEvent `json:"port_events"`
}
DiscoveryBoard is the whole discovery screen payload.
type DiscoveryFunnelStep ¶
DiscoveryFunnelStep is one funnel stage (Key: leads|fingerprinted|pending| promoted|managed — labels live in the frontend i18n).
type DiscoveryLayerRow ¶
type DiscoveryLayerRow struct {
Layer int `json:"layer"`
Label string `json:"label"`
Note string `json:"note,omitempty"`
}
DiscoveryLayerRow is one layer-ledger summary row.
type DiscoveryRunState ¶
type DiscoveryRunState struct {
ID string `json:"id"`
Vantage string `json:"vantage"`
Ports []int `json:"ports,omitempty"`
Cidrs []string `json:"cidrs"`
DoneCidrs []string `json:"done_cidrs"`
// Status: running | paused | done.
Status string `json:"status"`
StartedAt string `json:"started_at"`
UpdatedAt string `json:"updated_at"`
FoundSoFar int `json:"found_so_far"`
}
DiscoveryRunState is the persisted progress of one layered scan.
func LoadDiscoveryRun ¶
func LoadDiscoveryRun() (*DiscoveryRunState, error)
LoadDiscoveryRun returns the current run (nil when none/corrupt).
func (*DiscoveryRunState) Remaining ¶
func (r *DiscoveryRunState) Remaining() []string
Remaining returns the not-yet-probed nets in plan order.
type Evidence ¶
type Evidence struct {
Device string `json:"device"`
Command string `json:"command"`
Output string `json:"output"` // redacted excerpt
}
Evidence is one command-output pair backing a finding.
type ExecResult ¶
type ExecResult struct {
Device string `json:"device"`
Command string `json:"command"`
Class string `json:"class"`
Output string `json:"output"`
IsError bool `json:"is_error"`
Refused bool `json:"refused,omitempty"`
Refusal string `json:"refusal,omitempty"`
}
ExecResult is the tool-facing outcome of one command.
type ExpectedStateView ¶
type ExpectedStateView struct {
Total int `json:"total"` // 有采集面的设备数
Reachable int `json:"reachable"`
Missing []DeviceHealth `json:"missing"` // 期望在线但不可达
NoProbe []string `json:"noProbe"` // 清单里暂无健康采集面的设备
}
ExpectedStateView — 期望状态对比(§5.4 第三件):清单声明 vs 健康采集。
type ExposureBoard ¶
type ExposureBoard struct {
Simulated bool `json:"simulated"` // constant true — 推演角标
GeneratedAt string `json:"generated_at"`
Critical int `json:"critical"`
Warning int `json:"warning"`
Paths []AttackPath `json:"paths"` // top 50 by score
Cuts []CutSuggestion `json:"cut_suggestions"` // top 10
ExposurePoints []ExposurePoint `json:"exposure_points"`
Matrix []ExposureMatrixRow `json:"matrix"`
CVEBySeverity map[string]int `json:"cve_by_severity,omitempty"`
CVENeedsFeed bool `json:"cve_needs_feed"`
UnmanagedEnds int `json:"unmanaged_ends"`
MaxHops int `json:"max_hops"`
}
ExposureBoard is the whole exposure screen payload.
type ExposureMatrixRow ¶
type ExposureMatrixRow struct {
Device string `json:"device"`
Critical int `json:"critical"`
Warning int `json:"warning"`
Info int `json:"info"`
CveCritical int `json:"cve_critical"`
CveHigh int `json:"cve_high"`
Managed bool `json:"managed"`
}
ExposureMatrixRow is one device's exposure tally.
type ExposurePoint ¶
type ExposurePoint struct {
Device string `json:"device"`
FindingID string `json:"finding_id,omitempty"`
Reason string `json:"reason"`
Paths int `json:"paths"`
}
ExposurePoint is one 起点 with its simulated blast radius.
type Finding ¶
type Finding struct {
ID string `json:"id"`
Title string `json:"title"`
Severity string `json:"severity"`
Devices []string `json:"devices"`
Detail string `json:"detail"`
Evidence []Evidence `json:"evidence"`
Suggestion string `json:"suggestion,omitempty"` // what change to draft (netdev_propose), if any
CreatedAt time.Time `json:"created_at"`
// Source marks the automatic origin ("alert:<rule>:<device>",
// "syslog:<device>:<class>") — auto-findings dedup and auto-resolve by it;
// human/AI findings leave it empty.
Source string `json:"source,omitempty"`
// Status is the alert lifecycle: "" (human/AI finding) | active | resolved.
Status string `json:"status,omitempty"`
ResolvedAt *time.Time `json:"resolvedAt,omitempty"`
// Project is the site-scope snapshot stamped at save time: the project
// whose groups own the most of this finding's devices (ProjectForDevices).
// "" = 未分组 — such findings (unknown-source syslog, "(all)" sweeps,
// pre-Project legacy files) stay visible in EVERY project view so the
// blind-spot rule holds: alerts that match no project are never hidden.
// Stamped only when empty — a re-save keeps the original stamp; group
// membership is a view concern, the stamp is audit history.
Project string `json:"project,omitempty"`
}
Finding is one diagnosis conclusion.
func ImportNmapForConfig ¶
ImportNmapForConfig adapts ImportNmapXML to the config type (bridge seam).
func ImportNmapXML ¶
ImportNmapXML parses an nmap -oX dump and files one Finding: every live host with its open ports; hosts missing from the inventory are flagged 待确认 (they may be joinable — the user decides, nothing dials).
func ListFindings ¶
ListFindings returns findings newest-first.
type GoldenDrift ¶
type GoldenDrift struct {
Device string
Extra []string // in running, not in golden (意外配置)
Missing []string // in golden, not in running (丢失配置)
Note string // e.g. offline fallback marker
}
GoldenDrift is one check's outcome for one device.
type GoldenInfo ¶
GoldenInfo describes one device's baseline for the UI.
func GoldenInfoOf ¶
func GoldenInfoOf(device string) GoldenInfo
GoldenInfoOf reports the baseline's presence for the timeline header.
type HTTPFingerprint ¶
type HTTPFingerprint struct {
Title string `json:"title,omitempty"`
Server string `json:"server,omitempty"`
CertCN string `json:"cert_cn,omitempty"`
CertSAN string `json:"cert_san,omitempty"`
}
HTTPFingerprint is what one polite request learned.
type HealthSnapshot ¶
type HealthSnapshot struct {
PollIntervalSeconds int `json:"pollIntervalSeconds"`
Devices []DeviceHealth `json:"devices"`
}
HealthSnapshot is the whole fleet's latest state.
type HumanTTYRecording ¶
type HumanTTYRecording struct {
Device string `json:"device"`
Path string `json:"path"`
At string `json:"at"` // yyyymmdd-hhmmss
Bytes int64 `json:"bytes"`
}
HumanTTYRecording is one saved session recording (completion-spec §6 #5: 回放查看——文件已在落盘时 ANSI 剥离 + 脱敏,此处只做只读罗列/读取).
func ListHumanTTYRecordings ¶
func ListHumanTTYRecordings() ([]HumanTTYRecording, error)
ListHumanTTYRecordings scans the recording dir, newest first.
type HumanTTYState ¶
type HumanTTYState struct {
Device string `json:"device"`
Connected bool `json:"connected"`
StartedAt time.Time `json:"started_at"`
Bytes int64 `json:"bytes"` // total output bytes (for audit)
Error string `json:"error,omitempty"`
}
HumanTTYState is the terminal session lifecycle.
func HumanTTYStatus ¶
func HumanTTYStatus() []HumanTTYState
HumanTTYStatus reports all active sessions (for the live panel / audit tab).
type IfBriefCounts ¶
IfBriefCounts is the per-device interface tally lifted from the inspection battery's interface-brief output (line-count heuristic, not a per-interface ledger — good enough for a trend, labeled as such).
func SummarizeIfBrief ¶
func SummarizeIfBrief(output string) IfBriefCounts
SummarizeIfBrief counts interface rows by link state from a brief-style output (huawei `display interface brief` / cisco `show interfaces status`). Heuristic line counting — the R1 row is a trend input, not an interface DB.
type IfHealth ¶
type IfHealth struct {
Name string `json:"name"`
AdminUp bool `json:"adminUp"`
OperUp bool `json:"operUp"`
}
IfHealth is one interface row (ifDescr/ifAdminStatus/ifOperStatus).
type ImportDBSource ¶
type ImportDBSource struct {
Name string `json:"name"`
Type string `json:"type"`
Host string `json:"host"`
Port int `json:"port"`
Via []string `json:"via"`
}
ImportDBSource is a db_sources skeleton.
type ImportDevice ¶
type ImportDevice struct {
Name string `json:"name"`
Vendor string `json:"vendor"`
Kind string `json:"kind"`
Address string `json:"address"`
Group string `json:"group"`
Via []string `json:"via"`
}
ImportDevice is the skeleton from the export file.
type ImportDeviceConflict ¶
type ImportDeviceConflict struct {
Imported ImportDevice `json:"imported"`
Local ImportDevice `json:"local"`
}
ImportDeviceConflict adds the local view for side-by-side comparison.
type ImportPreview ¶
type ImportPreview struct {
// NewDevices would be ADDED (name not in current inventory).
NewDevices []ImportDevice `json:"new_devices"`
// ConflictDevices exist by name — their address/vendor may differ; the
// user decides keep-local vs take-imported per device.
ConflictDevices []ImportDeviceConflict `json:"conflict_devices"`
// DBNew / DBOverride mirror the same split for db_sources.
DBNew []ImportDBSource `json:"db_new"`
DBOverlap []string `json:"db_overlap"`
// SkippedCounts are informational: findings/series/audit tails are
// import-read-only (the local audit chain stays append-only).
FindingsSeen int `json:"findings_seen"`
ExportedAt string `json:"exported_at"`
Source string `json:"source"`
}
ImportPreview is the human-review diff for one import run.
type ImportTopoPreview ¶
type ImportTopoPreview struct {
Graph TopologyGraph `json:"graph"`
Stats TopoImportStats `json:"stats"`
Warnings []string `json:"warnings"`
}
ImportTopoPreview is the no-side-effect parse result the UI confirms.
func ImportDrawio ¶
func ImportDrawio(xmlText string, deviceNames []string, deviceAddrs map[string]string) (*ImportTopoPreview, error)
ImportDrawio parses a .drawio/.xml design into a preview. deviceNames and deviceAddrs drive the L3 fusion (managed markers); empty slices are fine.
func ImportVsdx ¶
func ImportVsdx(data []byte, deviceNames []string, deviceAddrs map[string]string) (*ImportTopoPreview, error)
ImportVsdx parses a .vsdx file into the standard preview. deviceNames / deviceAddrs drive the L3 fusion, exactly like ImportDrawio.
type IncidentCase ¶
type IncidentCase struct {
ID string `json:"id"`
Title string `json:"title"`
Status string `json:"status"` // open | closed
Devices []string `json:"devices,omitempty"`
Entries []CaseEntry `json:"entries,omitempty"`
IOCs []CaseIOC `json:"iocs,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
IncidentCase is one investigation.
func ListCases ¶
func ListCases() ([]*IncidentCase, error)
ListCases returns all cases, newest-updated first.
type InspectionJournalRow ¶
type InspectionJournalRow struct {
At string `json:"at"` // 2006-01-02T15:04:05
Kind string `json:"kind"` // inspection | baseline
Devices int `json:"devices"`
Checked int `json:"checked"`
Critical int `json:"critical"` // open tallies at run end — the risk-score trend source
Warning int `json:"warning"`
Info int `json:"info"`
BaselineHits int `json:"baseline_hits,omitempty"`
IfBrief map[string]IfBriefCounts `json:"if_brief,omitempty"`
}
InspectionJournalRow is one run's summary line (R1).
func ReadInspectionRows ¶
func ReadInspectionRows(limit int) []InspectionJournalRow
ReadInspectionRows returns up to limit newest R1 rows (oldest→newest order preserved for sparklines).
type InvestigationChain ¶
type InvestigationChain struct {
CaseID string `json:"case_id,omitempty"`
CaseTitle string `json:"case_title,omitempty"`
HasCase bool `json:"has_case"`
FindingID string `json:"finding_id,omitempty"`
// Counts carries per-kind node tallies — the inline stats row; the
// denominators are the node counts themselves (可查 via deep links).
Counts map[string]int `json:"counts"`
Nodes []ChainNode `json:"nodes"`
Edges []ChainEdge `json:"edges"`
Timeline []TimelineEvent `json:"timeline,omitempty"`
Truncated bool `json:"truncated"`
}
InvestigationChain is the whole chain screen payload.
type Job ¶
type Job struct {
ID string `json:"id"`
Name string `json:"name"`
Steps []JobStep `json:"steps"`
StepState []JobStepState `json:"step_state"`
Status string `json:"status"`
Budget JobBudget `json:"budget"`
CreatedAt time.Time `json:"created_at"`
StartedAt *time.Time `json:"started_at,omitempty"`
EndedAt *time.Time `json:"ended_at,omitempty"`
ActiveMS int64 `json:"active_ms"` // wall clock actually spent running (pauses excluded)
Commands int `json:"commands"` // attempts executed against the budget
Cursor int `json:"cursor"` // next step index (断点续跑 anchor)
BreakpointOK int `json:"breakpoint_ok"` // cursor whose PauseBefore was human-confirmed
PauseNote string `json:"pause_note,omitempty"` // why it paused — shown to the human
CreatedBy string `json:"created_by,omitempty"`
}
Job is one runbook run.
type JobBudget ¶
type JobBudget struct {
MaxWallSec int `json:"max_wall_sec,omitempty"` // default 1800
MaxCommands int `json:"max_commands,omitempty"` // default 200
FailStreak int `json:"fail_streak,omitempty"` // default 3 consecutive failures → circuit-break pause
}
Job watchdog budgets (v1 C 批). Zero fields take the defaults; a paused job does not burn wall clock.
type JobStep ¶
type JobStep struct {
Name string `json:"name"`
Device string `json:"device"`
Command string `json:"command"`
Expect string `json:"expect,omitempty"` // regex that must appear in the output within the timeout
TimeoutSec int `json:"timeout_sec,omitempty"` // per attempt (default 60)
Retries int `json:"retries,omitempty"` // extra attempts after the first failure (≤4)
OnFail string `json:"on_fail,omitempty"` // pause | abort | continue (default pause)
PauseBefore bool `json:"pause_before,omitempty"` // 断点:human confirms before this step runs
}
JobStep is one runbook entry: a single sealed read command with its own success criterion (Expect), timing, and failure policy.
type JobStepState ¶
type JobStepState struct {
Status string `json:"status"`
Attempts int `json:"attempts,omitempty"`
Output string `json:"output,omitempty"` // last attempt tail, already redacted by Exec
Error string `json:"error,omitempty"`
StartedAt *time.Time `json:"started_at,omitempty"`
EndedAt *time.Time `json:"ended_at,omitempty"`
}
JobStepState is the step's execution trail.
type LiveDeviceState ¶
type LiveDeviceState struct {
Device string `json:"device"`
Vendor string `json:"vendor"`
OS string `json:"os,omitempty"`
Group string `json:"group,omitempty"`
Connected bool `json:"connected"`
VTYUse int `json:"vtyUse"`
VTYCap int `json:"vtyCap"`
}
LiveDeviceState is one device's snapshot entry for the panel's initial paint.
type LiveEvent ¶
type LiveEvent struct {
Kind string `json:"kind"`
Device string `json:"device"`
Time int64 `json:"time"` // unix milliseconds
// LiveConn
State string `json:"state,omitempty"`
VTYUse int `json:"vtyUse,omitempty"` // sessions currently held on the device
VTYCap int `json:"vtyCap,omitempty"` // max_sessions_per_device (0 = unset)
// LiveCmdStart / LiveCmdEnd / LiveCmdRefused
Command string `json:"command,omitempty"`
Class string `json:"class,omitempty"` // read | write | dangerous | unknown | guardrail
// LiveCmdOutput
Chunk string `json:"chunk,omitempty"`
// LiveCmdEnd / LiveCmdRefused
Status string `json:"status,omitempty"` // ok | device-error | refused | failure
MS int64 `json:"ms,omitempty"` // wall-clock duration of the command
Bytes int `json:"bytes,omitempty"` // redacted output size
Reason string `json:"reason,omitempty"` // refusal / failure reason
}
LiveEvent is one observable moment. Fields are grouped by kind; only the relevant subset is populated.
type LiveSnapshot ¶
type LiveSnapshot struct {
Devices []LiveDeviceState `json:"devices"`
Spent int `json:"spent"` // commands spent this turn
Budget int `json:"budget"` // turn_command_budget (0 = unlimited)
}
LiveSnapshot is the panel's mount-time state: per-device connection/VTY state plus the per-turn budget counters.
type LocateHit ¶
type LocateHit struct {
Device string `json:"device"`
Interface string `json:"interface,omitempty"`
Line string `json:"line"`
}
LocateHit is one device's answer for the target.
type LocateResult ¶
type LocateResult struct {
Target string `json:"target"`
Hits []LocateHit `json:"hits"`
Devices []string `json:"searched"`
Skipped []string `json:"skipped,omitempty"`
Covered int `json:"covered_devices"`
Total int `json:"total_devices"`
BudgetStop bool `json:"budget_stopped"`
Note string `json:"note,omitempty"`
}
LocateResult is the fan-out outcome.
type LogFollowEvent ¶
type LogFollowEvent struct {
Device string `json:"device"`
Source string `json:"source"`
Chunk string `json:"chunk,omitempty"`
Done bool `json:"done,omitempty"`
Reason string `json:"reason,omitempty"`
}
LogFollowEvent is one streaming callback: a chunk of (redacted, line-aligned) text, or the terminal done event carrying the stop reason.
type LogSearchHit ¶
type LogSearchHit struct {
Device string `json:"device"`
Source string `json:"source"`
Line string `json:"line"`
Context []string `json:"context,omitempty"`
}
LogSearchHit is one matching line with its ±context.
type LogSearchResult ¶
type LogSearchResult struct {
Pattern string `json:"pattern"`
Hits []LogSearchHit `json:"hits"`
Devices []string `json:"devices_searched"`
Skipped []string `json:"skipped"` // device — reason strings
Covered int `json:"covered_devices"`
Total int `json:"total_devices"`
HitDevice int `json:"devices_with_hits"`
BudgetStop bool `json:"budget_stopped"`
Note string `json:"note,omitempty"`
}
LogSearchResult is the sweep outcome. Covered/Total + Skipped make partial coverage explicit — a budget-stopped sweep reports exactly what it missed.
type LogSourceProbe ¶
type LogSourceProbe struct {
Device string `json:"device"`
Services []string `json:"services"`
Containers []string `json:"containers"`
Files []ProbeFile `json:"files"`
Errors []string `json:"errors"`
}
LogSourceProbe is the probe result: running services (journal: candidates), containers (docker:), and /var/log files (file:). Errors carries per-leg notes (e.g. "docker: not available") — a missing docker is the common case, surfaced as a hint rather than a failure.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager owns per-device connections and enforces the structural read-only seal: every netdev_exec command passes the driver classifier, and anything that is not ClassRead is refused and audited — never executed. There is no code path from the agent to a write; writes arrive with the proposal pipeline (P4), which a human approves.
func NewManager ¶
func SharedManager ¶
SharedManager returns the process-wide Manager, refreshing its config. The desktop bridge and the scheduler call this instead of NewManager so reaper goroutines and per-device session caches exist exactly once (a Manager per call would leak a reaper and re-dial VTYs needlessly).
func (*Manager) ApproveProposal ¶
ApproveProposal is the human gate. It enforces: current status draft, group policies (confirm2 when demanded), and the change window of every involved group. The agent has NO path here — approval arrives only from the desktop bridge with the human's click. The load→check→set→save runs as one critical section so a racing reject/execute can't interleave (last-write-wins used to silently drop a transition).
func (*Manager) BackupDiffCurrent ¶
BackupDiffCurrent diffs a stored version (old) against the device's CURRENT running-config (new) — the restore-drafting backbone: "what must change to return to this version". The current side is a fresh sealed read (classifier / budget / redaction / audit all apply, same as any netdev_exec).
func (*Manager) BuildDiscoveryBoard ¶
func (m *Manager) BuildDiscoveryBoard() *DiscoveryBoard
BuildDiscoveryBoard assembles the discovery screen (needs Manager only for the config inventory).
func (*Manager) BuildExposureBoard ¶
func (m *Manager) BuildExposureBoard() *ExposureBoard
BuildExposureBoard assembles the exposure screen (推演 + 事实矩阵).
func (*Manager) BuildInvestigationChain ¶
func (m *Manager) BuildInvestigationChain(caseID, findingID string, hours int) *InvestigationChain
BuildInvestigationChain assembles the six-column chain (Manager method only to reuse Timeline; everything else reads package-level stores).
func (*Manager) Cfg ¶
Cfg exposes the manager's live config (the desktop import-apply bridge mirrors the merged inventory into the config being persisted).
func (*Manager) CredentialInventory ¶
CredentialInventory builds the credential-health page (markdown): every device's auth surface + which secrets are actually set.
func (*Manager) CutoverAbort ¶
func (m *Manager) CutoverAbort(id string) (*CutoverRun, error)
CutoverAbort stops everything for good (running or held).
func (*Manager) CutoverContinue ¶
func (m *Manager) CutoverContinue(id string) (*CutoverRun, error)
CutoverContinue presses 继续 at a hold.
func (*Manager) CutoverRollback ¶
CutoverRollback presses 回退 at a hold: the run's executed proposals unwind newest-first (each still audited); the run ends aborted.
func (*Manager) CutoverStart ¶
func (m *Manager) CutoverStart(def *CutoverRun) (*CutoverRun, error)
CutoverStart validates the runbook, snapshots the network baseline, and launches the runner. Every proposal a step references must already be approved — the cutover executes the approved change, it never approves.
func (*Manager) DBQuery ¶
DBQuery runs ONE allowlisted read-only statement against a configured [[netdev.db_sources]] entry. Output is JSON lines, redacted, row-capped.
func (*Manager) DeleteProposal ¶
DeleteProposal removes a proposal file. Only draft and terminal states (rejected/done/failed/closed) may be deleted — the live pipeline (approved/executing/partial/watching) must stay auditable. partial keeps its record because the applied steps are still on the devices.
func (*Manager) DiscoverLayer ¶
func (m *Manager) DiscoverLayer(ctx context.Context, vantage string, cidrs []string, ports []int) ([]DiscoverHostResult, error)
DiscoverLayer probes the CONFIRMED subnets through the vantage device's own SSH tunnel (direct-tcpip from its network position). Scope whitelist applies per CIDR at the dial boundary; results record as layer-discover.
func (*Manager) DiscoverPrecheck ¶
DiscoverPrecheck runs the three read-only table commands on a managed vantage through the sealed Exec path (audited by Exec itself) and folds them into a plan. Zero probe traffic — the polite-first layer of F4.
func (*Manager) DiscoverResume ¶
func (m *Manager) DiscoverResume(ctx context.Context) ([]DiscoverHostResult, error)
DiscoverResume continues a paused layered scan from its last checkpoint. Results accumulate in the 待确认区 either way; the return value carries only this continuation's finds.
func (*Manager) DiscoverTCP ¶
func (m *Manager) DiscoverTCP(ctx context.Context, via, cidr string, ports []int) ([]DiscoverHostResult, error)
DiscoverTCP probes ip:port combinations across a CIDR. via names a configured hop ("" = dial directly from this machine). The requested CIDR must sit INSIDE the configured [netdev.discovery] scopes — enforced here at the dial boundary, not trusted to the caller (guardrail invariant 3). Results land in the 待确认区 store (F1): parse passively, keep forever.
func (*Manager) DockerGet ¶
func (m *Manager) DockerGet(ctx context.Context, deviceName, what, arg string, tailN int) (string, error)
DockerGet answers ONE whitelisted GET against the device's Docker Engine. what ∈ ping | version | info | ps | inspect | logs | images. Sealed by sealAPIGet (guardrails + live + audit + redaction).
func (*Manager) EnsureHealthPoller ¶
func (m *Manager) EnsureHealthPoller()
EnsureHealthPoller starts the singleton sweep loop (idempotent); it reads the interval from the Manager's live config each tick, so a settings change to poll_interval_seconds applies within one interval without a restart.
func (*Manager) Exec ¶
func (m *Manager) Exec(ctx context.Context, deviceName, command string) ExecResult
Exec runs one command on one configured device under the read-only seal.
func (*Manager) ExecuteProposal ¶
ExecuteProposal rolls the approved change device-by-device: backup → apply → mark. The FIRST failure freezes the proposal as partial (later steps untouched) — a human then decides rollback (which runs the authored plan over the already-applied steps) or keep. There is no automatic rollback.
func (*Manager) ExpectedState ¶
func (m *Manager) ExpectedState() ExpectedStateView
ExpectedState diffs the inventory against the latest SNMP health sweep.
func (*Manager) ExportState ¶
ExportState writes one JSON snapshot into the netdev state dir and returns its path. Devices carry skeleton fields ONLY — credentials never leave the secret store.
func (*Manager) FirewallGet ¶
FirewallGet answers ONE whitelisted GET against the device's REST API.
func (*Manager) HandoffReport ¶
HandoffReport builds the shift-handoff page (markdown).
func (*Manager) HealthSnapshot ¶
func (m *Manager) HealthSnapshot() HealthSnapshot
HealthSnapshot returns the fleet's latest state.
func (*Manager) HumanTTYStart ¶
func (m *Manager) HumanTTYStart(deviceName string, onData func(chunk string)) (*HumanTTYState, error)
HumanTTYStart opens a human terminal session to a device: its own transport client + a PTY shell channel. The onData callback receives decoded output chunks (the desktop layer forwards them as "netdev:humantty" Wails events).
func (*Manager) HumanTTYWrite ¶
HumanTTYWrite sends user keystrokes to the device PTY.
func (*Manager) ImportApply ¶
ImportApply performs the reviewed merge: adds the named new devices and takes the imported side of the named conflicts. Credentials are NOT migrated — the skeleton lands with empty credential fields and the user re-enters them in settings (spec: 凭证不迁移).
func (*Manager) ImportPreview ¶
func (m *Manager) ImportPreview(path string) (*ImportPreview, error)
ImportPreview reads an export file and builds the confirm-area diff. No mutation happens here — preview only.
func (*Manager) JobStart ¶
JobStart validates the definition, persists it as running, and launches the runner goroutine. The definition arrives from the desktop bridge (UI / runbook presets) — the agent has no tool that creates jobs.
func (*Manager) KillAllConnections ¶
KillAllConnections is the emergency stop: every device connection and CLI session is closed immediately (freeing all VTY lines), audited as such. The Manager stays usable — the next diagnostic command reconnects on demand. Human terminals die with it too (§6.1 紧急停止同样杀人工终端).
func (*Manager) KubeGet ¶
func (m *Manager) KubeGet(ctx context.Context, deviceName, what, namespace, name string, tailN int) (string, error)
KubeGet answers ONE whitelisted GET against the device's pinned context. what ∈ version | pods | pod | podlog | events | deployments | nodes. Sealed by sealAPIGet (guardrails + live + audit + redaction).
func (*Manager) LiveState ¶
func (m *Manager) LiveState() LiveSnapshot
LiveState builds the snapshot over the configured inventory.
func (*Manager) Locate ¶
func (m *Manager) Locate(ctx context.Context, target string) LocateResult
Locate fans one IP/MAC across the inventory's ARP surfaces.
func (*Manager) LogFollow ¶
func (m *Manager) LogFollow(deviceName, source string, onEvent func(LogFollowEvent)) error
LogFollow starts (or replaces) the streaming follow of one log source on one device. Events stream to onEvent until a cap trips, the device drops, or LogFollowStop is called; the final event is always Done with a reason.
func (*Manager) LogFollowActive ¶
LogFollowActive reports the device's active follow source ("" = none).
func (*Manager) LogFollowStop ¶
LogFollowStop kills the device's active follow, if any.
func (*Manager) LogRead ¶
func (m *Manager) LogRead(ctx context.Context, deviceName, source string, tailN int, since, grep string) ExecResult
LogRead fetches one log source on one configured device. The composed command goes through the SAME sealed Exec path as netdev_exec (budget, classifier, redaction, audit, live events all apply); since/grep filtering happens client-side on the fetched lines.
func (*Manager) LogSearch ¶
func (m *Manager) LogSearch(ctx context.Context, pattern string, devices, sources []string, since string) LogSearchResult
LogSearch fans pattern across devices (empty = every linux host in the inventory). Custom sources override the default candidates; they ride the same per-device whitelist as netdev_log_read.
func (*Manager) MatchCVEsToFindings ¶
MatchCVEsToFindings runs the match and files each device's hits as ONE Finding (dedup key: "cve:" + device — re-runs update rather than pile up).
func (*Manager) NetconfRPC ¶
NetconfRPC runs one read-only NETCONF RPC on the device and returns the <rpc-reply>…</rpc-reply> document (raw XML text, redacted upstream of the model by the exec-path caller if surfaced there).
func (*Manager) NetprobeSweep ¶
func (m *Manager) NetprobeSweep(ctx context.Context, cidr string, doICMP bool) (*NetprobeSweepResult, error)
NetprobeSweep orchestrates cmd/netprobe over one in-scope CIDR. Leads land in the 待确认区 (source netprobe) — ICMP-only hosts become port-less alive rows, port hits merge like any other discovery source.
func (*Manager) NmapSweep ¶
NmapSweep orchestrates one user-supplied nmap service sweep over a CIDR. Gates (in order): engagement envelope → scope whitelist → host cap → binary presence. Results land in the 待确认区 store (source nmap-import) so the promote → 指纹回填 → CVE 匹配 loop consumes them like any lead.
func (*Manager) PollHealthOnce ¶
PollHealthOnce sweeps every SNMP-configured device once, then evaluates the alert rules over the fresh results.
func (*Manager) ProbeLogSources ¶
func (m *Manager) ProbeLogSources(ctx context.Context, deviceName string) LogSourceProbe
ProbeLogSources probes one configured device. Every command is a read-table line composed here (never model- or user-supplied), so a refusal can only mean the device's driver refused it — reported per leg, never fatal.
func (*Manager) ProposalNeedsConfirm2 ¶
ProposalNeedsConfirm2 reports whether any step demands the secondary confirmation: a proposal+confirm2 group (§6.3) OR a step whose verbs scanned as destructive (§7.1 — delete/scale-down 类动词落 dangerous + confirm2).
func (*Manager) RedfishGet ¶
RedfishGet performs ONE read-only GET against a device's BMC and returns the JSON body (redacted). vendor=redfish devices carry the BMC address and a passwordEnv-resolved credential; the TLS skip is deliberate — BMC certificates are almost universally self-signed, and the channel carries read-only inventory data.
func (*Manager) RejectProposal ¶
RejectProposal is the human veto (completion-spec §4.1): the write path's gate could only say yes — this is the "no". draft/approved may be rejected (approved-but-not-executed vetoes cost nothing; executing states are past the point of veto and must ride rollback instead). The reason is persisted on the proposal so the agent's next turn sees WHY it was turned down.
func (*Manager) RemoteWorkHandler ¶
func (m *Manager) RemoteWorkHandler() nettrans.WorkHandler
RemoteWorkHandler adapts netdev's read-only diagnostics onto the trust domain's delegated-work surface (TRUSTDOMAIN_SPEC §7.3 as-built) — the first real WorkHandler consumer. A fleet member holding a scoped token can pull this host's health board or run a host-triage battery on one of this host's OWN managed devices; everything flows the same read-only pipeline (guardrails, audit, refusal ledger) as local agent use.
能力隔离 (spec §1.4 #7): there is simply no write operation on this path — the vocabulary below cannot express one, and Serve's §7.3 gate runs before any of this code.
Resource/operation vocabulary:
netdev/health read → HealthSnapshot JSON
netdev/triage read {"device":"name"} → TriageReport JSON
func (*Manager) RollbackProposal ¶
RollbackProposal runs the authored rollback plan over the APPLIED steps only, oldest-last so state unwinds in reverse. Frozen/failed proposals only. A rollback failure marks the proposal failed (alert) and stops.
func (*Manager) RunBackup ¶
RunBackup reads one device's (or every device's, device == "") running config through the sealed Exec path and files each as a new version. Returns the versions written, in device order.
func (*Manager) RunBaseline ¶
RunBaseline reads every device's running-config through the sealed path (full audit, redaction before rules run) and files one Finding per violated rule plus a summary Finding. Mirror of RunInspection's flow.
func (*Manager) RunBaselineFor ¶
RunBaselineFor is RunBaseline scoped to a device set (nil/empty = all) — the P2-3 proposal-driven re-check runs only the devices a finished proposal actually touched, so an in-flight fix elsewhere can't be auto-resolved out from under its own alert.
func (*Manager) RunGoldenCheck ¶
RunGoldenCheck snapshots the device's running-config (sealed read) and diffs it against the baseline. Fires/resolves the golden Finding by source, like the alert engine. device="" sweeps every device that has a baseline; returns the per-device drifts (empty drifts included so callers can report "clean").
func (*Manager) RunInspection ¶
RunInspection sweeps every inventory device with a fixed read battery and files one Finding with the evidence — the manual form of 定时巡检 (the scheduler wiring lands with the jobs integration). Everything runs through the sealed read path (classifier + redaction + audit per command).
func (*Manager) RunJobSync ¶
RunJobSync starts a job and BLOCKS until its runner exits (terminal status, pause, or ctx end — on ctx end the job is aborted). This is how synchronous callers ride the engine (§4.1 体检电池 runbook): same step semantics, budgets, and persisted job trail, minus the async interaction.
func (*Manager) SFTPBrowse ¶
SFTPBrowse lists a whitelisted directory (ls, sealed).
func (*Manager) SFTPDownload ¶
SFTPDownload reads one file from a device over the existing SSH transport. Returns the content (bounded). The path must sit inside the device's log_paths whitelist or /var/log (§6.2: 路径浏览限白名单).
func (*Manager) SetLiveObserver ¶
SetLiveObserver installs the live-event sink (replaces any previous one). The callback must be fast and non-blocking — the desktop bridge coalesces and forwards on its own goroutine.
func (*Manager) SnmpQuery ¶
SnmpQuery runs ONE GET (oid exact) or a bounded WALK (prefix) against a vendor=snmp device. Results render as `OID = value` lines, redacted.
func (*Manager) SrvConfDrift ¶
func (m *Manager) SrvConfDrift(ctx context.Context, path string, devices []string) ([]SrvConfDriftRow, error)
SrvConfDrift compares the CURRENT content of one path across devices (the first device is the baseline): the 「同一份 nginx 配置三个环境差在哪」view (§7.3). Devices must share the path inside their own config_paths whitelist.
func (*Manager) SrvConfSnapshot ¶
func (m *Manager) SrvConfSnapshot(ctx context.Context, deviceName, path string) (*SrvConfVersion, error)
SrvConfSnapshot reads one whitelisted config file and stores a version.
func (*Manager) TemplateApply ¶
TemplateApply renders + validates + saves ONE draft proposal whose steps are per-device cli steps (rolling execution, first-failure freeze, per-device rollback matrix — the existing proposal pipeline unchanged, §7.2).
func (*Manager) TemplateRender ¶
func (m *Manager) TemplateRender(t *Template, userVars map[string]string) ([]TemplatePreviewDevice, error)
TemplateRender is the 逐台 dry-run(§7.2):renders every target's commands, classifies each rendered product (渲染产物仍逐条过分类器), and flags destructive verbs — with NO side effects on any device.
func (*Manager) TestConnection ¶
func (m *Manager) TestConnection(ctx context.Context, deviceName string) TestResult
TestConnection dials one configured device and opens (then closes) a CLI session. It never prompts interactively.
func (*Manager) Timeline ¶
func (m *Manager) Timeline(device string, hours int) []TimelineEvent
Timeline assembles the correlation stream for one device ("" = all) inside the window (hours, default 24, cap 168).
func (*Manager) TopologySnapshot ¶
func (m *Manager) TopologySnapshot(ctx context.Context) (*TopologyGraph, error)
func (*Manager) Triage ¶
func (m *Manager) Triage(ctx context.Context, deviceName string) TriageReport
Triage runs the battery on one host and returns the structured report. §4.1 runbook 升级:电池作为 Job 引擎 runbook 执行——每步 30s 超时、 on-fail=continue(诊断电池要尽量收全)、预算封顶(墙钟 10 分钟 / 命令数 = 电池长度),全程留 job 轨迹(jobs 目录可见、可审计);步骤语义(expect /retry/断点)对电池默认关闭但引擎已具备——R5 入侵排查向导直接复用。
func (*Manager) TurnBegin ¶
func (m *Manager) TurnBegin()
TurnBegin resets the per-turn command budget. The desktop bridge calls it on every user submit (netdev mode), making turn_command_budget a true per-ask control: each question the user asks buys a fresh budget of read commands, and nothing carries over.
func (*Manager) ValidateProposal ¶
ValidateProposal checks a draft against the inventory policies: every step's device must exist, no step may target a read-only group, and each structured type's own contract must hold (§7.1 — e.g. sql-migration without a down script is not submittable). Group policy `proposal+confirm2` is legal but flags ApproveProposal for the secondary confirmation.
func (*Manager) WeakCredCheck ¶
func (m *Manager) WeakCredCheck(ctx context.Context, deviceName, tier, dictPath string) (WeakCredResult, error)
WeakCredCheck tests a device's login against the tier's candidate set. Gated on the engagement envelope; every attempt audited (password NEVER logged — only the attempt ordinal).
func (*Manager) WeeklyReport ¶
WeeklyReport builds the weekly ops digest (markdown).
type MetricPoint ¶
type MetricPoint struct {
Time time.Time `json:"time"`
Reachable bool `json:"up"`
UptimeSec int64 `json:"us"`
IfUp int `json:"iu"`
IfDown int `json:"id"`
// 水位(DASHBOARD spec §7.3 SNMP OID 扩展):cpu/mem 为百分比,octets
// 为 ifTable 计数器累计和——速率(bps)由读取端按相邻点差分计算。
Cpu int `json:"cu,omitempty"`
Mem int `json:"me,omitempty"`
InOct uint64 `json:"io,omitempty"`
OutOct uint64 `json:"oo,omitempty"`
}
MetricPoint is one poll's rollup for one device.
func MetricHistory ¶
func MetricHistory(device string, limit int) []MetricPoint
MetricHistory returns the device's newest-first points (bounded).
type NetDevEvent ¶
NetDevEvent is one structured ring entry (syslog/trap).
func SyslogEventsSince ¶
func SyslogEventsSince(device string, since time.Time) []NetDevEvent
SyslogEventsSince returns one device's ring entries (structured) since t.
func TrapEventsSince ¶
func TrapEventsSince(device string, since time.Time) []NetDevEvent
TrapEventsSince returns one device's trap ring entries since t.
type NetprobeHost ¶
type NetprobeHost struct {
IP string `json:"ip"`
ICMP bool `json:"icmp"`
Ports []struct {
Port int `json:"port"`
Open bool `json:"open"`
Banner string `json:"banner,omitempty"`
} `json:"ports"`
}
NetprobeHost mirrors cmd/netprobe's hostResult JSON.
type NetprobeSweepResult ¶
type NetprobeSweepResult struct {
CIDR string `json:"cidr"`
ICMP bool `json:"icmp"`
Alive int `json:"alive"`
WithPorts int `json:"with_ports"`
Results []NetprobeHost `json:"results"`
Command string `json:"command"`
Duration string `json:"duration"`
}
NetprobeSweepResult summarizes one orchestrated sweep.
type NmapHost ¶
type NmapHost struct {
IP string `json:"ip"`
Hostname string `json:"hostname,omitempty"`
Services []NmapHostService `json:"services"`
}
NmapHost is one host with its open services.
type NmapHostService ¶
type NmapHostService struct {
Port int `json:"port"`
Proto string `json:"proto,omitempty"`
Service string `json:"service,omitempty"` // name, e.g. ssh/http/redis
Product string `json:"product,omitempty"` // e.g. OpenSSH / nginx
Version string `json:"version,omitempty"`
}
NmapHostService is one open service on one host.
type NmapSweepResult ¶
type NmapSweepResult struct {
CIDR string `json:"cidr"`
Hosts int `json:"hosts"`
OpenPorts int `json:"open_ports"`
Results []NmapHost `json:"results"`
Command string `json:"command"`
Duration string `json:"duration"`
}
NmapSweepResult summarizes one orchestrated sweep.
type NotifyPusher ¶
NotifyPusher is the embedded IM gateway's push seam (desktop injects the live *bot.BotGateway at start/stop — interface to avoid the import cycle, mirroring builtin.SetIMPusher).
type PlanStep ¶
type PlanStep struct {
CIDR string `json:"cidr"`
Class string `json:"class"`
Hosts int `json:"hosts"`
DefaultOn bool `json:"default_on"`
}
PlanStep mirrors one planned probe for the UI's plan card.
type PortEvent ¶
type PortEvent struct {
At string `json:"at"`
IP string `json:"ip"`
Port int `json:"port"`
Kind string `json:"kind"` // newly-opened | newly-closed
}
PortEvent is one observed port state change on an unmanaged lead.
func ListPortEvents ¶
ListPortEvents returns up to limit newest port-change events.
type PrecheckResult ¶
type PrecheckResult struct {
Vantage string `json:"vantage"`
Vendor string `json:"vendor,omitempty"`
Interfaces []string `json:"interfaces"` // name/ip/mask rows (display)
DirectSubnets []SubnetClass `json:"direct_subnets"`
RoutedSubnets []SubnetClass `json:"routed_subnets"`
ArpKnownIPs []string `json:"arp_known_ips"`
Uncovered string `json:"uncovered_note,omitempty"` // aggregates beyond coverage
Warnings []string `json:"warnings"`
At string `json:"at"`
}
PrecheckResult is what one vantage's three read-only tables yielded.
type ProbeFile ¶
type ProbeFile struct {
Name string `json:"name"` // messages
Path string `json:"path"` // /var/log/messages
Size string `json:"size"` // 2.1M (from ls -lh; "" = unknown)
Allowed bool `json:"allowed"` // readable via file: as-is
}
ProbeFile is one candidate log file. Allowed marks whether it sits inside the device's log whitelist (/var/log + log_paths): a false file needs its directory registered (one click in the UI) before file: reads work.
type Proposal ¶
type Proposal struct {
ID string `json:"id"`
Intent string `json:"intent"`
Status string `json:"status"`
Steps []ProposalStep `json:"steps"`
// 恢复提案来源(备份→恢复闭环):从备份版本起草的恢复提案记录来源
// 版本 ID(device@nanos);审计与前端据此回放「恢复自哪一版」。
RestoreFrom string `json:"restore_from,omitempty"`
CreatedAt time.Time `json:"created_at"`
ApprovedAt time.Time `json:"approved_at,omitempty"`
ExecutedAt time.Time `json:"executed_at,omitempty"`
Approver string `json:"approver,omitempty"`
Confirm2 bool `json:"confirm2"` // secondary confirmation for proposal+confirm2 groups
Note string `json:"note,omitempty"` // freeze/rollback reason trail
// 驳回(completion-spec §4.1):draft/approved 可被人否决;Reason 随变更
// 持久化,agent 下一轮读到变更即见被拒原因。
RejectedAt time.Time `json:"rejected_at,omitempty"`
RejectReason string `json:"reject_reason,omitempty"`
// 观察期(§7.1):done → watching(默认 30 分钟)→ closed;劣化触发 Finding。
WatchUntil *time.Time `json:"watch_until,omitempty"`
WatchNote string `json:"watch_note,omitempty"`
// HealthBase is the ifDown count per target device at watch start(-1 =
// 当时不可达)——观察期劣化检测(健康轮询对比)的基线。仅 SNMP 配置
// 设备有信号;其余设备跳过对比。
HealthBase map[string]int `json:"health_base,omitempty"`
}
func ListProposals ¶
ListProposals returns proposals newest-first.
type ProposalStep ¶
type ProposalStep struct {
Device string `json:"device"`
Type string `json:"type,omitempty"` // "" | cli | k8s-apply | sql-migration | file-upload | cert-replace
Commands []string `json:"commands"` // cli: the change, in order
Rollback []string `json:"rollback,omitempty"` // cli: reverse commands, authored with the change
// Structured payloads (§7.1) — only the fields of the step's type matter.
YAML string `json:"yaml,omitempty"` // k8s-apply: full manifest
UpSQL string `json:"up_sql,omitempty"` // sql-migration
DownSQL string `json:"down_sql,omitempty"` // sql-migration — REQUIRED (missing ⇒ not submittable)
LocalPath string `json:"local_path,omitempty"` // file-upload / cert-replace: local cert file
RemotePath string `json:"remote_path,omitempty"` // file-upload / cert-replace: absolute target path
KeyLocalPath string `json:"key_local_path,omitempty"` // cert-replace: local private key file
KeyRemotePath string `json:"key_remote_path,omitempty"`
Checksum string `json:"checksum,omitempty"` // optional sha256 the uploaded bytes must match
ReloadCmd string `json:"reload_cmd,omitempty"` // cert-replace: service reload after the swap
// restore-verify(§7.3 备份恢复演练): restore a config snapshot to a
// STAGING target and run the verify read. Device = receiver.
RestoreDevice string `json:"restore_device,omitempty"` // snapshot source device
RestoreVersion string `json:"restore_version,omitempty"` // snapshot id; "" = latest at execute time
VerifyCmd string `json:"verify_cmd,omitempty"` // e.g. `nginx -t`
Backup string `json:"backup,omitempty"` // captured pre-change state (redacted)
Applied bool `json:"applied"`
Error string `json:"error,omitempty"`
Dangerous bool `json:"dangerous,omitempty"` // destructive verb scan ⇒ forces confirm2 (§7.1)
}
ProposalStep is one device's slice of a change.
type Result ¶
type Result struct {
Command string
Output string // cleaned text: echo and trailing prompt removed
IsError bool // a driver error pattern matched
}
Result is one command's outcome.
type ScheduleStamp ¶
type ScheduleStamp struct {
At string `json:"at"` // 2006-01-02T15:04:05
Kind string `json:"kind"` // inspection
Ok bool `json:"ok"`
Title string `json:"title,omitempty"` // the filed Finding's title
Note string `json:"note,omitempty"` // failure note
}
ScheduleStamp records the last SCHEDULED run's outcome (manual runs do not stamp — the compliance card answers "did the scheduler fire").
func LoadScheduleStamp ¶
func LoadScheduleStamp() *ScheduleStamp
LoadScheduleStamp reads it back; nil = the scheduler never fired.
type SeriesPoint ¶
type Session ¶
type Session struct {
// contains filtered or unexported fields
}
Session is one PTY-driven interactive CLI session with a network device, built on a transport.Client's SSH connection. It exists because network devices generally do not support one-shot exec channels: the CLI must be driven interactively (paging off, prompt detection, echo stripping) — the driver layer supplies the vendor knowledge.
func OpenConsoleSession ¶
func OpenConsoleSession(ctx context.Context, portName string, baud int, drv driver.Driver, encoding string) (*Session, error)
OpenConsoleSession opens the line, runs the driver's paging-off sequence, and waits for the first driver prompt — then hands back a Session whose stdin IS the line and whose output buffer is fed by a reader goroutine. Session.Run (echo strip, prompt match, pager advance, clean) works unchanged; Close is nil-ssh-safe.
func OpenSession ¶
func OpenSession(ctx context.Context, client *transport.Client, drv driver.Driver, encoding string) (*Session, error)
OpenSession establishes the interactive CLI over an already-connected transport.Client: requests a PTY, starts the shell, disables paging per the driver, and waits for the first prompt.
func (*Session) Close ¶
Close ends the session (the transport Client stays connected). A console session has no ssh.Session behind it (nil) — only its serial line closes.
func (*Session) Run ¶
Run sends one command and returns when the device prompt reappears. The command must already be classified Read by the caller — the exec tool enforces that; the session layer stays classification-agnostic.
func (*Session) SetOutputObserver ¶
SetOutputObserver installs the live output tap (nil removes it). Callers own the callback's thread-safety; Manager's observer coalesces off-thread.
type SrvConfDriftRow ¶
type SrvConfDriftRow struct {
Device string `json:"device"`
Group string `json:"group"`
Status string `json:"status"` // same | drift | error | absent
Diff string `json:"diff,omitempty"`
Error string `json:"error,omitempty"`
}
SrvConfDriftRow is one device's drift verdict against the baseline device.
type SrvConfVersion ¶
type SrvConfVersion struct {
ID string `json:"id"`
Device string `json:"device"`
Path string `json:"path"`
At string `json:"at"`
Bytes int `json:"bytes"`
Lines int `json:"lines"`
}
SrvConfVersion is one config-file snapshot.
func SrvConfVersions ¶
func SrvConfVersions(device, path string) []SrvConfVersion
SrvConfVersions lists a (device, path)'s snapshots, newest first.
type StateEventMeta ¶
type StateEventMeta struct {
ID int `json:"id"`
Time time.Time `json:"time"`
Kind string `json:"kind"`
Entity string `json:"entity,omitempty"`
Actor string `json:"actor"`
Paths []string `json:"paths"`
Live []StateLiveEntity `json:"live,omitempty"`
CanRestore bool `json:"canRestore"`
CanRedo bool `json:"canRedo"`
}
StateEventMeta is the timeline-facing summary of one state event.
func StateEventMetas ¶
func StateEventMetas() []StateEventMeta
StateEventMetas lists events newest-first with the ZCode-style safety classification: an event is restorable unless the suffix it would revert touches an entity that is currently live (a proposal executing/watching, a job running/paused, a cutover running/hold). Redo is offered only on the newest event when it is a restore-keep — a newer event would be collateral of the redo's own suffix restore.
type StateLiveEntity ¶
type StateLiveEntity struct {
Type string `json:"type"` // proposal | job | cutover
ID string `json:"id"` // entity id
Status string `json:"status"` // why it is live (executing / running / hold…)
}
StateLiveEntity names an entity the suffix of a candidate restore touches that is currently mid-flight; restoring under it would fight its runner.
type StateRestoreResult ¶
type StateRestoreResult struct {
Written []string `json:"written"`
Deleted []string `json:"deleted"`
ReverseEventID int `json:"reverseEventId"`
}
StateRestoreResult reports what a suffix restore wrote back / deleted and the synthetic reverse event that enables redo.
func StateRestore ¶
func StateRestore(id int, actor string) (StateRestoreResult, error)
StateRestore rewinds local state to just before event id: every path touched by id or any later event is restored to its earliest post-id snapshot (deleted when it did not exist). Before touching anything it re-checks the live-entity classification (the preview may be stale) and writes a restore-keep reverse event so the rewind itself is redoable. Local records only — devices are never contacted.
type SubnetClass ¶
type SubnetClass struct {
CIDR string `json:"cidr"`
Class string `json:"class"` // direct-small | routed-small | medium | large
Hosts int `json:"hosts"`
DefaultOn bool `json:"default_on"`
}
SubnetClass is one classified network with its plan default.
type SyslogCountRow ¶
type SyslogCountRow struct {
Hour string `json:"hour"` // YYYY-MM-DDTHH
Device string `json:"device"`
Class string `json:"class"`
N int `json:"n"`
}
SyslogCountRow is one {hour, device, class} bucket.
func SyslogCountTail ¶
func SyslogCountTail(limit int) []SyslogCountRow
SyslogCountTail returns up to limit newest R3 rows across day files.
type SyslogStatusView ¶
type SyslogStatusView struct {
Listening bool `json:"listening"`
Port int `json:"port"`
Buffered int `json:"buffered"`
}
SyslogStatusView is the bridge payload for the UI.
type Template ¶
type Template struct {
ID string `json:"id"`
Name string `json:"name"`
Intent string `json:"intent"`
Vars []string `json:"vars,omitempty"` // user-supplied variable names
Steps []TemplateStep `json:"steps"` // per-device command templates
Targets []string `json:"targets"` // explicit device names
CreatedAt time.Time `json:"created_at"`
}
Template is one batch change template. Vars carries omitempty so a variable-less template serializes without the key (the frontend's `?? []` guards the absent case) instead of as JSON null.
func ListTemplates ¶
ListTemplates returns templates newest-first.
type TemplatePreviewDevice ¶
type TemplatePreviewDevice struct {
Device string `json:"device"`
Available bool `json:"available"` // device exists & not read-only-group
Reason string `json:"reason,omitempty"`
Steps []TemplatePreviewStep `json:"steps"`
}
TemplatePreviewDevice is one target's rendered result (dry-run, §7.2).
type TemplatePreviewStep ¶
type TemplatePreviewStep struct {
Commands []string `json:"commands"`
Rollback []string `json:"rollback"`
Classes []string `json:"classes"` // per rendered command: read|write|dangerous|unknown
Dangerous bool `json:"dangerous"` // verb scan hit — confirm2 forced on apply
}
TemplatePreviewStep is one rendered command with its classifier verdict.
type TemplateStep ¶
type TemplateStep struct {
Commands []string `json:"commands"`
Rollback []string `json:"rollback"`
}
TemplateStep is one per-device step's COMMAND TEMPLATE ({{var}} placeholders).
type TestResult ¶
type TestResult struct {
Device string `json:"device"`
Status string `json:"status"` // ok | unknown-host-key | auth-failed | refused-by-classifier | error
Detail string `json:"detail,omitempty"`
Host string `json:"host,omitempty"`
KeyType string `json:"keyType,omitempty"`
Fingerprint string `json:"fingerprint,omitempty"`
}
TestConnection runs the first-device flow: connect → TOFU (captured, not interactive) → CLI session (paging-off exercises the prompt state machine). The result tells the UI exactly what to do next: trust the host key, fix credentials, or celebrate.
Two-step TOFU: the strict prompt captures the presented key and rejects, so no code path can silently trust anything — the human confirms the fingerprint in the UI and calls TrustHostKey with the SAME fingerprint.
type TimelineEvent ¶
type TimelineEvent struct {
Time time.Time `json:"time"`
Kind string `json:"kind"` // change | finding | event
Device string `json:"device"`
Title string `json:"title"`
Detail string `json:"detail,omitempty"`
}
TimelineEvent is one correlation entry on the unified axis.
type TopoImportStats ¶
type TopoImportStats struct {
Total int `json:"total"`
Managed int `json:"managed"`
NewNodes int `json:"new_nodes"`
UnresolvedRole int `json:"unresolved_role"`
Pages int `json:"pages"`
UsedPage string `json:"used_page"`
}
TopoImportStats summarizes one preview.
type TopoReconcile ¶
type TopoReconcile struct {
Tri TriSourceAgg `json:"tri"`
Platforms map[string]int `json:"platforms"` // edge source → count (lldp/cdp/design/bastion)
}
TopoReconcile is the topology tab's reconciliation card: the offline design↔plan diff plus neighbor-platform coverage (edges by source).
func BuildTopoReconcile ¶
func BuildTopoReconcile(cfg *config.Config) *TopoReconcile
BuildTopoReconcile is pure over the two offline stores (design import + IP-plan inference); zero probes — a view, never a scan.
type TopologyDesign ¶
type TopologyDesign struct {
ImportedAt string `json:"imported_at"`
SourceFile string `json:"source_file"`
Graph TopologyGraph `json:"graph"`
}
TopologyDesign is the persisted third source (plan | design | snapshot).
func LoadTopologyDesign ¶
func LoadTopologyDesign() (*TopologyDesign, error)
LoadTopologyDesign returns the stored design, or nil when none exists.
type TopologyEdge ¶
type TopologyEdge struct {
LocalDevice string `json:"local_device"`
LocalPort string `json:"local_port"`
RemoteDevice string `json:"remote_device"`
RemotePort string `json:"remote_port,omitempty"`
RemoteIP string `json:"remote_ip,omitempty"`
Source string `json:"source"` // lldp | cdp
// Platform is the neighbor's self-described platform/model (F2): CDP
// "Platform:" line or LLDP "System description" — vendor/model words that
// feed role inference and the 待确认区 hints. Free text from the wire,
// display-only.
Platform string `json:"platform,omitempty"`
}
TopologyEdge is one adjacency.
type TopologyGraph ¶
type TopologyGraph struct {
Nodes []TopologyNode `json:"nodes"`
Edges []TopologyEdge `json:"edges"`
At string `json:"at"`
}
TopologyGraph is the merged snapshot for the layout's mini-map.
func InferTopology ¶
func InferTopology(cfg *config.Config) TopologyGraph
iptopo.go — the LOCAL topology view (the user's rendering doctrine): the program computes the network's architecture itself, mostly from the intranet IP plan, with ZERO device sessions and ZERO model calls. The map shows on click; real link data (LLDP/CDP) only arrives when the user explicitly asks for the measured sweep — the two never mix silently.
Inference order per device (first hit wins):
- the user's own group assignment (核心/汇聚/接入… — their words are the ground truth, the view is just another surface for it);
- name conventions (CORE/AGG/ACC/FW prefixes common in IP plans);
- subnet ordering: with nothing else to go on, devices in the numerically lowest /24 read as closer to the core (10.0.0.x under 10.0.2.x in a planned network) — marked inferred, correctable by assigning a group.
Links are NEVER fabricated from IPs: two devices sharing a management subnet proves nothing about their physical wiring. The plan view draws structure (bands + subnet clusters); edges exist only from real neighbor data or an explicit bastion (via) chain.
func TopologyFused ¶
func TopologyFused(cfg *config.Config) TopologyGraph
TopologyFused is the zero-session graph the attack-path sim and the exposure board share: IP-plan inference + persisted design import merged.
type TopologyNode ¶
type TopologyNode struct {
Name string `json:"name"`
Managed bool `json:"managed"`
DeviceIP string `json:"device_ip,omitempty"`
// Subnet annotates the local IP-plan view (the /24 the address lives in).
Subnet string `json:"subnet,omitempty"`
// Tier is the LOCAL inference's band (0 core / 1 agg / 2 access / 3
// unmanaged) from group words, name conventions, or subnet ordering —
// present only in the IP-plan view; the LLDP snapshot leaves it at -1
// and the map falls back to its degree heuristic.
Tier int `json:"tier"`
// Role is the device-class icon key (router/switch/firewall/ips/vpn/
// bastion/server/ap/cloud; "" = unknown), ORTHOGONAL to tier: tier picks
// the band, role picks the icon, health keeps stroke color, managed keeps
// solid/dashed. RoleSource records the provenance (config|kind|group|
// model|vendor|label|none) for the tooltip's confidence hint.
Role string `json:"role,omitempty"`
RoleSource string `json:"role_source,omitempty"`
}
TopologySnapshot runs the neighbor query on every inventory device (the sealed read path, redacted) and merges the edges. Nodes not in the inventory carry Unmanaged=true — visible, never connectable.
type TriSourceAgg ¶
type TriSourceAgg struct {
Design int `json:"design"`
Plan int `json:"plan"`
Matched int `json:"matched"`
OnlyDesign int `json:"only_design"`
OnlyPlan int `json:"only_plan"`
}
TriSourceAgg is the design↔plan reconciliation (图实一致性, honest counts).
type TriageReport ¶
type TriageSection ¶
Source Files
¶
- alert.go
- alertqueue.go
- apiseal.go
- apitools.go
- assess.go
- attackpath.go
- audit.go
- auditanchor.go
- backup.go
- banner.go
- baseline.go
- cases.go
- console.go
- console_other.go
- cutover.go
- cve.go
- dashboards.go
- dbquery.go
- dbquery_more.go
- dbtunnel.go
- discover.go
- discovered.go
- discoveryrun.go
- dockerapi.go
- dockerpipe_other.go
- escalate.go
- finding.go
- firewall.go
- golden.go
- handoff.go
- health.go
- healthwater.go
- httpfp.go
- humantty.go
- inspect.go
- iptopo.go
- job.go
- journal.go
- kubeapi.go
- layerdiscover.go
- live.go
- locate.go
- logfollow.go
- logsearch.go
- logsource.go
- metrics.go
- netconf.go
- netprobeorch.go
- nmaporch.go
- notify.go
- probelog.go
- proposal.go
- proposal_steps.go
- redact.go
- redfish.go
- remotepeer.go
- remotetool.go
- report.go
- role.go
- scanimport.go
- secrets.go
- selfexport.go
- selfimport.go
- series.go
- session.go
- sftp.go
- snmp.go
- snmpfp.go
- srvconf.go
- statehist.go
- syslogrecv.go
- template.go
- testconn.go
- timeline.go
- tools.go
- topoimport.go
- topology.go
- topovsdx.go
- traprecv.go
- triage.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package driver holds the per-vendor network-device CLI knowledge: how to classify a command (the structural read-only seal), how to disable paging, what the prompt looks like, and what error output looks like.
|
Package driver holds the per-vendor network-device CLI knowledge: how to classify a command (the structural read-only seal), how to disable paging, what the prompt looks like, and what error output looks like. |
|
Package transport is the netdev SSH connection layer: host resolution (configured entries + ~/.ssh/config layering), authentication, host-key verification (system known_hosts read-only + a netdev-managed TOFU file), and a supervised connection with keepalive and exponential-backoff reconnect.
|
Package transport is the netdev SSH connection layer: host resolution (configured entries + ~/.ssh/config layering), authentication, host-key verification (system known_hosts read-only + a netdev-managed TOFU file), and a supervised connection with keepalive and exponential-backoff reconnect. |