Docs GitHub
-- --
Theme:
Language:
EN فا

Architecture#

bgscan is layered. main initializes the theme and hands control to a BubbleTea TUI. The TUI runs a three-stage experience — splash, startup, workspace — where the startup stage initializes loggers, registers result schemas, loads and validates config, and runs binary health checks. Once startup passes, the workspace drives a multi-stage scanner engine whose probes and writers are pluggable.

Directory layout#

.
├── assets
│   ├── dns-tunneling
│   │   ├── dnstt
│   │   ├── vaydns
│   │   └── slipstream
│   ├── xray
│   │   ├── configs
│   │   └── outbounds
│   └── slipstream-client
├── cmd
│   └── bgscan
│       └── main.go
├── docs
├── internal
│   ├── core
│   │   ├── core.go          schema registration
│   │   ├── config
│   │   │   └── validate
│   │   ├── dns
│   │   ├── fileutil
│   │   ├── iplist
│   │   ├── netutil
│   │   ├── process
│   │   ├── result
│   │   ├── scanner
│   │   │   ├── engine
│   │   │   ├── portmgr
│   │   │   └── probe
│   │   │       ├── dnsttprobe
│   │   │       ├── httpprobe
│   │   │       ├── icmpprobe
│   │   │       ├── resolveprobe
│   │   │       ├── slipstreamprobe
│   │   │       ├── tcpprobe
│   │   │       ├── vaydnsprobe
│   │   │       └── xrayprobe
│   │   ├── socks
│   │   ├── ssh
│   │   ├── speedtest
│   │   └── xray
│   ├── logger
│   └── ui
│       ├── components
│       ├── main
│       │   ├── app
│       │   ├── body
│       │   ├── footer
│       │   ├── header
│       │   ├── splash
│       │   ├── startup
│       │   └── workspace
│       ├── shared
│       └── theme
├── ips
├── scripts
└── settings

Layer overview#

┌─────────────────────────────────────────────┐
│                 cmd/bgscan                   │
│   theme.Init → app.New → tea.Run             │
├─────────────────────────────────────────────┤
│                internal/ui                   │
│   main                                       │
│     splash  startup  workspace               │
│     app (root model, stage transitions)      │
│     body, header, footer                     │
│   components (menus, tables, inspector)      │
│   shared (layout, env, dialog, ui, valid.)   │
│   theme                                      │
│                                              │
│   ui/main/startup performs (inside the TUI): │
│     logger, core.Init, config, binary checks │
├─────────────────────────────────────────────┤
│               internal/core                  │
│  ┌──────────┐ ┌────────┐ ┌───────────────┐  │
│  │ scanner  │ │ config │ │    result     │  │
│  │ engine   │ │ Store  │ │ writer+schema │  │
│  │ probe    │ │validate│ │   registry    │  │
│  │ portmgr  │ └────────┘ └───────────────┘  │
│  └──────────┘                                │
│  ┌────────┐ ┌─────┐ ┌──────┐ ┌───────────┐  │
│  │ iplist │ │ dns │ │ xray │ │ speedtest │  │
│  └────────┘ └─────┘ └──────┘ └───────────┘  │
│  ┌─────────┐ ┌──────────┐ ┌─────────────┐   │
│  │ netutil │ │ process  │ │  fileutil   │   │
│  └─────────┘ └──────────┘ └─────────────┘   │
├─────────────────────────────────────────────┤
│              internal/logger                 │
├─────────────────────────────────────────────┤
│          assets/  ips/  settings/           │
└─────────────────────────────────────────────┘

Directory reference#

PathDescription
cmd/bgscanEntry point. Initializes the theme, constructs the app, and starts the BubbleTea program.
internal/core/core.gocore.Init() registers every built-in probe schema into result.DefaultRegistry. Called from the startup stage.
internal/core/configScannerConfig types, compiled-in defaults, and the Store that reads and writes settings/*.toml.
internal/core/config/validatePer-section validators and normalizers, combined by aggregate.go.
internal/core/scannerScanner interface, StageConfig, and the stage builders.
internal/core/scanner/enginePipeline execution: single scan, sequential, streaming, batch, pause control.
internal/core/scanner/probeProbe interface plus one subpackage per probe.
internal/core/scanner/portmgrLocal port leasing for probes that spawn client binaries.
internal/core/resultResult interface, schemas, registry, async writer, CSV merge, loader.
internal/core/iplistIP list import, parsing, registry, shuffle, streaming.
internal/core/netutilHost normalization, TLS version parsing, and SNI extraction for the HTTP probes.
internal/core/dnsDNS queries, transports, DNSTT/VayDNS/Slipstream config services, and tunnel management.
internal/core/socksSOCKS5 client for tunnel validation.
internal/core/sshSSH client for tunneled connections through SSH proxies.
internal/core/xrayXray process control, inbound and outbound config, share link parsing.
internal/core/speedtestLatency, download, and upload measurement used by the Xray probe.
internal/core/processCross-platform process spawn and kill.
internal/core/fileutilCSV, JSON, TOML, text, temp-file, sorting, and path helpers.
internal/loggerThree leveled log streams with lumberjack rotation and live subscribers.
internal/ui/main/appRoot BubbleTea model. Owns the stage machine: splash → startup → workspace.
internal/ui/main/splashAnimated splash screen, runs first.
internal/ui/main/startupSequential health checks (Logger, Config, Xray, DNSTT, Slipstream, Vaydns, App) shown to the user with a live status sidebar.
internal/ui/main/workspaceMain workspace shell (header, body, footer) after startup passes.
internal/ui/main/bodyStack of body screens (main menu, scan, settings, logs) inside the workspace.
internal/ui/main/header / footerWorkspace chrome.
internal/ui/componentsWidgets, settings inspectors, menus, tables, and the live scanner view.
internal/ui/sharedLayout geometry, dialog system, key modes, component interface, validation.
internal/ui/themeDark and light palettes plus the huh form theme adapter.
assets/xrayBundled Xray binary location, configs, and outbound templates.
assets/dnstt-client, assets/slipstream-clientOptional tunnel client binaries.
assets/dns-tunnelingDNS tunnel config files (DNSTT, VayDNS, Slipstream).
ipsBundled provider IP lists as CSV.
settingsLive .toml settings.
scriptsInstall, build, and release helpers.

Application flow#

main()
  │
  ├─ theme.Init()                   resolve dark / light palette
  │
  ├─ config.AppVersion = Version    recorded for the result files
  │
  ├─ app := app.New()               splash + startup + workspace components
  ├─ p := tea.NewProgram(app)       hand off to BubbleTea
  │
  └─ inside the TUI:
        │
        ├─ StageSplash              animated ASCII logo + version
        │
        ├─ StageStartup             ui/main/startup runs each check
        │     │                     in a goroutine, reports status live
        │     │
        │     ├─ Logger             init loggers, then core.Init()
        │     │                     to register probe schemas
        │     ├─ Config             store.Load(), validate.NormalizeAll,
        │     │                     report any clamped values
        │     ├─ Xray               locate binary, check version
        │     ├─ DNSTT              validate config files
        │     ├─ Slipstream         find binary, verify, validate configs
        │     ├─ Vaydns             validate config files
        │     └─ App                wait for Enter
        │
        └─ StageWorkspace           main workspace:
              ├─ header
              ├─ body               component stack: main menu → scan / settings / logs
              └─ footer

on Run Scan:
  ├─ scanner.NewScanner(ctx, input)
  ├─ AddStage(BuildICMPStage) ...
  ├─ Run()
  │     ├─ one stage   → engine.RunScan
  │     └─ many stages → engine.RunScanWithChain
  │                        sequential | streaming | batch
  └─ result.Writer → CSV merge → disk

Key design principles#

Config is passed, not global. There is no config singleton. The startup stage constructs a config.Store, calls store.Load(), and puts the resulting *ScannerConfig and *Store on ui.AppState for components and the scanner to read. Tests construct a Store with WithSettingsDir against a temp directory, so nothing touches real settings.

Invalid config self-heals. validate.NormalizeAll clamps out-of-range fields to defaults and reports each correction in the startup sidebar. A bad hand edit degrades to a working default instead of failing the run — corrected values live in memory until the user saves a section through the settings inspector.

Probes are pluggable. probe.Probe is the only contract: Init, Run, Schema, Close. Run takes a netip.Addr, which is what gives IPv4 and IPv6 one code path.

Results are self-describing. Each probe ships a result.ResultSchema naming its output directory, columns, and parser. Registering it in core.Init() is what makes the writer, the file browser, and the result table understand a new probe. No shared result struct to extend.

The engine is protocol-agnostic. It moves addresses and results, and never inspects what a probe measured. Pipeline mode is a config choice, not an engine rewrite.

External binaries are optional. ICMP, TCP, HTTP, and DNS resolver probes need only the Go standard library and golang.org/x/net. Xray, DNSTT, and Slipstream are checked at startup, and a missing binary disables just that scan type.

The UI is a component tree. Every screen implements ui.Component. Overlays stack, and the top one consumes all input.

  • Core — engine, probes, config, and the result pipeline in detail
  • UI — component model, layout, and theming
  • Getting Started — build and run